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

8.1 KiB
Raw Blame History

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
pkg/utils/excel.go
internal/task/device_import.go
internal/service/my_commission/service.go
true
CRITICAL-07
CRITICAL-08
truths artifacts key_links
DeviceRow struct 含 IMEI string 字段
ParseDeviceExcel 中表头映射支持 IMEI 列名imei/IMEI/设备IMEI/IMEI号
device_import.go 创建 model.Device 时赋值 IMEI: row.IMEI
my_commission/service.go 冻结余额后检查 RowsAffected == 0不满足时返回余额不足错误
path provides contains
pkg/utils/excel.go DeviceRow 含 IMEI 字段ParseDeviceExcel 列名映射新增 IMEI IMEI
path provides contains
internal/task/device_import.go 导入时填充 IMEI 字段 IMEI: row.IMEI
path provides contains
internal/service/my_commission/service.go RowsAffected 校验,并发冻结失败返回错误 RowsAffected == 0
from to via pattern
pkg/utils/excel.go internal/task/device_import.go DeviceRow.IMEI → model.Device.IMEI DeviceRow
from to via pattern
internal/service/my_commission/service.go tx.Model(&AgentWallet{}).Updates() result.RowsAffected == 0 → 返回余额不足错误 RowsAffected
修复设备导入缺失 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 校验,并发失败返回错误

<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 的检查
Task 1: 设备导入补充 IMEI 字段CRITICAL-07 pkg/utils/excel.go, internal/task/device_import.go **修改 `pkg/utils/excel.go`**

① 在 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 ./...,编译通过后提交独立 commitper 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 ./... 编译通过

Task 2: 提现冻结并发校验CRITICAL-08 internal/service/my_commission/service.go **修改 `internal/service/my_commission/service.go`(第 162-170 行):**

将只检查 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 ./...,编译通过后提交独立 commitper 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>

  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>
完成后创建 `.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 完成状态