From f6f3097112b9881253b521d508135359b4ec903d Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Thu, 24 Apr 2025 19:00:14 +0800 Subject: [PATCH] =?UTF-8?q?Commit=20on=202025/04/24=20=E5=91=A8=E5=9B=9B?= =?UTF-8?q?=2019:00:14.19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 科研/Mesa仿真.md | 35 ++++++ 科研/草稿.md | 67 +++++++----- 科研/陈茂森论文.md | 87 +++++++++++++-- 自学/Redis.md | 14 +++ 自学/linux服务器.md | 14 ++- 自学/力扣Hot 100题.md | 49 ++++++++- 自学/苍穹外卖.md | 245 ++++++++++++++++++++++++++++-------------- 7 files changed, 387 insertions(+), 124 deletions(-) create mode 100644 科研/Mesa仿真.md diff --git a/科研/Mesa仿真.md b/科研/Mesa仿真.md new file mode 100644 index 0000000..9f73306 --- /dev/null +++ b/科研/Mesa仿真.md @@ -0,0 +1,35 @@ +# Mesa仿真 + +## 配置环境 + +**requirements.txt** + +```text +mesa[rec] # 包含 networkx、matplotlib、ipywidgets、solara 等推荐依赖 +jupyterlab +numpy +pandas +``` + +**Conda 命令行** + +```bash +# 1) 添加 conda-forge 通道并设为最高优先级 +conda config --add channels conda-forge +conda config --set channel_priority strict + +# 2) 创建并激活新环境(这里以 python 3.11 为例) +conda create -n mesa-env python=3.11 -y +conda activate mesa-env + +# 3a) 通过 pip 安装(使用上面的 requirements.txt) +pip install -r requirements.txt + +# 或者 3b) 纯 Conda 安装等价包(推荐所有包都从 conda-forge) +conda install \ + mesa=3.1.5 networkx matplotlib ipywidgets solara \ + numpy pandas jupyterlab \ + -c conda-forge + +``` + diff --git a/科研/草稿.md b/科研/草稿.md index 8b6b46d..b7628be 100644 --- a/科研/草稿.md +++ b/科研/草稿.md @@ -1,34 +1,47 @@ -您发现了符号不一致的问题!确实,这里需要统一符号表示。根据论文和您之前的笔记,正确的对应关系如下: +分布式计算和集中式计算是完全等价的。将分布式算法中的本地观测 +$$ +b_i(k) = \sum_j a_{ij} x_j(k) +$$ +代入瑞利商公式 +$$ +y(k) = \frac{\sum_i x_i(k) b_i(k)}{\sum_i x_i(k)^2} +$$ +即可得到集中式计算的Rayleigh商形式 +$$ +\frac{x(k)^T (A x(k))}{x(k)^T x(k)}, +$$ +这与特征值估计的幂迭代公式 +$$ +\lambda_{\max} \approx \frac{x^T A x}{x^T x} +$$ +完全一致。 --- -### **修正后的公式** -1. **瑞利商计算**(对应论文式4-51): - - 所有节点交换的是**本地滤波结果** $\hat{b}_{i,k|k}$(即UKF更新后的状态估计值),而非中间变量$b_i(k)$。 - - 正确的全局状态计算应为: - $$ - y(k) = \frac{\sum_{i=1}^N x_i(k) \hat{b}_{i,k|k}}{\sum_{i=1}^N x_i^2(k)} - $$ - - **物理意义**:$x_i(k)$是节点$i$的状态分量(用于正交化),$\hat{b}_{i,k|k}$是UKF更新的本地估计值。 +### 代数示例 -2. **正交化**(对应论文式4-52): - - 更新的是**状态分量** $x_i(k+1)$,其归一化分母应为所有节点的$\hat{b}_{i,k|k}$的2-范数: - $$ - x_i(k+1) = \frac{\hat{b}_{i,k|k}}{\|\hat{b}(k)\|_2}, \quad \text{其中} \quad \|\hat{b}(k)\|_2 = \sqrt{\sum_{i=1}^N \hat{b}_{i,k|k}^2} - $$ +考虑一个简单的2×2矩阵 +$$ +A = \begin{pmatrix}2 & 1\\1 & 2\end{pmatrix}, \quad x = \begin{pmatrix}2\\1\end{pmatrix}. +$$ ---- +1. **集中式计算** +$$ +\lambda \approx \frac{x^T A x}{x^T x} += \frac{\begin{pmatrix}2 & 1\end{pmatrix} \begin{pmatrix}5\\4\end{pmatrix}}{2^2 + 1^2} += \frac{10 + 4}{5} += \frac{14}{5} = 2.8. +$$ -### **符号一致性说明** -- **$\hat{b}_{i,k|k}$**:节点$i$在时刻$k$的UKF后验估计值(即滤波结果)。 -- **$x_i(k)$**:节点$i$的状态分量(用于分布式正交化,与$\hat{b}_{i,k|k}$同步更新)。 -- **$b_i(k)$**:在论文中可能被用作中间变量(如式4-40的幂迭代状态),但最终滤波输出统一为$\hat{b}_{i,k|k}$。 +2. **分布式计算** +各节点分别计算本地观测值 +$$ +b_1 = (2 \cdot 2 + 1 \cdot 1) = 5, \quad b_2 = (1 \cdot 2 + 2 \cdot 1) = 4, +$$ +然后通过全网共识计算 +$$ +y = \frac{2 \cdot 5 + 1 \cdot 4}{2^2 + 1^2} += \frac{14}{5} = 2.8. +$$ ---- - -### **修正后的流程** -1. **UKF更新**:每个节点计算$\hat{b}_{i,k|k}$(原笔记Step 4输出)。 -2. **一致性协议**:节点交换$\hat{b}_{i,k|k}$,计算瑞利商$y(k)$。 -3. **正交化**:用$\hat{b}_{i,k|k}$更新$x_i(k+1)$,确保状态分量正交性。 - -这样既符合论文的分布式滤波逻辑,又保持了符号一致性。是否需要进一步解释某一步骤? \ No newline at end of file +两种方法得到的结果完全相同。 \ No newline at end of file diff --git a/科研/陈茂森论文.md b/科研/陈茂森论文.md index 90849bd..46530d6 100644 --- a/科研/陈茂森论文.md +++ b/科研/陈茂森论文.md @@ -17,7 +17,7 @@ $$ 在所有概率分布里,只有指数分布 $$ - P(T>t) = e^{-\lambda t} +P(T>t) = e^{-\lambda t} $$ 具有这种“无记忆性”特征: @@ -43,7 +43,7 @@ $$ - 从断开(0)到连通(1)的等待时间 $T_{01} \sim \text{Exp}(\lambda_{01})$ - 从连通(1)到断开(0)的等待时间 $T_{10} \sim \text{Exp}(\lambda_{10})$ -其中,$\lambda_{01}$ 和 $\lambda_{10}$ 为转移速率,表示单位时间内事件(转移)发生的**平均次数** +其中,**$\lambda_{01}$ 和 $\lambda_{10}$ 为转移速率**,表示单位时间内事件(转移)发生的**平均次数** #### **2.推导单条链路的连通概率** @@ -302,6 +302,8 @@ $$ ## 网络特征谱参数的估算 +由于邻接矩阵不能保证半正定性,因此会产生幂迭代估算过程不能收敛的问题。需构造$A^T A$ + ### 基于奇异值分解改进幂迭代估算(集中式) **输入**:矩阵 $B = A^T A$,目标特征值数量 $k$,收敛阈值 $\delta$ @@ -359,6 +361,63 @@ $$ +### 瑞利商公式 + +1. 集中式: + + $$ + y(k)= \frac{x(k)^T A x(k)}{x(k)^T x(k)} + $$ + +2. 分布式一致性计算: + + $$ + y(k) = \frac{\sum_{i=1}^N x_i(k) b_i(k)}{\sum_{i=1}^N x_i^2(k)} + $$ + 其中 + $$ + b_i(k) = \sum_j a_{ij} x_j(k) + $$ + + +**两者是等价的:** + +考虑一个简单的2×2矩阵 +$$ +A = \begin{pmatrix}2 & 1\\1 & 2\end{pmatrix}, \quad x = \begin{pmatrix}2\\1\end{pmatrix}. +$$ + +1. **集中式计算** + +$$ +y= \frac{x^T A x}{x^T x} += \frac{\begin{pmatrix}2 & 1\end{pmatrix} \begin{pmatrix}5\\4\end{pmatrix}}{2^2 + 1^2} += \frac{10 + 4}{5} += \frac{14}{5} = 2.8. +$$ + +2. **分布式计算** + 各节点分别计算本地观测值 + +​ 节点1的计算: +$$ +b_1 = a_{11}x_1 + a_{12}x_2 = 2 \cdot 2 + 1 \cdot 1 = 5. +$$ + +​ 节点2的计算: +$$ +b_2 = a_{21}x_1 + a_{22}x_2 = 1 \cdot 2 + 2 \cdot 1 = 4. +$$ +​ 然后通过全网共识计算 +$$ +y = \frac{2 \cdot 5 + 1 \cdot 4}{2^2 + 1^2} += \frac{14}{5} = 2.8. +$$ + + + + + ### 主要符号表 | 符号 | 类型 | 含义 | 存储/计算位置 | @@ -402,7 +461,7 @@ $$ - **Repeat**: a. **第一轮通信(计算$z=Av$)**: $$ - z_j^{(t)} = \sum_{k \in 𝒩_j} a_{jk} v_{n,k}^{(t)} \quad \text{(邻居交换$v_{n,k}^{(t)}$)} + z_j^{(t)} = \sum_{k \in 𝒩_j} a_{jk} v_{n,k}^{(t)} \quad \text{(邻居交换$v_{n,k}^{(t)}$)} $$ b. **第二轮通信(计算$y=A^T z$)**: $$ @@ -422,7 +481,7 @@ $$ $$ f. **终止条件**: $$ - \text{If } \frac{|\lambda^{(t)} - \lambda^{(t-1)}|}{|\lambda^{(t)}|} < \delta \text{ then break} + \text{If } \frac{|\lambda^{(t)} - \lambda^{(t-1)}|}{|\lambda^{(t)}|} < \delta \text{ then break} $$ 3. **保存结果**: @@ -434,6 +493,8 @@ $$ + + ### 分布式计算左奇异向量$u_{n,j}$ 对于邻接矩阵 $A \in \mathbb{R}^{N \times N}$,其奇异值分解为: @@ -595,8 +656,8 @@ $$ #### **符号说明** - **$i$**: 节点索引,$N$ 为总节点数 -- **$x_i(k)$**: 节点 $i$ 在时刻 $k$ 的状态分量 (特征向量$x$) -- **$b_i(k)$**: 节点 $i$ 的本地状态估计值 ($A^TAx$) +- **$x_i(k)$**: 节点 $i$ 在时刻 $k$ 的状态分量 ($x$) +- **$b_i(k)$**: 节点 $i$ 的本地状态估计值 (相当于$Ax$) - **$a_{ij}$**: 邻接矩阵元素(链路权重) - **$Q_k, R_k$**: 过程噪声与观测噪声协方差 - **$\mathcal{X}_{i,j}$**: 节点 $i$ 的第 $j$ 个 Sigma 点 @@ -666,12 +727,16 @@ $$ 1. **邻居状态融合**: - - 节点 $i$ 从邻居 $j$ 获取状态 $x_j(k)$,生成观测值: + - 节点 $ i $ 从邻居 $ j $ 获取其本地观测值 $ b_{j,\text{local}}(k) $: + + $$ + b_{j,\text{local}}(k) = \sum_{l=1}^N a_{jl} x_l(k) \quad \text{(节点 $ j $ 对邻居状态的加权融合)} + $$ + - 节点 $ i $ 综合邻居信息生成自身观测: $$ - b_i^H(k) = \sum_{j=1}^N a_{ji} b_{0,j}(k) + r_k, \quad b_{0,j}(k) = \sum_{j=1}^N a_{ij} x_j(k) + b_i^H(k) = \sum_{j=1}^N a_{ji} b_{j,\text{local}}(k) + r_k $$ - - - **$r_k$** 为观测噪声 ,来自分布式通信中的信息传输误差(如延时、丢包) + - **注**:$ r_k $ 为通信噪声,反映信息传输误差(如延时、丢包)。 --- @@ -725,7 +790,7 @@ $$ 2. **正交化**: - - 更新本地状态分量: + - 更新本地状态分量(相当于幂迭代$x=Ax$再归一化): $$ x_i(k+1) = \frac{\hat{b}_{i,k|k}}{\|\hat{b}(k)\|_2} $$ diff --git a/自学/Redis.md b/自学/Redis.md index f3cec48..728f9c0 100644 --- a/自学/Redis.md +++ b/自学/Redis.md @@ -358,6 +358,20 @@ public class SpringDataRedisTest { } ``` +**对于非String类型:** + +**写入时序列化** + +- 调用 `opsForValue().set(key, value)` 时,`value`(比如 `List`)会先走一遍 **ValueSerializer**。 +- 默认是 `JdkSerializationRedisSerializer`,它用 Java 的 `ObjectOutputStream` 把整个对象图打包成一个 byte[]。 +- 这个 byte[] 直接就存成了 Redis 的 String 值。 + +**读取时反序列化** + +- 调用 `opsForValue().get(key)` 时,RedisTemplate 拿回那段 byte[],再用同一个 JDK 序列化器(`ObjectInputStream`)把它变回 `List`。 + + + **哈希测试** ```java diff --git a/自学/linux服务器.md b/自学/linux服务器.md index 8f21f45..5aecd24 100644 --- a/自学/linux服务器.md +++ b/自学/linux服务器.md @@ -820,10 +820,20 @@ curl -v -X POST "https://yourdomain/api/resources/${ENCODED_PATH}?override=true" --data-binary "@${FILE_PATH}" ``` -**拼接下载url:** +**获取分享链接url:** ```text -String downloaded_url=yourdomain + "/api/raw/" + ${ENCODED_PATH}; +# 假设已经执行过 /api/login 并把 JWT 保存在环境变量 TOKEN +# 同样复用之前算好的 ENCODED_PATH 和 REMOTE_PATH + +# 调用分享接口(注意:POST,body 为空 JSON "{}") +curl -s -X POST "https://yourdomain/api/share/${ENCODED_PATH}" \ + -H "X-Auth: ${TOKEN}" \ + -H "Cookie: auth=${TOKEN}" \ + -H "Content-Type: text/plain;charset=UTF-8" \ + -d '{}' \ +| jq -r '.hash' \ +| xargs -I{} printf "https://yourdomain/api/public/dl/%s/%s\n" {} "${REMOTE_PATH}" ``` diff --git a/自学/力扣Hot 100题.md b/自学/力扣Hot 100题.md index c7fe662..67791da 100644 --- a/自学/力扣Hot 100题.md +++ b/自学/力扣Hot 100题.md @@ -80,6 +80,45 @@ public static int compare(int x, int y) { +### 位运算 + +按位与 `&`:只有两个对应位都为 1 时,结果位才为 1。 + +```java +int a = 5; // 0101₂ +int b = 3; // 0011₂ +int c = a & b; // 0001₂ = 1 +System.out.println(c); // 输出 1 +``` + +按位或 `|`: 只要两个对应位有一个为 1,结果位就为 1。 + +```java +int a = 5; // 0101₂ +int b = 3; // 0011₂ +int c = a | b; // 0111₂ = 7 +System.out.println(c); // 输出 7 +``` + + 按位异或 `^`: 两个对应位不同则为 1,相同则为 0。 + +```java +int a = 5; // 0101₂ +int b = 3; // 0011₂ +int c = a ^ b; // 0110₂ = 6 +System.out.println(c); // 输出 6 +``` + +左移 `<<`: 整体二进制左移 N 位,右侧补 0;相当于乘以 2ⁿ。 + +```java +int a = 3; // 0011₂ +int b = a << 2; // 1100₂ = 12 +System.out.println(b); // 输出 12 +``` + + + ## 常用数据结构 ### String @@ -1097,6 +1136,10 @@ public class ArraySortExample { } ``` +`Arrays.sort(nums, i + 1, n);` 等价于把 `nums[i+1]` 到 `nums[n-1]` 这段做升序排序。 + + + 自定义降序: @@ -1110,9 +1153,11 @@ public class DescendingSortExample { Integer[] arr = {5, 2, 9, 1, 5, 6}; // 使用Comparator进行降序排序(使用lambda表达式) - Arrays.sort(arr, (a, b) -> b - a); + Arrays.sort(arr, (a, b) -> Integer.compare(b, a)); // 或者使用Collections.reverseOrder()也可以: - // Arrays.sort(arr, Collections.reverseOrder()); + + // 对下标 [1, 4) 的区间,也就是 {2,9,1},按降序排序 + Arrays.sort(arr, 1, 4, Collections.reverseOrder()); // 输出排序后的数组 System.out.println(Arrays.toString(arr)); diff --git a/自学/苍穹外卖.md b/自学/苍穹外卖.md index 03c0ca0..b0a120e 100644 --- a/自学/苍穹外卖.md +++ b/自学/苍穹外卖.md @@ -708,46 +708,38 @@ public class FileBrowserUtil { /** * —— 第一步:登录拿 token —— - * 调用 /api/login 接口,返回原始的 JWT token 字符串 - * curl -X POST "https://fshare.bitday.top/api/login" \ - * -H "Content-Type: application/json" \ - * -d '{ - * "username":"admin", - * "password":"asdf14789" - * }' - * 返回值: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9… + * 调用 /api/login 接口,返回纯 JWT 字符串,或 {"token":"..."} 结构 */ - public String login() throws IOException { String url = domain + "/api/login"; - // 创建 HttpClient 实例 - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - // 构造 POST 请求 - HttpPost httpPost = new HttpPost(url); - httpPost.setHeader("Content-Type", "application/json"); + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpPost post = new HttpPost(url); + post.setHeader("Content-Type", "application/json"); - // 构造 JSON body - JSONObject json = new JSONObject(); - json.put("username", username); - json.put("password", password); + // 构造登录参数 + JSONObject cred = new JSONObject(); + cred.put("username", username); + cred.put("password", password); + post.setEntity(new StringEntity(cred.toString(), StandardCharsets.UTF_8)); - // 设置请求体 - StringEntity requestEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8); - requestEntity.setContentType("application/json"); - httpPost.setEntity(requestEntity); + try (CloseableHttpResponse resp = client.execute(post)) { + int status = resp.getStatusLine().getStatusCode(); + String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8).trim(); - // 发送请求并处理响应 - try (CloseableHttpResponse response = httpClient.execute(httpPost)) { - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode >= 200 && statusCode < 300) { - HttpEntity respEntity = response.getEntity(); - String body = EntityUtils.toString(respEntity, StandardCharsets.UTF_8); - log.info("token:{}",body); - // 返回原始返回值(假设就是 JWT token 字符串) + if (status >= 200 && status < 300) { + // 如果返回 JSON 对象,则解析出 token 字段 + if (body.startsWith("{") && body.endsWith("}")) { + JSONObject obj = JSONObject.parseObject(body); + String token = obj.getString("token"); + log.info("登录成功,token={}", token); + return token; + } + // 否则直接当成原始 JWT 返回 + log.info("登录成功,token={}", body); return body; } else { - log.error("Login failed, HTTP status code: " + statusCode); - return ""; + log.error("Login failed: HTTP {} - {}", status, body); + throw new IOException("Login failed: HTTP " + status); } } } @@ -755,50 +747,98 @@ public class FileBrowserUtil { /** * —— 第二步:上传文件 —— - + * curl -v -X POST \ - + * "$DOMAIN/api/resources/$REMOTE_PATH?override=true" \ // 服务器上相对路径 - + * -H "X-Auth: $TOKEN" \ - + * -H "Content-Type: image/jpeg" \ // 根据文件类型替换 - + * --data-binary "@/path/to/local/photo.jpg" // 以 raw body 方式上传 + * POST {domain}/api/resources/{encodedPath}?override=true + * Header: X-Auth: token */ - - public String uploadAndGetUrl(byte[] fileBytes, String fileName) throws IOException { - // 1. 登录拿 token + public String uploadFile(byte[] fileBytes, String fileName) throws IOException { String token = login(); - - // 2. 确定远端存储路径和编码(保留斜杠) String remotePath = "store/" + fileName; - String encodedPath = URLEncoder.encode(remotePath, StandardCharsets.UTF_8.toString()) + String encodedPath = URLEncoder + .encode(remotePath, StandardCharsets.UTF_8) .replace("%2F", "/"); - // 3. 根据文件名猜 MIME 类型,fallback 到 application/octet-stream + // 根据后缀猜 MIME 类型 String mimeType = URLConnection.guessContentTypeFromName(fileName); if (mimeType == null) { mimeType = "application/octet-stream"; } - // 4. 构造上传 URL String uploadUrl = domain + "/api/resources/" + encodedPath + "?override=true"; - - // 5. 执行 Multipart upload try (CloseableHttpClient client = HttpClients.createDefault()) { HttpPost post = new HttpPost(uploadUrl); post.setHeader("X-Auth", token); post.setEntity(new ByteArrayEntity(fileBytes, ContentType.create(mimeType))); + try (CloseableHttpResponse resp = client.execute(post)) { int status = resp.getStatusLine().getStatusCode(); - String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); + String respBody = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8); if (status < 200 || status >= 300) { - log.error("文件上传失败: HTTP {},{}", status, body); - return ""; + log.error("文件上传失败: HTTP {} - {}", status, respBody); + throw new IOException("Upload failed: HTTP " + status); } + log.info("文件上传成功,remotePath={}", remotePath); + return remotePath; } } + } - // 6. 拼接 raw 下载链接并返回 - String downloadUrl = domain + "/api/raw/" + encodedPath; - log.info("文件下载链接:{}", downloadUrl); - return downloadUrl; + /** + * 第三步:生成公开分享链接 + * 模拟浏览器的 POST /api/share/{encodedPath} 请求,body 为 "{}" + */ + public String createShareLink(String remotePath) throws IOException { + String token = login(); + + // URL encode 并保留斜杠 + String encodedPath = URLEncoder + .encode(remotePath, StandardCharsets.UTF_8) + .replace("%2F", "/"); + + String shareUrl = domain + "/api/share/" + encodedPath; + log.info("准备创建分享链接,POST {}", shareUrl); + + try (CloseableHttpClient client = HttpClients.createDefault()) { + HttpPost post = new HttpPost(shareUrl); + post.setHeader("Cookie", "auth=" + token); + post.setHeader("X-Auth", token); + post.setHeader("Content-Type", "text/plain;charset=UTF-8"); + post.setEntity(new StringEntity("{}", StandardCharsets.UTF_8)); + + try (CloseableHttpResponse resp = client.execute(post)) { + int status = resp.getStatusLine().getStatusCode(); + String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8).trim(); + + if (status < 200 || status >= 300) { + log.error("创建分享失败 HTTP {} - {}", status, body); + throw new IOException("Share failed: HTTP " + status); + } + + // ========= 这里改为先检测是对象还是数组 ========= + JSONObject json; + if (body.startsWith("[")) { + // 如果真返回数组(老版本可能是 [{...}]) + json = JSONArray.parseArray(body).getJSONObject(0); + } else if (body.startsWith("{")) { + // 当前版本直接返回对象 + json = JSONObject.parseObject(body); + } else { + throw new IOException("Unexpected share response: " + body); + } + + String hash = json.getString("hash"); + String publicUrl = domain + "/api/public/dl/" + hash + "/" + remotePath; + log.info("创建分享链接成功:{}", publicUrl); + return publicUrl; + } + } + } + + /** + * —— 整合示例:上传并立即返回分享链接 —— + */ + public String uploadAndGetUrl(byte[] fileBytes, String fileName) throws IOException { + String remotePath = uploadFile(fileBytes, fileName); + return createShareLink(remotePath); } } ``` @@ -1407,7 +1447,9 @@ Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要 | @CachePut | 将方法的返回值**放**到缓存中 | | @CacheEvict | 将一条或多条数据从缓存中**删除** | -**@CachePut 说明:** + + +**1)@CachePut 说明:** ​ 作用: 将方法返回值,放入缓存 @@ -1421,46 +1463,50 @@ Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要 在Redis中并没有直接的“缓存名”概念,而是通过键(key)来访问数据。Spring Cache通过`cacheNames`属性来模拟不同的“缓存区”,实际上这是通过将这些名称作为键的一部分来实现的。例如,如果你有一个缓存名为 `userCache`,那么所有相关的缓存条目的键可能以 `"userCache::"` 开头。 - +**在save方法上加注解@CachePut** ```java - @PostMapping - @CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1 - public User save(@RequestBody User user){ - userMapper.insert(user); - return user; - } +@PostMapping +@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1 +public User save(@RequestBody User user){ + userMapper.insert(user); + return user; +} ``` **说明:**key的写法如下 -#user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ; +#user.id : #user指的是**方法形参**的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ; #result.id : #result代表方法返回值,该表达式 代表以返回对象的id属性作为key ; -**@Cacheable 说明:** +**2)@Cacheable 说明:** -​ 作用: 在方法执行前,spring先查看缓存中是否有**指定的key的**数据,如果有数据,则直接返回缓存数据,不执行后续sql操作;若没有数据,调用方法并将方法返回值放到缓存中。 +作用: 在方法执行前,spring先查看缓存中是否有**指定的key的**数据,如果有数据,则直接返回缓存数据,不执行后续sql操作;若没有数据,调用方法并将方法返回值放到缓存中。 -所以,@Cacheable(cacheNames = "userCache",key="#id")中的#id表示的是函数形参中的id,而不能是返回值中的user.id +**在getById上加注解@Cacheable** ```java @GetMapping - @Cacheable(cacheNames = "userCache",key="#id") - public User getById(Long id){ - User user = userMapper.getById(id); - return user; - } +@Cacheable(cacheNames = "userCache",key="#id") +public User getById(Long id){ + User user = userMapper.getById(id); + return user; +} ``` -**@CacheEvict 说明:** -​ 作用: 清理指定缓存 + +**3)@CacheEvict 说明:** + +作用: 清理指定缓存 + +**在 delete 方法上加注解@CacheEvict** ```java -@DeleteMapping + @DeleteMapping @CacheEvict(cacheNames = "userCache",key = "#id")//删除某个key对应的缓存数据 public void deleteById(Long id){ userMapper.deleteById(id); @@ -1475,23 +1521,52 @@ Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要 -## 微信支付 +**总结:**新增数据的时候->添加缓存@CachePut ; -小程序支付 +查询的时候->判断有无缓存@Cacheable; -https://pay.weixin.qq.com/static/product/product_index.shtml +删除的时候->删除缓存@CacheEvict。 -![image-20221214223910840](https://pic.bitday.top/i/2025/03/19/u7z7u7-2.png) +Spring Cache是经典缓存的上位替代!!! + +注意,如果缓存的是套餐分类,即一个套餐分类中含有多个套餐,那么在新增套餐的时候,需要清除相应的套餐分类缓存,因为当你新增一个属于分类 5 的套餐时,原来缓存里那份「分类 5 的列表」已经不再完整──它少了新加的那个套餐。 + +但是如果缓存的就是套餐本身,新增套餐的时候就可以直接缓存套餐。不要混淆两者! -**5.商户系统调用微信后台:** +## 下单支付 + +**下单** + +image-20221214200913654image-20221214200959943 + +| 表名 | 含义 | 说明 | +| ------------ | ---------- | ------------------------------------------------------------ | +| orders | 订单表 | 主要存储订单的基本信息(如: 订单号、状态、金额、支付方式、下单用户、收件地址等) | +| order_detail | 订单明细表 | 主要存储订单详情信息(如: 该订单关联的套餐及菜品的信息) | + + + +### 微信支付 + +官方文档:https://pay.weixin.qq.com/static/product/product_index.shtml + +image-20221214223910840 + + + +**商户系统调用微信后台:** + +**JSAPI下单:**商户系统调用该接口在微信支付服务后台生成预支付交易单(对应时序图的第5步) ![image-20221214224409174](https://pic.bitday.top/i/2025/03/19/u7zxts-2.png) -**10.用户调起微信支付** +**微信小程序调起支付:** + +通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付(对应时序图的第10步) ![image-20221214224551220](https://pic.bitday.top/i/2025/03/19/u7zlem-2.png) @@ -1507,8 +1582,6 @@ https://pay.weixin.qq.com/static/product/product_index.shtml 1)下载地址:https://dashboard.cpolar.com/get-started - - **2). cpolar指定authtoken** 复制authtoken: @@ -1537,6 +1610,14 @@ cpolar.exe http 8080 +**原理:** + +1. 客户端向 cpolar 的中转节点发起 **出站**(outbound)连接,完成身份认证(authtoken),并在连接上报出要映射的本地端口,比如 HTTP 的 8080 +2. 中转节点分配一个公网端点,如abcd1234.cpolar.com +3. **外部用户** 访问 `http://abcd1234.cpolar.com`,落到 cpolar 的中转节点,中转节点 **通过先前建立好的持久隧道**,把流量转发到你本地运行的客户端。 + + + ## Spring Task **Spring Task** 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。