feat(p4): content analysis service with Neo4j knowledge graph and ClickHouse analytics
P4 阶段交付物: - services/content: 内容资源服务(NestJS) - textbooks: 教材 CRUD + 知识图谱绑定 - config/neo4j.ts: Neo4j driver 单例 - textbooks.service.ts: MySQL CRUD + Neo4j 知识图谱(createKnowledgeGraph/getPrerequisites) - package.json: 补充 @opentelemetry/sdk-node + exporter-trace-otlp-http - services/data-ana: 数据分析服务(Python FastAPI) - main.py: FastAPI + /healthz + class_performance + student_weakness 骨架 - clickhouse_client.py: ClickHouse 客户端封装 - config.py: 环境变量配置 - packages/shared-proto/proto/content.proto: TextbookService + KnowledgeGraphService 契约 - packages/shared-proto/proto/analytics.proto: AnalyticsService 契约(class_performance/student_weakness)
This commit is contained in:
75
services/data-ana/src/data_ana/main.py
Normal file
75
services/data-ana/src/data_ana/main.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""数据分析服务入口."""
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||
from prometheus_client import make_asgi_app
|
||||
import structlog
|
||||
|
||||
logger = structlog.get_logger()
|
||||
tracer = trace.get_tracer(__name__)
|
||||
|
||||
|
||||
def init_tracer() -> None:
|
||||
"""初始化 OpenTelemetry."""
|
||||
provider = TracerProvider()
|
||||
exporter = OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
provider.add_span_processor(BatchSpanProcessor(exporter))
|
||||
trace.set_tracer_provider(provider)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""应用生命周期."""
|
||||
init_tracer()
|
||||
logger.info("data-ana service starting")
|
||||
yield
|
||||
logger.info("data-ana service stopping")
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Data Analytics Service",
|
||||
version="0.1.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# Prometheus 指标
|
||||
app.mount("/metrics", make_asgi_app())
|
||||
|
||||
|
||||
@app.get("/healthz")
|
||||
async def healthz():
|
||||
"""健康检查."""
|
||||
return {"status": "ok", "service": "data-ana"}
|
||||
|
||||
|
||||
@app.get("/analytics/class/{class_id}/performance")
|
||||
async def class_performance(class_id: str):
|
||||
"""班级成绩分析."""
|
||||
with tracer.start_as_current_span("class_performance"):
|
||||
# P4 骨架:从 ClickHouse 查询分析数据
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"classId": class_id,
|
||||
"averageScore": 0,
|
||||
"passRate": 0,
|
||||
"message": "P4 skeleton - ClickHouse integration pending",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/analytics/student/{student_id}/weakness")
|
||||
async def student_weakness(student_id: str):
|
||||
"""学生薄弱知识点分析."""
|
||||
with tracer.start_as_current_span("student_weakness"):
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"studentId": student_id,
|
||||
"weakPoints": [],
|
||||
"message": "P4 skeleton - weakness analysis pending",
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user