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

107 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.py``CacheTypeEnum` 定义了 7 个枚举值:
```python
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 条记录"
```python
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.py` 中 `write_cache()` 的 `expires_at` 参数:
```python
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`
```python
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` 字段定义:
```sql
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 缓存为失效,确保前端读到最新分析。