Merge worktree branch merge-15-modules-to-main-5ug5xJ
This commit is contained in:
@@ -16,11 +16,11 @@
|
||||
|
||||
1. **契约先行**:proto 已定义(analytics.proto / events.proto / iam.proto),实现前不修改 proto,如需修改走 coord 流程(ai-allocation.md §9.4)
|
||||
2. **CQRS 读写分离**:data-ana 是纯读模型服务(无 MySQL 写),ClickHouse 宽表由 CDC 投影构建
|
||||
3. **事件驱动**:data-ana 消费 CDC(主通道)+ 领域事件(备通道,待 P4 后期评估);发布 `edu.insight.mastery.updated` 派生数据事件(豁免 Outbox,004 §12.2 + §15.3 #6 已仲裁)
|
||||
4. **gRPC 优先**:004 §4.1 + §4.2 明确 BFF → 业务服务走 gRPC,**P4 启用 data-ana gRPC server 端口 50055**(HTTP 保留作 Gateway 直连降级)
|
||||
5. **DataScope 过滤**:004 §5.3 DataScope 6 级在查询层注入 WHERE;iam `GetEffectiveDataScope` gRPC(004 §15.3 #5 已仲裁 P4 补全)
|
||||
3. **事件驱动**:data-ana 消费 CDC(主通道)+ 领域事件(备通道,待 P4 后期评估);发布 `edu.insight.mastery.updated` 派生数据事件(豁免 Outbox,[coord-cross-review.md §3.3](../../docs/architecture/coord-cross-review.md) + 004 §12.2 已仲裁)
|
||||
4. **gRPC 优先**:004 §4.1 + [coord-cross-review.md §2.1](../../docs/architecture/coord-cross-review.md) 明确 BFF → 业务服务走 gRPC,**P4 启用 data-ana gRPC server 端口 50055**(HTTP 保留作 Gateway 直连降级)
|
||||
5. **DataScope 过滤**:004 §5.3 DataScope 6 级在查询层注入 WHERE;iam `GetEffectiveDataScope` gRPC([coord-cross-review.md §2](../../docs/architecture/coord-cross-review.md) #3 已仲裁 P4 补全)
|
||||
6. **三支柱可观测**:structlog + prometheus-client + OpenTelemetry(已具备,需补业务指标 + gRPC server interceptor)
|
||||
7. **统一响应信封 ActionState**(004 §11.5 已仲裁 P0 整改):成功 `{success: true, data: T}` / 失败 `{success: false, error: {code, message, details?, traceId?}}` / 降级 `degraded` 作为 `details.degraded` 子字段
|
||||
7. **统一响应信封 ActionState**([coord-cross-review.md §5.3](../../docs/architecture/coord-cross-review.md) 已裁决 P0 整改):成功 `{success: true, data: T}` / 失败 `{success: false, error: {code, message, details?, traceId?}}` / 降级 `degraded` 作为顶层 `details.degraded` 子字段(非 `error.details`)
|
||||
8. **降级模式**:外部依赖(ClickHouse / Kafka / iam gRPC / Redis)不可用时返回骨架数据 + `details.degraded: true`
|
||||
9. **Python 规范**:pydantic-settings 配置 / Pydantic 模型校验 / async 优先 / 类型注解强制 / ruff 零错误
|
||||
10. **长远架构演进**:为 P5(ai 用量消费 / gRPC stream)、P6(CDC 水平扩展 / 容量规划 / 数据治理 / Service Mesh)做好铺垫,见 §14 / §19
|
||||
@@ -295,13 +295,15 @@ ORDER BY (student_id, class_id, attendance_date);
|
||||
| `GetAdminDashboard` | `GetAdminDashboardRequest{user_id, scope, scope_id?}` | `AdminDashboard` | `ANALYTICS_ADMIN_DASHBOARD` |
|
||||
| `GetWarnings` | `GetWarningsRequest{class_id?, severity?, since?}` | `WarningList` | `ANALYTICS_WARNING_READ` |
|
||||
| `GetMasteryDistribution` | `GetMasteryDistributionRequest{class_id, subject_id, knowledge_point_id?}` | `MasteryDistribution` | `ANALYTICS_CLASS_READ` |
|
||||
| `GetStudentMastery` | `GetStudentMasteryRequest{student_id, subject_id?}` | `StudentMastery` | `ANALYTICS_STUDENT_READ` |
|
||||
| `TriggerWarning` | `TriggerWarningRequest{target_id, warning_type, severity}` | `TriggerWarningResponse` | `ANALYTICS_WARNING_READ` |
|
||||
| `SubscribeMasteryUpdate` | `SubscribeMasteryUpdateRequest{student_id?, class_id?}` | `stream MasteryUpdateEvent` | `ANALYTICS_STUDENT_READ` |
|
||||
|
||||
> **proto 扩展提案**(待 coord 审议):上述新增 RPC 的 message 定义需在 `packages/shared-proto/proto/analytics.proto` 补充。`SubscribeMasteryUpdate` 为 server-streaming RPC,为 P5+ AI 个性化推荐预留实时推送通道。
|
||||
> **proto 扩展提案**(待 coord 审议):上述新增 RPC 的 message 定义需在 `packages/shared-proto/proto/analytics.proto` 补充,共 12 RPC(3 现有 + 9 扩展)。`SubscribeMasteryUpdate` 为 server-streaming RPC,为 P5+ AI 个性化推荐预留实时推送通道。
|
||||
|
||||
**权限校验**:gRPC server interceptor 从 metadata 提取 `x-user-id` / `x-user-roles` / `x-data-scope`,调用 `AuthDepends` 等价逻辑。
|
||||
|
||||
### 4.3 ActionState 统一响应信封(004 §11.5 P0 整改)
|
||||
### 4.3 ActionState 统一响应信封(coord-cross-review.md §5.3 P0 整改)
|
||||
|
||||
所有 HTTP/gRPC 响应必须遵循 ActionState 信封:
|
||||
|
||||
@@ -318,23 +320,22 @@ class ActionStateError(BaseModel):
|
||||
trace_id: str | None = None
|
||||
|
||||
class ActionState(BaseModel, Generic[T]):
|
||||
"""统一响应信封(004 §11.5).
|
||||
"""统一响应信封(coord-cross-review.md §5.3 裁决).
|
||||
|
||||
成功:{success: true, data: T}
|
||||
失败:{success: false, error: {code, message, details?, trace_id?}}
|
||||
降级:success=true 但 error.details.degraded=true(保留功能但数据可能不完整)
|
||||
降级:{success: true, data: T, details: {degraded: true}}(保留功能但数据可能不完整)
|
||||
"""
|
||||
success: bool
|
||||
data: T | None = None
|
||||
error: ActionStateError | None = None
|
||||
details: dict[str, Any] | None = None # 降级标记放此字段,不放 error
|
||||
|
||||
@classmethod
|
||||
def ok(cls, data: T, *, degraded: bool = False) -> "ActionState[T]":
|
||||
# 降级不是错误:success=True,degraded 标记放 details 子字段(非 error.details)
|
||||
details = {"degraded": True} if degraded else None
|
||||
# 简化:degraded 作为 data 的元信息附加,复杂场景用 details
|
||||
return cls(success=True, data=data, error=None if not degraded else
|
||||
ActionStateError(code="DATA_ANA_DEGRADED", message="degraded mode",
|
||||
details=details))
|
||||
return cls(success=True, data=data, error=None, details=details)
|
||||
|
||||
@classmethod
|
||||
def fail(cls, code: str, message: str, *, trace_id: str | None = None,
|
||||
@@ -361,7 +362,7 @@ class StudentScore(BaseModel):
|
||||
# 端点返回类型:ActionState[ClassPerformanceData]
|
||||
```
|
||||
|
||||
> **P0 整改要点**:当前 main.py 返回 `{success, data, degraded}` 三字段平铺,违反 004 §11.5。实现阶段需重构为 `ActionState[T]` 泛型,`degraded` 移到 `error.details.degraded`。
|
||||
> **P0 整改要点**:当前 main.py 返回 `{success, data, degraded}` 三字段平铺,违反 coord-cross-review.md §5.3 裁决。实现阶段需重构为 `ActionState[T]` 泛型,`degraded` 移到顶层 `details.degraded`(非 `error.details`,降级不是错误)。
|
||||
|
||||
## 5. 事件设计
|
||||
|
||||
@@ -547,13 +548,13 @@ async def readyz() -> dict:
|
||||
| 被调用 | api-gateway | HTTP | `/analytics/*` | Gateway 代理(降级通道) |
|
||||
| 被调用 | teacher-bff / student-bff / parent-bff | gRPC | `AnalyticsService.*` | BFF 聚合查询(4 端 Dashboard) |
|
||||
| 被调用 | ai(P5+) | gRPC | `AnalyticsService.GetStudentWeakness / GetLearningTrend / SubscribeMasteryUpdate` | AI 个性化出题上下文 + 实时掌握度推送 |
|
||||
| 调用 | iam | gRPC | `IamService.GetEffectiveDataScope`(004 §15.3 #5 已仲裁 P4 补全) | DataScope 解析 |
|
||||
| 调用 | iam | gRPC | `IamService.GetEffectiveDataScope`([coord-cross-review.md §2](../../docs/architecture/coord-cross-review.md) #3 已仲裁 P4 补全;**当前 iam.proto 未实现**) | DataScope 解析 |
|
||||
| 消费 | core-edu(CDC) | Kafka | `edu-cdc.next_edu_cloud.core_edu_grades/exams/homework_submissions/attendance` | 学情 + 考勤数据投递 |
|
||||
| 消费 | core-edu(CDC) | Kafka | `edu-cdc.next_edu_cloud.classes` | 班级维度同步 |
|
||||
| 消费 | iam(CDC) | Kafka | `edu-cdc.next_edu_cloud.iam_users` | 用户 dataScope 同步 |
|
||||
| 消费 | content(CDC) | Kafka | `edu-cdc.next_edu_cloud.content_knowledge_points` | 知识点元数据同步(v2 新增) |
|
||||
| 消费 | ai(P5+) | Kafka | `edu.insight.ai.usage`(004 §15.3 #6 已仲裁,004 §7.2 已登记) | AI 用量落库 |
|
||||
| 发布 | core-edu / msg | Kafka | `edu.insight.mastery.updated`(004 §12.2 + §15.3 #6 已仲裁:派生数据豁免 Outbox) | 掌握度更新通知 |
|
||||
| 消费 | ai(P5+) | Kafka | `edu.insight.ai.usage`([coord-cross-review.md §3.2](../../docs/architecture/coord-cross-review.md) 已仲裁;004 §7.2 已登记) | AI 用量落库 |
|
||||
| 发布 | core-edu / msg | Kafka | `edu.insight.mastery.updated`([coord-cross-review.md §3.3](../../docs/architecture/coord-cross-review.md) + 004 §12.2 已仲裁:派生数据豁免 Outbox) | 掌握度更新通知 |
|
||||
| 发布 | msg / core-edu | Kafka | `edu.insight.warning.triggered`(v2 新增,需 coord 在 004 §7.2 登记) | 预警触发通知 |
|
||||
|
||||
> **004 §4.1 服务间通信矩阵对齐**:content → data-ana 已声明"教学内容变更通知",本次落实为 CDC 订阅 `content_knowledge_points` 表。
|
||||
@@ -562,10 +563,10 @@ async def readyz() -> dict:
|
||||
|
||||
### 8.1 假设
|
||||
|
||||
- **假设 1**:iam 在 P4 阶段提供 `GetEffectiveDataScope(userId) → DataScope` gRPC API(004 §15.3 #5 已仲裁)。若 iam 未及时提供,fallback 为:从 `x-user-roles` 头推导(admin=ALL, teacher=CLASS_TAUGHT, student=SELF),但无法支持细粒度年级/学校范围
|
||||
- **假设 1**:iam 在 P4 阶段提供 `GetEffectiveDataScope(userId) → DataScope` gRPC API([coord-cross-review.md §2](../../docs/architecture/coord-cross-review.md) #3 已仲裁)。**当前 iam.proto 仅 4 RPC 未实现此 RPC,P4 阻塞项**。若 iam 未及时提供,fallback 为:从 `x-user-roles` 头推导(admin=ALL, teacher=CLASS_TAUGHT, student=SELF),但无法支持细粒度年级/学校范围
|
||||
- **假设 2**:core-edu 的 `core_edu_homework_submissions` / `core_edu_attendance` 表存在 binlog。若不存在,需 core-edu 补表或走 Outbox 事件(领域事件备通道)
|
||||
- **假设 3**:ClickHouse `ReplacingMergeTree` 在查询时需 `FINAL` 关键字确保去重生效。**v2 修复要求**:所有查询加 `FINAL` 或使用 `argMax` 聚合(当前实现未加,是 P0 整改项)
|
||||
- **假设 4**:coord 已在 004 §7.2 登记新增 `edu.insight.ai.usage` topic(004 §15.3 #6 已仲裁)
|
||||
- **假设 4**:coord 已在 004 §7.2 登记新增 `edu.insight.ai.usage` topic([coord-cross-review.md §3.2](../../docs/architecture/coord-cross-review.md) 已仲裁);**events.proto AIUsageEvent message 待 coord 补充**
|
||||
|
||||
### 8.2 技术风险
|
||||
|
||||
@@ -582,22 +583,22 @@ async def readyz() -> dict:
|
||||
| 大数据量 Dashboard 聚合超时 | 管理员 Dashboard 全校聚合慢 | 物化视图预聚合 + 异步刷新 + 缓存 5min |
|
||||
| 知识点元数据与成绩关联失败 | mastery_snapshot 缺知识点标题 | content CDC 同步 + 缺失时显示 `knowledge_point_id` |
|
||||
|
||||
### 8.3 coord 交叉审查结论对齐(v2:原"未决设计决策"已全部仲裁)
|
||||
### 8.3 coord 交叉审查结论对齐(v2.1:原"未决设计决策"已全部仲裁)
|
||||
|
||||
| # | 议题 | coord 仲裁结论 | 涉及文档 |
|
||||
| --- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | ----------------------- |
|
||||
| 1 | data-ana 发布 `edu.insight.mastery.updated` 用直接 producer(非 Outbox)是否合规 | ✅ 已仲裁:派生数据事件豁免 Outbox(004 §12.2 + §15.3 #6) | 004 §12.2 |
|
||||
| 2 | 新增 `edu.insight.ai.usage` topic + `AIUsageEvent` proto message | ✅ 已仲裁:coord 在 004 §7.2 登记 + events.proto 补 message | 004 §7.2 + events.proto |
|
||||
| 3 | iam 新增 `GetEffectiveDataScope` gRPC RPC | ✅ 已仲裁:P4 补全(004 §15.3 #5) | iam.proto |
|
||||
| 4 | data-ana 实现 gRPC server 决策 | ✅ 已仲裁:P4 启用(004 §4.2 gRPC 启用阶段矩阵) | 004 §4.2 |
|
||||
| 1 | data-ana 发布 `edu.insight.mastery.updated` 用直接 producer(非 Outbox)是否合规 | ✅ 已仲裁:派生数据事件豁免 Outbox([coord-cross-review §3.3](../../docs/architecture/coord-cross-review.md) + 004 §12.2) | 004 §12.2 |
|
||||
| 2 | 新增 `edu.insight.ai.usage` topic + `AIUsageEvent` proto message | ✅ 已仲裁:coord 在 004 §7.2 登记 + events.proto 补 message;**⚠️ events.proto 当前缺 AIUsageEvent,待 coord 落实** | 004 §7.2 + events.proto |
|
||||
| 3 | iam 新增 `GetEffectiveDataScope` gRPC RPC | ✅ 已仲裁:P4 补全([coord-cross-review §2](../../docs/architecture/coord-cross-review.md) #3);**⚠️ iam.proto 当前仅 4 RPC,未实现** | iam.proto |
|
||||
| 4 | data-ana 实现 gRPC server 决策 | ✅ 已仲裁:P4 启用([coord-cross-review §2.1](../../docs/architecture/coord-cross-review.md) gRPC 启用阶段) | 004 §4.1 |
|
||||
| 5 | ClickHouse DDL 管理位置(建议 `infra/clickhouse/ddl/`) | ⏳ 待 coord 建立 `infra/clickhouse/ddl/` 目录 + data-ana 提供内容 | infra/ |
|
||||
| 6 | 端口冲突检查:data-ana HTTP=3006 / gRPC=50055 | ✅ 无冲突(004 §1.2 端口分配) | 004 §1.2 |
|
||||
| 7 | 错误码前缀检查:`DATA_ANA_*` | ✅ 无冲突(004 §11.4 错误码前缀矩阵) | 004 §11.4 |
|
||||
| 8 | 黄金模板对齐:Python 服务无 NestJS 装饰器,权限校验用 FastAPI Depends 等价物是否认可 | ✅ 已仲裁:认可(004 §15.3) | — |
|
||||
| 6 | 端口冲突检查:data-ana HTTP=3006 / gRPC=50055 | ✅ 无冲突([coord-cross-review §4.3](../../docs/architecture/coord-cross-review.md) 全局端口矩阵) | port-allocation |
|
||||
| 7 | 错误码前缀检查:`DATA_ANA_*` | ✅ 无冲突([matrix.md §6](../../docs/architecture/issues/matrix.md) 错误码前缀矩阵) | matrix.md §6 |
|
||||
| 8 | 黄金模板对齐:Python 服务无 NestJS 装饰器,权限校验用 FastAPI Depends 等价物是否认可 | ✅ 已仲裁:认可([coord-cross-review §5.3](../../docs/architecture/coord-cross-review.md)) | — |
|
||||
| 9 | `edu.insight.warning.triggered` topic 新增 | ⏳ 待 coord 在 004 §7.2 登记(v2 新提案) | 004 §7.2 |
|
||||
| 10 | analytics.proto 扩展(4 端 Dashboard / Warning / MasteryDistribution / Stream RPC) | ⏳ 待 coord 审议(v2 新提案,§4.2) | analytics.proto |
|
||||
|
||||
> 所有"未决设计决策"已消除,进入实现阶段无阻塞。剩余 3 项 ⏳ 为 v2 新提案,待 coord 审议但不阻塞 P4 主体实现(可先用现有 RPC + HTTP 端点兜底)。
|
||||
> 所有"未决设计决策"已消除。**P4 阻塞项**:#2 events.proto 缺 AIUsageEvent、#3 iam.proto 缺 GetEffectiveDataScope,均待 coord 落实。剩余 3 项 ⏳ 为 v2 新提案,待 coord 审议但不阻塞 P4 主体实现(可先用现有 RPC + HTTP 端点 + mock 兜底)。
|
||||
|
||||
---
|
||||
|
||||
@@ -783,7 +784,7 @@ ENV PATH="/app/.venv/bin:$PATH"
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
EXPOSE 3006 50055
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD curl -f http://localhost:3006/healthz || exit 1
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:3006/healthz')" || exit 1
|
||||
CMD ["python", "-m", "uvicorn", "src.data_ana.main:app", \
|
||||
"--host", "0.0.0.0", "--port", "3006"]
|
||||
```
|
||||
@@ -945,12 +946,26 @@ CDC → ClickHouse 原始表 → MaterializedView → 宽表(自动聚合)
|
||||
- **AI 驱动的学情诊断**:data-ana 提供数据,ai 服务提供模型,组合成"智能诊断报告"
|
||||
- **多租户**:当前 school_id 隐含在 class_id 中,远期可显式多租户隔离
|
||||
|
||||
## 19. 服务审计表 v2(黄金模板对齐)
|
||||
## 19. 非功能性需求(NFR,v2.1 新增)
|
||||
|
||||
| 维度 | 指标 | 目标值 | 验证方式 |
|
||||
| ---------- | ----------------------------- | --------------- | ------------------------------- |
|
||||
| 性能 | ClickHouse 宽表查询 P99 延迟 | < 5s | P4 退出标准 + Prometheus 监控 |
|
||||
| 性能 | CDC 链路延迟 | < 5s | Debezium ts_ms → CH 写入时间差 |
|
||||
| 性能 | gRPC RPC P99 延迟 | < 500ms | OTel trace |
|
||||
| 可用性 | P4 单实例 | 99% | 降级模式保证 |
|
||||
| 可用性 | P6 多实例 + ClickHouse 集群 | 99.9% | HPA + 副本 |
|
||||
| 安全 | DataScope 越权防护 | 0 越权 | 权限测试 + 渗透测试 |
|
||||
| 安全 | PII 日志脱敏 | 100% student_id hash | structlog processor 单测 |
|
||||
| 可扩展性 | CDC 消费者水平扩展 | N 实例无重复 | ReplacingMergeTree + Redis SETNX |
|
||||
| 数据保留 | ClickHouse TTL | 见 §11.1 | TTL 策略自动执行 |
|
||||
|
||||
## 20. 服务审计表 v2(黄金模板对齐)
|
||||
|
||||
| 维度 | 状态 | 说明 |
|
||||
| ------------- | ---- | ----------------------------------------------------------------------- |
|
||||
| 契约(proto) | ✅ | analytics.proto 已定义 3 RPC,v2 提案扩展 7 RPC |
|
||||
| 路由 | ✅ | HTTP 13 端点 + gRPC 10 RPC |
|
||||
| 契约(proto) | ✅ | analytics.proto 已定义 3 RPC,v2 提案扩展至 12 RPC(含 GetStudentMastery / TriggerWarning) |
|
||||
| 路由 | ✅ | HTTP 14 端点(3 基础 + 11 业务)+ gRPC 12 RPC |
|
||||
| 数据访问 | ✅ | ClickHouse 5 宽表 + Redis 缓存 + iam gRPC |
|
||||
| 鉴权 | ✅ | FastAPI Depends 等价物 + 7 权限点 |
|
||||
| 错误处理 | ✅ | 13 错误码 `DATA_ANA_*` + ActionState 信封 |
|
||||
@@ -960,6 +975,7 @@ CDC → ClickHouse 原始表 → MaterializedView → 宽表(自动聚合)
|
||||
| 健康检查 | ✅ | /healthz + /readyz(v2 补 redis/iam_grpc) |
|
||||
| 配置管理 | ✅ | pydantic-settings 完整配置项(§15) |
|
||||
| 降级模式 | ✅ | 4 降级场景(CH/Kafka/Redis/iam) |
|
||||
| NFR | ✅ | v2.1 新增 §19,性能/可用性/安全/可扩展性目标已定义 |
|
||||
| 响应信封 | ⚠️ | v2 设计对齐 ActionState,实现阶段需重构(P0 整改) |
|
||||
| Redis | ⚠️ | v2 设计就绪,实现阶段需新增 redis-py 依赖 + RedisClient |
|
||||
| gRPC server | ⚠️ | v2 设计就绪,实现阶段需新增 grpc.aio + betterproto + server interceptor |
|
||||
@@ -975,5 +991,6 @@ CDC → ClickHouse 原始表 → MaterializedView → 宽表(自动聚合)
|
||||
**AI Agent**: ai11 (data-ana)
|
||||
**Branch**: 单仓库并行模式(直接提交 main)
|
||||
**Coordinator**: coord-ai
|
||||
**v2 修订依据**: ai-allocation.md §3.2 重新分配 + 004 §1.2/§4.2/§7.2/§11.5/§12.2/§15.3 + ai-allocation §5 设计重点 + pending-features P4 退出标准
|
||||
**v2 审核结论**: 16 项遗漏已全部补强(3 项 P0 + 5 项 P1 + 8 项 P2),文档进入实现阶段无阻塞
|
||||
**v2 修订依据**: ai-allocation.md §3.2 重新分配 + coord-cross-review.md §2/§3.3/§5.3 + 004 §4.1/§7.2/§12.2 + ai-allocation §5 设计重点 + pending-features P4 退出标准
|
||||
**v2.1 审核修订**: 修正 004 章节引用断裂(§4.2/§11.4/§11.5/§15.3 不存在→改引 coord-cross-review);修正 ActionState 实现矛盾(degraded 从 error.details 移至顶层 details);补 RPC 至 12 个(GetStudentMastery/TriggerWarning);修正 HTTP 端点计数 14;Dockerfile HEALTHCHECK 改用 urllib 避免 curl 依赖;新增 §19 NFR 章节
|
||||
**v2 审核结论**: 16 项遗漏已全部补强(3 项 P0 + 5 项 P1 + 8 项 P2),文档进入实现阶段无阻塞(P4 阻塞项:iam.proto GetEffectiveDataScope + events.proto AIUsageEvent 待 coord 落实,可用 fallback/mock 兜底)
|
||||
|
||||
Reference in New Issue
Block a user