Blizzard
|
f16f63284f
|
feat(cluster): gateway 后台定时器加 leader 锁——多副本安全(B5)
此前两个 gateway 定时器(订阅推进/掉单补偿)每实例各扫一遍:多副本下重复查库,
且对微信查单调用量随副本线性放大(微信有频控,先被限流的是自己)。
store.TryRunExclusive:用 PG advisory try-lock 做集群级单实例执行(leader 选举)——每轮
tick 非阻塞抢锁,抢到才跑、跑完释放;抢不到说明别的实例是 leader、本轮跳过。自愈:锁随
持有连接释放,leader 挂了下一轮别的实例自然抢到接管,无需显式故障转移。订阅/补偿用不同
锁键(可由不同实例分别 lead)。非 PG(sqlite 测试)/无 DB → 退回本地直接跑(单实例安全)。
两个定时器的 tick 各包一层 TryRunExclusive。至此 gateway 可安全多副本(SSE 走共享 Redis
流无需粘性、状态全外置、JWT 无状态,剩数据层 HA 属 C 层 ops)。带 fallback 单测。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-21 16:21:31 +08:00 |
|
Blizzard
|
ac38d5e663
|
fix(prod): 后端生产级 A 类硬伤全清(7 项:授权/崩溃点/全表扫/限流/鉴权/限额)
部署前生产级审计(可靠性/数据层/安全三路)后,清掉 7 处代码级硬伤:
A1 后台定时器 goroutine 无 panic recover → 单个 DB panic 崩整个 gateway。加 safeGo/
safeCall,包住订阅/掉单补偿/微信推送/探针 goroutine,单轮 tick 再兜一层。
A2 提示词控制面(建/激活/停用,热广播全服务)只 RequireAuth → 任意登录用户改全局提示词。
三写端点+列表挂 RequireAdmin。
A3 HITL 审批端点无角色门 → viewer 可放行烧钱执行。加 RequireTenantRole(member)。
A4 审计/护栏列表 limit 无校验,limit=-1 让 gorm 取消 LIMIT 全表扫。加 clampLimit/
clampOffset,AdminTasks/AdminSpaces 补上界。
A5 限流 Redis 一挂就完全放行(fail-open)。加进程内固定窗口兜底(fail-safe) + 登录/注册
按 IP 专用严限流(10/min)。
A6 公开 by-id 端点(stream/exec/report导出/kb导入流)无鉴权无租户过滤。加
AuthFromHeaderOrQuery(从 ?token= 取 JWT) + task/report 按 owner 归属校验;桌面端
5 处 EventSource/下载 URL 经 tokenQuery 附 JWT。
A7 文件上传无大小上限(整文件进内存 OOM 面) → 50MB 闸(KB_MAX_UPLOAD_BYTES)+ LimitReader;
http.Server 加 ReadHeaderTimeout/ReadTimeout/MaxHeaderBytes(不设 WriteTimeout 保 SSE)。
带单测:clampLimit/safeCall/procLimiter/AuthFromHeaderOrQuery/TaskOwner。
build+vet+全量 test 绿;desktop tsc 绿。B(迁移工具/实时探针/出网韧性/登录锁定/leader选举)
与 C(TLS/PG HA/K8s/备份自动化/可观测)分期后做,参照 production_readiness.md。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-21 15:04:47 +08:00 |
|
Blizzard
|
70b0419387
|
fix(payment): 支付轮询链路收口——查单节流 + 前端轮询重写
后端:
- 主动查单加最小间隔(5s)。此前 BillingOrderStatus 见 pending 就直连微信查单,
而前端 2.5s 轮一次 —— 单笔订单在 30min TTL 内可打出约 720 次渠道调用,
微信侧有频控,并发用户一多先被限流的是我们自己。回调才是入账主路径,
查单只是兜底,节流最坏只让在场用户多等 5s。
- 标记随订单落终态清除,并在补偿定时器每轮 prune 掉超 TTL 的残留
(用户扫码前就关弹窗的订单不会再被轮询,其标记无人回收)。
- 下单响应补 expires_at:二维码有效期由服务端 orderTTL 说了算,
前端硬编码一份迟早漂移。
前端(sundynix-web):
- orderStatus 不再丢掉 warn。服务端在「已付但金额与订单不符」时不入账、
挂起人工核对,订单一直停在 pending —— 丢掉 warn 用户就会一直等一个
永远不会来的结果。现在弹窗显式告警并给出订单号。
- setInterval → 递归 setTimeout:请求慢于间隔时 setInterval 会把请求摞起来,
对「每次可能触发渠道查单」的端点尤其糟。
- 标签页切走时暂停轮询(用户扫完码要切去微信 App),切回前台立刻补查一次。
- 连续失败指数退避至 15s 封顶,断网时不再定频猛打。
- 二维码倒计时 + 失效遮罩,过期不再让用户扫一个必然失败的码。
- 明说「关掉也不会丢钱,到账会自动补上」——掉单补偿定时器本就兜底,
但此前 UI 没讲,用户只能守着弹窗。
- 关闭弹窗一律刷新余额:用户可能在关闭前一刻付款、状态刚落地。
测试:新增 handler 包首个测试,钉住节流与标记回收行为(-race 通过)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-20 12:46:40 +08:00 |
|
Blizzard
|
3a4e1d53a5
|
refactor(payment): 渠道抽象成 Channel 接口 —— 接新渠道=加 adapter 不动骨架
PAYMENT_DESIGN §3 承诺的 internal/payment/channel.go 适配器接口此前不存在,微信硬编码在
manager/handler 里。补齐抽象:
- channel.go:Channel 接口(Name/CreatePay/QueryOrder/VerifyCallback)+ 统一 PayIntent/PayResult
+ 渠道名常量。入参用基本类型不吃 *store.PaymentOrder,payment 包不反依赖 store。
- Wechat 实现 Channel(编译期断言 var _ Channel);QueryResult 归一为 PayResult;CreatePay 返回 PayIntent。
- Manager 从「持一个 *Wechat」改为渠道注册表:Get(name)/Available()/Status(name)/ReloadWechat;
按渠道名持有已装配实例,热重载不变。
- 回调路由收敛 /billing/callback/wechat → /billing/callback/:channel 按名路由(旧 notify URL 仍匹配);
查单/掉单补偿据 order.Channel 路由,不再写死微信。下单支持可选 channel(缺省 wechat)。
- 支付宝/Stripe 现在真·只差一个 adapter+注册。唯一未泛化:回调 ack 应答格式(现微信态,注释标明)。
- payment 包首个测试:Manager 注册表 4 用例(空/注册摘除/空配置/配置不全)。build/vet/test/lint 全绿。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 13:56:24 +08:00 |
|
Blizzard
|
3db2de1ef6
|
feat(billing): 支付 P5.3 —— 掉单补偿定时器 + admin 订单流 + 日终对账
支付线封口。此前 pending 单只在「用户开着账单页轮询」时才查单确认——用户扫完码
关页面,钱付了、积分永不到账。
- 掉单补偿定时器(payment_reconcile.go):gateway 内每分钟扫 pending 微信单,
逐单 reconcileOrder 主动查单落态。把「用户在不在场」从入账链路摘掉。
reconcileOrder 从 BillingOrderStatus 抽出、前端轮询与定时器共用一份幂等
落态逻辑(不重蹈 GenerateReport/SubmitTask 的漂移)。渠道未配置时空转不炸。
- admin 订单流 GET /admin/orders(状态计数+全平台订单,可筛)。
- 日终对账 GET /admin/orders/reconcile:paid 单 ↔ 账本 grant 分录逐单比对,
抓 order_without_ledger(钱到了积分没给,最严重)/ ledger_without_paid_order。
- admin 计费页「充值订单与对账」块:计数卡片+订单流+一键对账。
⚠️ live 抓到并修掉一个真 bug:OrderStats 复用同一个 gorm.DB 链式 Count 三次,
WHERE 累加成 status=A AND status=B → 恒 0(订单流显示 2 单但计数全 0)。
改成每次起新 query builder。—— 又一次只有 live 才暴露的。
验证:go 6 包测试+tsc+41 vitest 全绿;live 造差异单对账正确抓出
order_without_ledger、清账后回零差异;补偿器启动日志+渠道未配置空转不炸;
浏览器验订单流卡片+一键对账绿条。TTL 过期路径需真渠道触发,部署后自然覆盖。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
2026-07-18 11:25:37 +08:00 |
|