Files
junhong_cmp_fiber/.planning/phases/01-p0/01-05-PLAN.md

234 lines
8.1 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: 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 ./...`,编译通过后提交独立 commitper 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 ./...`,编译通过后提交独立 commitper 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>