Files
junhong_cmp_fiber/.planning/phases/02-perm-fin-realname-poll/02-CONTEXT.md
2026-03-28 10:11:04 +08:00

8.2 KiB
Raw Blame History

Phase 2: 权限/财务/实名/轮询修复 - Context

Gathered: 2026-03-28 Status: Ready for planning

## Phase Boundary

修复和补全四个独立业务域的已知断点企业账号权限边界PERM、提现财务流程完整性FIN、实名激活架构重构REALNAME/G、轮询调度小修POLL。共 14 个需求全部为修复性变更无新功能模块REALNAME 涉及破坏性 DB 迁移DROP 旧字段 + ADD 新字段)。

## Implementation Decisions

执行分组策略

  • D-01: 拆分为 2 个 Plan,而非 1 个或 4 个:
    • Plan 1小修合并BPERM-01/02+ HPOLL-01/02/03/04+ CFIN-01/02/03
    • Plan 2REALNAME 独立)GREALNAME-01~05—— 单独 Plan独立可回滚
  • D-02: Plan 1 内部执行顺序B → H → C权限修复最小化先上轮询小修次之财务流程收尾
  • D-03: Plan 2REALNAME必须原子执行不可拆分 —— 5 个需求在一个 Plan 的同一事务性推进中完成

企业权限PERM

  • D-04: asset.go:Resolve —— 移除企业账号拦截(企业应可查询被授权资产)
  • D-05: asset.go:Refresh —— 新增企业账号拦截(企业只读,不可触发运营商刷新)
  • D-06: 其他 8 个 AssetHandler 方法维持现状(无拦截/已拦截均不动)

财务流程FIN

  • D-07: FIN-01commission_withdrawal/service.go:Approve() 补填 approved_by / approved_at 字段
  • D-08: FIN-02commission_withdrawal.go:Reject() 在 BodyParser 后补 validator.Validate(&req)
  • D-09: FIN-03激活配置并发保护使用 GORM v2 语法 tx.Clauses(clause.Locking{Strength: "UPDATE"}) —— 规格书写的是 GORM v1 的 gorm:query_option,项目实际用 GORM v2agent 使用正确语法

REALNAME 架构重构G

  • D-10: DB 迁移策略:直接 ADD DEFAULT 'from_activation',旧字段 DROP
    • 不做 enable_realname_activation=false → from_purchase 的数据迁移
    • 原因:旧字段控制的是"是否等实名",新字段 expiry_base 控制的是"计时起点",两者语义不同,无法直接映射;旧字段已无存在意义,直接清理
    • 现有生产套餐记录全部默认 from_activation,后续如需 from_purchase 由运营手动修改
  • D-11: activateMainPackage 激活决策逻辑改为按三维判断:
    • 行业卡(card_category == "industry")→ 永远直接激活
    • C 端购买(buyerType == "personal"order.OrderType 标记 C 端)→ 直接激活(已前置实名检查)
    • 后台囤货 → 按 pkg.ExpiryBasefrom_purchase 直接激活,from_activation 进入等实名队列
  • D-12: C 端实名检查改为按卡类型(card_category == "normal")判断,删除 packagesNeedRealname() 函数
  • D-13: ActivateByRealnameExpiryBase 选择计时基准:from_purchaseCreatedAtfrom_activation 用当前时刻

轮询系统小修POLL

  • D-14: POLL-01scheduler.go 主调度循环中,复用已有的 processManualQueue / processTimedQueue,补充 polling:protect 的调度接入
  • D-15: POLL-02stopCardWithRetry / resumeCardWithRetry 两处,gatewayClient == nil 时改为返回明确业务错误
  • D-16: POLL-03GetPackages() 接口新增分页参数,默认 page=1, pageSize=50,最大 100
  • D-17: POLL-04package_series/service.go:Delete() 前置 CountBySeriesID() 检查,有关联套餐则拒绝删除

Agent's Discretion

  • FIN-03 事务内 FOR UPDATE 的具体 GORM v2 clause.Locking 写法agent 参考项目现有 clause.OnConflict 用法对齐风格
  • isCEndPurchase 的具体推断字段(buyerType == "personal" 或 order 字段agent 阅读 activateMainPackage 上下文确认最准确的判断路径
  • 每个子修复独立一个 commit沿用 Phase 1 的粒度规范)

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

