This commit is contained in:
2026-04-14 16:27:47 +08:00
commit 4b38a4c952
134 changed files with 7478 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,158 @@
<?xml version='1.0' encoding='utf-8'?>
<mxfile host="app.diagrams.net" modified="2026-04-14T09:05:00.000Z" agent="Codex GPT-5" version="24.7.17">
<diagram id="multi-role-flow" name="数据流图(多角色)">
<mxGraphModel dx="1800" dy="1200" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1800" pageHeight="1200" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="2" value="多角色数据流图" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=24;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="30" y="20" width="220" height="30" as="geometry" />
</mxCell>
<mxCell id="3" value="入口:微信小程序 / React 后台处理链路Java 分布式服务 -&gt; Python AI 服务 -&gt; Redis / PostgreSQL" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=13;fontColor=#666666;" vertex="1" parent="1">
<mxGeometry x="30" y="52" width="860" height="22" as="geometry" />
</mxCell>
<mxCell id="10" value="学生" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=15;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="70" y="140" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="11" value="教师" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=15;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="70" y="350" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="12" value="机构管理员" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=15;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="70" y="560" width="160" height="70" as="geometry" />
</mxCell>
<mxCell id="20" value="P1 接入与身份认证&lt;br&gt;微信小程序 / React -&gt; Java Gateway" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="340" y="80" width="290" height="80" as="geometry" />
</mxCell>
<mxCell id="21" value="P2 作业 / 资料管理&lt;br&gt;Java 作业服务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="340" y="205" width="290" height="80" as="geometry" />
</mxCell>
<mxCell id="22" value="P3 AI批改与错因分析&lt;br&gt;Java 编排服务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="340" y="330" width="290" height="80" as="geometry" />
</mxCell>
<mxCell id="23" value="P4 错题本 / 复习计划&lt;br&gt;Java 复习服务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="340" y="455" width="290" height="80" as="geometry" />
</mxCell>
<mxCell id="24" value="P5 推荐与消息推送&lt;br&gt;Java 推荐服务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="340" y="580" width="290" height="80" as="geometry" />
</mxCell>
<mxCell id="25" value="P6 Python AI处理服务&lt;br&gt;OCR / 批改 / 语音评测" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="760" y="250" width="300" height="80" as="geometry" />
</mxCell>
<mxCell id="26" value="P7 讲解评估 / 教学干预 / 运营监控&lt;br&gt;Java 教学与运营服务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="760" y="470" width="330" height="90" as="geometry" />
</mxCell>
<mxCell id="30" value="D1 PostgreSQL 用户 / 班级库&lt;br&gt;支持分库分表 / 分区" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="70" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="31" value="D2 PostgreSQL 作业 / 批改库&lt;br&gt;支持分库分表 / 分区" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="190" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="32" value="D3 PostgreSQL 错题 / 复习库&lt;br&gt;支持分区归档" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="310" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="33" value="D4 知识库 / 题库 / 对象存储" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="430" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="34" value="D5 Redis Cluster&lt;br&gt;会话、缓存、热数据、推荐结果" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="550" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="35" value="D6 日志 / 审计 / BI" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" vertex="1" parent="1">
<mxGeometry x="1180" y="670" width="260" height="75" as="geometry" />
</mxCell>
<mxCell id="40" value="登录 / 绑定" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="10" target="20">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="41" value="登录 / 班级关系" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="11" target="20">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="42" value="高权限登录" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" edge="1" parent="1" source="12" target="20">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="43" value="用户 / 班级 / 权限" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="20" target="30">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="44" value="会话 / Token" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="20" target="34">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="45" value="发布作业 / 资料" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="11" target="21">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="46" value="上传作业 / 答题" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="10" target="21">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="47" value="作业 / 资料 / 提交" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="21" target="31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="48" value="文件 / 课件" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="21" target="33">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="49" value="批改请求" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="21" target="22">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="50" value="调用 Python AI" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="22" target="25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="51" value="解析结果 / 错因 / 评分" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="25" target="22">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="52" value="知识点 / 题库匹配" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="25" target="33">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="53" value="批改结果" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="22" target="31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="54" value="错题 / 薄弱项" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="22" target="23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="55" value="批改结果 / 错因" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="22" target="10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="56" value="复核任务 / 学情" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="22" target="11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="57" value="错题 / 复习节点" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="23" target="32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="58" value="画像 / 复习信号" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="23" target="24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="59" value="错题本 / 复习计划" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="23" target="10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="60" value="微课 / 变式题召回" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="24" target="33">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="61" value="缓存推荐结果" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="24" target="34">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="62" value="推荐内容 / 提醒" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="24" target="10">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="63" value="语音讲解提交" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" edge="1" parent="1" source="10" target="26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="64" value="点评 / 评分 / 学情查询" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="11" target="26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="65" value="机构监控 / 审核" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" edge="1" parent="1" source="12" target="26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="66" value="讲解评估调用" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="26" target="25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="67" value="讲解记录 / 教学数据" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="26" target="31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="68" value="报表 / 审计" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" edge="1" parent="1" source="26" target="35">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="69" value="班级学情 / 教学建议" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" edge="1" parent="1" source="26" target="11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="70" value="机构看板 / 质量报表" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" edge="1" parent="1" source="26" target="12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,450 @@
<mxfile host="65bd71144e">
<diagram id="sys-arch" name="系统架构">
<mxGraphModel dx="1267" dy="1104" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1800" pageHeight="1200" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="AI智能学习系统系统架构" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=24;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="30" y="20" width="420" height="30" as="geometry"/>
</mxCell>
<mxCell id="3" value="技术栈:微信小程序 + React + Java + Python(AI处理供 Java 调用) + Redis + PostgreSQL" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=13;fontColor=#666666;" parent="1" vertex="1">
<mxGeometry x="30" y="52" width="760" height="22" as="geometry"/>
</mxCell>
<mxCell id="10" value="角色与前端接入层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="100" width="240" height="600" as="geometry"/>
</mxCell>
<mxCell id="11" value="学生端&lt;br&gt;微信小程序&lt;br&gt;学习执行 / 作业上传 / 推荐接收" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="70" y="155" width="180" height="90" as="geometry"/>
</mxCell>
<mxCell id="12" value="教师端&lt;br&gt;React 管理后台&lt;br&gt;班级管理 / 作业发布 / 教学干预" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="70" y="345" width="180" height="90" as="geometry"/>
</mxCell>
<mxCell id="13" value="机构端&lt;br&gt;React 管理后台&lt;br&gt;运营管控 / 质控 / 知识库审核" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="70" y="535" width="180" height="90" as="geometry"/>
</mxCell>
<mxCell id="20" value="接入层&lt;br&gt;SLB / Nginx / Java Gateway(BFF) 集群&lt;br&gt;统一鉴权 / Token / RBAC / 限流 / 聚合接口" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="340" y="315" width="230" height="120" as="geometry"/>
</mxCell>
<mxCell id="30" value="Java 分布式业务服务集群" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="640" y="80" width="760" height="420" as="geometry"/>
</mxCell>
<mxCell id="31" value="认证与用户中心&lt;br&gt;登录、绑定、机构 / 班级 / 角色权限" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="690" y="140" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="32" value="作业与内容服务&lt;br&gt;作业发布、上传、文件元数据、提交记录" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="940" y="140" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="33" value="批改编排与学情服务&lt;br&gt;任务编排、错因分析、讲解评估、学情画像" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1190" y="140" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="34" value="错题 / 复习 / 推荐服务&lt;br&gt;错题本、艾宾浩斯计划、推荐触达" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="690" y="280" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="35" value="知识库与机构运营服务&lt;br&gt;知识库审核、运营看板、质量监控、报表导出" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="940" y="280" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="36" value="分布式治理能力&lt;br&gt;服务注册发现 / 配置中心 / 链路追踪 / 灰度发布" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1190" y="280" width="220" height="80" as="geometry"/>
</mxCell>
<mxCell id="40" value="Python AI处理层供 Java 调用)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="1460" y="80" width="300" height="260" as="geometry"/>
</mxCell>
<mxCell id="41" value="Python OCR 服务&lt;br&gt;文档解析 / 图片增强 / 结构化提取" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1490" y="135" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="42" value="Python 批改与生成服务&lt;br&gt;LLM批改 / 错因标签 / 变式题生成" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1490" y="215" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="43" value="Python 语音评测服务&lt;br&gt;ASR / 讲解评分 / 教学建议" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1490" y="295" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="50" value="公共支撑层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="1460" y="390" width="300" height="210" as="geometry"/>
</mxCell>
<mxCell id="51" value="知识库 / 题库 / 课程资源&lt;br&gt;学科 / 年级 / 知识点内容组织" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1490" y="445" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="52" value="画像与推荐策略&lt;br&gt;召回、排序、效果回流" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1490" y="525" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="60" value="数据层与基础设施" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="550" y="625" width="1200" height="250" as="geometry"/>
</mxCell>
<mxCell id="61" value="PostgreSQL 集群&lt;br&gt;业务主库 + 分库分表 + 分区表" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="620" y="650" width="200" height="85" as="geometry"/>
</mxCell>
<mxCell id="62" value="Redis Cluster&lt;br&gt;缓存、会话、排行榜、热数据" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="860" y="650" width="200" height="85" as="geometry"/>
</mxCell>
<mxCell id="63" value="对象存储&lt;br&gt;图片 / PDF / 音频 / 课件 / 讲解录音" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="1100" y="650" width="200" height="85" as="geometry"/>
</mxCell>
<mxCell id="64" value="MQ / 任务调度&lt;br&gt;异步批改、推荐推送、报表任务" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="1340" y="650" width="200" height="85" as="geometry"/>
</mxCell>
<mxCell id="65" value="日志 / 审计 / BI&lt;br&gt;操作日志、链路日志、运营分析" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="1580" y="650" width="160" height="85" as="geometry"/>
</mxCell>
<mxCell id="70" value="外部能力" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="740" width="500" height="150" as="geometry"/>
</mxCell>
<mxCell id="71" value="微信登录 / 订阅消息 / 短信" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="80" y="795" width="180" height="60" as="geometry"/>
</mxCell>
<mxCell id="72" value="第三方 OCR / LLM / ASR 模型接口" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="300" y="795" width="200" height="60" as="geometry"/>
</mxCell>
<mxCell id="80" value="学习接口" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="11" target="20" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="81" value="教学接口" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="12" target="20" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="82" value="运营接口" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="13" target="20" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="83" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="20" target="31" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="84" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="20" target="32" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="85" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="20" target="33" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="20" target="34" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="87" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="20" target="35" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="88" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="20" target="36" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="31" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="90" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="31" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="91" value="登录 / 通知" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="31" target="71" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="92" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="63" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="94" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="64" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="95" value="Java RPC / HTTP" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="41" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="96" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="42" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="97" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="98" value="知识点 / 课程匹配" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="33" target="51" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="99" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="100" value="画像 / 策略" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="34" target="52" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="101" value="复习内容" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="34" target="51" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="102" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="34" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="103" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="34" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="104" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="35" target="51" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="105" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="106" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="65" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="107" value="监控 / 链路" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="36" target="65" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="108" value="模型调用" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="41" target="72" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="109" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="42" target="72" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="43" target="72" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="111" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="51" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="112" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="51" target="63" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="113" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="52" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="114" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="52" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="code-arch" name="代码架构">
<mxGraphModel dx="887" dy="773" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1800" pageHeight="1200" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="AI智能学习系统代码架构" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=24;fontStyle=1;" parent="1" vertex="1">
<mxGeometry x="30" y="20" width="420" height="30" as="geometry"/>
</mxCell>
<mxCell id="3" value="前端:微信小程序 + React后端Java 分布式服务AIPython 服务数据Redis + PostgreSQL" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;fontSize=13;fontColor=#666666;" parent="1" vertex="1">
<mxGeometry x="30" y="52" width="780" height="22" as="geometry"/>
</mxCell>
<mxCell id="10" value="前端应用层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="90" width="1700" height="150" as="geometry"/>
</mxCell>
<mxCell id="11" value="apps/weapp-student&lt;br&gt;Taro / UniApp 风格小程序&lt;br&gt;作业上传、错题本、推荐页" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="90" y="145" width="270" height="70" as="geometry"/>
</mxCell>
<mxCell id="12" value="apps/react-teacher-admin&lt;br&gt;React 教师后台&lt;br&gt;班级管理、作业发布、教学点评" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="420" y="145" width="270" height="70" as="geometry"/>
</mxCell>
<mxCell id="13" value="apps/react-org-admin&lt;br&gt;React 机构后台&lt;br&gt;权限配置、运营看板、质量监控" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="750" y="145" width="270" height="70" as="geometry"/>
</mxCell>
<mxCell id="20" value="Java 接入层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="270" width="1700" height="150" as="geometry"/>
</mxCell>
<mxCell id="21" value="gateway/java-bff-cluster&lt;br&gt;REST API、聚合接口、限流、灰度、鉴权" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="90" y="325" width="290" height="70" as="geometry"/>
</mxCell>
<mxCell id="22" value="security/auth-center&lt;br&gt;JWT、RBAC、登录态、审计中间件" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="430" y="325" width="290" height="70" as="geometry"/>
</mxCell>
<mxCell id="23" value="integration/openapi-adapter&lt;br&gt;微信、短信、对象存储、消息回调" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="770" y="325" width="290" height="70" as="geometry"/>
</mxCell>
<mxCell id="24" value="governance/distributed-kit&lt;br&gt;注册发现、配置中心、链路追踪、熔断重试" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1110" y="325" width="320" height="70" as="geometry"/>
</mxCell>
<mxCell id="30" value="Java 领域服务层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="450" width="1700" height="250" as="geometry"/>
</mxCell>
<mxCell id="31" value="service/user-class-center&lt;br&gt;用户、班级、机构、角色权限" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="70" y="530" width="240" height="80" as="geometry"/>
</mxCell>
<mxCell id="32" value="service/homework-content&lt;br&gt;作业、资料、上传、文件元数据" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="340" y="530" width="240" height="80" as="geometry"/>
</mxCell>
<mxCell id="33" value="service/grading-orchestrator&lt;br&gt;批改编排、错因分析、学情画像" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="610" y="530" width="240" height="80" as="geometry"/>
</mxCell>
<mxCell id="34" value="service/wrongbook-review&lt;br&gt;错题本、复习计划、掌握状态" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="880" y="530" width="240" height="80" as="geometry"/>
</mxCell>
<mxCell id="35" value="service/recommendation&lt;br&gt;召回排序、内容推荐、消息触达" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1150" y="530" width="240" height="80" as="geometry"/>
</mxCell>
<mxCell id="36" value="service/ops-knowledge&lt;br&gt;机构运营、质控、知识库审核、报表" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1420" y="530" width="260" height="80" as="geometry"/>
</mxCell>
<mxCell id="40" value="Python AI服务层独立部署供 Java 调用)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="730" width="980" height="180" as="geometry"/>
</mxCell>
<mxCell id="41" value="python/ocr-service&lt;br&gt;文档解析、图片增强、结构化输出" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="80" y="795" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="42" value="python/grading-llm-service&lt;br&gt;主观题批改、错因标签、变式题生成" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="320" y="795" width="240" height="60" as="geometry"/>
</mxCell>
<mxCell id="43" value="python/speech-eval-service&lt;br&gt;ASR、讲解评分、反馈建议" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="590" y="795" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="44" value="python/feature-worker&lt;br&gt;画像特征、推荐特征、离线计算" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="830" y="795" width="160" height="60" as="geometry"/>
</mxCell>
<mxCell id="50" value="共享库 / 契约层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="1070" y="730" width="670" height="180" as="geometry"/>
</mxCell>
<mxCell id="51" value="contracts/api-schema&lt;br&gt;DTO、API Schema、OpenAPI" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1100" y="795" width="140" height="60" as="geometry"/>
</mxCell>
<mxCell id="52" value="contracts/domain-model&lt;br&gt;实体、值对象、枚举、状态机" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1260" y="795" width="140" height="60" as="geometry"/>
</mxCell>
<mxCell id="53" value="common/toolkit&lt;br&gt;日志、异常、配置、工具组件" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1420" y="795" width="140" height="60" as="geometry"/>
</mxCell>
<mxCell id="54" value="client/ai-bridge&lt;br&gt;Java 调 Python AI RPC / HTTP SDK" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1580" y="795" width="130" height="60" as="geometry"/>
</mxCell>
<mxCell id="60" value="数据与基础设施层" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f9fa;strokeColor=#b7c3d0;fontSize=16;fontStyle=1;align=left;spacingLeft=12;verticalAlign=top;" parent="1" vertex="1">
<mxGeometry x="40" y="940" width="1700" height="190" as="geometry"/>
</mxCell>
<mxCell id="61" value="infra/postgresql-cluster&lt;br&gt;业务主库、分库分表、分区表、读写分离" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="70" y="1010" width="250" height="75" as="geometry"/>
</mxCell>
<mxCell id="62" value="infra/redis-cluster&lt;br&gt;缓存、会话、排行榜、热点数据" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="360" y="1010" width="250" height="75" as="geometry"/>
</mxCell>
<mxCell id="63" value="infra/object-storage&lt;br&gt;图片、PDF、音频、视频、课件" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="650" y="1010" width="250" height="75" as="geometry"/>
</mxCell>
<mxCell id="64" value="infra/mq-scheduler&lt;br&gt;异步批改、推荐任务、定时报表" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="940" y="1010" width="250" height="75" as="geometry"/>
</mxCell>
<mxCell id="65" value="infra/search-bi&lt;br&gt;检索索引、统计聚合、运营分析" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="1230" y="1010" width="250" height="75" as="geometry"/>
</mxCell>
<mxCell id="66" value="infra/open-platform&lt;br&gt;微信、短信、OCR、LLM、ASR" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
<mxGeometry x="1520" y="1010" width="190" height="75" as="geometry"/>
</mxCell>
<mxCell id="80" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="11" target="21" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="12" target="21" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="82" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="13" target="21" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="83" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="21" target="22" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="84" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="21" target="23" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="85" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="21" target="24" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="86" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="22" target="31" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="87" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="21" target="32" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="88" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#82b366;" parent="1" source="21" target="33" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="89" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="21" target="34" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="90" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#6c8ebf;" parent="1" source="21" target="35" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="91" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="21" target="36" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="92" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="41" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="93" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="42" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="94" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="95" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="44" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="96" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="31" target="51" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="97" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="32" target="52" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="98" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="33" target="54" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="99" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="34" target="52" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="100" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="35" target="51" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="101" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#9673a6;" parent="1" source="36" target="53" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="102" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="31" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="103" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="31" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="104" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="105" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="63" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="106" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="32" target="64" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="107" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="108" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="33" target="64" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="109" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="34" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="110" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="34" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="111" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="112" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="113" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="35" target="64" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="114" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="36" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="115" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="36" target="65" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="116" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="23" target="63" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="117" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="23" target="66" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="118" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="24" target="65" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="119" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="41" target="66" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="120" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="42" target="66" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="121" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#b85450;" parent="1" source="43" target="66" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="122" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="44" target="61" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="123" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#d6b656;" parent="1" source="44" target="62" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

