8.1 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-p0 | 05 | execute | 1 |
|
true |
|
|
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 校验,并发失败返回错误
<execution_context> @$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md @$HOME/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/phases/01-p0/01-CONTEXT.md修复规格书(方案 A-6 第 621-670 行,方案 A-7 第 673-705 行)
@.sisyphus/plans/修正业务-完整方案.md
```go type DeviceRow struct { Line int VirtualNo string // IMEI 字段缺失 ← 需新增 DeviceName string DeviceModel string DeviceType string MaxSimSlots int Manufacturer string ICCIDs []string } ```// 当前:只检查 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 的检查
① 在 DeviceRow struct(第 35-43 行)新增 IMEI 字段:
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:
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:
device := &model.Device{
// ... 已有字段 ...
IMEI: row.IMEI, // 新增
}
执行 go build ./...,编译通过后提交独立 commit(per D-05)。
go build ./... && rg "IMEI" pkg/utils/excel.go internal/task/device_import.go | grep -c "IMEI" | grep -v "^0$" && echo "PASS: IMEI 字段已添加" || echo "FAIL"
- DeviceRow struct 含 IMEI string 字段
- ParseDeviceExcel 支持 imei/IMEI/设备IMEI/IMEI号 列名
- device_import.go processBatch 填充 IMEI: row.IMEI
- go build ./... 编译通过
将只检查 Error 的写法改为同时检查 RowsAffected:
// 修改前(只检查 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)。
go build ./... && rg "RowsAffected" internal/service/my_commission/service.go && echo "PASS: RowsAffected 校验已添加" || echo "FAIL: RowsAffected 校验缺失"
- my_commission/service.go 冻结余额使用 result := tx...Updates(),检查 result.Error 和 result.RowsAffected == 0
- RowsAffected == 0 时返回 errors.New(errors.CodeInsufficientBalance, ...)
- go build ./... 编译通过
# 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 确认提现单数量与余额冻结成功次数一致
<success_criteria>
go build ./...编译通过- DeviceRow struct 含 IMEI 字段,ParseDeviceExcel 支持 IMEI 列名,device_import.go 填充 IMEI
- my_commission/service.go 冻结余额后检查 RowsAffected,为 0 时返回余额不足错误
- 两个独立 commit 已提交(CRITICAL-07 一个,CRITICAL-08 一个) </success_criteria>