shanshanzhong147
634b5a7bd0
feat(simnet): add SimNet protocol end-to-end support
...
- model: SimNet protocol fields + NormalizeSimnet; type+port protocol uniqueness
- server config: OmnXT runtime config delivery via compatible() simnet case
- credentials: derive per-user psk/key_id from subscription (pkg/simnet), no new table
- subscription: adapter buildOmnxtSimnetConfigs + base64 buildOmnxtProtocolLinks + OmnXT SimNet application (migration 02161)
- UA gating: hide experimental protocols from non first-party clients (download + JSON node-list)
- admin: normalize simnet on create/update and on GET responses
- tests: 21 simnet unit tests; full suite green
2026-07-27 00:17:02 -07:00
shanshanzhong147
5ef3f2717e
feat( #4 ): 抽奖中奖用户文案调整
...
- 免费时长: 用户消息改为「稍后您的 N 天免费时长将会自动添加至您的账户。
如果超过24小时未添加成功,请联系人工客服处理。」(N=中奖天数动态)
ledger.payload.message 仍保留descriptive内部文案供后台对账
- 人工发放(crypto/manual): 消息改为「请凭此截图直接联系人工客服兑换奖励。」
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-17 02:56:38 -07:00
shanshanzhong147
2c1ee78bc4
修复( #4 ): 奖品更新支持修改 type(原来 type 被静默忽略)
...
UpdateLotteryPrize 之前可改字段漏了 type,导致 PUT 带 type 不生效、
只能删了重建。补上 UpdateAdminLotteryPrizeRequest.Type + fields["type"]。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-17 01:03:42 -07:00
shanshanzhong147
13bafd5847
feat( #4 ): 删除抽奖活动接口 DELETE /admin/lottery/activities/:id
...
- 软删活动 + 硬删其奖品(同事务)+ 审计日志
- 运行中的活动禁止删除(需先暂停)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-17 00:16:34 -07:00
shanshanzhong147
e8e3a3a72b
feat( #4 ): 后台抽奖记录列表 GET /admin/lottery/draws
...
- 分页列出 lottery_draw,join 奖品快照 + 用户邮箱 + 发放账本(grant_ledger)
- 支持 activity/user/win/dispatch_state/prize_type/时间窗过滤
- 展示中奖人/奖品/发放状态/发放结果(如"已新建订阅并加 30 天")
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-16 23:36:53 -07:00
shanshanzhong147
58c346abec
重构( #4 ): 抽奖奖品改/删 id 走 URL path(RESTful)
...
- PUT /admin/lottery/prizes/:id、DELETE /admin/lottery/prizes/:id
- handler 从 c.Param("id") 取 id;types 用 path:"id"
- 同步 apis/admin/lottery.api 定义
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-16 19:45:23 -07:00
shanshanzhong147
abd8c068b6
修复( #4 ): 抽奖发奖修正 — 保底奖不参与随机 + 无订阅时按套餐自动新建订阅
...
- weighted_picker: Pick 排除 is_fallback 保底奖,避免真实奖被"谢谢参与"挤占
- vpn_duration handler: 无活跃订阅且奖品配置 subscribe_id 时,按该套餐在抽奖
事务内新建订阅并发放时长;未配置则沿用旧的安全跳过
- 补充单测:picker 排除保底奖、vpn_duration 自动新建订阅、draw 端到端链路
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-14 09:31:08 -07:00
shanshanzhong147
cce147108c
修复( #4 ): 抽奖 8 宫格默认值 + 100500 msg 脱敏
...
Closes part of HIF-4 (Stage 2 前端集成反馈 F8 + F10)
F8 (P1): Activity.GridSize 默认值 9 → 8
- migration 02160 ALTER DEFAULT 8(幂等)
- 02156 up.sql 注释同步更新
- admin/lottery.go 兜底值 9 → 8
- 布局 A:3x3 挖中心,中心是抽奖按钮不是奖品
F10 (P1): wrapInternal msg 脱敏
- 内部错误 err.Error() 只写日志,不外传
- 用户端 msg 只带通用文案 (xerr.MapErrMsg lookup)
- 回归护栏:TestWrapInternal_ScrubsErrorDetailsFromMsg + TestWrapInternal_PreservesCodeErrors
CI 全绿;无 API 契约变更;F9 独立在 PR #45 处理
2026-07-12 19:55:25 -07:00
shanshanzhong147
980e5adb90
修复( #11 ): 抽奖 Stage 2 Claim.ClaimData 空字符串违反 MySQL JSON 校验
...
Closes HIF-11 (Stage 2 P0)
F7 (P0): Claim.ClaimData=\"\" → MySQL error 3140 (Invalid JSON text: The document is empty)
- 修法:service.go:450 dispatchOrEnqueueClaim 构造 Claim 时显式 ClaimData=\"{}\"
- 完全同构 PR C PrizeSnapshot.Config / PR E UnmetReasons=\"[]\" / PR F Payload=\"{}\" 三处守卫
回归护栏:TestDraw_ManualClaimClaimDataIsValidJSON + claimDataIsValidJSON per-arg matcher(既拒 \"\" 也 json.Valid 校验)
Backend 反向自检:git stash fix 后测试立即抛 F7 regression 明确错误
CI 全绿;2 files, +116/-5;无 DB / API / flag 变更
架构师复盘:这是 code review 第 4 次漏检查同类 JSON 空串守卫(Stage 1 的 F4/F6 + Stage 2 的 F7)。感谢 QA 三次挖坑救场。Stage 3 起 code review 硬性 checklist 第一步:grep -RIn type:json sweep 全库。
2026-07-12 19:09:54 -07:00
shanshanzhong147
07409eb602
新功能( #4 ): 抽奖 Stage 2 人工奖领奖工单(crypto / physical / manual_other)
...
Closes HIF-4
Stage 2 交付:人工奖领奖工单完整闭环。crypto / physical / manual_other 三类奖品从抽中到 mark-paid 的全流程可用。
- 迁移 02159_lottery_claim:UNIQUE(draw_id) + 3 支持索引,状态机 pending_claim→reviewing→paying→paid,rejected 可复活,超时 expired
- 3 个 PrizeHandler:Dispatch→ErrDispatchNotSupported 兜底、ClaimSchema 各自形态、ValidateClaim 表驱动
- BuildCryptoClaimSchema:抽中时按奖品 config.networks 注入 enum,前端下拉直接可用
- Draw service dispatchOrEnqueueClaim:人工奖同 tx 插 pending_claim(回滚双清),nonce 重放回读 ExpiresAt + ClaimFormSchema
- POST /claim 实装:ownership 校验 → prize 类型校验 → handler.ValidateClaim → crypto network 白名单二次校验 → tx CAS status IN (pending_claim, rejected) AND expires_at > now
- Admin CRUD 5 接口:list(IN 批拉 snap + user,无 N+1)、summary(GROUP BY 一次拿计数 + overdue 单查)、approve/reject/mark-paid 全走 CAS + audit
- Scheduler @every 1h 扫过期,级联 lottery_draw.dispatch_state → expired
- 新增错误码 100005-100011(already_submitted / invalid_claim_data / draw_not_found / not_your_draw / claim_expired / claim_state_invalid)
- Rebase 后 Stage 2 测试主动 reuse PR E 的 unmetReasonsNotEmpty + evaluatedAtNotZero matcher,人工奖分支若绕过守卫会立即挂
- Stage 1 全部 4 处 guardrail 后端 rebase 时自检过:UnmetReasons、EvaluatedAt、GrantLedger.Payload、AdminMetaMiddleware 全保留
CI 全绿;28 files, +2442/-126;覆盖率 handler 78.9% / model.lottery 74.3% / draw 68.7% / queue/lottery 76.9%
2026-07-10 04:01:34 -07:00
shanshanzhong147
117dc0d6a7
修复( #3 ): 抽奖 GrantLedger.Payload 空字符串违反 MySQL JSON 校验
...
Closes HIF-3 (Stage 1 P0 from QA smoke - third and final of the same class)
F6 (P0): GrantLedger.Payload=\"\" → MySQL error 3140 (Invalid JSON text: The document is empty)
- 修法:方式 A 对称守卫 Payload=\"{}\" (与 PR E UnmetReasons=\"[]\", PR C PrizeSnapshot.Config=\"{}\" 三处守卫模式统一)
- QA 已 sweep 全部 6 个 lottery JSON 列,这是最后一处漏守
- handler 成功 dispatch 后会 UpdateColumn 覆盖真实 payload;Reserve 阶段用 {} 兜底
回归护栏:TestReserve_EmptyPayloadDefaultsToEmptyJSONObject 用 payloadNotEmptyString per-arg matcher
CI 全绿;2 files, +83/-0
架构师复盘:三次同一 pattern 漏检查(F2 audit ctx keys / F4 UnmetReasons / F6 Payload)。Stage 2 起硬性规则:grep -RIn 'type:json' internal/model/ 全量清单逐列 sweep + 每列至少一条 per-arg matcher 单测。感谢 QA 三次挖坑。
2026-07-09 09:27:14 -07:00
shanshanzhong147
c92495c5b9
修复( #3 ): 抽奖 EligibilitySnapshot.UnmetReasons 空字符串违反 MySQL JSON 校验
...
Closes HIF-3 (Stage 1 P0 from QA smoke)
F4 (P0): EligibilitySnapshot.UnmetReasons=\"\" → MySQL error 3140 (Invalid JSON text: The document is empty)
- 修法:方式 A 对称守卫 UnmetReasons=\"[]\" (与 PrizeSnapshot.Config 的 \"{}\" 守卫模式一致)
- Stage 1 只有 passed=true 分支进 insertSnapshots,语义正确
F5 (P2 顺手): EvaluatedAt 显式 time.Now() 避免 GORM zero time 触 sql_mode STRICT
回归护栏:TestInsertSnapshots_UnmetReasonsIsValidJSON 用 sqlmock per-arg matcher (unmetReasonsNotEmpty + evaluatedAtNotZero),退化时 t.Fatalf 立即抛出
CI 全绿;2 files, +108/-0;无 DB 变更
架构师侧透明:这是 PR C review 时漏检查——PrizeSnapshot.Config 的空串守卫看到了,但没同步检查 EligibilitySnapshot.UnmetReasons。single-code-path 的错觉是审查盲点,Stage 2 起会 sweep 所有 JSON/字符串写库字段。
2026-07-09 08:56:19 -07:00
shanshanzhong147
92cf2921dd
修复( #3 ): 抽奖 Stage 1 后台审计 IP/UA 丢失 + 冒烟脚本 Bearer 前缀
...
Closes HIF-3 (Stage 1 P1 defects from QA smoke report)
F2: admin_action_log.ip / user_agent 恒为空
- 根因:requestMeta 从 ctx 读裸字符串 key,全库无 writer 塞
- 修复:新增 AdminMetaMiddleware 用 typed constant.CtxKeyIP / CtxKeyUserAgent
- 挂载:lottery admin 组末尾(不 gate access)
- 回归护栏:UsesTypedKey + IgnoresBareStringKeys 双向断言 typed key,防止未来退化回裸字符串
F3: qa/lottery/stage1_curl.sh Bearer 前缀
- 删除两处 Bearer 前缀 + 加注释说明 ppanel AuthMiddleware 不 strip
单测 5 用例全绿;scope 严格限于 lottery admin 组,其它路径零改动;无 DB 变更。
Merged: architect review 后,将触发 staging 二次部署 — 期望这次能一并解决 shanshanzhong147 手工 SSH 后 Lottery.Enable=true 未生效的问题(若真是 mount/restart 未正确 pick up)。
2026-07-09 07:45:58 -07:00
shanshanzhong147
ce3babcc33
新功能( #3 ): 抽奖 Stage 1 收官 — 用户 API + 后台 CRUD + 集成
...
Closes HIF-3
Stage 1 完整闭环 PR C:用户 API + 后台 CRUD + 抽奖事务服务 + 审计 + QA curl。合并后 Stage 1 可交测试。
架构师 review R1(rulecaps depth≤8/nodes≤64/bytes≤8KB)+ R2(InviteHook source_ref 加 order: 前缀)已全部落地。
- 迁移 02158_admin_action_log:后台写操作审计
- xerr 100xxx 段:抽奖错误码(NotEligible/NoChances/ActivityEnded/RateLimited/NotClaimable/InternalError/RuleTooDeep/RuleTooMany/RuleTooLarge)
- feature flag config.Lottery.Enable 默认 false,合并后线上零副作用
- draw service:feature flag → rate limit → pre-tx reads → nonce dedupe → Consume → Pick → 乐观扣库存 → 双快照 → Dispatch → finalize
- 用户 API 4 个:GET /config、POST /draw、GET /records、POST /claim(Stage 1 返回 100010)
- 后台 CRUD:活动 / 奖品 / rules PUT(rulecaps gate)/ chances/grant
- audit.WriteAdminAction:与调用方 tx 同生共死,SHA1 body 摘要
- QA 脚本:qa/lottery/stage1_curl.sh 全链路 curl
测试覆盖:model 76.5% / draw 69% / handler 68.3% / hook 91.9% / rulecaps 87.8% / audit 100%
100 并发抢库存 + 10000 次概率分布 e2e 推 QA 环境(sqlmock 无法忠实模拟 InnoDB 行锁)
CI 全绿:构建/Vet/测试 + golangci-lint
2026-07-08 22:33:18 -07:00
shanshanzhong147
a46fb83054
新功能( #3 ): 抽奖 Stage 1 handler 真实业务对接 + 邀请钩子
...
Closes HIF-3 (阶段 PR B)
PR B:handler 真实业务对接 + 邀请钩子(迭代含 R1 修复)
- 迁移 02157_lottery_grant_ledger:external_ref UNIQUE 作为发奖幂等键
- log.CommissionTypeLottery=339(架构师批准的新常量)
- DispatchRequest.IdempotencyKey(架构师 review 建议第 2 条)
- GrantLedger + LedgerService.Reserve:INSERT ON CONFLICT DO NOTHING 幂等 upsert
- VPNDurationHandler:ResolveEffectiveUser 归位家庭 owner + UpdateSubscribe,ExpireTime 三分支对齐 grantGiftDays
- CommissionHandler:UpdateCommission + WriteCommissionLog(339),发给中奖者本人,不做家庭组归位
- Handler 从 model 层迁到 logic 层(避免 model → logic 反向依赖);noop 保留在 model 层
- InviteHook:fire-and-forget 独立 goroutine + 10s timeout,扫 running 活动的 invite_success 源
- ServiceContext 新增 LotteryChance / LotteryLedger / LotteryInviteHook
- activateOrderLogic.handleCommission 两条分支通过 invokeInviteHookIfEligible 助手触发,助手内统一 gate IsNew(架构师 R1 打回后的修复)
架构师 R1 打回:branch B 未按"首次付款激活"gate,续费也会给 referer 发抽奖机会 → 已通过助手函数集中收拢,避免 branch A/B 判断漂移。
测试覆盖率:model 76.5% / handler 73.2% / hook 91.7%;补 4 个回归测试覆盖 IsNew 门槛(含关键的 DoesNotFireOnRenewal)。
线上仍零可见变更:无对外路由,钩子仅在活动 status=running 时生效,Stage 1 全流程无活动记录时 loadRunningActivities 返回空。
2026-07-08 21:19:28 -07:00
shanshanzhong147
9933d34bdd
新功能( #3 ): 抽奖活动 Stage 1 骨架(DB + 规则引擎 + Handler 抽象)
...
Closes HIF-3
Stage 1 骨架:7 张表迁移 + 门槛规则引擎 + 加权选奖 + 次数入账/消耗(幂等)+ 发奖 handler 抽象与注册表。
- 单测覆盖 75.5%(未覆盖行 = stub handler ErrNotImplemented,合理)
- CI 全绿(构建/Vet/测试 + golangci-lint)
- 骨架不接入真实业务,vpn_duration/commission handler 在 Dispatch 中返回 ErrNotImplemented;合并后线上零变更
架构师 review 通过,4 项决策已在 issue 上给出:
1. 邀请转化语义 = 首次付款激活
2. 佣金日志类型 = 新增 CommissionTypeLottery=339
3. 家庭组归属 = 穿透到 owner
4. 管理端 IP 白名单 = 不做(推到 nginx/ingress 层)
后续 PR B/C 补真实业务对接 + 用户 API + 后台 CRUD + 集成/并发/概率测试。
2026-07-08 20:05:05 -07:00
shanshanzhong147
d2710d356f
fix: align revenue statistics with order type
2026-06-23 09:29:17 -07:00
shanshanzhong147
f0a5288e20
修复( #51 ): 修正封禁用户订阅返回码
...
封禁用户拉订阅时返回纯文本 500 退化为业务码 20004,统一走 result.HttpResult。覆盖 /api/subscribe 和泛域名两条入口,补回归测试。
2026-06-16 07:53:37 -07:00
shanshanzhong147
77fa0cadd2
新功能( #48 ): 用户封禁链路接入 ( #32 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-16 06:28:47 -07:00
shanshanzhong147
3d1a31a19f
修复: 用户维度限速在过期节点组分支生效 + 统一 speed_limit 单位为 Mbps
...
- getServerUserListLogic.getExpiredUsers 之前完全忽略 user_subscribe.speed_limit,
现在带出用户级覆盖并与过期节点组 speed_limit 取更严(mergeSpeedLimit:0 视为无限制)
- node_group.SpeedLimit 注释从 "KB/s" 修正为 "Mbps"(旧注释是笔误,实际下发节点的
ServerUser.SpeedLimit 字段语义就是 Mbps,节点端 ppanel-node 按 *1e6/8 换算为 Byte/s)
- apis/node/node.api 给 ServerUser.SpeedLimit 加 Mbps 单位注释
- 新增 TestMergeSpeedLimit 表驱动测试覆盖 8 种边界
主链路(活跃用户、套餐 traffic_limit 阶梯)行为不变,已在生产 (server_id=52)
验证 147 个限速用户下发正确,与 DB 完全对应。
2026-06-12 22:39:15 -07:00
shanshanzhong147
08434cfa32
新功能: 佣金回退日志 content 改成中文友好描述
...
之前接口返回原始 JSON 字符串(前端不好展示):
"{\"type\":333,\"amount\":-649,\"order_no\":\"xxx\",\"timestamp\":\"...\"}"
改为可读文字:
333 订单退款回佣 → "订单退款回佣(订单号 xxx)"
337 提现驳回 → "提现申请被驳回,佣金已退回"
338 提现取消 → "已取消提现,佣金已退回"
同时影响以下两个接口的 content 字段:
- /v1/public/user/withdrawal_log?biz_type=commission_refund (deprecated)
- /v1/public/user/commission_return_log
2026-06-12 20:35:27 -07:00
shanshanzhong147
be09a115ec
新功能: withdrawal_log 接口加 summary 字段 + admin 禁直接扣减 commission
...
接口侧:
- /v1/public/user/withdrawal_log 响应新增 summary 字段, 包含:
- commission_balance (当前余额)
- locked_by_pending (待审批占用)
- available_to_withdraw (可提现)
- total_historical_amount (已通过提现总额)
- total_refunded_amount (退款回扣总额)
- total_income_amount (收入总额)
- 前端可据此自洽展示账目对账, 用户能在一个接口里看清整笔账
代码守护:
- updateUserBasicInfoLogic 拒绝任何 change<0 的 commission 修改
- 扣减必须走 approveWithdrawal 写 type=334 日志
- 防止未来 admin 误操作再次造成 user.commission 与 system_logs 失衡
时间戳修复:
- queryWithdrawalLogLogic 和 queryCommissionReturnLogLogic 的时间戳
从 UnixMilli 改回 Unix (秒级), 符合项目"后端统一秒级"约定
测试:
- 补 buildSummary 的 4 个 mock 查询期望
- 加 summary 字段值正确性断言
2026-06-12 19:49:03 -07:00
shanshanzhong147
cfb253d96f
修复( #38 ): commission_refund 收窄到只查 type=333(移除 337/338 提现退佣混入)
...
Closes HIF-38
owner 业务定义:commission_refund 这个分类只应返回「下级退单导致用户拿到的佣金被扣回」记录。
改动:
- queryWithdrawalLogLogic.go: ?biz_type=commission_refund 分支 SQL 过滤从 IN(333,337,338) 改为 = 333
- 同步更新 logic/handler 单测
不在范围:
- /commission_return_log(新推荐接口)继续返回 333/337/338 — 前端暂未切,owner 决定不动
- 337/338 不另外开 UI 入口 — 提现记录 status 字段(rejected/cancelled)已表达
2026-06-12 01:51:48 -07:00
shanshanzhong147
f11097ab83
新功能( #26 ): 拆分退款日志查询接口
...
拆分用户中心退款日志查询:
- 新增 GET /v1/public/user/commission_return_log(333/337/338)
- 旧 GET /v1/public/user/withdrawal_log?biz_type=commission_refund 复用新逻辑做兼容
- 默认 withdrawal_log 行为不变(仍查 withdrawals 表)
- 单测覆盖 333/337/338 happy path、坏 JSON 跳过、object_id 隔离、handler 级 HTTP 响应
父 issue: HIF-25
子 issue: HIF-26
2026-06-11 22:02:22 -07:00
shanshanzhong147
c39bfd39dd
修复( #21 ): 禁止通用订单状态写入claimed ( #20 )
...
* 修复(#21 ): 禁止通用订单状态写入claimed
Co-authored-by: multica-agent <github@multica.ai >
* 文档(#21 ): 补充PR说明
Co-authored-by: multica-agent <github@multica.ai >
---------
Co-authored-by: multica-agent <github@multica.ai >
2026-06-11 02:47:48 -07:00
shanshanzhong147
de388ac1ef
fix: expose user subscription speed override
测试环境部署 / 构建镜像并部署到测试环境 (push) Has been cancelled
持续集成 / 构建/Vet/测试 (pull_request) Has been cancelled
持续集成 / golangci-lint (pull_request) Has been cancelled
2026-06-11 02:39:16 -07:00
shanshanzhong147
6157b2c571
修复( #23 ): 修复用户订阅列表未回显用户级限速
...
* 修复(#23 ): 修复用户订阅列表未回显用户级限速
Co-authored-by: multica-agent <github@multica.ai >
* 修复(#23 ): 移除 PR 草稿文件
Co-authored-by: multica-agent <github@multica.ai >
---------
Co-authored-by: multica-agent <github@multica.ai >
2026-06-11 01:50:47 -07:00
shanshanzhong147
268ae1ebf8
修复( #19 ): 续费订单支持限时活动价 ( #19 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-10 00:31:24 -07:00
shanshanzhong147
f5ad26b943
新功能( #18 ): 用户提现记录支持区分提现和退佣 ( #18 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-10 00:19:12 -07:00
shanshanzhong147
9b5ff89ee8
修复( #17 ): 修复订阅流量限制更新未生效
...
Closes HIF-17
2026-06-09 22:56:40 -07:00
shanshanzhong147
e74958e17f
修复( #16 ): 修复订单退款状态与后台恢复冲突 ( #16 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-09 11:31:31 -07:00
shanshanzhong147
21811f4d63
修复( #13 ): 邀请列表返回设备标识和设备号 ( #15 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-08 22:24:51 -07:00
shanshanzhong147
24caf58987
修复( #12 ): 优化用户列表限速计算性能 ( #14 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-07 23:29:15 -07:00
shanshanzhong147
b98d718f3c
修复( #11 ): 修复家庭组订单退款订阅归属 ( #13 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-07 08:23:19 -07:00
shanshanzhong147
bcb8cd222c
修复( #10 ): 禁止通用订单状态接口标记退款 ( #12 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-05 23:35:42 -07:00
shanshanzhong147
34cd1c524e
修复( #8 ): 分组管理核心缺陷与测试覆盖 ( #11 )
...
Co-authored-by: multica-agent <github@multica.ai >
2026-06-04 03:13:38 -07:00
shanshanzhong147
19d28a8f89
修复( #1 ): 服务器用户列表缓存按 protocol 隔离 + 兜底不写缓存
...
服务器用户列表缓存跨协议污染修复(HIF-1 / 详见 PR #5 四件套):
1. 缓存 key 加 protocol 维度(`server:user:{server_id}:{protocol}`),对齐 ServerConfig 已有约定
2. 显式枚举协议清除用户列表缓存(AllProtocols + ServerUserListCacheKeysForServer),不用 SCAN
3. 三个兜底分支不写缓存 + Errorw 日志(带 server_id + protocol 字段)
4. hysteria2 → hysteria 兼容归一化 + 6 个新单测
Closes HIF-1
2026-06-03 05:37:03 -07:00
shanshanzhong147
c540091ef9
修复( #143 ): 邀请权益查询排序,消除 map 迭代序 flake
...
Closes HIF-143. 让 `inviteeAndInviterIds` 在 append 后 `slices.Sort` 升序,让 sqlmock IN 参数匹配稳定,同时让生产 SQL EXPLAIN 计划稳定。修复 GitHub Actions Deploy Staging 自 2026-06-03 03:51 起连续 8 次 `go test ./...` 失败问题。
测试: go test -count=10 修复前 4/10 fail, 修复后 30/30 pass.
改动: 2 files, +4/-2 (internal/logic/admin/invite/{benefits.go,benefits_test.go})
2026-06-02 22:04:16 -07:00
shanshanzhong147
87ebfa1fac
修复( #137 ): DeferCloseOrder 关单前反查支付网关 (启用 confirmationPayment)
...
Build docker and publish / build (20.15.1) (push) Failing after 18m47s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m31s
Squash merge of fix/137-defer-close-反查网关 (1367c4f ).
DeferCloseOrder 直接将 status=1 订单关单,会把已经在网关侧完成支付但
notify 静默失败的订单错误关闭。本次在关单前调用 EPay 的网关查询接口
(confirmationPayment) 拿到三态结果:
- Paid: 原子化把 status 1->2,写回 trade_no,再投递 asynq 走激活流程
- Unpaid: 继续原来的 close 事务,把 status 改为 cancelled
- Unknown / 网关失败: 保持 status=1,下一轮 DeferClose 再试
新增 closeOrderLogic_test.go (232 行),覆盖三态分支 + recoverPaidOrder
的并发幂等。单测全量 PASS, go build + go vet 均干净。E2E 验收因测试环境
访问受限暂未跑,QA 已在 issue 上注明阻塞原因 (qa_partial_blocked_on_e2e_access)。
Co-authored-by: multica-agent <github@multica.ai >
2026-06-02 20:24:32 -07:00
shanshanzhong147
ac25eb4d91
修复( #140 ): 去掉 cancelWithdrawalLogic 在新流程下的重复退款
...
Build docker and publish / build (20.15.1) (push) Has been cancelled
Build docker and publish / build (20.15.1) (pull_request) Has been cancelled
Squash merge of fix/140-去掉撤销提现的重复退款 (86896cd ).
HIF-22 引入 rejectWithdrawal 反查支付网关后,cancelWithdrawalLogic 仍在
事务体内调用 UpdateCommission(+amount),对已在 rejectWithdrawal 中退还
的金额做了二次退款。本次只保留 withdrawal.status -> Cancelled,与
rejectWithdrawal 行为对齐。
新增 cancelWithdrawalLogic_test.go 用 sqlmock 严格断言:撤销 happy path
只触发 BEGIN / SELECT FOR UPDATE / UPDATE withdrawals / COMMIT 四条 SQL,
对 user / system_logs 零读零写。
Co-authored-by: multica-agent <github@multica.ai >
2026-06-02 20:23:45 -07:00
架构师
750b7be424
修复( #136 ): EPay notify 静默失败硬化 + 回写 trade_no
...
Build docker and publish / build (20.15.1) (push) Failing after 8m55s
Build docker and publish / build (20.15.1) (pull_request) Failing after 22m22s
P01 签名校验失败由 return nil 改为返回 xerr.SignatureInvalid,handler 回 400,EPay 网关重试;Debug 旁路保留
P02 订单不存在维持 error 返回,仅 status=5 Finished 保留 nil 作幂等短路
P03 支付完成路径写入 order.trade_no = req.TradeNo,再 UpdateOrderStatus 刷缓存
附加:TradeStatus != TRADE_SUCCESS 降为 INFO 日志 + metric epay_notify_trade_not_success
抽离纯决策函数 evaluateEPayNotify,新增单测覆盖 4 个核心分支 + 签名优先级回归 + SQL 写路径 + URL 解析(9/9 PASS,-race 干净)
仅 internal/logic/notify/ePayNotifyLogic.go 与对应单测,无其他文件变更。
Co-authored-by: multica-agent <github@multica.ai >
2026-06-01 03:08:31 -07:00
架构师
aa11588c8f
修复( #129 ): 新注册无历史用户不再误命中沉默促销
...
Build docker and publish / build (20.15.1) (push) Failing after 22m17s
Build docker and publish / build (20.15.1) (pull_request) Failing after 22m28s
evaluateInactiveUserPromo 在 ErrRecordNotFound 时之前 return true,导致
新注册无任何订阅历史的用户被错误判定为"沉默用户"命中 inactive_user 规则。
改为 return false 保持判定语义一致:没有历史订阅 ≠ 沉默用户。
Squash from origin/fix/129-新注册误命中沉默促销 (77377ed )
Co-authored-by: multica-agent <github@multica.ai >
2026-06-01 00:28:26 -07:00
shanshanzhong147
5b9f384f81
修复( #132 ): 退款幂等校验 + 已退款订单防重新激活
...
Build docker and publish / build (20.15.1) (push) Failing after 21m4s
Build docker and publish / build (20.15.1) (pull_request) Failing after 21m47s
P01:refundOrderLogic.RefundOrder 在事务内 FOR UPDATE 后、lockCommissionSource 前新增
333 退款日志扫描,命中即返回 OrderAlreadyRefunded(61006),不再写日志/扣 commission/
改 order.status。
P02:堵住已退款订单状态被回退入口
- queue/logic/order/stuckOrderRecoveryLogic.go:批扫 status=6 时新增 333 日志守卫,
已退款订单不再被重置为 5 + 重新入队 activate(HIF-131 trace 中订单 53647 被刷回 5
的真凶)
- queue/logic/order/activateOrderLogic.go:releaseClaim 同步加守卫做防御性兜底
新增 internal/model/log/refund.go 共享 helper HasRefundCommissionLog:
type=33 + content LIKE 走索引粗筛,再 JSON 反序列化确认 content.type==333 AND
content.order_no==orderNo,防 LIKE 子串误判。
测试:单元测试覆盖正常退款 / 已有 333 日志拒绝 / 子串误判防御 / 脏 JSON 容错;
sqlmock 严格断言命中后事务序列只含 BEGIN/SELECT order FOR UPDATE/SELECT
system_logs/ROLLBACK,无任何 commission 写入。
不做:calculateCommission、status 枚举拆分、表结构变更、支付通道 notify、用户余额回补。
Co-authored-by: multica-agent <github@multica.ai >
2026-05-31 20:19:35 -07:00
shanshanzhong147
7236ca4cf2
修复( #128 ): 按规则类型决定促销资格,让 InactiveUser/Campaign 对老用户生效
...
Build docker and publish / build (20.15.1) (push) Has been cancelled
Build docker and publish / build (20.15.1) (pull_request) Has been cancelled
此前 calculatePurchasePrice 把 allowPromo 绑死在 orderType == 1,导致只要用户
有任何过往付费订阅(含已过期),就会被 paidSubscriptionQuery 路由为续费
(orderType=2),跳过所有促销评估。后果:
- InactiveUser 召回促销永远无法触发(其目标人群恰好就是有过期订阅的用户)
- Campaign 全员活动对老用户 / 升级加购场景完全失效
- 套餐列表(loadSubscribePromoMap)直接调 EvaluatePromo 不感知 orderType,
可能显示促销价但下单时却拿到原价
修复方式:把 isFirstPurchase 下放给 EvaluatePromo,由规则类型决定 gating:
- NewUser 要求 isFirstPurchase=true(保留首购语义)
- InactiveUser 由规则自身的"上次订阅过期 N 月以上"条件判定
- Campaign 时间窗内对任意用户生效
新增 common.HasPaidSubscription 助手,套餐列表与下单走同一份 isFirstPurchase
判定,确保展示价与实际下单价口径一致。
测试:补充 EvaluatePromo / calculatePurchasePrice 的 NewUser 屏蔽 + Campaign
放开用例;更新 loadSubscribePromoMap 测试覆盖新增 HasPaidSubscription 查询。
注:renewalLogic.go 仍未接入促销(属于方向 B 的彻底统一,本次未涵盖)。
2026-05-31 19:00:30 -07:00
shanshanzhong147
ae126296e3
修复( #128 ): 统一续费场景促销判断
...
Build docker and publish / build (20.15.1) (push) Failing after 23m3s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m4s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-30 23:12:39 -07:00
shanshanzhong147
1e99cfb83c
修复( #128 ): 兼容促销规则毫秒时间戳
...
Build docker and publish / build (20.15.1) (push) Failing after 19m21s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m48s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-30 04:16:21 -07:00
shanshanzhong147
0659a930f8
修复( #128 ): 修复家庭成员邀请流水可见性
...
Build docker and publish / build (20.15.1) (push) Failing after 18m41s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m16s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-30 03:27:04 -07:00
shanshanzhong147
c2d1b5a0d8
修复( #130 ): 统一家庭成员促销资格口径
...
Build docker and publish / build (20.15.1) (push) Failing after 22m15s
Build docker and publish / build (20.15.1) (pull_request) Failing after 18m6s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-30 01:58:15 -07:00
shanshanzhong147
3644e9ce3f
修复( #128 ): 统一促销价格资格口径
...
Build docker and publish / build (20.15.1) (push) Failing after 18m40s
Build docker and publish / build (20.15.1) (pull_request) Failing after 19m24s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-29 22:42:04 -07:00
shanshanzhong147
e5d6539d79
修复( #126 ): 修复家庭组邀请记录漏查验收分支
...
Build docker and publish / build (20.15.1) (push) Failing after 20m3s
Build docker and publish / build (20.15.1) (pull_request) Failing after 21m10s
Co-authored-by: multica-agent <github@multica.ai >
2026-05-29 20:35:37 -07:00