Files
junhong_cmp_fiber/openspec/changes/archive/2026-03-16-asset-wallet-interface/tasks.md

158 lines
12 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.
## 1. 数据库迁移(先行)
- [x] 1.1 创建迁移文件:`tb_card_wallet``tb_asset_wallet``tb_card_wallet_transaction``tb_asset_wallet_transaction``tb_card_recharge_record``tb_asset_recharge_record`(三张表在同一个迁移文件中完成)
- [x] 1.2 创建迁移文件:`tb_asset_wallet_transaction.reference_id (bigint, nullable)``reference_no (varchar 50, nullable)`ALTER TABLE RENAME COLUMN + ALTER COLUMN TYPE
- [x] 1.3 执行全部迁移,使用 PostgreSQL MCP 确认三张表改名成功,`reference_no` 字段类型为 varchar(50)
## 2. Model 层全量重命名
- [x] 2.1 重命名 `internal/model/card_wallet.go``internal/model/asset_wallet.go`,文件内所有类型改名:
- `CardWallet``AssetWallet``TableName()` 返回 `"tb_asset_wallet"`
- `CardWalletTransaction``AssetWalletTransaction``TableName()` 返回 `"tb_asset_wallet_transaction"`
- `CardRechargeRecord``AssetRechargeRecord``TableName()` 返回 `"tb_asset_recharge_record"`
- [x] 2.2 更新 `AssetWalletTransaction` 结构体字段:`CardWalletID uint json:"card_wallet_id"``AssetWalletID uint json:"asset_wallet_id"`GORM column tag 同步更新为 `column:asset_wallet_id``ReferenceID *uint json:"reference_id,omitempty"``ReferenceNo *string json:"reference_no,omitempty"`GORM column tag 改为 `column:reference_no;type:varchar(50)`
- [x] 2.3 更新 `AssetRechargeRecord` 结构体字段:`CardWalletID uint json:"card_wallet_id"``AssetWalletID uint json:"asset_wallet_id"`GORM column tag 同步更新)
- [x] 2.4 运行 `go build ./...` 确认 Model 层无编译错误
## 3. Store 层全量重命名
- [x] 3.1 重命名 `internal/store/postgres/card_wallet_store.go``asset_wallet_store.go`,类型 `CardWalletStore``AssetWalletStore`,构造函数 `NewCardWalletStore``NewAssetWalletStore`,方法内 `model.CardWallet``model.AssetWallet`
- [x] 3.2 重命名 `internal/store/postgres/card_wallet_transaction_store.go``asset_wallet_transaction_store.go`,类型 `CardWalletTransactionStore``AssetWalletTransactionStore`,构造函数及方法内 Model 引用同步更新
- [x] 3.3 重命名 `internal/store/postgres/card_recharge_store.go``asset_recharge_store.go`,类型 `CardRechargeStore``AssetRechargeStore`,构造函数及方法内 Model 引用同步更新
- [x] 3.4 运行 `go build ./...` 确认 Store 层无编译错误
## 4. Bootstrap 层更新
- [x] 4.1 更新 `internal/bootstrap/stores.go`:字段名 `CardWallet``AssetWallet``CardWalletTransaction``AssetWalletTransaction``CardRecharge``AssetRecharge`;构造函数调用同步更新为 `NewAssetWalletStore``NewAssetWalletTransactionStore``NewAssetRechargeStore`
- [x] 4.2 更新 `internal/bootstrap/services.go`:依赖注入中所有 `s.CardWallet*` 引用改为 `s.AssetWallet*``s.CardRecharge` 改为 `s.AssetRecharge`
- [x] 4.3 运行 `go build ./...` 确认 bootstrap 层无编译错误
## 5. 常量层更新
- [x] 5.1 更新 `pkg/constants/redis.go``RedisCardWalletBalanceKey``RedisAssetWalletBalanceKey`,函数体不变,仅函数名改变
- [x] 5.2 全局搜索 `RedisCardWalletBalanceKey` 调用处card_wallet_store.go替换为 `RedisAssetWalletBalanceKey`
## 6. Service 层适配order service
- [x] 6.1 更新 `internal/service/order/service.go`:结构体字段 `cardWalletStore *postgres.CardWalletStore``assetWalletStore *postgres.AssetWalletStore`,构造函数参数及所有调用点同步更新
- [x] 6.2 在 `WalletPay` 卡钱包支付路径(`resourceType != "shop"` 分支)中,扣款成功后在同一事务内补写 `AssetWalletTransaction` 扣款流水:
- 在事务内扣款前记录 `balanceBefore = wallet.Balance`
- 扣款成功(`RowsAffected == 1`)后,`INSERT` 一条 `AssetWalletTransaction``AssetWalletID=wallet.ID``ResourceType=resourceType``ResourceID=resourceID``UserID=buyerID``TransactionType="deduct"``Amount=-order.TotalAmount``BalanceBefore=balanceBefore``BalanceAfter=balanceBefore-order.TotalAmount``Status=1``ReferenceType=strPtr("order")``ReferenceNo=&order.OrderNo``Remark=strPtr("钱包支付套餐")``ShopIDTag=wallet.ShopIDTag``EnterpriseIDTag=wallet.EnterpriseIDTag`
- [x] 6.3 运行 `go build ./...` 确认 order service 无编译错误
## 7. Service 层适配recharge service
- [x] 7.1 更新 `internal/service/recharge/service.go`:结构体字段及构造函数 `cardWalletStore``assetWalletStore``cardWalletTransactionStore``assetWalletTransactionStore`;所有 Model 引用 `model.CardWallet*``model.AssetWallet*`
- [x] 7.2 更新充值回调写入流水记录处(约第 320 行):`ReferenceID: &recharge.ID``ReferenceNo: &recharge.RechargeNo`(同时删除原 `ReferenceID` 字段赋值)
- [x] 7.3 运行 `go build ./...` 确认 recharge service 无编译错误
## 8. DTO 新增
- [x] 8.1 新建 `internal/model/dto/asset_wallet_dto.go`,定义以下 DTO含所有字段及 `description` tag
**AssetWalletResponse**(钱包概况响应):
- `wallet_id uint``resource_type string``resource_id uint`
- `balance int64``frozen_balance int64``available_balance int64`
- `currency string``status int``status_text string`
- `created_at time.Time``updated_at time.Time`
**AssetWalletTransactionListRequest**(流水列表请求,查询参数):
- `page int`(默认 1`page_size int`(默认 20最大 100
- `transaction_type *string`可选oneof=recharge deduct refund
- `start_time *time.Time`(可选)、`end_time *time.Time`(可选)
**AssetWalletTransactionItem**(单条流水):
- `id uint`
- `transaction_type string``transaction_type_text string`
- `amount int64``balance_before int64``balance_after int64`
- `reference_type *string``reference_no *string`
- `remark *string`
- `created_at time.Time`
**AssetWalletTransactionListResponse**(流水列表响应):
- `list []*AssetWalletTransactionItem`
- `total int64``page int``page_size int``total_pages int`
- [x] 8.2 运行 `lsp_diagnostics` 确认 DTO 无错误
## 9. AssetWalletService 新增
- [x] 9.1 新建 `internal/service/asset_wallet/service.go`,定义 `Service` 结构体,依赖注入:`AssetWalletStore``AssetWalletTransactionStore`
- [x] 9.2 实现 `GetWallet(ctx, assetType string, assetID uint) (*dto.AssetWalletResponse, error)`
-`assetType``card`/`device`)映射到 `resourceType``iot_card`/`device`
- 调用 `AssetWalletStore.GetByResourceTypeAndID(ctx, resourceType, assetID)`
- 组装 `AssetWalletResponse`(计算 `available_balance = balance - frozen_balance`,翻译 `status_text`
- 钱包不存在时返回 `errors.New(errors.CodeNotFound, "该资产暂无钱包记录")`
- [x] 9.3 实现 `ListTransactions(ctx, assetType string, assetID uint, req *dto.AssetWalletTransactionListRequest) (*dto.AssetWalletTransactionListResponse, error)`
-`assetType` 映射为 `resourceType`
- 调用 `AssetWalletTransactionStore.ListByResourceID(ctx, resourceType, assetID, offset, limit)``CountByResourceID` 获取分页数据
- 组装响应:翻译 `transaction_type_text`recharge→充值 / deduct→扣款 / refund→退款计算 `total_pages`
- 如有 `transaction_type` 过滤参数,在 Store 层新增对应过滤方法(或在 Service 层 in-memory 过滤——推荐 Store 层)
- [x] 9.4 运行 `lsp_diagnostics` 确认 Service 无错误
## 10. Store 层新增查询方法
- [x] 10.1 在 `AssetWalletTransactionStore` 中新增 `ListByResourceIDWithFilter(ctx, resourceType string, resourceID uint, transactionType *string, startTime, endTime *time.Time, offset, limit int) ([]*model.AssetWalletTransaction, error)` 方法,支持 `transaction_type`、时间范围过滤,应用 `ApplyShopTagFilter` 数据权限
- [x] 10.2 在 `AssetWalletTransactionStore` 中新增 `CountByResourceIDWithFilter(ctx, resourceType string, resourceID uint, transactionType *string, startTime, endTime *time.Time) (int64, error)` 方法
- [x] 10.3 运行 `lsp_diagnostics` 确认 Store 无错误
## 11. AssetWalletHandler 新增
- [x] 11.1 新建 `internal/handler/admin/asset_wallet.go`,定义 `AssetWalletHandler` 结构体(依赖 `*assetWalletSvc.Service`),实现两个 Handler 方法:
**GetWallet**`GET /api/admin/assets/:asset_type/:id/wallet`
- 检查企业账号:`user_type == UserTypeEnterprise` → 返回 403
- 解析路径参数 `asset_type`(校验为 `card``device`)和 `id`(校验为正整数)
- 调用 `assetWalletSvc.GetWallet(ctx, assetType, id)` → 返回 `response.Success`
**ListTransactions**`GET /api/admin/assets/:asset_type/:id/wallet/transactions`
- 检查企业账号:同上
- 解析路径参数和查询参数(`QueryParser` 绑定 `AssetWalletTransactionListRequest`
- 参数验证:`page_size` 最大 100`transaction_type` 需为合法枚举值
- 调用 `assetWalletSvc.ListTransactions(ctx, assetType, id, &req)` → 返回 `response.Success`
- [x] 11.2 运行 `lsp_diagnostics` 确认 Handler 无编译错误
## 12. 路由注册
- [x] 12.1 在 `internal/routes/asset.go``registerAssetRoutes` 函数末尾追加两条路由(需传入 `*admin.AssetWalletHandler` 参数):
```go
Register(assets, doc, groupPath, "GET", "/:asset_type/:id/wallet", walletHandler.GetWallet, RouteSpec{
Summary: "资产钱包概况",
Description: "查询指定卡或设备的钱包余额概况。企业账号禁止调用。",
Tags: []string{"资产管理"},
Input: new(dto.AssetTypeIDRequest),
Output: new(dto.AssetWalletResponse),
Auth: true,
})
Register(assets, doc, groupPath, "GET", "/:asset_type/:id/wallet/transactions", walletHandler.ListTransactions, RouteSpec{
Summary: "资产钱包流水列表",
Description: "分页查询指定资产的钱包收支流水,含充值/扣款来源编号。企业账号禁止调用。",
Tags: []string{"资产管理"},
Input: new(dto.AssetWalletTransactionListRequest),
Output: new(dto.AssetWalletTransactionListResponse),
Auth: true,
})
```
- [x] 12.2 更新 `registerAssetRoutes` 函数签名,增加 `walletHandler *admin.AssetWalletHandler` 参数
- [x] 12.3 更新 `internal/routes/routes.go` 中 `registerAssetRoutes` 的调用处,传入 `AssetWalletHandler`
## 13. Bootstrap 层注册新 Handler/Service
- [x] 13.1 更新 `internal/bootstrap/types.go``Handlers` 结构体新增 `AssetWallet *admin.AssetWalletHandler``Services` 结构体新增 `AssetWallet *assetWalletSvc.Service`(如需独立 service 包则导入)
- [x] 13.2 更新 `internal/bootstrap/services.go`:实例化 `assetWalletSvc.New(s.AssetWallet, s.AssetWalletTransaction)`
- [x] 13.3 更新 `internal/bootstrap/handlers.go`:实例化 `admin.NewAssetWalletHandler(svcs.AssetWallet)`
- [x] 13.4 更新 `cmd/api/docs.go` 和 `cmd/gendocs/main.go``handlers.AssetWallet = admin.NewAssetWalletHandler(nil)`(文档生成器注册)
- [x] 13.5 运行 `go build ./...` 全量确认无编译错误
## 14. 文档和最终验收
- [x] 14.1 运行 `go run cmd/gendocs/main.go` 确认两个新接口(资产钱包概况、资产钱包流水列表)出现在 OpenAPI 文档中
- [x] 14.2 使用 PostgreSQL MCP 验证三张表改名成功:`tb_asset_wallet`、`tb_asset_wallet_transaction`(含 `reference_no varchar(50)` 字段)、`tb_asset_recharge_record`
- [x] 14.3 使用 PostgreSQL MCP 或 curl 验证H5 充值接口 `GET /api/h5/wallets/recharges/:id` 响应中 `wallet_id` 字段仍正常返回
- [x] 14.4 运行 `go build ./...` 全量确认无编译错误
- [x] 14.5 tasks.md 全部任务标记完成