docs(01-p0): 创建 Phase 1 P0 紧急修复执行计划(5 个 Plan,涵盖 CRITICAL-01~08)
This commit is contained in:
233
.planning/phases/01-p0/01-05-PLAN.md
Normal file
233
.planning/phases/01-p0/01-05-PLAN.md
Normal file
@@ -0,0 +1,233 @@
|
||||
---
|
||||
phase: 01-p0
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- pkg/utils/excel.go
|
||||
- internal/task/device_import.go
|
||||
- internal/service/my_commission/service.go
|
||||
autonomous: true
|
||||
requirements:
|
||||
- CRITICAL-07
|
||||
- CRITICAL-08
|
||||
must_haves:
|
||||
truths:
|
||||
- "DeviceRow struct 含 IMEI string 字段"
|
||||
- "ParseDeviceExcel 中表头映射支持 IMEI 列名(imei/IMEI/设备IMEI/IMEI号)"
|
||||
- "device_import.go 创建 model.Device 时赋值 IMEI: row.IMEI"
|
||||
- "my_commission/service.go 冻结余额后检查 RowsAffected == 0,不满足时返回余额不足错误"
|
||||
artifacts:
|
||||
- path: pkg/utils/excel.go
|
||||
provides: "DeviceRow 含 IMEI 字段,ParseDeviceExcel 列名映射新增 IMEI"
|
||||
contains: "IMEI"
|
||||
- path: internal/task/device_import.go
|
||||
provides: "导入时填充 IMEI 字段"
|
||||
contains: "IMEI: row.IMEI"
|
||||
- path: internal/service/my_commission/service.go
|
||||
provides: "RowsAffected 校验,并发冻结失败返回错误"
|
||||
contains: "RowsAffected == 0"
|
||||
key_links:
|
||||
- from: pkg/utils/excel.go
|
||||
to: internal/task/device_import.go
|
||||
via: "DeviceRow.IMEI → model.Device.IMEI"
|
||||
pattern: "DeviceRow"
|
||||
- from: internal/service/my_commission/service.go
|
||||
to: "tx.Model(&AgentWallet{}).Updates()"
|
||||
via: "result.RowsAffected == 0 → 返回余额不足错误"
|
||||
pattern: "RowsAffected"
|
||||
---
|
||||
|
||||
<objective>
|
||||
修复设备导入缺失 IMEI 字段(CRITICAL-07)和提现冻结并发校验缺失(CRITICAL-08)。这两个 Bug 互相独立,也与前序 Plan 无依赖,可以与 Plan 01/04 并行执行。
|
||||
|
||||
Purpose:
|
||||
- CRITICAL-07: 设备导入后 IMEI 为空,导致 Gateway 调用失败(IMEI 是调 Gateway 必填字段)
|
||||
- CRITICAL-08: 并发提现请求中,余额冻结失败(RowsAffected=0)未检测到,导致余额不足时仍创建重复提现单
|
||||
|
||||
Output:
|
||||
- excel.go: DeviceRow 新增 IMEI 字段,ParseDeviceExcel 支持 IMEI 列名
|
||||
- device_import.go: 创建 Device 时填充 IMEI
|
||||
- my_commission/service.go: RowsAffected 校验,并发失败返回错误
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.config/opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/01-p0/01-CONTEXT.md
|
||||
|
||||
# 修复规格书(方案 A-6 第 621-670 行,方案 A-7 第 673-705 行)
|
||||
@.sisyphus/plans/修正业务-完整方案.md
|
||||
|
||||
<interfaces>
|
||||
<!-- DeviceRow 当前结构(pkg/utils/excel.go:35-43)-->
|
||||
```go
|
||||
type DeviceRow struct {
|
||||
Line int
|
||||
VirtualNo string
|
||||
// IMEI 字段缺失 ← 需新增
|
||||
DeviceName string
|
||||
DeviceModel string
|
||||
DeviceType string
|
||||
MaxSimSlots int
|
||||
Manufacturer string
|
||||
ICCIDs []string
|
||||
}
|
||||
```
|
||||
|
||||
<!-- my_commission/service.go 当前错误代码(第 162-170 行)-->
|
||||
```go
|
||||
// 当前:只检查 Error,不检查 RowsAffected
|
||||
if err := tx.WithContext(ctx).Model(&model.AgentWallet{}).
|
||||
Where("id = ? AND balance >= ?", wallet.ID, req.Amount).
|
||||
Updates(map[string]interface{}{
|
||||
"balance": gorm.Expr("balance - ?", req.Amount),
|
||||
"frozen_balance": gorm.Expr("frozen_balance + ?", req.Amount),
|
||||
}).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "冻结余额失败")
|
||||
}
|
||||
// ← 缺少 RowsAffected == 0 的检查
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: 设备导入补充 IMEI 字段(CRITICAL-07)</name>
|
||||
<files>pkg/utils/excel.go, internal/task/device_import.go</files>
|
||||
<action>
|
||||
**修改 `pkg/utils/excel.go`:**
|
||||
|
||||
① 在 DeviceRow struct(第 35-43 行)新增 IMEI 字段:
|
||||
```go
|
||||
type DeviceRow struct {
|
||||
Line int
|
||||
VirtualNo string
|
||||
IMEI string // 新增,对应 Excel IMEI 列
|
||||
DeviceName string
|
||||
DeviceModel string
|
||||
DeviceType string
|
||||
MaxSimSlots int
|
||||
Manufacturer string
|
||||
ICCIDs []string
|
||||
}
|
||||
```
|
||||
|
||||
② 在 `ParseDeviceExcel` 函数的表头列名映射 switch-case(约第 290-320 行)中,新增 IMEI 的 case:
|
||||
```go
|
||||
case "imei", "IMEI", "设备IMEI", "IMEI号":
|
||||
row.IMEI = cellValue
|
||||
```
|
||||
|
||||
找到现有 case 的位置(如 `case "虚拟号", "virtual_no"`),在同一 switch 结构内添加 IMEI 的 case。
|
||||
|
||||
**修改 `internal/task/device_import.go`:**
|
||||
|
||||
在 `processBatch` 函数(约第 178 行)创建 `model.Device{}` 的位置,新增 `IMEI: row.IMEI`:
|
||||
```go
|
||||
device := &model.Device{
|
||||
// ... 已有字段 ...
|
||||
IMEI: row.IMEI, // 新增
|
||||
}
|
||||
```
|
||||
|
||||
执行 `go build ./...`,编译通过后提交独立 commit(per D-05)。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>go build ./... && rg "IMEI" pkg/utils/excel.go internal/task/device_import.go | grep -c "IMEI" | grep -v "^0$" && echo "PASS: IMEI 字段已添加" || echo "FAIL"</automated>
|
||||
</verify>
|
||||
<done>
|
||||
- DeviceRow struct 含 IMEI string 字段
|
||||
- ParseDeviceExcel 支持 imei/IMEI/设备IMEI/IMEI号 列名
|
||||
- device_import.go processBatch 填充 IMEI: row.IMEI
|
||||
- go build ./... 编译通过
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: 提现冻结并发校验(CRITICAL-08)</name>
|
||||
<files>internal/service/my_commission/service.go</files>
|
||||
<action>
|
||||
**修改 `internal/service/my_commission/service.go`(第 162-170 行):**
|
||||
|
||||
将只检查 Error 的写法改为同时检查 RowsAffected:
|
||||
|
||||
```go
|
||||
// 修改前(只检查 Error)
|
||||
if err := tx.WithContext(ctx).Model(&model.AgentWallet{}).
|
||||
Where("id = ? AND balance >= ?", wallet.ID, req.Amount).
|
||||
Updates(map[string]interface{}{
|
||||
"balance": gorm.Expr("balance - ?", req.Amount),
|
||||
"frozen_balance": gorm.Expr("frozen_balance + ?", req.Amount),
|
||||
}).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "冻结余额失败")
|
||||
}
|
||||
|
||||
// 修改后(新增 RowsAffected 校验,per CRITICAL-08 规格)
|
||||
result := tx.WithContext(ctx).Model(&model.AgentWallet{}).
|
||||
Where("id = ? AND balance >= ?", wallet.ID, req.Amount).
|
||||
Updates(map[string]interface{}{
|
||||
"balance": gorm.Expr("balance - ?", req.Amount),
|
||||
"frozen_balance": gorm.Expr("frozen_balance + ?", req.Amount),
|
||||
})
|
||||
if result.Error != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, result.Error, "冻结余额失败")
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return errors.New(errors.CodeInsufficientBalance, "余额不足或并发冲突,请稍后重试")
|
||||
}
|
||||
```
|
||||
|
||||
执行 `go build ./...`,编译通过后提交独立 commit(per D-05)。
|
||||
</action>
|
||||
<verify>
|
||||
<automated>go build ./... && rg "RowsAffected" internal/service/my_commission/service.go && echo "PASS: RowsAffected 校验已添加" || echo "FAIL: RowsAffected 校验缺失"</automated>
|
||||
</verify>
|
||||
<done>
|
||||
- my_commission/service.go 冻结余额使用 result := tx...Updates(),检查 result.Error 和 result.RowsAffected == 0
|
||||
- RowsAffected == 0 时返回 errors.New(errors.CodeInsufficientBalance, ...)
|
||||
- go build ./... 编译通过
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
整体验收:
|
||||
|
||||
```bash
|
||||
# 1. 编译验证
|
||||
go build ./...
|
||||
|
||||
# 2. CRITICAL-07 验证
|
||||
rg "IMEI" pkg/utils/excel.go # 应有 struct 字段和 case 映射
|
||||
rg "IMEI" internal/task/device_import.go # 应有 IMEI: row.IMEI
|
||||
|
||||
# 3. CRITICAL-08 验证
|
||||
rg "RowsAffected" internal/service/my_commission/service.go # 应有 == 0 检查
|
||||
```
|
||||
|
||||
人工验收(参见修正业务-完整方案.md A-6、A-7 验收清单):
|
||||
- CRITICAL-07: 设备导入后 DBHub 查询 `SELECT id, virtual_no, imei FROM tb_device ORDER BY id DESC LIMIT 20;` 确认 IMEI 已填充
|
||||
- CRITICAL-08: 并发提现场景下,DBHub 确认提现单数量与余额冻结成功次数一致
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. `go build ./...` 编译通过
|
||||
2. DeviceRow struct 含 IMEI 字段,ParseDeviceExcel 支持 IMEI 列名,device_import.go 填充 IMEI
|
||||
3. my_commission/service.go 冻结余额后检查 RowsAffected,为 0 时返回余额不足错误
|
||||
4. 两个独立 commit 已提交(CRITICAL-07 一个,CRITICAL-08 一个)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
完成后创建 `.planning/phases/01-p0/01-05-SUMMARY.md`,记录:
|
||||
- CRITICAL-07: DeviceRow 改动说明,ParseDeviceExcel 改动说明,device_import.go 改动说明
|
||||
- CRITICAL-08: my_commission/service.go 改动前后对比
|
||||
- 编译验证结果
|
||||
- CRITICAL-07 和 CRITICAL-08 完成状态
|
||||
</output>
|
||||
Reference in New Issue
Block a user