# PRD:积分流水列表功能 ## 1. 背景与目标 当前应用已有完整的积分账户系统: - 积分账户表:`user_points` - 积分业务流水:`points_ledger`(记录所有积分变动) - 积分余额查询接口:`GET /api/v1/points/balance` - 积分中心页面:`CoinCenterScreen` 用户在积分中心可以看到余额和购买套餐,但无法查看积分的收支明细。本任务目标是增加积分流水列表功能,让用户了解自己的积分变动历史。 ## 2. 当前事实 ### 2.1 后端现状 **已有的 `points_ledger` 表结构**: | 字段 | 类型 | 说明 | |------|------|------| | `id` | UUID | 主键 | | `user_id` | UUID | 用户 ID | | `direction` | smallint | 方向:1=收入,-1=支出 | | `amount` | bigint | 变动数量(正数) | | `balance_after` | bigint | 变动后余额 | | `change_type` | varchar | 类型:register/consume/adjust/purchase/refund | | `biz_type` | varchar | 业务类型:chat/payment(可空) | | `biz_id` | UUID | 业务 ID(可空) | | `event_id` | varchar | 幂等事件 ID | | `operator_id` | UUID | 操作者 ID(可空) | | `metadata` | jsonb | 扩展元数据 | | `created_at` | timestamptz | 创建时间 | **已有的索引**: - `ix_points_ledger_user_created_at`:支持按用户+时间倒序查询 - `uq_points_ledger_user_event`:用户+事件唯一约束 **已有的 Repository 方法**: - `append_ledger()`:写入流水 - `has_ledger_event()`:检查事件是否存在 **缺失**: - 无分页查询流水列表方法 - 无 HTTP API 端点 ### 2.2 前端现状 **已有的积分中心页面**: - `apps/lib/features/settings/presentation/screens/coin_center_screen.dart` - 显示余额和套餐卡片 **已有的 API 调用**: - `apps/lib/features/points/data/apis/points_api.dart`:仅支持 `getPackages()` **缺失**: - 无流水列表 API 调用 - 无流水列表页面/组件 ## 3. 需求范围 ### 3.1 必须实现 **后端**: - 新增 `GET /api/v1/points/ledger` 接口 - 支持分页(游标分页,按 `created_at` 倒序) - 返回流水列表,包含:时间、类型、金额、余额、描述 **前端**: - 在积分中心页面添加「查看流水」入口 - 新建流水列表页面,支持分页加载 - 流水项展示:类型图标、类型文案、金额(+绿色/-红色)、时间、余额 ### 3.2 本期不做 - 按类型筛选(可作为后续优化) - 导出流水 - 流水详情页 ## 4. 数据契约 ### 4.1 API 接口 **请求**: ``` GET /api/v1/points/ledger?limit=20&cursor={created_at_iso} ``` **响应**: ```json { "items": [ { "id": "uuid", "direction": 1, "amount": 100, "balanceAfter": 500, "changeType": "purchase", "displayText": "购买积分包", "createdAt": "2026-04-28T10:00:00Z" } ], "nextCursor": "2026-04-27T10:00:00Z", "hasMore": true } ``` ### 4.2 change_type 对应展示文案 | change_type | direction | 展示文案(中文) | 展示文案(英文) | |-------------|-----------|------------------|------------------| | register | 1 | 注册赠送 | Registration bonus | | purchase | 1 | 购买积分包 | Purchase credits | | consume | -1 | AI 对话消耗 | AI chat cost | | adjust | ±1 | 系统调整 | System adjustment | | refund | -1 | 退款 | Refund | ## 5. 技术方案 ### 5.1 后端实现 **Repository 层**(`v1/points/repository.py`): ```python async def list_ledger( self, *, user_id: UUID, limit: int, cursor: datetime | None = None, ) -> tuple[list[PointsLedger], bool]: # 查询 points_ledger 表 # 按 created_at DESC 分页 # 返回 (items, has_more) ``` **Service 层**(`v1/points/service.py`): ```python async def get_ledger_list( self, *, user_id: UUID, limit: int = 20, cursor: str | None = None, ) -> LedgerListResult: # 调用 repository # 组装 display_text # 返回响应 ``` **Router 层**(`v1/points/router.py`): ```python @router.get("/ledger", response_model=LedgerListResponse) async def get_points_ledger(...): ... ``` ### 5.2 前端实现 **目录结构**: ``` apps/lib/features/points/ ├── data/ │ ├── apis/ │ │ └── points_api.dart # 新增 getLedger() │ └── models/ │ ├── package_info.dart # 已有 │ └── ledger_item.dart # 新增 └── presentation/ └── screens/ └── points_ledger_screen.dart # 新增 ``` **入口**: - 在 `CoinCenterScreen` 的余额卡片下方添加「查看流水」按钮 - 点击后导航到 `PointsLedgerScreen` ## 6. 实现步骤 ### Phase 1: 后端 API 1. 新增 Schema:`LedgerItem`、`LedgerListResponse`(`v1/points/schemas.py`) 2. 新增 Repository 方法:`list_ledger()`(`v1/points/repository.py`) 3. 新增 Service 方法:`get_ledger_list()`(`v1/points/service.py`) 4. 新增 Router 端点:`GET /ledger`(`v1/points/router.py`) 5. 编写单元测试 ### Phase 2: 前端 UI 1. 新增 Model:`LedgerItem`(`features/points/data/models/ledger_item.dart`) 2. 新增 API 方法:`getLedger()`(`features/points/data/apis/points_api.dart`) 3. 新增页面:`PointsLedgerScreen`(`features/points/presentation/screens/points_ledger_screen.dart`) 4. 修改 `CoinCenterScreen`,添加入口按钮 5. 添加 i18n 文案(`app_zh.arb`、`app_en.arb`、`app_zh_hant.arb`) ## 7. 验收标准 - [ ] 后端 API 返回正确的分页数据 - [ ] 前端能正确加载并展示流水列表 - [ ] 流水类型显示对应文案 - [ ] 金额按收支方向显示不同颜色 - [ ] 分页加载正常工作 - [ ] 无数据时显示空状态 - [ ] 加载中显示 loading 状态