diff --git a/后端学习/JAVA面试题.md b/后端学习/JAVA面试题.md index c4f508e..4d01b6b 100644 --- a/后端学习/JAVA面试题.md +++ b/后端学习/JAVA面试题.md @@ -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(); + } +} + +``` + diff --git a/后端学习/力扣Hot 100题.md b/后端学习/力扣Hot 100题.md index b199544..7a4971e 100644 --- a/后端学习/力扣Hot 100题.md +++ b/后端学习/力扣Hot 100题.md @@ -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); } - } + ``` diff --git a/科研/草稿.md b/科研/草稿.md index cd2ea1e..31047b9 100644 --- a/科研/草稿.md +++ b/科研/草稿.md @@ -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] = λ - - # 执行收缩 - A^{(k+1)} = A^{(k)} - λ * u @ u.T - - # 验证新矩阵性质 - μ_k = np.mean(A^{(k+1)}[off_diag]) - σ²_k = np.var(A^{(k+1)}[off_diag]) - - k += 1 +#### 线性系统验证 -$\sigma_1$. +设系统传递函数$H(z)=\sum_h b_h z^{-h}$: +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\} + $$ +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)}\} + $$ +故$\{\sigma_k(A_t)\}$仍是LTI系统输出,但系统响应$\{b_h\}$需通过(2)式确定。 +--- -您说得对,在奇异值分解(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]≈(N−1)μ+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]=(N−1)μ+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\;} +$$ +…… diff --git a/论文/高飞论文.md b/论文/高飞论文.md index f80edad..251ff60 100644 --- a/论文/高飞论文.md +++ b/论文/高飞论文.md @@ -58,6 +58,10 @@ + + + + ### 证明主特征值序列平稳 #### **(1) 均值恒定性的推导** @@ -530,3 +534,9 @@ LSTM 的隐藏状态 $h_i \in \mathbb{R}^{d'' \times 1}$(其中 $d''$ 为 LSTM 该论文可能有点问题,每个节点只能预测自身未来位置,无法获取全局位置信息。如果先LSTM后GCN可能可以! + + + + + + diff --git a/项目/拼团交易系统.md b/项目/拼团交易系统.md index 0a79716..319d626 100644 --- a/项目/拼团交易系统.md +++ b/项目/拼团交易系统.md @@ -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 image-20250725105608390 -逆向的流程,要分析用户是在哪个流程节点下进行中断行为。包括3个场景; +逆向的流程,要分析用户是在哪个流程节点下进行退单行为。包括3个场景; -**已锁单、未支付** +**已锁单、未支付**:redis恢复量+1,mysql中锁单量-1 -- **用户行为**:完成锁单后未发起支付。 -- **结果**:订单超时自动关单。 -- 补偿 - - 若用户在临界时刻支付,则需执行“逆向退款”流程——退还支付金额并告知“优惠已过期,请重新参与”。 - - 否则该订单自动失效,释放拼团名额给后续用户。 +**已锁单、已支付,但拼团未成团**:redis恢复量+1,mysql中锁单量、完成量-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
发起退单申请]:::blue + C[订单状态扭转
退单中]:::grey + E[RefundSuccessTopicListener
接收MQ消息
执行退款和订单状态变更]:::green + end + + subgraph pdd["拼团系统"] + style pdd fill:#ffffff,stroke:#333,stroke-width:2 + B[MarketTradeController
接收退单申请]:::yellow + D[TradeRefundOrderService
退单策略处理]:::red + F[TradeRepository
发送MQ消息]:::purple + G([MQ消息队列
退单成功消息]):::orange + H[RefundSuccessTopicListener
接收MQ消息
恢复库存]:::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` @@ -1828,3 +1973,38 @@ output { +### RPC微服务调用 + +1.父Pom统一版本 + +```xml + + + org.apache.dubbo + dubbo-bom + 3.3.5 + pom + import + +``` + +2.pay-mall-infrustruct(Consumer)group-buying-sys-trigger (Provider)引入依赖 + +```xml + + + + org.apache.dubbo + dubbo-spring-boot-starter + + + + + org.apache.dubbo + dubbo-registry-nacos + + + +``` + +**** diff --git a/项目/智能协同云图库.md b/项目/智能协同云图库.md index 61b26d2..9801987 100644 --- a/项目/智能协同云图库.md +++ b/项目/智能协同云图库.md @@ -129,6 +129,103 @@ AI扩图 4、一些小问题的优化:比如 WebSocket 连接建立之后,如果用户退出了登录,这时 WebSocket 的连接是没有断开的。不过影响并不大,可以思考下怎么处理。 +## 踩坑 + +#### **精度损失和日期格式转换问题** + +##### **前端 → 后端** + +**日期** + +前端把日期格式化成后端期待的纯日期**字符串**,例如 `"2025-08-14"`,后端 DTO 用 `LocalDate` 接收(配合 `@JsonFormat(pattern="yyyy-MM-dd")`),Jackson 反序列化成 `LocalDate`。 + +**精度:** + +JavaScript 的 `number` 类型只能安全地表示到 2^53−1(约 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^53−1),直接以数字输出会丢失精度。必须把 `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 表的,分为普通用户和管理员;另一套是对团队空间的权限进行校验。 diff --git a/项目/苍穹外卖.md b/项目/苍穹外卖.md index 6eebf2f..dbad02c 100644 --- a/项目/苍穹外卖.md +++ b/项目/苍穹外卖.md @@ -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 +2)nginx.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文件 +3)docker-compose文件 ```yml version: "3.8"