## 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 全部任务标记完成
