FinBuddy_Web 错误统一处理与功能计费

· FinBuddy_Web

今日进展

  1. 错误统一处理架构落地:ErrorCode 枚举集中管理、AppException 体系、全局异常处理器三件套
  2. 新增功能计费服务 feature_billing_service:按功能扣费,与客户端 FC 拦截层对齐
  3. 新增权限仓库 permission_repo:RBAC 权限查询下沉到 Repository 层
  4. 新增认证查询服务 auth_query_service:认证读写分离,查询走从库
  5. 前端引入 vitest 测试框架,补充 http 服务和类型测试
  6. 接口文档补全:认证/积分/LLM代理/数据研究/会员设备/管理后台/支付 7 个模块

关键代码/伪代码

错误统一处理 — 三件套

# 之前错误处理最头疼的问题:
# 1. 错误码散落在各个文件里,重复定义
# 2. 有的路由返回 {error: "xxx"},有的返回 {message: "xxx"}
# 3. 前端不知道该判断 code 还是 message 还是 error
# 解决方案:三件套统一

# 第一件:ErrorCode 枚举 — 集中管理所有错误码
CLASS ErrorCode(IntEnum):
    UNAUTHORIZED = 401
    FORBIDDEN = 403
    NOT_FOUND = 404
    RATE_LIMITED = 429
    # 业务域 1001-1999:积分相关
    INSUFFICIENT_CREDITS = 1001
    CREDIT_FROZEN = 1002
    # 业务域 2001-2999:LLM 相关
    LLM_PROVIDER_ERROR = 2001
    LLM_RATE_LIMITED = 2002
    # 通用业务 4001-4099
    VALIDATION_ERROR = 4001

# 第二件:AppException 体系 — 业务异常基类
CLASS AppException(HTTPException):
    def __init__(self, code: ErrorCode, message: str):
        self.code = code
        self.message = message
        super().__init__(status_code=code // 100 * 100, detail=message)

# 第三件:全局异常处理器 — 统一响应格式
@app.exception_handler(AppException)
ASYNC def app_exception_handler(request, exc):
    RETURN JSONResponse(
        status_code=exc.status_code,
        content={"code": exc.code, "message": exc.message, "data": None}
    )

功能计费服务 — 按功能扣费

# 之前计费逻辑和 LLM 代理耦合在一起
# 现在拆出来,任何功能都可以独立计费

CLASS FeatureBillingService:
    """功能计费 — 按功能扣费,与客户端 FC 拦截层对齐"""

    ASYNC DEF charge(self, user_id: str, feature: str, tokens: int = 0):
        # 1. 查询功能定价
        pricing = AWAIT self.get_pricing(feature)
        # LLM 类功能:按 token 计费
        IF pricing.billing_type == "token":
            cost = pricing.unit_price * tokens / 1000
        # 固定计费功能:按次计费
        ELIF pricing.billing_type == "fixed":
            cost = pricing.unit_price
        # 2. 扣费(冻结 → 结算 → 退款 三步事务)
        AWAIT self.credit_service.deduct(user_id, cost, feature)
        RETURN BillingResult(cost=cost, balance=AWAIT self.get_balance(user_id))

遇到的问题

  • 错误码范围划分争议:积分和 LLM 的错误码边界模糊。最终决定按业务域分段:1001-1999 积分、2001-2999 LLM、4001-4099 通用
  • 功能计费和 LLM 代理的 token 计费有重叠:LLM 代理本身也要扣费,但功能计费也要扣。解决方案:LLM 代理走 token 计费,功能走固定计费,互不干扰
  • 前端 vitest 配置和 Vue 3 的兼容问题:需要额外配置 @vitejs/plugin-vue

明日计划

  • 错误统一处理在所有 Service 层落地,替换裸 raise HTTPException
  • 支付模块微信支付对接
  • 调度器引擎生命周期测试