5.6 KiB
5.6 KiB
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}
响应:
{
"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):
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):
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):
@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
- 新增 Schema:
LedgerItem、LedgerListResponse(v1/points/schemas.py) - 新增 Repository 方法:
list_ledger()(v1/points/repository.py) - 新增 Service 方法:
get_ledger_list()(v1/points/service.py) - 新增 Router 端点:
GET /ledger(v1/points/router.py) - 编写单元测试
Phase 2: 前端 UI
- 新增 Model:
LedgerItem(features/points/data/models/ledger_item.dart) - 新增 API 方法:
getLedger()(features/points/data/apis/points_api.dart) - 新增页面:
PointsLedgerScreen(features/points/presentation/screens/points_ledger_screen.dart) - 修改
CoinCenterScreen,添加入口按钮 - 添加 i18n 文案(
app_zh.arb、app_en.arb、app_zh_hant.arb)
7. 验收标准
- 后端 API 返回正确的分页数据
- 前端能正确加载并展示流水列表
- 流水类型显示对应文案
- 金额按收支方向显示不同颜色
- 分页加载正常工作
- 无数据时显示空状态
- 加载中显示 loading 状态