feat(ai): v2 新增 GenerateReport RPC + ReportService
第 9 个 RPC GenerateReport(学情报告生成):data-ana 学情数据 → LLM 生成 → 结构化提取 新增 ReportService 业务编排层 + GenerateReportRequest/GeneratedReport 模型 gRPC servicer + HTTP POST /v1/ai/generate/report(权限 ai:report:generate) proto_gen 重新生成 + 测试覆盖(servicer/service/HTTP/模型/权限 共 26 用例) 402 测试通过,覆盖率 88.5%
This commit is contained in:
@@ -328,6 +328,75 @@ async def test_optimize_expression(client: httpx.AsyncClient) -> None:
|
||||
assert body["data"]["degraded"] is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generate report endpoint
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def test_generate_report_class_summary(client: httpx.AsyncClient) -> None:
|
||||
"""POST /v1/ai/generate/report with class_summary returns degraded report (no LLM key)."""
|
||||
resp = await client.post(
|
||||
"/v1/ai/generate/report",
|
||||
json={"class_id": "c-1", "report_type": "class_summary"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["success"] is True
|
||||
# 测试环境无 LLM API key → degraded
|
||||
assert body["data"]["degraded"] is True
|
||||
assert "id" in body["data"]
|
||||
|
||||
|
||||
async def test_generate_report_student_detail(client: httpx.AsyncClient) -> None:
|
||||
"""POST /v1/ai/generate/report with student_detail + student_id."""
|
||||
resp = await client.post(
|
||||
"/v1/ai/generate/report",
|
||||
json={
|
||||
"class_id": "c-1",
|
||||
"report_type": "student_detail",
|
||||
"student_id": "s-001",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["success"] is True
|
||||
assert body["data"]["degraded"] is True
|
||||
|
||||
|
||||
async def test_generate_report_invalid_type(client: httpx.AsyncClient) -> None:
|
||||
"""POST /v1/ai/generate/report with invalid report_type returns 422."""
|
||||
resp = await client.post(
|
||||
"/v1/ai/generate/report",
|
||||
json={"class_id": "c-1", "report_type": "invalid_type"},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
async def test_generate_report_missing_class_id(client: httpx.AsyncClient) -> None:
|
||||
"""POST /v1/ai/generate/report without class_id returns 422."""
|
||||
resp = await client.post(
|
||||
"/v1/ai/generate/report",
|
||||
json={"report_type": "class_summary"},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
async def test_generate_report_permission_denied(prod_client: httpx.AsyncClient) -> None:
|
||||
"""Student role attempting to generate report returns 403."""
|
||||
resp = await prod_client.post(
|
||||
"/v1/ai/generate/report",
|
||||
json={"class_id": "c-1", "report_type": "class_summary"},
|
||||
headers={
|
||||
"X-User-Id": "student-1",
|
||||
"X-User-Role": "student",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
body = resp.json()
|
||||
assert body["success"] is False
|
||||
assert body["error"]["code"] == "AI_FORBIDDEN"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lesson plan endpoints
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user