Files
Neo-ZQYY/docs/prd/Neo_Specs/review-audit/P5.1-NS3-04.md
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:03:48 +08:00

4.1 KiB
Raw Blame History

P5.1→NS3 缺失项 #4App4-App7 的缓存策略

简要结论

  • 状态:⚠️ 部分解决
  • 风险等级:🟠
  • cache_type 枚举和保留策略500 条上限 + 清理机制)已实现,但缺少 expires_at 过期策略和主动失效条件的定义与实现。

详细审查

审查范围

  • apps/backend/app/ai/cache_service.py — AI 缓存读写服务
  • apps/backend/app/ai/schemas.py — CacheTypeEnum 枚举定义
  • docs/database/ddl/zqyy_app__biz.sql — ai_cache 表 DDL
  • docs/prd/specs/P5-miniapp-ai-integration.md — P5 spec 缓存策略定义

发现

已实现部分

  1. cache_type 枚举schemas.pyCacheTypeEnum 定义了 7 个枚举值:

    APP2_FINANCE = "app2_finance"
    APP3_CLUE = "app3_clue"
    APP4_ANALYSIS = "app4_analysis"
    APP5_TACTICS = "app5_tactics"
    APP6_NOTE_ANALYSIS = "app6_note_analysis"
    APP7_CUSTOMER_ANALYSIS = "app7_customer_analysis"
    APP8_CLUE_CONSOLIDATED = "app8_clue_consolidated"
    

    与 P5 spec 定义完全一致。

  2. 保留策略cache_service.py_cleanup_excess() 实现了 P5 spec 定义的"每个 (cache_type, site_id, target_id) 组合保留最近 500 条记录"

    def _cleanup_excess(self, ..., max_count: int = 500) -> int:
    

    清理时机:每次 write_cache() 写入新记录后异步清理。

  3. target_id 约定:各应用的 target_id 含义在 P5 spec 中有明确定义App2=时间维度编码、App3/6/7/8=member_id、App4/5=assistant_id_member_id

  4. 数据库表结构ai_cache 表包含 expires_at timestamp with time zone 字段DDL 层面支持过期时间。

未实现部分

  1. expires_at 过期策略

    • DDL 中 expires_at 字段存在但代码中从未设置有效值
    • write_cache() 接受 expires_at 参数但所有调用方App2-App8 的 run() 函数)均未传入 expires_at
    • 没有定时任务或查询逻辑检查 expires_at 并清理过期记录
    • get_latest() 查询时不过滤已过期记录
  2. 主动失效条件

    • P5 spec 未定义具体的失效条件(如"数据源更新后旧缓存失效"
    • NS3 spec 提到"ai_cache 表但未定义策略"——确实如此
    • 当前实现是"只增不删"(除 500 条上限清理外),没有基于业务事件的缓存失效机制
  3. 缓存读取时的新鲜度检查

    • get_latest() 仅按 created_at DESC 取最新一条,不检查缓存是否仍然有效
    • 前端读取缓存时无法判断数据是否过时

证据

cache_service.pywrite_cache()expires_at 参数:

def write_cache(
    self,
    cache_type: str,
    site_id: int,
    target_id: str,
    result_json: dict,
    triggered_by: str | None = None,
    score: int | None = None,
    expires_at: datetime | None = None,  # 接受但从未被调用方传入
) -> int:

App2 调用 write_cache 时未传入 expires_at

cache_svc.write_cache(
    cache_type=CacheTypeEnum.APP2_FINANCE.value,
    site_id=site_id,
    target_id=time_dimension,
    result_json=result,
    triggered_by=f"user:{user_id}",
    # 无 expires_at
)

DDL 中 expires_at 字段定义:

expires_at timestamp with time zone  -- 存在但未被使用

建议

  1. 定义各应用的缓存过期策略

    应用 建议 expires_at 理由
    App2 当日 08:00营业日切点 财务数据每日更新,次日数据变化后旧缓存无意义
    App3 7 天 消费数据线索在下次消费前有效
    App4/5 3 天 关系分析和话术时效性较强
    App6 无过期 备注分析结果不会因时间失效
    App7 7 天 客户分析随新数据更新
    App8 无过期 整合线索由上游触发更新
  2. 实现过期检查:在 get_latest() 中增加 WHERE expires_at IS NULL OR expires_at > NOW() 过滤。

  3. 考虑增加缓存失效事件:当 App3/App6 产出新内容后,可标记旧的 App7 缓存为失效,确保前端读到最新分析。