Commit on 2025/04/24 周四 19:00:14.19
This commit is contained in:
parent
65fc5aa440
commit
f6f3097112
35
科研/Mesa仿真.md
Normal file
35
科研/Mesa仿真.md
Normal file
@ -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
|
||||
|
||||
```
|
||||
|
67
科研/草稿.md
67
科研/草稿.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)$,确保状态分量正交性。
|
||||
|
||||
这样既符合论文的分布式滤波逻辑,又保持了符号一致性。是否需要进一步解释某一步骤?
|
||||
两种方法得到的结果完全相同。
|
87
科研/陈茂森论文.md
87
科研/陈茂森论文.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}
|
||||
$$
|
||||
|
14
自学/Redis.md
14
自学/Redis.md
@ -358,6 +358,20 @@ public class SpringDataRedisTest {
|
||||
}
|
||||
```
|
||||
|
||||
**对于非String类型:**
|
||||
|
||||
**写入时序列化**
|
||||
|
||||
- 调用 `opsForValue().set(key, value)` 时,`value`(比如 `List<DishVO>`)会先走一遍 **ValueSerializer**。
|
||||
- 默认是 `JdkSerializationRedisSerializer`,它用 Java 的 `ObjectOutputStream` 把整个对象图打包成一个 byte[]。
|
||||
- 这个 byte[] 直接就存成了 Redis 的 String 值。
|
||||
|
||||
**读取时反序列化**
|
||||
|
||||
- 调用 `opsForValue().get(key)` 时,RedisTemplate 拿回那段 byte[],再用同一个 JDK 序列化器(`ObjectInputStream`)把它变回 `List<DishVO>`。
|
||||
|
||||
|
||||
|
||||
**哈希测试**
|
||||
|
||||
```java
|
||||
|
@ -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}"
|
||||
```
|
||||
|
||||
|
||||
|
@ -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));
|
||||
|
245
自学/苍穹外卖.md
245
自学/苍穹外卖.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。
|
||||
|
||||

|
||||
Spring Cache是经典缓存的上位替代!!!
|
||||
|
||||
注意,如果缓存的是套餐分类,即一个套餐分类中含有多个套餐,那么在新增套餐的时候,需要清除相应的套餐分类缓存,因为当你新增一个属于分类 5 的套餐时,原来缓存里那份「分类 5 的列表」已经不再完整──它少了新加的那个套餐。
|
||||
|
||||
但是如果缓存的就是套餐本身,新增套餐的时候就可以直接缓存套餐。不要混淆两者!
|
||||
|
||||
|
||||
|
||||
**5.商户系统调用微信后台:**
|
||||
## 下单支付
|
||||
|
||||
**下单**
|
||||
|
||||
<img src="https://pic.bitday.top/i/2025/04/24/sm31f9-0.png" alt="image-20221214200913654" style="zoom:50%;" /><img src="https://pic.bitday.top/i/2025/04/24/snaksp-0.png" alt="image-20221214200959943" style="zoom:50%;" />
|
||||
|
||||
| 表名 | 含义 | 说明 |
|
||||
| ------------ | ---------- | ------------------------------------------------------------ |
|
||||
| orders | 订单表 | 主要存储订单的基本信息(如: 订单号、状态、金额、支付方式、下单用户、收件地址等) |
|
||||
| order_detail | 订单明细表 | 主要存储订单详情信息(如: 该订单关联的套餐及菜品的信息) |
|
||||
|
||||
|
||||
|
||||
### 微信支付
|
||||
|
||||
官方文档:https://pay.weixin.qq.com/static/product/product_index.shtml
|
||||
|
||||
<img src="https://pic.bitday.top/i/2025/03/19/u7z7u7-2.png" alt="image-20221214223910840" style="zoom:80%;" />
|
||||
|
||||
|
||||
|
||||
**商户系统调用微信后台:**
|
||||
|
||||
**JSAPI下单:**商户系统调用该接口在微信支付服务后台生成预支付交易单(对应时序图的第5步)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
**10.用户调起微信支付**
|
||||
**微信小程序调起支付:**
|
||||
|
||||
通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付(对应时序图的第10步)
|
||||
|
||||

|
||||
|
||||
@ -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框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。
|
||||
|
Loading…
x
Reference in New Issue
Block a user