seckill_system.html 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8"/>
  5. <title>秒杀系统时序图</title>
  6. <script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
  7. <style>
  8. html,body{margin:0;height:100%;background:#f5f5f5;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;}
  9. .wrap{display:flex;flex-direction:column;height:100%;}
  10. .toolbar{flex:0 0 50px;display:flex;align-items:center;justify-content:center;background:#fff;box-shadow:0 2px 4px rgba(0,0,0,.08);}
  11. .toolbar button{margin:0 6px;padding:6px 14px;border:0;border-radius:4px;background:#2196f3;color:#fff;cursor:pointer;}
  12. .viewport{flex:1;overflow:hidden;position:relative;background:#fafafa;}
  13. #mermaid-svg{position:absolute;top:0;left:0;cursor:grab;transition:none;}
  14. #mermaid-svg.grabbing{cursor:grabbing;}
  15. </style>
  16. </head>
  17. <body>
  18. <div class="wrap">
  19. <div class="toolbar">
  20. <button onclick="zoom(0.9)">缩小</button>
  21. <button onclick="zoom(1.1)">放大</button>
  22. <button onclick="reset()">重置</button>
  23. <button onclick="toggleDrag()">拖拽开关</button>
  24. </div>
  25. <div class="viewport" id="viewport">
  26. <div id="mermaid-svg"></div>
  27. </div>
  28. </div>
  29. <script>
  30. /* ---------- 1. 渲染时序图 ---------- */
  31. mermaid.initialize({
  32. startOnLoad: false,
  33. theme: 'default',
  34. sequence: { diagramMarginX: 50, diagramMarginY: 10, actorMargin: 50, width: 150, height: 65 }
  35. });
  36. const code = `
  37. sequenceDiagram
  38. participant U as 用户
  39. participant SC as SeckillController
  40. participant SGA as SeckillGuardAspect
  41. participant UC as UserContext
  42. participant RS as RedissonClient
  43. participant RSD as RedisStockDao
  44. participant LU as Lua脚本
  45. participant SMP as SeckillMessageProducer
  46. participant SSS as StockSyncService
  47. participant SIS as StockInitializerService
  48. participant SJ as StockSyncJob
  49. participant SMC as SeckillMessageConsumer
  50. U->>SC: 发起秒杀请求
  51. SC->>SGA: 调用被@SeckillGuard注解的方法
  52. SGA->>SGA: 检查系统配置和灰度规则
  53. alt 非灰度用户或未启用新逻辑
  54. SGA->>SC: 降级到旧逻辑
  55. SC-->>U: 返回旧逻辑处理结果
  56. else 灰度用户且启用新逻辑
  57. SGA->>RS: 用户限流检查(RAtomicLong.incrementAndGet)
  58. RS-->>SGA: 返回用户访问次数
  59. alt 超过用户限制
  60. SGA-->>U: 抛出用户限流异常
  61. else 未超过用户限制
  62. SGA->>RS: 获取信号量许可(RSemaphore.tryAcquire)
  63. RS-->>SGA: 返回许可获取结果
  64. alt 未获取到许可(系统繁忙)
  65. SGA-->>U: 抛出系统繁忙异常
  66. else 获取到许可
  67. SGA->>RSD: 检查库存是否存在(existsStock)
  68. RSD-->>SGA: 返回库存存在状态
  69. alt 库存不存在
  70. SGA->>SIS: 初始化库存(checkAndInitializeStock)
  71. SIS-->>SGA: 返回初始化结果
  72. end
  73. SGA->>UC: 生成订单号(UUID)
  74. SGA->>RSD: 执行库存扣减(decrStock)
  75. RSD->>LU: 执行Lua脚本
  76. LU-->>RSD: 返回剩余库存
  77. RSD-->>SGA: 返回扣减结果
  78. alt 库存不足
  79. SGA->>RS: 释放信号量许可
  80. SGA-->>U: 抛出库存不足异常
  81. else 库存充足
  82. SGA->>UC: 设置订单Token(setOrderToken)
  83. SGA->>SC: 执行业务逻辑(proceed)
  84. SC-->>SGA: 返回业务执行结果
  85. alt 业务执行成功
  86. SGA->>SMP: 发送秒杀成功消息
  87. SGA->>SMP: 发送库存回滚消息
  88. SGA->>SSS: 记录库存变化
  89. SGA->>RS: 释放信号量许可
  90. SGA-->>U: 返回秒杀成功结果
  91. else 业务执行失败
  92. SGA->>RSD: 回滚库存(incrStock)
  93. SGA->>RS: 释放信号量许可
  94. SGA-->>U: 抛出业务异常
  95. end
  96. end
  97. end
  98. end
  99. end
  100. `;
  101. mermaid.render('mermaid-svg', code).then(({ svg }) => {
  102. document.getElementById('mermaid-svg').innerHTML = svg;
  103. reset(); // 初始化居中
  104. });
  105. /* ---------- 2. 无溢出缩放 + 丝滑拖拽 ---------- */
  106. let scale = 1, translateX = 0, translateY = 0;
  107. let dragOn = false, dragging = false, startX, startY;
  108. const svgRoot = () => document.querySelector('#mermaid-svg svg');
  109. const viewport = document.getElementById('viewport');
  110. function applyTransform() {
  111. const el = svgRoot();
  112. if (!el) return;
  113. el.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
  114. el.style.transformOrigin = '0 0';
  115. }
  116. function zoom(factor, anchorX, anchorY) {
  117. const el = svgRoot();
  118. if (!el) return;
  119. const rect = el.getBoundingClientRect();
  120. anchorX = anchorX ?? rect.left + rect.width / 2;
  121. anchorY = anchorY ?? rect.top + rect.height / 2;
  122. const newScale = Math.max(0.3, Math.min(5, scale * factor));
  123. const dx = (anchorX - rect.left) * (1 - newScale / scale);
  124. const dy = (anchorY - rect.top) * (1 - newScale / scale);
  125. scale = newScale;
  126. translateX += dx;
  127. translateY += dy;
  128. applyTransform();
  129. }
  130. function reset() {
  131. scale = 1; translateX = 0; translateY = 0;
  132. const el = svgRoot();
  133. if (!el) return;
  134. // 居中
  135. const vRect = viewport.getBoundingClientRect();
  136. const sRect = el.getBoundingClientRect();
  137. translateX = (vRect.width - sRect.width) / 2;
  138. translateY = 20;
  139. applyTransform();
  140. }
  141. function toggleDrag() {
  142. dragOn = !dragOn;
  143. viewport.style.cursor = dragOn ? 'grab' : 'default';
  144. }
  145. /* 滚轮缩放以鼠标位置为锚点 */
  146. viewport.addEventListener('wheel', e => {
  147. e.preventDefault();
  148. zoom(e.deltaY < 0 ? 1.1 : 0.9, e.clientX, e.clientY);
  149. });
  150. /* 拖拽 */
  151. viewport.addEventListener('mousedown', e => {
  152. if (!dragOn) return;
  153. dragging = true;
  154. startX = e.clientX - translateX;
  155. startY = e.clientY - translateY;
  156. svgRoot()?.classList.add('grabbing');
  157. });
  158. window.addEventListener('mousemove', e => {
  159. if (!dragging) return;
  160. translateX = e.clientX - startX;
  161. translateY = e.clientY - startY;
  162. applyTransform();
  163. });
  164. window.addEventListener('mouseup', () => {
  165. dragging = false;
  166. svgRoot()?.classList.remove('grabbing');
  167. });
  168. </script>
  169. </body>
  170. </html>