207
docs/plan/architecture.md Normal file
View File

@@ -0,0 +1,207 @@
# K12Study 首版项目框架规划
## Progress
- [x] 根目录骨架已创建:`backend``frontend``app``init/pg`
- [x] `backend` Maven 多模块目录与基础 POM 已落地
- [x] `gateway``auth``upms``boot-dev``python-ai` 首版占位代码已创建
- [x] `init/pg` 已按模块拆分,并接入根目录 `sys_area.sql`
- [x] 学校租户表命名已修正为 `tb_sys_tenant`
- [x] 跨包 DTO、Enums 已收口到对应 `api-*` 模块
- [x] Web 端已从 workspace 收敛为单一 React 项目
- [x] Web 端请求层已切换为原生 `fetch` 封装
- [x] 根目录已新增独立 `app` 微信小程序骨架
- [x] VS Code `docker-dev` 双模式已补齐:`external` 直连外部 PG/Redis`internal` 内置 PG/Redis
- [x] `backend` Maven 聚合编译、`frontend` 构建、`python-ai` 语法校验已通过
- [x] `boot-dev` 本地模式烟测已通过,`/api/auth/login``/api/upms/routes``/api/actuator/health` 可访问
- [ ] 下一步继续补真实持久化、分片路由封装与更完整联调
## Summary
- 根目录固定为:
- `backend`
- `frontend`
- `app`
- 参考 `Tik / urbanLifeline` 的模块拆分和基础层组织,但不复用 `Dubbo``Vue` 和旧业务实现。
- 首版目标是“可运行骨架”:
- 后端:`Spring Boot + Spring Cloud Gateway + Spring RESTful + JWT + RBAC + MyBatis-Plus + PostgreSQL + Redis`
- Web单一 `React` 项目,只保留 `api / types / utils / components / dynamic layout / dynamic route`
- App独立微信小程序骨架
- AI独立 `Python` 服务占位
- 本地开发采用双模式:
- 分布式目录与服务边界长期保留
- `boot-dev` 聚合模块一键启动主要 Java 能力
- `/api` 只属于 `gateway``boot-dev` 的外层上下文,不属于子服务自身前缀。
## Key Changes
- `backend` 采用 Maven 多模块,固定为:
- `backend/common``common-core``common-web``common-security``common-mybatis``common-redis``common-api`
- `backend/apis``api-auth``api-upms``api-ai`
- `backend/gateway`统一入口、鉴权、路由、跨域、trace 透传
- `backend/auth`登录、token、当前用户
- `backend/upms`:用户、角色、权限、菜单、动态路由元数据、组织与区域基座
- `backend/ai-client`Java 调 Python 的适配层
- `backend/boot-dev`:本地聚合启动模块
- `backend/python-ai`:独立 Python AI 服务占位
- 服务通信固定为 `REST`,不使用 `Dubbo`
- 认证固定为 `JWT + RBAC`
- `gateway` 统一验签
- 下游服务走网关信任模式
- 数据访问固定为 `MyBatis-Plus + PostgreSQL + Redis`
- PostgreSQL 预留分库分表、分区、读写分离扩展位
- Redis 负责 token、权限缓存、动态路由缓存、热点数据
- `frontend` 固定为单一 React 项目:
- `frontend/src/api`
- `frontend/src/types`
- `frontend/src/utils`
- `frontend/src/components`
- `frontend/src/layouts`
- `frontend/src/router`
- 只保留基础壳与底层能力,不做业务页面堆砌
- `app` 固定为根目录独立微信小程序工程:
- `app/src/app.*`
- `app/src/pages/*`
- `app/src/api`
- `app/src/utils`
- `boot-dev` 本地模式固定策略:
- 保持和分布式服务同样的模块结构与接口边界
- 本地可单进程启动
- 可用一个 PostgreSQL 实例承载所有逻辑分片,但字段、路由键、表结构必须与未来分布式模式一致
## Region / Tenant Model
- 系统是“总校 -> 省级分校 -> 市区分校”的多层级租户结构
- 每个校区下再有部门,当前部门维度至少支持“年级、学科”等业务组织
- 区域是分库分表的核心路由维度:
- 以“省份区域”作为首要分片依据
- 业务表设计时必须显式保留区域路由字段
- `sys_area.sql` 视为区域基础数据来源约束:
- 首版必须预留 `sys_area` 基础表和初始化脚本接入位
- 区域编码、层级、父子关系以 `sys_area.sql` 为准
- 首版数据模型明确区分两棵树:
- 区域树:省 / 市 / 区县
- 组织树:总校 / 分校 / 校区 / 部门
- 首版 `upms` 基础对象固定包含:
- `SysArea`
- `SysTenant`
- `SysDept`
- `SysUser`
- `SysRole`
- `SysPermission`
- 所有租户级业务主表统一预留字段:
- `province_code`
- `area_code`
- `tenant_id``school_id`
- `tenant_path`
- `dept_id`
- `dept_path`
- 路由与隔离规则固定:
- 区域字段负责数据库路由与物理分片
- `tenant_path` / `dept_path` 负责组织级数据隔离
- 不允许只靠 `dept_path` 承担全部多租户职责
## Database Init Layout
- PostgreSQL 初始化脚本固定放在 `init` 目录下,且按模块独立管理
- 目录结构固定为类似:
- `init/pg/00_create_db.sql`
- `init/pg/01_create_schema.sql`
- `init/pg/sys/sys_area.sql`
- `init/pg/auth/*.sql`
- `init/pg/upms/*.sql`
- `init/pg/ai/*.sql`
- 原则固定:
- 每个模块维护自己的建表 SQL、索引 SQL、初始化数据 SQL
- 不把所有表混在一个超大 SQL 文件中
- 公共基础表单独归 `sys``common` 目录
- `sys_area.sql` 固定归属:
- 放在 `init/pg/sys/`
- 作为区域基础数据的首批初始化脚本
- 模块 SQL 的职责边界固定:
- `auth`登录、token、认证相关表
- `upms`:用户、角色、权限、菜单、学校租户、部门、区域引用关系
- `ai`AI 调用记录、任务记录、模型配置占位
- 初始化脚本执行规则固定:
- 先执行库 / Schema 基础脚本
- 再执行 `sys`
- 再执行各业务模块
- 各模块内部按 `create table -> index -> init data` 顺序组织
- 本地开发模式固定:
- `boot-dev` 使用同一套 `init/pg` 脚本
- 即使单库启动,也不能做一套“临时简化 SQL”绕过正式字段设计
## Public APIs / Interfaces
- 外部访问前缀固定为:
- `/api/auth/**`
- `/api/upms/**`
- `/api/actuator/health``/actuator/**`
- 子服务内部前缀固定为:
- `auth``/auth/**`
- `upms``/upms/**`
- 路由映射固定为:
- `gateway: /api/auth/** -> auth: /auth/**`
- `gateway: /api/upms/** -> upms: /upms/**`
- `boot-dev` 本地聚合模式下同样暴露 `/api/auth/**``/api/upms/**`
- 统一响应结构固定为:
- `code`
- `message`
- `data`
- `traceId`
- `upms` 首版接口能力必须覆盖:
- 当前用户信息
- 用户 / 角色 / 权限基座
- 区域树查询
- 学校租户树查询
- 部门树查询
- 动态路由元数据查询
- React 动态路由元数据至少包含:
- `id`
- `path`
- `name`
- `component`
- `layout`
- `children`
- `meta`
- Python AI 服务首版只固定:
- `GET /health`
## VS Code Docker Dev
- 支持两种开发模式:
- `external`PG、Redis 等直连外部数据库
- `internal`:开发容器内部创建 PG、Redis
- 代码同步方式为 bind mount
- 宿主机目录直接挂载到容器工作目录
- 本地修改会实时反映到容器内
- 不依赖额外“同步脚本”复制代码
## Test Plan
- `backend` 根级 Maven 聚合可编译通过
- `backend/boot-dev` 可单进程启动,并对外暴露 `/api/auth/**``/api/upms/**`
- `backend/gateway` 可独立启动并完成到 `auth``upms` 的路由转发
- JWT 鉴权链路可烟测通过
- `frontend` 单项目可完成 `install``dev``build`
- React 端可基于 `upms` 返回的动态路由元数据完成路由挂载
- `app` 可用微信开发者工具直接打开骨架工程
- `backend/python-ai` 可独立启动并通过 `/health`
- PostgreSQL 与 Redis 本地联调配置可跑通
- SQL 初始化验证必须覆盖:
- `init/pg` 下脚本可按顺序执行
- `sys_area.sql` 可独立导入
- `auth``upms` 模块脚本可独立维护且组合执行无冲突
- 区域与租户模型最小验证必须覆盖:
- 区域树可查询
- 学校租户树可查询
- 部门树可挂接到学校租户下
- 带不同 `province_code / area_code` 的数据写入与查询能走统一路由键封装
## Assumptions
- 不使用 `Dubbo`
- 首版不做真实业务页面、不做学生端真实业务、不做真实 AI 推理
- `upms` 继续承担首版系统管理中心职责
- `sys_area.sql` 是必须接入的区域基础数据脚本,且归档在 `init/pg/sys/`
- 部门当前先按“年级、学科等组织维度”建模,不在首版细化更复杂教学组织规则