Files
eryao/.trellis/tasks/archive/2026-04/04-28-feat-points-ledger/prd.md
T

5.6 KiB
Raw Blame History

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

  1. 新增 SchemaLedgerItemLedgerListResponsev1/points/schemas.py
  2. 新增 Repository 方法:list_ledger()v1/points/repository.py
  3. 新增 Service 方法:get_ledger_list()v1/points/service.py
  4. 新增 Router 端点:GET /ledgerv1/points/router.py
  5. 编写单元测试

Phase 2: 前端 UI

  1. 新增 ModelLedgerItemfeatures/points/data/models/ledger_item.dart
  2. 新增 API 方法:getLedger()features/points/data/apis/points_api.dart
  3. 新增页面:PointsLedgerScreenfeatures/points/presentation/screens/points_ledger_screen.dart
  4. 修改 CoinCenterScreen,添加入口按钮
  5. 添加 i18n 文案(app_zh.arbapp_en.arbapp_zh_hant.arb

7. 验收标准

  • 后端 API 返回正确的分页数据
  • 前端能正确加载并展示流水列表
  • 流水类型显示对应文案
  • 金额按收支方向显示不同颜色
  • 分页加载正常工作
  • 无数据时显示空状态
  • 加载中显示 loading 状态