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

144 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 2: 权限/财务/实名/轮询修复 - Context
**Gathered:** 2026-03-28
**Status:** Ready for planning
<domain>
## Phase Boundary
修复和补全四个独立业务域的已知断点企业账号权限边界PERM、提现财务流程完整性FIN、实名激活架构重构REALNAME/G、轮询调度小修POLL。共 14 个需求全部为修复性变更无新功能模块REALNAME 涉及破坏性 DB 迁移DROP 旧字段 + ADD 新字段)。
</domain>
<decisions>
## 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-01`commission_withdrawal/service.go:Approve()` 补填 `approved_by` / `approved_at` 字段
- **D-08:** FIN-02`commission_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.ExpiryBase``from_purchase` 直接激活,`from_activation` 进入等实名队列
- **D-12:** C 端实名检查改为按卡类型(`card_category == "normal"`)判断,删除 `packagesNeedRealname()` 函数
- **D-13:** `ActivateByRealname``ExpiryBase` 选择计时基准:`from_purchase``CreatedAt``from_activation` 用当前时刻
### 轮询系统小修POLL
- **D-14:** POLL-01`scheduler.go` 主调度循环中,复用已有的 `processManualQueue` / `processTimedQueue`,补充 `polling:protect` 的调度接入
- **D-15:** POLL-02`stopCardWithRetry` / `resumeCardWithRetry` 两处,`gatewayClient == nil` 时改为返回明确业务错误
- **D-16:** POLL-03`GetPackages()` 接口新增分页参数,默认 `page=1, pageSize=50`,最大 100
- **D-17:** POLL-04`package_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 的粒度规范)
</decisions>
<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.go`FIN-03 应参照同样风格使用 `clause.Locking`
- `enable_realname_activation`:在 `package.go``package_dto.go`3处`package_store.go`2处两步写入特殊处理均有引用全部需清理
- `card_category`:在 `polling_handler.go:108` 已有 `if card.CardCategory == "industry"` 判断REALNAME-02 决策逻辑与此对齐
- `processManualQueue` / `processTimedQueue``scheduler.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
- REALNAME`package_store.go` 的两步写入特殊处理在 REALNAME-01 后需删除(字段删除后特殊处理失去意义)
- POLL-03 分页:若 `GetPackages` 上层调用点(`handler/admin/asset.go:102``handler/app/client_asset.go:213`)无分页参数传入,需同时更新 Handler 层
</code_context>
<specifics>
## Specific Ideas
- Plan 2REALNAME原子执行——规格书明确指出"REALNAME-01~05 是原子重构,必须在一个 plan 内完整执行,不可拆分"STATE.md Accumulated Context
- 旧字段 `enable_realname_activation` 直接清理,不做 false→from_purchase 映射,现有生产套餐全部默认 from_activation后续运营按需手动调整
</specifics>
<deferred>
## Deferred Ideas
None — discussion stayed within phase scope
</deferred>
---
*Phase: 02-perm-fin-realname-poll*
*Context gathered: 2026-03-28*