docs(02): capture phase context

This commit is contained in:
2026-03-28 10:11:04 +08:00
parent c083596ef6
commit 84aca2fd63
2 changed files with 201 additions and 0 deletions

View 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小修合并**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*

View File

@@ -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.