docs(02): capture phase context
This commit is contained in:
143
.planning/phases/02-perm-fin-realname-poll/02-CONTEXT.md
Normal file
143
.planning/phases/02-perm-fin-realname-poll/02-CONTEXT.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 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(小修合并)**:B(PERM-01/02)+ H(POLL-01/02/03/04)+ C(FIN-01/02/03)
|
||||
- **Plan 2(REALNAME 独立)**:G(REALNAME-01~05)—— 单独 Plan,独立可回滚
|
||||
- **D-02:** Plan 1 内部执行顺序:B → H → C(权限修复最小化先上,轮询小修次之,财务流程收尾)
|
||||
- **D-03:** Plan 2(REALNAME)必须原子执行,不可拆分 —— 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 v2,agent 使用正确语法
|
||||
|
||||
### 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` — 完整修复规格书,含每个需求的文件路径、代码片段、人工验收清单:
|
||||
- 方案 B(PERM-01/02):第 766-863 行
|
||||
- 方案 C(FIN-01/02/03):第 864-944 行
|
||||
- 方案 G(REALNAME-01~05):第 1492-1611 行
|
||||
- 方案 H(POLL-01~04):第 1612-1711 行
|
||||
|
||||
### 核心修改文件
|
||||
- `internal/handler/admin/asset.go` — PERM-01(删 Resolve 企业拦截)、PERM-02(加 Refresh 企业拦截)
|
||||
- `internal/service/commission_withdrawal/service.go` — FIN-01(Approve 补审批人字段)
|
||||
- `internal/handler/admin/commission_withdrawal.go` — FIN-02(Reject 加 Validate)
|
||||
- `internal/service/commission_withdrawal_setting/service.go` — FIN-03(并发行锁)
|
||||
- `internal/store/postgres/wechat_config_store.go` — FIN-03(微信配置并发行锁)
|
||||
- `internal/model/package.go` — REALNAME-01(drop EnableRealnameActivation,add ExpiryBase)
|
||||
- `internal/model/dto/package_dto.go` — REALNAME-05(DTO 更新 expiry_base 字段)
|
||||
- `internal/service/order/service.go` — REALNAME-02(activateMainPackage 三维决策)
|
||||
- `internal/service/client_order/service.go` — REALNAME-03(按卡类型检查实名)
|
||||
- `internal/service/package/activation_service.go` — REALNAME-04(ActivateByRealname 计时基准)
|
||||
- `internal/store/postgres/package_store.go` — REALNAME-01(删 enable_realname_activation 两步写入特殊处理)
|
||||
- `internal/polling/scheduler.go` — POLL-01(补 protect 调度)
|
||||
- `internal/task/polling_handler.go` — POLL-02(nil gateway 改返错误)
|
||||
- `internal/service/asset/service.go` 或相关 package 接口 — POLL-03(GetPackages 分页)
|
||||
- `internal/service/package_series/service.go` — POLL-04(Delete 前检查关联套餐)
|
||||
|
||||
### 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 2(REALNAME)原子执行——规格书明确指出"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*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Phase 2: 权限/财务/实名/轮询修复 - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-03-28
|
||||
**Phase:** 02-perm-fin-realname-poll
|
||||
**Areas discussed:** 执行分组策略, REALNAME 数据迁移风险
|
||||
|
||||
---
|
||||
|
||||
## 执行分组策略
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| B+C+H 合一、G 独立 | 2 个 Plan:小修删掉合成一个,REALNAME 单独一个。风险隔离好,G 可以独立回滚 | ✓ |
|
||||
| B、C、G、H 各独立 | 4 个 Plan:最细粒度,但总费用较高(尤其 B 和 H 都很小) | |
|
||||
| 全部合并成一个 | 1 个 Plan:最快完成,但如果 G 失败就不知道是哪个需求的问题 | |
|
||||
|
||||
**User's choice:** B+C+H 合一、G 独立
|
||||
**Notes:** Plan 1 内部执行顺序确认为 B → H → C
|
||||
|
||||
---
|
||||
|
||||
## 执行顺序(Plan 1 内部)
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| B → H → C | 先权限修复(最小化验证),然后轮询小修,最后收尾财务流程。财务类改动放最后更安全 | ✓ |
|
||||
| H → B → C | 先轮询,再权限,最后财务 | |
|
||||
| Agent 自行决定 | 顺序不重要,由 agent 按依赖关系安排 | |
|
||||
|
||||
**User's choice:** B → H → C
|
||||
|
||||
---
|
||||
|
||||
## REALNAME 数据迁移风险
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| 直接默认 from_activation | 新字段 DEFAULT 'from_activation'。旧的 false 记录不做映射。expiry_base 控制计时基准,语义与旧字段不同,无法直接映射 | ✓ |
|
||||
| 脚本映射 false → from_purchase | UPDATE tb_package SET expiry_base='from_purchase' WHERE enable_realname_activation=false。但实际上旧字段的语义与新字段不完全对应 | |
|
||||
|
||||
**User's choice:** 直接默认 from_activation,旧字段清理掉
|
||||
**Notes:** "如果旧的字段没有用了就清理掉吧"
|
||||
|
||||
---
|
||||
|
||||
## Agent's Discretion
|
||||
|
||||
- FIN-03 GORM v2 行锁语法(`clause.Locking`)
|
||||
- `isCEndPurchase` 的具体推断字段
|
||||
- 每个子修复独立 commit 粒度
|
||||
- POLL-03 Handler 层分页参数传入方式
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
None mentioned during discussion.
|
||||
Reference in New Issue
Block a user