feat(#1895): 会员积分系统后端(P1-P4,默认关闭) #1923

Closed
xiaoxia wants to merge 7 commits from feat/1895-membership-points-p1-p2 into develop
Owner

feat(#1895): 会员积分系统后端(P1-P4)

概述

实现两档会员(free/paid)+ 积分消耗制的后端完整框架。默认通过 POINTS_ENABLED=false 关闭,不影响现有业务。

P1 - 数据库与 Model

  • Alembic 076:users 表新增 is_member/member_type/member_expires_at/points_balance 4 字段;新建 points_accounts/points_transactions/points_orders/daily_usage_records 4 张表(含索引、FK、唯一约束)
  • SQLAlchemy Model:新增 4 个 Model,UserModel 加 4 个字段;保留 subscription_plan/max_* 等旧字段兼容
  • 4 个 Repository:PointsAccount/PointsTransaction/PointsOrder/DailyUsageRecord,支持原子余额更新、分页筛选、行锁防超扣、Redis Lua 原子计数

P2 - PointsService 核心逻辑

  • domain/points.py:POINTS_RULES(9 个 AI 场景积分规则)、POINTS_PACKAGES(3档积分包)、MEMBER_PACKAGE_DISCOUNT(月/季/年 0.9/0.87/0.8 折)、calc_points 纯函数(免费用户 ×1.15 向上取整)
  • PointsService 核心方法:get_account/get_balance/check_and_deduct(事务+行锁)/refund(幂等防重退)/earn_points/create_order/mark_order_paid(自动识别会员套餐单履约)/check_and_incr_daily_free_clips(Redis Lua + DB 兜底)/get_daily_usage/calculate_cost/subscribe_member/list_transactions/create_membership_order

P3 - API 路由

  • GET /points/balance 余额+会员状态+今日免费额度
  • GET /points/transactions 积分流水分页
  • GET /points/packages 积分包列表(按会员折扣返回 discounted_price_cents)
  • POST /points/recharge 创建充值订单(payment_params 占位待微信接入)
  • POST /points/check 积分预估(不扣费)
  • POST /points/refund 退还积分
  • GET /points/rules 消耗规则
  • POST /points/payment-callback 支付回调占位
  • GET /usage/daily 每日免费混剪额度
  • GET /subscription/current 当前会员状态(两档)
  • GET /subscription/plans 月/季/年套餐
  • POST /subscription/subscribe 开通/续费会员(占位支付)
  • POST /subscription/cancel 取消自动续费(占位)
  • 保留旧 subscription 端点兼容

P4 - points_gate 中间件 + AI 路由接入

  • packages/middleware/points_gate.py:points_deduction contextmanager(开关控制、免费额度优先、异常自动退款、余额不足 402)
  • 已接入场景:ai_voice(TTS/preview)、ai_video(生成任务/预览)、ai_digital_human(lipsync/avatar render)、voice_clone_train(免费,仅打点)、ai_title(AI 标题)、ai_cover(AI 封面)
  • 注册成功赠送 50 积分(开关控制,失败不阻断注册)
  • 开启方式:设置 POINTS_ENABLED=true 重启 API

验证

  • 单测 15128 passed / 28 skipped(存量全绿)
  • black + ruff 0 报错
  • Alembic upgrade/downgrade SQLite 测试通过
feat(#1895): 会员积分系统后端(P1-P4) ## 概述 实现两档会员(free/paid)+ 积分消耗制的后端完整框架。默认通过 `POINTS_ENABLED=false` 关闭,不影响现有业务。 ## P1 - 数据库与 Model - Alembic 076:users 表新增 is_member/member_type/member_expires_at/points_balance 4 字段;新建 points_accounts/points_transactions/points_orders/daily_usage_records 4 张表(含索引、FK、唯一约束) - SQLAlchemy Model:新增 4 个 Model,UserModel 加 4 个字段;保留 subscription_plan/max_* 等旧字段兼容 - 4 个 Repository:PointsAccount/PointsTransaction/PointsOrder/DailyUsageRecord,支持原子余额更新、分页筛选、行锁防超扣、Redis Lua 原子计数 ## P2 - PointsService 核心逻辑 - domain/points.py:POINTS_RULES(9 个 AI 场景积分规则)、POINTS_PACKAGES(3档积分包)、MEMBER_PACKAGE_DISCOUNT(月/季/年 0.9/0.87/0.8 折)、calc_points 纯函数(免费用户 ×1.15 向上取整) - PointsService 核心方法:get_account/get_balance/check_and_deduct(事务+行锁)/refund(幂等防重退)/earn_points/create_order/mark_order_paid(自动识别会员套餐单履约)/check_and_incr_daily_free_clips(Redis Lua + DB 兜底)/get_daily_usage/calculate_cost/subscribe_member/list_transactions/create_membership_order ## P3 - API 路由 - GET /points/balance 余额+会员状态+今日免费额度 - GET /points/transactions 积分流水分页 - GET /points/packages 积分包列表(按会员折扣返回 discounted_price_cents) - POST /points/recharge 创建充值订单(payment_params 占位待微信接入) - POST /points/check 积分预估(不扣费) - POST /points/refund 退还积分 - GET /points/rules 消耗规则 - POST /points/payment-callback 支付回调占位 - GET /usage/daily 每日免费混剪额度 - GET /subscription/current 当前会员状态(两档) - GET /subscription/plans 月/季/年套餐 - POST /subscription/subscribe 开通/续费会员(占位支付) - POST /subscription/cancel 取消自动续费(占位) - 保留旧 subscription 端点兼容 ## P4 - points_gate 中间件 + AI 路由接入 - packages/middleware/points_gate.py:points_deduction contextmanager(开关控制、免费额度优先、异常自动退款、余额不足 402) - 已接入场景:ai_voice(TTS/preview)、ai_video(生成任务/预览)、ai_digital_human(lipsync/avatar render)、voice_clone_train(免费,仅打点)、ai_title(AI 标题)、ai_cover(AI 封面) - 注册成功赠送 50 积分(开关控制,失败不阻断注册) - 开启方式:设置 POINTS_ENABLED=true 重启 API ## 验证 - 单测 15128 passed / 28 skipped(存量全绿) - black + ruff 0 报错 - Alembic upgrade/downgrade SQLite 测试通过

🚀 预览环境已部署

项目 详情
PR号 #1923
预览链接 https://pr-1923.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1923 | | 预览链接 | [https://pr-1923.preview.xiaoxiajianji.com](https://pr-1923.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
xiaoxia added 7 commits 2026-09-15 12:10:17 +08:00
- 新增 packages/middleware/points_gate.py:提供 _is_active_member、_insufficient_points 工具和 points_deduction contextmanager
- APISettings 新增 POINTS_ENABLED 开关(默认 False)
- 接入扣费端点:
  * tts.py: POST /synthesize、POST /preview(ai_voice,按文本长度粗估时长)
  * generation_tasks.py: POST /tasks(ai_video,按变体数扣费;免费用户每日2条免费额度,异常按单条/批量退款)
  * generation_preview.py: POST /preview(ai_video,预览同样扣费,异常退款)
  * lipsync.py: POST /jobs(ai_digital_human,按次15积分兜底)
  * ai_avatar_render.py: POST /render(ai_digital_human)
  * voice_clones.py: POST /、POST /{clone_id}/retry 打点(voice_clone_train=0积分,不退款)
  * generation_cover.py: POST /generate-cover(ai_cover,upload 类型不扣费)
  * ai.py: POST /titles/generate(ai_title,新增加鉴权)
- auth.py: 注册成功后赠送 50 积分(POINTS_ENABLED 开启时)
- POINTS_ENABLED 默认 false,不影响现有功能
- 单测 15128 passed
- SubscriptionInfo 补 plan_id/plan_name/billing_cycle/current_period_*/amount/auto_renew 等旧字段
- BillingRecord 补 plan_id/plan_name/billing_cycle/amount/status/paid_at/period_* 等旧字段
- ChangePlanRequest target_plan_id/billing_cycle 改必填(对齐 fixture 校验期望)
- ToggleAutoRenewRequest.enabled 改必填(对齐 422 校验测试)
- ChangePlanResponse 加 new_subscription 字段返回新版 SubscriptionInfo
fix(#1895): 旧版 change-plan/toggle-auto-renew 使用 pydantic schema 校验入参
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 39s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 40s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m10s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m51s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m20s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 5m0s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m52s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m39s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 11s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 20s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 16m33s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
AI Code Review / AI Code Review (pull_request) Successful in 21m10s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 27h4m31s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 27h13m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 27h13m19s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 27h13m20s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 27h3m54s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 27h12m43s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 27h12m26s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 27h12m32s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 27h12m36s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 27h12m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 27h12m42s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 27h12m43s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 27h12m25s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 27h3m53s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 27h12m26s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 27h12m49s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 27h3m53s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 27h3m54s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 27h12m43s
087c99c89b
- /change-plan 使用 ChangePlanRequest 强校验(返回旧套餐下线提示前先校验 target_plan_id/billing_cycle 必填及合法性)
- /toggle-auto-renew 使用 ToggleAutoRenewRequest 强校验 enabled 必填
- 修复集成测试 test_error_scenarios 中 422 校验场景预期
xiaoxia force-pushed feat/1895-membership-points-p1-p2 from d6da60a464 to 087c99c89b 2026-09-15 12:10:17 +08:00 Compare
Author
Owner

发现 PR #1919 已经由其他后端同学合入了完整的会员积分后端实现(PR#1920 前端也已合入),staging 部署成功。本 PR 重复,关闭。

发现 PR #1919 已经由其他后端同学合入了完整的会员积分后端实现(PR#1920 前端也已合入),staging 部署成功。本 PR 重复,关闭。
xiaoxia closed this pull request 2026-09-15 12:26:07 +08:00

🗑️ 预览环境已清理

PR #1923 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1923 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
xiaoxia reopened this pull request 2026-09-15 12:35:06 +08:00
xiaoxia closed this pull request 2026-09-16 06:31:34 +08:00
Author
Owner

本 PR 内容已拆分为 9 个小 PR (#1936→#1945) 全部合入 develop 并部署到 staging(最新版 7564b50):

  • P1 基础建设(DB/Model/Repo/Service/每日免费额度)→ 已在 #1936+后续合并
  • P2 各业务路由扣点 gate:TTS+voice_clones (#1938)、lipsync (#1939)、scripts_ai (#1940)、generation_tasks (#1941)、generation_preview (#1942)、ai_avatar_render (#1943)、generation_cover (#1944)
  • P3 注册送50积分 (#1945)
  • P4 points_gate 中间件 (#1936) 及 @points_gate 装饰器已接入以上路由

POINTS_ENABLED 默认 false,灰度可控。关闭本大 PR,避免冲突/重复。

本 PR 内容已拆分为 9 个小 PR (#1936→#1945) 全部合入 develop 并部署到 staging(最新版 7564b50): - P1 基础建设(DB/Model/Repo/Service/每日免费额度)→ 已在 #1936+后续合并 - P2 各业务路由扣点 gate:TTS+voice_clones (#1938)、lipsync (#1939)、scripts_ai (#1940)、generation_tasks (#1941)、generation_preview (#1942)、ai_avatar_render (#1943)、generation_cover (#1944) - P3 注册送50积分 (#1945) - P4 points_gate 中间件 (#1936) 及 @points_gate 装饰器已接入以上路由 POINTS_ENABLED 默认 false,灰度可控。关闭本大 PR,避免冲突/重复。

🗑️ 预览环境已清理

PR #1923 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1923 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Some checks are pending
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 39s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 40s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m10s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m51s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m20s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 5m0s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m52s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m39s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 11s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 20s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 16m33s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
AI Code Review / AI Code Review (pull_request) Successful in 21m10s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 27h4m31s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 27h13m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 27h13m19s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 27h13m20s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 27h3m54s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 27h12m43s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 27h12m26s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 27h12m32s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 27h12m36s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 27h12m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 27h12m42s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 27h12m43s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 27h12m25s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 27h3m53s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 27h12m26s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 27h12m49s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 27h3m53s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 27h3m54s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 27h12m43s

Pull request closed

Sign in to join this conversation.