Commit on 2025/08/06 周三 22:17:46.88

This commit is contained in:
zhangsan 2025-08-06 22:17:46 +08:00
parent b3275c206e
commit bb082e5dc3
7 changed files with 618 additions and 136 deletions

View File

@ -90,3 +90,51 @@ if (V == A) {
}
```
如何多线程循环打印1-100数字
```java
public class AlternatePrint {
private static int count = 1;
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
while (count <= 100) {
synchronized (lock) {
if (count % 2 == 1) {
System.out.println(Thread.currentThread().getName() + ": " + count++);
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "Thread-1").start();
new Thread(() -> {
while (count <= 100) {
synchronized (lock) {
if (count % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + count++);
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "Thread-2").start();
}
}
```

View File

@ -183,6 +183,8 @@ public class RandomDemo {
子序列:字符串中**按顺序选取的一段字符**,可以不连续。
子数组:等于连续子序列
异位词:字母相同、字母频率相同、顺序不同,如`"listen"``"silent"`
@ -1175,6 +1177,22 @@ public class QuickSort {
**Java随机**
```java
mport java.util.Random;
public class RandomDemo {
public static void main(String[] args) {
Random random = new Random();
int num = random.nextInt(6); // 生成 [0, 5] 的随机整数
System.out.println(num);
}
}
```
### 快速选择
时间复杂度: O(n)
@ -1183,58 +1201,53 @@ public class QuickSort {
public class QuickSelect {
/**
* 在 nums[low..high] 区间内,寻找排序后下标为 k 的元素(第 k 小)
* 对 arr 在 [low..high] 范围内,使用“挖坑填坑”分区,返回枢轴的最终位置
*/
public int quickselect(int[] nums, int low, int high, int k) {
// 区间内只有一个元素,直接返回
if (low == high) {
return nums[low];
}
// 选取区间第一个元素作为枢轴
int pivot = nums[low];
private int partition(int[] arr, int low, int high) {
int pivot = arr[low];
int left = low, right = high;
// “挖坑填坑”分区:左边填小于 pivot 的值,右边填大于 pivot 的值
while (left < right) {
// 从右向左找第一个 < pivot
while (left < right && nums[right] >= pivot) {
while (left < right && arr[right] >= pivot) {
right--;
}
nums[left] = nums[right]; // 填到左“坑”
// 从左向右找第一个 > pivot 的
while (left < right && nums[left] <= pivot) {
arr[left] = arr[right];
while (left < right && arr[left] <= pivot) {
left++;
}
nums[right] = nums[left]; // 填到右“坑”
}
// 把 pivot 放回最终位置
nums[left] = pivot;
// 根据 pivot 位置与 k 比较,决定去哪一边继续
if (left == k) {
return nums[left];
} else if (k < left) {
return quickselect(nums, low, left - 1, k);
} else {
return quickselect(nums, left + 1, high, k);
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
/**
* 返回数组中第 k 大的元素
* @param nums 输入数组
* @param k 1-based第 k 大
* 在 arr[low..high] 区间内,寻找第 k 小元素k 从 0 开始)
*/
public int quickselect(int[] arr, int low, int high, int k) {
if (low <= high) {
int pivotPos = partition(arr, low, high);
if (pivotPos == k) {
return arr[pivotPos];
} else if (k < pivotPos) {
return quickselect(arr, low, pivotPos - 1, k);
} else {
return quickselect(arr, pivotPos + 1, high, k);
}
}
// 理论上不会走到这里,除非 k 越界
throw new IllegalArgumentException("k is out of bounds");
}
/**
* 返回数组中第 k 大的元素k 从 1 开始)
*/
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
// 第 k 大对应第 (n - k) 小
// 第 k 大就是第 (n - k) 小
return quickselect(nums, 0, n - 1, n - k);
}
}
```

View File

@ -1,79 +1,103 @@
### 收缩矩阵的逐次特征值提取方法
#### 1. 初始矩阵性质
对于实对称矩阵 $A \in \mathbb{R}^{N \times N}$,当满足:
- 非对角元素 $a_{ij}$ 独立同分布
- 均值 $\mathbb{E}[a_{ij}] = \mu$
- 方差 $\text{Var}(a_{ij}) = \sigma^2$
- 对角元素 $a_{ii} = 0$
其最大特征值 $\lambda_1$ 服从高斯分布:
### 定理2
多智能体随机网络矩阵奇异值信号系统具有线性特征。
#### 证明
根据定理1奇异值序列$\sigma_{\tilde{\kappa}}(A_t)$服从高斯分布$\mathcal{N}(m_{\tilde{\kappa}}, 2\sigma_{\tilde{\kappa}}^2)$,其协方差结构满足:
$$
\mathbb{E}[\lambda_1] (N-1)\mu + \frac{\sigma^2}{\mu}, \quad \text{Var}(\lambda_1) = 2\sigma^2
\gamma_{\tilde{\kappa}}(h) = 2\sigma_{\tilde{\kappa}}^2\delta_h^0
$$
#### 2. 收缩操作定义
通过秩一修正实现矩阵收缩:
定义中心化变量:
$$
A^{(k+1)} = A^{(k)} - \lambda_k u_k u_k^T
\tilde{\sigma}_t = \sigma_{\tilde{\kappa}}(A_t) - m_{\tilde{\kappa}}
$$
可表示为:
$$
\tilde{\sigma}_t = \sqrt{2}\sigma_{\tilde{\kappa}}\varepsilon_t, \quad \varepsilon_t \overset{i.i.d.}{\sim} \mathcal{N}(0,1)
$$
#### 线性系统验证
该系统为MA(0)过程,系统增益$h_0 = \sqrt{2}\sigma_{\tilde{\kappa}}$,满足:
1. **齐次性**
$$a\tilde{\sigma}_t = h_0(a\varepsilon_t)$$
2. **叠加性**
$$\tilde{\sigma}_t^{(1)} + \tilde{\sigma}_t^{(2)} = h_0(\varepsilon_t^{(1)} + \varepsilon_t^{(2)})$$
#### 结论
奇异值序列的完整表示:
$$
\sigma_{\tilde{\kappa}}(A_t) = m_{\tilde{\kappa}} + h_0\varepsilon_t
$$
其中:
- $\lambda_k$ 为当前矩阵 $A^{(k)}$ 的最大特征值
- $u_k$ 为对应特征向量(单位范数)
- 初始条件 $A^{(1)} = A$
- $m_{\tilde{\kappa}}$为稳态偏置项
- $h_0\varepsilon_t$为线性系统响应
#### 3. 特征值递推关系
第 $k$ 大特征值可通过收缩矩阵提取:
根据线性系统定义(需引用文献),同时满足齐次性与可加性即构成线性系统,故得证。
---
### ② 定理2修订线性系统特征
#### 原MA(0)情形回顾
当$\gamma_k(h)=2\sigma_k^2\delta_h$时,
$$
\lambda_k(A) = \lambda_1(A^{(k)})
\tilde{\sigma}_t=\sigma_k(A_t)-m_k=\sqrt{2}\sigma_k\varepsilon_t, \quad \varepsilon_t \overset{i.i.d.}{\sim} \mathcal{N}(0,1)
$$
若收缩后矩阵 $A^{(k)}$ 保持:
- 非对角元素均值 $\mu_k$
- 方差 $\sigma_k^2$
#### 新协方差结构下的表示
则特征值统计量满足:
当$\gamma_k(h)=C_h$(允许$C_h\neq0$根据Wiener-Kolmogorov表示定理
$$
\begin{cases}
\mathbb{E}[\lambda_k] = (N-k)\mu_k + \frac{\sigma_k^2}{\mu_k} \\
\text{Var}(\lambda_k) = 2\sigma_k^2
\end{cases}
\tilde{\sigma}_t=\sum_{h=-\infty}^{+\infty} b_h w_{t-h} \tag{1}
$$
其中$\{b_h\}\in\ell^2$满足:
$$
\gamma_k(h)=\sum_{\ell=-\infty}^{+\infty} b_\ell b_{\ell+h} \tag{2}
$$
#### 4. 实现步骤
1. **初始化**:设 $A^{(1)} = A$$k=1$
2. **迭代过程**
```python
while k <= K:
# 计算当前最大特征对
λ, u = eigsh(A^{(k)}, k=1)
#### 线性系统验证
# 记录统计量
λ_sequence[k] = λ
设系统传递函数$H(z)=\sum_h b_h z^{-h}$
# 执行收缩
A^{(k+1)} = A^{(k)} - λ * u @ u.T
1. **齐次性**
$$
a\tilde{\sigma}_t=a\sum_h b_h w_{t-h}=\sum_h b_h (a w_{t-h})=H(z)\{a w_t\}
$$
# 验证新矩阵性质
μ_k = np.mean(A^{(k+1)}[off_diag])
σ²_k = np.var(A^{(k+1)}[off_diag])
2. **叠加性**
$$
\tilde{\sigma}_t^{(1)}+\tilde{\sigma}_t^{(2)}=\sum_h b_h(w_{t-h}^{(1)}+w_{t-h}^{(2)})=H(z)\{w_t^{(1)}+w_t^{(2)}\}
$$
k += 1
故$\{\sigma_k(A_t)\}$仍是LTI系统输出但系统响应$\{b_h\}$需通过(2)式确定。
$\sigma_1$.
---
### 性质对比
您说得对,在奇异值分解(SVD)中,通常用 **u****v** 来表示左右奇异向量。以下是修正后的Markdown格式表示
对于随机网络矩阵 `A`,设奇异值从大到小依次为 `{\sigma _1},{\sigma _2}, \ldots ,{\sigma _n}`,对应的左、右奇异向量分别为 ${u}_1, {u}_2, \ldots, {u}_n$ 和 ${v}_1, {v}_2, \ldots,{v}_n$。
或者更紧凑地表示为:
`A = \sum_{i=1}^n \sigma_i \mathbf{u}_i \mathbf{v}_i^\top`
需要其他数学符号表示或格式调整可以随时告诉我!
| 性质 | $\gamma_k(h)=2\sigma_k^2\delta_h$ | $\gamma_k(h)=C_h$ |
| -------- | --------------------------------- | -------------------------------- |
| 宽平稳 | ✅ | ✅ |
| 白噪声 | ✅ | ❌ |
| 系统类型 | MA(0) | 通用LTI可能MA($\infty$) |
| 谱密度 | $S(f)=2\sigma_k^2$ | $S(f)=\sum_h C_h e^{-j2\pi f h}$ |
@ -81,18 +105,126 @@ $\sigma_1$.
不必大动干戈,只要把“等号”换成“近似”等价写法,或显式加上误差项,就足够严谨。下面给出两种常见做法,你任选其一即可——
### 随机网络稳态奇异值的平稳性证明
| 写法 | 建议格式 | 说明 |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **近似号写法** | E[σ1]≈(N1)μ+v+σ2μ,Var[σ1]≈2σ2E[\sigma_1]\approx (N-1)\mu+v+\dfrac{\sigma^2}{\mu},\qquad \operatorname{Var}[\sigma_1]\approx 2\sigma^2 | 直接用 “≈” 表明这是 N→∞N\to\infty 的主导项;与前面“可在 O(1/N)O(1/\sqrt N) 范围内逼近高斯”完全呼应。 |
| **误差项写法** | E[σ1]=(N1)μ+v+σ2μ+O(1N),Var[σ1]=2σ2+O(1N)E[\sigma_1]=(N-1)\mu+v+\dfrac{\sigma^2}{\mu}+O\!\left(\tfrac{1}{N}\right),\qquad \operatorname{Var}[\sigma_1]=2\sigma^2+O\!\left(\tfrac{1}{N}\right) | 把小量显式写成 O(1/N)O(1/N)。这样保留等号,同时说明误差级别,更“硬核”一些。 |
#### 1. 稳态奇异值分布特性
> **为什么推荐修改?**
>
> - 你的正文已经说“在 O(1/N)O(1/\sqrt N) 的范围内可被高斯分布逼近”,说明后续公式仅是渐近主项。直接用 “=” 容易让读者误以为 **完全等于** 主项。
> - 只要在公式里加 “≈” 或 “+O(\cdot)” 就能避免歧义,而且和引用的 F & K 定理保持一致。
当随机网络进入稳态后,其矩阵序列$\{A_t\}$的任意奇异值$\sigma_k(A_t)$服从高斯分布:
$$
\sigma_k(A_t) \sim \mathcal{N}(m_k, \gamma_k(0))
$$
其中参数满足:
其余内容(条件、符号、文字描述)都没问题,不必再改。
- **均值**$m_k = (N-1)\mu_k + v_k + \frac{\sigma_k^2}{\mu_k}$
$N$为网络规模,$\mu_k,v_k,\sigma_k$为网络参数)
- **方差**$\gamma_k(0) = 2\sigma_k^2$
$\approx$
#### 2. 宽平稳性验证
对任意时刻$t$
1. **均值稳定性**
$$
\mathbb{E}[\sigma_k(A_t)] = m_k \quad \text{(常数)}
$$
2. **协方差结构**
- 当$h=0$时:
$$
\text{Cov}(\sigma_k(A_t), \sigma_k(A_t)) = \gamma_k(0)
$$
- 当$h \neq 0$时:
$$
\text{Cov}(\sigma_k(A_t), \sigma_k(A_{t+h})) = \gamma_k(h)=0
$$
(由稳态下矩阵的独立性保证)
#### 3. 结论
自协方差函数$\gamma_k(h)$仅依赖于时滞$h$,因此奇异值信号序列$\{\sigma_k(A_t)\}$满足宽平稳过程的定义。
---
**注**:本证明基于以下假设:
1. 网络规模$N$足够大,使得高斯逼近有效
2. 稳态下矩阵序列$\{A_t\}$具有独立性
### 定理2
多智能体随机网络矩阵奇异值信号系统具有线性特征。
#### 证明
根据定理1奇异值序列$\sigma_{\tilde{\kappa}}(A_t)$服从高斯分布$\mathcal{N}(m_{\tilde{\kappa}}, 2\sigma_{\tilde{\kappa}}^2)$,其协方差结构满足:
$$
\gamma_{\tilde{\kappa}}(h) = 2\sigma_{\tilde{\kappa}}^2\delta_h^0
$$
定义中心化变量:
$$
\tilde{\sigma}_t = \sigma_{\tilde{\kappa}}(A_t) - m_{\tilde{\kappa}}
$$
可表示为:
$$
\tilde{\sigma}_t = \sqrt{2}\sigma_{\tilde{\kappa}}\varepsilon_t, \quad \varepsilon_t \overset{i.i.d.}{\sim} \mathcal{N}(0,1)
$$
#### 线性系统验证
该系统为MA(0)过程,系统增益$h_0 = \sqrt{2}\sigma_{\tilde{\kappa}}$,满足:
1. **齐次性**
$$a\tilde{\sigma}_t = h_0(a\varepsilon_t)$$
2. **叠加性**
$$\tilde{\sigma}_t^{(1)} + \tilde{\sigma}_t^{(2)} = h_0(\varepsilon_t^{(1)} + \varepsilon_t^{(2)})$$
#### 结论
奇异值序列的完整表示:
$$
\sigma_{\tilde{\kappa}}(A_t) = m_{\tilde{\kappa}} + h_0\varepsilon_t
$$
其中:
- $m_{\tilde{\kappa}}$为稳态偏置项
- $h_0\varepsilon_t$为线性系统响应
根据线性系统定义(需引用文献),同时满足齐次性与可加性即构成线性系统,故得证。
……由协方差结构 γ_k(h)=2σ_k^2δ_h^0 可知,中心化变量
$$
\tilde σ_t = σ_k(A_t)-m_k,\qquad
\mathbb E[\tilde σ_t]=0,\; \mathrm{Cov}(\tilde σ_t,\tilde σ_{t+h})=
2σ_k^{2}\delta_h^{0}.
$$
**根据 Wold 分解定理①**,任何零均值、纯非确定性的宽平稳过程都可以唯一表示为
$$
\tilde σ_t=\sum_{j=0}^{\infty}ψ_j\;ε_{t-j},
\qquad ε_t\stackrel{i.i.d.}{\sim}\mathcal N(0,1),\
\sum_{j=0}^{\infty}|ψ_j|^2<\infty.
$$
而在本情形下 $\gamma_k(h)=0\,(h\neq 0)$,因此
$$
ψ_0=\sqrt{2}\,σ_k,\quad ψ_j=0\;(j\ge 1),
$$
退化为一个 **MA(0)** 过程:
$$
\boxed{\;\tilde σ_t=\sqrt{2}\,σ_k\,ε_t\;}
$$
……

View File

@ -58,6 +58,10 @@
### 证明主特征值序列平稳
#### **(1) 均值恒定性的推导**
@ -530,3 +534,9 @@ LSTM 的隐藏状态 $h_i \in \mathbb{R}^{d'' \times 1}$(其中 $d''$ 为 LSTM
该论文可能有点问题每个节点只能预测自身未来位置无法获取全局位置信息。如果先LSTM后GCN可能可以

View File

@ -14,7 +14,11 @@
用户锁单-》支付宝付款-》成功后return_url设置了用户支付完毕后跳转回哪个地址是给前端用户看的 alipay_notify_url设置了支付成功后alipay调用你的后端哪个接口。
这里有小商城和拼团系统notify_url指拼团系统中拼团达到指定人数后通知小商城的地址这里用rabbitmq。然后小商场将订单中相应拼团的status都设置为deal_done。然后小商场内部也再发一个'支付成功'消息,主要用于通知这些拼团对应的订单进入下一环节:发货(感觉'支付成功'取名不够直观)。
这里有小商城和拼团系统notify_url指拼团系统中拼团达到指定人数后通知小商城的HTTP地址但是如果notify_type为MQ则notify_url为空并且notify_mq非空指明是拼团成功通知还是用户退单通知。
若为拼团成功通知小商场将订单中相应拼团的status都设置为deal_done然后小商场内部也再发一个'支付成功'消息,主要用于通知这些拼团对应的订单进入下一环节:发货(感觉'支付成功'取名不够直观)。
若为用户退单通知,小商场需处理退款业务。
@ -378,45 +382,130 @@ EndNode.apply() → 组装结果并返回 TrialBalanceEntity
<img src="https://pic.bitday.top/i/2025/07/25/hgggjo-0.png" alt="image-20250725105608390" style="zoom:67%;" />
逆向的流程,要分析用户是在哪个流程节点下进行中断行为。包括3个场景
逆向的流程,要分析用户是在哪个流程节点下进行退单行为。包括3个场景
**已锁单、未支付**
**已锁单、未支付**redis恢复量+1mysql中锁单量-1
- **用户行为**:完成锁单后未发起支付。
- **结果**:订单超时自动关单。
- 补偿
- 若用户在临界时刻支付,则需执行“逆向退款”流程——退还支付金额并告知“优惠已过期,请重新参与”。
- 否则该订单自动失效,释放拼团名额给后续用户。
**已锁单、已支付,但拼团未成团**redis恢复量+1mysql中锁单量、完成量-1退款
**已锁单、已支付,但拼团未成团**
- **用户行为**:完成支付,组团人数不足暂未成团。
- 补偿策略
(可配置优先级):
1. **先退拼团,再退款,**
2. **先退款,再退拼团**
- 具体执行哪种方式,可由拼团活动策略决定——“优先保障个人”或“优先保障成团”。
**已锁单、已支付,且拼团已成团**
- **用户行为**:支付成功,且组团人数已凑齐。
- 补偿流程
- 先退还用户支付金额;
- 再撤销对应的拼团完成量。
- **注意**:已成团订单视为“已完成含退单”,仍然成团、不再开放新用户参与,确保团队成团状态一致。
**已锁单、已支付,且拼团已成团**redis恢复量无需+1因为成团之后不开放给别人mysql中锁单量、完成量-1退款拼团设置为'已完成含退单'状态,但拼团中所有人都退单,更新为失败!
### 策略模板应用
### 核心流程说明
根据订单状态和拼团状态动态选择退单策略。
#### 阶段一:退单操作流程
1. 客户**主动提交**退单请求
2. 通过责任链模式处理数据加载Node查询订单 → 重复检查Node防止重复退单 → 策略执行Node
3. 策略选择
根据订单状态和拼团状态选择对应退单策略(三种之一)
4. 执行退单
更新**数据库**操作(锁单量、完成量、拼团状态、订单状态...
5. 消息通知 + 任务补偿
发送MQ退单消息通知未支付退单、已支付未成团...三种消息 notify_category
将消息写入notify_task表定时任务扫描未成功处理的消息以做补偿兜底。
#### 阶段二:库存恢复流程
6. 消息监听
MQ**监听器接收**退单成功消息
7. 服务调用
调用恢复库存服务
8. 策略选择
根据退单类型选择对应策略(已成团的无需恢复了,反正新用户也无法再参与该拼团)
9. 库存恢复
执行Redis库存恢复操作带分布式锁保护
---
### 设计模式应用
1. **责任链模式**
`TradeRefundRuleFilterFactory` 构建的过滤链:
`DataNodeFilter``UniqueRefundNodeFilter``RefundOrderNodeFilter`
2. **策略模式**
- 策略接口:`RefundOrderStrategy`
- 实现策略:
`Unpaid2RefundStrategy`(未付款退单的流程)
`Paid2RefundStrategy`(已付款退单)
`PaidTeam2RefundStrategy`(已成团退单)
3. **工厂模式**
`TradeRefundRuleFilterFactory` 负责组装责任链
4. **模板方法模式**
`AbstractRefundOrderStrategy` 提供:
- 公共方法封装 发送退单MQ消息、库存恢复redis
- 依赖注入支持
### 退单触发入口
1用户主动退单
2定时任务定时任务扫描锁单但未结算的订单若支付时间超过设定值对这笔订单执行退单操作。
```mermaid
flowchart LR
subgraph mall["小型支付商城"]
style mall fill:#ffffff,stroke:#333,stroke-width:2
A[AliPayController<br/>发起退单申请]:::blue
C[订单状态扭转<br/>退单中]:::grey
E[RefundSuccessTopicListener<br/>接收MQ消息<br/>执行退款和订单状态变更]:::green
end
subgraph pdd["拼团系统"]
style pdd fill:#ffffff,stroke:#333,stroke-width:2
B[MarketTradeController<br/>接收退单申请]:::yellow
D[TradeRefundOrderService<br/>退单策略处理]:::red
F[TradeRepository<br/>发送MQ消息]:::purple
G([MQ消息队列<br/>退单成功消息]):::orange
H[RefundSuccessTopicListener<br/>接收MQ消息<br/>恢复库存]:::green
end
A -- "1. 发起退单请求" --> B
B -- "2. 处理退单" --> D
D -- "3. 发送MQ消息" --> F
F -- "4. 发布消息 (异步+本地消息表补偿)" --> G
F -- "5. 返回结果" --> C
G -- "6. 消费消息 (恢复库存)" --> H
G -. "7. 消费消息 (执行退款)" .-> E
classDef blue fill:#dbe9ff,stroke:#6fa1ff,stroke-width:1;
classDef grey fill:#e5e5e5,stroke:#9e9e9e,stroke-width:1;
classDef green fill:#d6f2d6,stroke:#76b076,stroke-width:1;
classDef yellow fill:#fef3cd,stroke:#f5c700,stroke-width:1;
classDef red fill:#f8d7da,stroke:#e55353,stroke-width:1;
classDef purple fill:#e4dbf9,stroke:#9370db,stroke-width:1;
classDef orange fill:#ffecca,stroke:#ffa500,stroke-width:1;
```
## 收获
@ -459,6 +548,60 @@ public class PayActivityEntity {
### 单例模式
**懒汉**
```java
public class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (LazySingleton.class) {
if (instance == null) { // 第二次检查
instance = new LazySingleton();
}
}
}
return instance;
}
}
```
**饿汉**
```java
public class EagerSingleton {
// 类加载时就初始化实例
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造函数
private EagerSingleton() {
// 防止反射创建实例
if (INSTANCE != null) {
throw new IllegalStateException("Singleton already initialized");
}
}
// 全局访问点
public static EagerSingleton getInstance() {
return INSTANCE;
}
// 防止反序列化破坏单例
private Object readResolve() {
return INSTANCE;
}
}
```
### 模板方法
**核心思想**
@ -770,7 +913,7 @@ while 循环:
### 规则树流程
!![image-20250725120957709](https://pic.bitday.top/i/2025/07/25/k01knr-0.png)
![image-20250725120957709](https://pic.bitday.top/i/2025/07/25/k01knr-0.png)
**整体分层思路**
@ -1562,6 +1705,8 @@ public boolean tryOccupy(String counterKey,
}
```
本项目有两层防护:第一层是下单前的人数/库存校验比较基础由于前端可能更新不及时显示还差X人拼团但用户点进去时已达人数的情况。第二层是真正的并发保证即**Redis 原子操作** + **后置校验/补偿**
### `Supplier<T>`
@ -1828,3 +1973,38 @@ output {
### RPC微服务调用
1.父Pom统一版本
```xml
<!-- 统一锁版本,避免不同模块写不同小版本 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.3.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
```
2.pay-mall-infrustructConsumergroup-buying-sys-trigger (Provider)引入依赖
```xml
<dependencies>
<!-- Dubbo 核心 + Spring Boot 自动装配 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!-- Nacos 注册中心扩展 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
</dependency>
</dependencies>
```
****

View File

@ -129,6 +129,103 @@ AI扩图
4、一些小问题的优化比如 WebSocket 连接建立之后,如果用户退出了登录,这时 WebSocket 的连接是没有断开的。不过影响并不大,可以思考下怎么处理。
## 踩坑
#### **精度损失和日期格式转换问题**
##### **前端 → 后端**
**日期**
前端把日期格式化成后端期待的纯日期**字符串**,例如 `"2025-08-14"`,后端 DTO 用 `LocalDate` 接收(配合 `@JsonFormat(pattern="yyyy-MM-dd")`Jackson 反序列化成 `LocalDate`
**精度:**
JavaScript 的 `number` 类型只能安全地表示到 2^531约 9×10^15的整数超过这个范围就会丢失精度`number` 传给后端时末尾只能补0
解决办法:前端 ID 当做字符串传给后端。
Spring MVC 会自动调用 `Long.parseLong("1951619197178556418")` 并赋值给你方法签名里的 `long id`即还是写作long来接收不变
##### **后端 → 前端**
**日期:**
后端用 `LocalDate` / `LocalDateTime` 之类的 Java 8 类型,经过 Jackson 序列化为指定格式的字符串(比如 `"yyyy-MM-dd"` / `"yyyy-MM-dd HH:mm:ss"`)供前端消费,避免时间戳或默认格式的不一致。
**精度:**
Java 的 `long` 可能超过 JavaScript `number` 的安全范围2^531直接以数字输出会丢失精度。必须把 `long`/`Long` 序列化成**字符串**(例如 ID 输出为 `"1951648800160399362"`),前端拿到字符串再展示。
对 Jackson 用作 Spring 的 HTTP 消息转换器的 `ObjectMapper` 进行配置日期格式、Java 8 时间支持、Long 转字符串等)示例代码:
```java
@Configuration
public class JacksonConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String TIME_FORMAT = "HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
builder.simpleDateFormat(DATETIME_FORMAT);
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
JavaTimeModule javaTime = new JavaTimeModule();
javaTime.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
javaTime.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
javaTime.addSerializer(LocalTime.class,
new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
javaTime.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
javaTime.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
javaTime.addDeserializer(LocalTime.class,
new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
SimpleModule longToString = new SimpleModule();
longToString.addSerializer(Long.class, ToStringSerializer.instance);
longToString.addSerializer(Long.TYPE, ToStringSerializer.instance);
builder.modules(javaTime, longToString);
};
}
}
```
**数据库密码加密**
加密存储确保即使数据库泄露,攻击者也不能轻易获取用户原始密码。
spring security中提供了一个加密类**BCryptPasswordEncoder**。
它采用[哈希算法](https://so.csdn.net/so/search?q=哈希算法&spm=1001.2101.3001.7020) SHA-256 +随机盐+密钥对密码进行加密。加密算法是一种**可逆**的算法,而哈希算法是一种**不可逆**的算法。
因为有随机盐的存在,所以**相同的明文密码**经过加密后的密码是**不一样**的盐在加密的密码中是有记录的所以需要对比的时候springSecurity是可以从中获取到盐的
验证密码 **matches**
```java
// 使用 matches 方法来对比明文密码和存储的哈希密码
boolean judge= passwordEncoder.matches(rawPassword, user.getPassword());
```
注意,`matches`的第一个参数**必须** 是 “**原始明文**”,第二个参数 **必须** 是 “**已经加密过的密文**”!!!**顺序不能反!!!**
## 收获
@ -453,7 +550,9 @@ public String info() {
}
```
#### 多账号体系
#### passwordEncoder多账号体系
若项目中存在两套权限校验体系。一套是 user 表的,分为普通用户和管理员;另一套是对团队空间的权限进行校验。

View File

@ -834,15 +834,15 @@ net start winnat
直接部署开发完毕的前端代码,准备:
0.创建docker网络`docker network create sky-net`
0创建docker网络`docker network create sky-net`
1.静态资源html文件夹(npm run build 打包源码获得)
1静态资源html文件夹(npm run build 打包源码获得)
2.nginx.conf
2nginx.conf
![image-20250515111352735](https://pic.bitday.top/i/2025/05/15/iexngr-0.png)
注意把nginx.conf中的server改为Docker 容器(或服务)在**同一网络中**的主机名,如
注意把nginx.conf中的server改为Docker 容器(或服务)在**同一网络中**的服务名,如
```nginx
upstream webservers {
@ -852,7 +852,7 @@ upstream webservers {
因为同一个网络下的服务名会自动注册DNS进行地址解析
3.docker-compose文件
3docker-compose文件
```yml
version: "3.8"