修复规格(核心参考)

  • .sisyphus/plans/修正业务-完整方案.md — 完整修复规格书,含每个需求的文件路径、代码片段、人工验收清单:
    • 方案 BPERM-01/02第 766-863 行
    • 方案 CFIN-01/02/03第 864-944 行
    • 方案 GREALNAME-01~05第 1492-1611 行
    • 方案 HPOLL-01~04第 1612-1711 行

核心修改文件

  • internal/handler/admin/asset.go — PERM-01删 Resolve 企业拦截、PERM-02加 Refresh 企业拦截)
  • internal/service/commission_withdrawal/service.go — FIN-01Approve 补审批人字段)
  • internal/handler/admin/commission_withdrawal.go — FIN-02Reject 加 Validate
  • internal/service/commission_withdrawal_setting/service.go — FIN-03并发行锁
  • internal/store/postgres/wechat_config_store.go — FIN-03微信配置并发行锁
  • internal/model/package.go — REALNAME-01drop EnableRealnameActivationadd ExpiryBase
  • internal/model/dto/package_dto.go — REALNAME-05DTO 更新 expiry_base 字段)
  • internal/service/order/service.go — REALNAME-02activateMainPackage 三维决策)
  • internal/service/client_order/service.go — REALNAME-03按卡类型检查实名
  • internal/service/package/activation_service.go — REALNAME-04ActivateByRealname 计时基准)
  • internal/store/postgres/package_store.go — REALNAME-01删 enable_realname_activation 两步写入特殊处理)
  • internal/polling/scheduler.go — POLL-01补 protect 调度)
  • internal/task/polling_handler.go — POLL-02nil gateway 改返错误)
  • internal/service/asset/service.go 或相关 package 接口 — POLL-03GetPackages 分页)
  • internal/service/package_series/service.go — POLL-04Delete 前检查关联套餐)

DB 迁移

  • migrations/ 目录 — 遵循现有迁移文件命名规范REALNAME 迁移需新建一个 .sql 文件

项目规范

  • AGENTS.md — 语言要求、错误处理、分层规范
  • docs/api-documentation-guide.md — 若有 DTO 变更需同步更新文档

</canonical_refs>

<code_context>

Existing Code Insights

关键现状确认

  • asset.go:Resolve(第 41-44 行):企业拦截代码已确认存在,直接删除即可
  • asset.go:Refresh(第 78 行附近):当前无企业检查,需在方法开头新增
  • GORM v2 locking 语法:项目已有 clause.OnConflict 使用(package_usage_daily_record_store.goFIN-03 应参照同样风格使用 clause.Locking
  • enable_realname_activation:在 package.gopackage_dto.go3处package_store.go2处两步写入特殊处理均有引用全部需清理
  • card_category:在 polling_handler.go:108 已有 if card.CardCategory == "industry" 判断REALNAME-02 决策逻辑与此对齐
  • processManualQueue / processTimedQueuescheduler.go:243-250 已有 3 个 polling 类型,补 protect 参照同一模式

Established Patterns

  • 行锁:tx.Clauses(clause.Locking{...}) — 参考 clause.OnConflict 已有用法
  • 错误返回Service 层用 errors.New(code, msg) / errors.Wrap(code, err, msg)
  • 分页:QueryOptions{Page: page, PageSize: pageSize} — 参考已有 List 接口
  • Commit 粒度:每个独立 Bug 修复一个 commit沿用 Phase 1 规范)

Integration Points

  • REALNAMEpackage_store.go 的两步写入特殊处理在 REALNAME-01 后需删除(字段删除后特殊处理失去意义)
  • POLL-03 分页:若 GetPackages 上层调用点(handler/admin/asset.go:102handler/app/client_asset.go:213)无分页参数传入,需同时更新 Handler 层

</code_context>

## Specific Ideas
  • Plan 2REALNAME原子执行——规格书明确指出"REALNAME-01~05 是原子重构,必须在一个 plan 内完整执行,不可拆分"STATE.md Accumulated Context
  • 旧字段 enable_realname_activation 直接清理,不做 false→from_purchase 映射,现有生产套餐全部默认 from_activation后续运营按需手动调整
## Deferred Ideas

None — discussion stayed within phase scope


Phase: 02-perm-fin-realname-poll Context gathered: 2026-03-28