# 校园政策与办事指南问答机器人 **Repository Path**: software_project_training/campus-policy-qa-bot ## Basic Information - **Project Name**: 校园政策与办事指南问答机器人 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-13 - **Last Updated**: 2026-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 校园政策与办事指南问答机器人 > 依据《校园政策与办事指南问答机器人系统分析与设计文档(修订版3)》生成的**前后端分离**可运行代码。 > 采用 **RAG + 知识图谱混合检索**:问答时并行走「图谱检索」与「向量(语义)检索」,融合排序后再交给大模型合成可溯源答案。 --- ## 1. 技术栈与架构 | 层 | 技术 | 说明 | |---|---|---| | 表现层 | Vue3 + Vite + Element Plus | 问答 / 办事导航 / 流程可视化 / 角色切换 | | 网关层 | Spring Security + JWT + 限流 | 统一鉴权、按 IP 令牌桶限流 | | 应用层 | Spring Boot 3(Java 17) | 6 大子系统(见文档) | | 数据层 | MySQL / H2(演示默认) | t_user / t_role / t_dept / t_permission / t_doc / t_item / t_step / t_resource / t_qa_log | | 检索 | Neo4j(图谱) + 向量库(语义) | 图谱默认**内存实现**、`neo4j` profile 可切真实 Neo4j;向量默认 TF-IDF | | 大模型 | OpenAI 兼容协议 | 未配 Key 时自动**离线降级**为检索摘要 | **子系统对应文档**:问答交互(UC-01)、知识检索(3.3 RAG+图谱)、知识图谱管理(3.2)、政策爬取更新(UC-06)、权限账号(UC-07)、办事导航(UC-02/UC-04)。 --- ## 2. 目录结构 ``` campus-qa-bot/ ├── backend/ # Spring Boot 后端 │ ├── pom.xml │ ├── src/main/java/com/campus/qa/ │ │ ├── config/ # JWT、安全、限流、OpenAPI、Llm、**Neo4j** 配置 │ │ ├── entity/ # 9 张表 JPA 实体 │ │ ├── repository/ # JPA Repository │ │ ├── dto/ # 请求/响应 DTO │ │ ├── service/ │ │ │ ├── auth/ # 鉴权、权限、部门 │ │ │ ├── retrieval/ # 图谱查询/维护(内存 **或** Neo4j) / 向量检索(内存 TF-IDF) / 融合编排 / 答案合成 │ │ │ ├── llm/ # LlmClient 接口 / 远程 OpenAI / 离线降级 Mock │ │ │ ├── navigation/ # 办事卡片 + 流程可视化 │ │ │ ├── crawler/ # 政策爬取与向量重建 │ │ │ └── qa/ # 问答编排 ChatService │ │ ├── controller/ # Auth/Chat/Retrieve/Navigate/Admin/KnowledgeAdmin/AccountAdmin │ │ ├── service/knowledge/ # 【UC-05】知识库管理:变更单 + 双岗复核 + 生效 + 留痕 │ │ ├── service/auth/ # 鉴权 + 【UC-07】权限与账号管理 AccountAdminService │ │ └── knowledge/ # 启动种子数据 + 知识图谱构建器 KnowledgeGraphBuilder │ ├── docker-compose.yml # 本地 Neo4j 5.18(图谱切换用) │ └── src/test/ # 上下文加载 + MockMvc 集成测试(含 UC-05 / UC-07 流程) └── frontend/ # Vue3 前端 ├── package.json ├── vite.config.js # /api 代理到后端(可用 VITE_BACKEND 覆盖地址) └── src/ ├── api/ # axios 封装 + 各模块接口(含 **graph.js** 图谱) ├── store/ # Pinia 用户态(token/角色) ├── router/ # 路由 + 登录守卫 + 管理台权限守卫 └── views/ # Login / Chat / Navigate / **Graph(知识图谱)** / Knowledge(知识库管理台)/ Admin(系统管理台) ``` --- ## 3. 快速运行(开箱演示) ### 3.1 后端(默认 H2,无需数据库) ```bash cd backend mvn -Dmaven.compiler.useIncrementalCompilation=false spring-boot:run # 启动后访问:http://localhost:8080/swagger-ui.html ``` > 首次运行需联网下载 Maven 依赖;之后可加 `-o` 离线。JDK 需 17+。 ### 3.2 前端 ```bash cd frontend npm install npm run dev # 开发服务器 http://localhost:5173 # 或打包 npm run build # 产物在 frontend/dist ``` ### 3.3 演示账号(种子数据) | 用户名 | 密码 | 角色 | 可见事项 | |---|---|---|---| | admin | admin123 | 系统管理员 | 全部 | | km | km123 | 知识管理员 | 含助学金等敏感事项 | | km2 | km2123 | 知识管理员(第二复核人) | 含助学金等敏感事项 | | student | student123 | 师生 | 公开事项 | > `km2` 是 UC-05 双岗复核必需的第二个管理员账号: > 规则要求「提交人不能复核自己的变更」,只有两名管理员分别通过后变更才生效。 --- ### 3.4 问答口径切换(UC-03 角色扮演) 「智能问答」页顶部提供口径选择器:**自动识别 / 大一新生 / 在校师生 / 教职工**。 - **自动识别**:按问题关键词判断(含「新生/大一」→ 大一新生,「老师/教职工」→ 教职工,否则在校师生); - **显式选择**:口径随 `POST /api/chat` 的 `role` 参数下发,后端按口径合成回答,并在每条回答上标注「口径:XXX」; - 选择持久化在 localStorage,刷新页面不丢失。 --- ## 3.5 知识库管理台(UC-05)演示路径 1. 用 **admin** 登录 → 左侧「知识库管理」 → 点「+ 提交知识变更」 - 目标类型选「政策文档」,变更类型「修改」,选中《本科生奖学金评定办法》 - 填写变更后正文(例如把「排名前30%」改为「排名前20%」)→ 提交 2. 退出改用 **km** 登录 → 管理台看到该单状态「待复核」→ 点「复核」→ 通过 → 状态变为「复核中(已过 1 岗)」 3. 再换 **km2** 登录 → 复核通过 → 状态变为 **已生效**, 政策正文被写回 `t_doc`,向量索引与知识图谱同步重建 4. 回到「智能问答」提问「奖学金申请条件是什么?」→ 答案已使用新口径, 并附带权威来源 5. 管理台「操作留痕」可查看提交 / 关键变更通知 / 两岗通过 / 生效的全部记录 若用 **student** 登录,侧边栏不会出现「知识库管理」,直接访问 `/#/knowledge` 也会被路由守卫挡回。 --- ## 4. 核心接口(文档 6.3) | 方法 | 路径 | 说明 | |---|---|---| | POST | `/api/auth/login` | 登录获取 JWT(免鉴权) | | POST | `/api/chat` | 智能问答:返回 `answer + sources + cards + flow` | | GET | `/api/navigate?keyword=` | 办事卡片(链接/表单/预约) | | GET | `/api/navigate/flow?itemId=` | 流程可视化(环节/材料/入口) | | GET | `/api/retrieve?q=` | 混合检索(图谱+向量)原始结果 | | GET | `/api/retrieve/graph/query?keyword=` | 图谱查询(参数化,防注入) | | GET | `/api/retrieve/graph/all` | 全量图谱(前端可视化用) | | GET | `/api/retrieve/graph/subgraph?itemId=` | 展开某事项子图(步骤/资源/部门/依据) | | POST | `/api/admin/graph/nodes` | 新增/更新图谱节点(id 幂等) | | DELETE | `/api/admin/graph/nodes/{id}` | 删除节点及其关系 | | POST | `/api/admin/graph/relations` | 新增图谱关系(from/to/type 去重) | | DELETE | `/api/admin/graph/relations?from&to&type` | 删除指定关系 | | POST | `/api/admin/kb/crawl/run` | 手动触发一次政策爬取(UC-06,知识管理员) | | GET | `/api/admin/kb/crawl/runs` | 爬取任务运行监控记录(UC-06) | | GET/POST/PUT/DELETE | `/api/admin/account/**` | 权限与账号管理(UC-07,详见 4.2) | ### 4.1 知识库管理接口(UC-05,本次实现) | 方法 | 路径 | 说明 | 主要错误码 | |---|---|---|---| | POST | `/api/admin/kb/changes` | 提交知识变更单(含变更前快照) | 400 参数缺失 / 401 / 403 非管理员 / 404 目标不存在 | | GET | `/api/admin/kb/changes` | 变更单列表(status / targetType / keyword) | 403 | | GET | `/api/admin/kb/changes/{id}` | 详情:变更前后对比 + 复核记录 + 留痕 | 404 | | POST | `/api/admin/kb/changes/{id}/review` | 双岗复核(APPROVE / REJECT) | 403 提交人自审 / 409 重复复核或已终态 / 400 驳回未填原因 | | POST | `/api/admin/kb/changes/{id}/cancel` | 撤回(提交人本人或系统管理员) | 403 / 409 | | GET | `/api/admin/kb/changes/{id}/audit` | 单条变更的操作留痕 | — | | GET | `/api/admin/kb/audit` | 全局操作留痕(最近 100 条) | 401 | | GET | `/api/admin/kb/targets` | 可选变更目标(政策文档 / 办事事项) | 403 | ### 4.3 政策爬取更新接口(UC-06,本次实现) | 方法 | 路径 | 说明 | 主要错误码 | |---|---|---|---| | POST | `/api/admin/kb/crawl/run` | 手动触发一次政策爬取(列表页自动遍历子链接) | 403 非知识管理员 | | GET | `/api/admin/kb/crawl/runs` | 爬取任务运行监控记录(状态/新增/变更/跳过/错误 + 明细) | 403 | **列表页自动遍历**:在 `application.yml` 的 `campus.crawler.sites` 中给站点加 `crawlSubLinks: true`, 爬虫会抓取该列表页并自动抽取其中的政策子链接(`a[href]`),按**同域 + 白/黑名单**过滤后逐条递归爬取, 深度由 `maxDepth` 控制(默认 1 = 仅列表页下的直接内容页)。每个子链接同样走 hash 增量比对、去重、`maxPagesPerSite` 上限与限频错峰, 发现的新增/变更回流 UC-05 双岗复核(演示模式 `autoApprove: true` 时直接入库)。 JWT 用法:请求头 `Authorization: Bearer `。 **双岗复核状态机** ``` 提交 ──> PENDING(待复核) │ 第 1 岗通过 ▼ HALF_APPROVED(复核中 1/2) │ 第 2 岗通过(另一人) ▼ APPROVED(生效:写回知识库 + 重建向量索引 + 重建知识图谱) 任意一岗驳回 ──> REJECTED(附驳回原因,知识库不变更) 提交人撤回 ──> CANCELLED ``` **关键约束**(文档 7.2.2「提交—复核双岗」): - 仅 `ADMIN` / `KM` 可提交与复核; - **提交人不能复核自己的变更**; - 同一人对同一变更单只能复核一次; - 累计 **2 岗**通过才生效,任一岗驳回即整单驳回且必须填写原因; - 涉及「金额/条件/时限/名额」或敏感事项的变更自动标记为**关键变更**并写入通知留痕; - 全部动作写入 `t_kb_audit`(操作人、动作、摘要、脱敏 IP),不落敏感原文。 **新增数据表**:`t_kb_change`(变更单)、`t_kb_review`(复核记录)、`t_kb_audit`(操作留痕), DDL 见 `backend/src/main/resources/schema-mysql.sql`。 ### 4.2 权限与账号管理接口(UC-07,本次实现) 系统管理员在「系统管理」台(前端 `/#/admin`,仅 `ADMIN` 角色可见)维护部门、角色、可见范围权限策略与用户账号; 全部写操作强制 `ROLE_ADMIN` 鉴权(文档 7.2.2)。 | 方法 | 路径 | 说明 | 主要错误码 | |---|---|---|---| | GET | `/api/admin/account/depts` | 部门列表 | 403 非管理员 | | POST | `/api/admin/account/depts` | 新增部门 `{name, parentId}` | 400 名称缺失 / 403 | | PUT | `/api/admin/account/depts/{id}` | 修改部门 | 404 不存在 / 403 | | DELETE | `/api/admin/account/depts/{id}` | 删除部门 | 404 / 403 | | GET | `/api/admin/account/roles` | 角色列表 | 403 | | POST | `/api/admin/account/roles` | 新增角色 `{code, name, description}` | 400 / 409 编码重复 / 403 | | PUT | `/api/admin/account/roles/{id}` | 修改角色 | 404 / 409 / 403 | | DELETE | `/api/admin/account/roles/{id}` | 删除角色(内置 `ADMIN` 不可删) | 400 内置角色 / 404 / 403 | | GET | `/api/admin/account/permissions` | 权限/可见范围列表 | 403 | | GET | `/api/admin/account/permissions/role/{roleId}` | 某角色的可见范围 | 403 | | PUT | `/api/admin/account/permissions/role/{roleId}` | 设置可见范围 `{itemScope}`(空或 `*` 表示全部可见) | 404 角色不存在 / 403 | | GET | `/api/admin/account/users` | 用户账号列表(含部门名/角色名) | 403 | | POST | `/api/admin/account/users` | 新增用户 `{username, password, deptId, roleIds, status}` | 409 用户名重复 / 403 | | PUT | `/api/admin/account/users/{id}` | 修改用户(部门/角色/状态/密码) | 404 / 403 | | DELETE | `/api/admin/account/users/{id}` | 禁用用户(逻辑删除,status=0) | 404 / 403 | | POST | `/api/admin/account/users/{id}/enable` | 启用用户 | 404 / 403 | > 可见范围(权限策略)与问答检索的敏感事项过滤直接联动:见 `PermissionService.canViewItem`, > 敏感事项(`t_item.sensitive=1`,如助学金)仅对在此显式授权范围内的角色可见;非敏感事项对授权用户始终公开。 **UC-07 演示路径**:用 `admin / admin123` 登录 → 左侧「系统管理」→ 在「部门管理 / 角色管理 / 可见范围权限 / 用户账号」四个标签页中维护 → 「可见范围权限」中把师生(USER)的可见范围由「奖学金,转专业,报到,宿舍」改为「报到」,再以 `student` 登录提问「助学金怎么申请」将被过滤(无来源)。 集成测试:`backend/src/test/java/com/campus/qa/auth/AccountAdminFlowTest.java`(覆盖增删改查、越权 403、内置 ADMIN 保护、重复用户名 409)。 --- ## 5. 切换到生产组件 代码默认用**内存实现**等价替代 Neo4j / 向量库 / 大模型,方便演示。要接入真实组件,只需替换对应 Service 实现并配置: 1. **MySQL**:复制 `src/main/resources/application-mysql.yml` 内容到 `application.yml` 的 `spring.datasource`,并执行 `schema-mysql.sql` 建表(DDL 见文档 4.2)。 2. **Neo4j(知识图谱)**:代码已内置双实现,通过 Spring Profile 切换,无需改代码: - 默认 `InMemoryGraphQueryService` + `InMemoryGraphManagementService`(内存,开箱即跑,支持节点/关系增删改、子图展开、全量读取、关键词匹配)。 - 启用真实图谱:`--spring.profiles.active=neo4j` 切到 `Neo4jGraphService`(Neo4j Java Driver 5.18)。所有用户输入走**参数化 Cypher**(`Map.of` 占位符),标签/关系类型经 `ALLOWED_LABELS` / `ALLOWED_TYPES` 白名单清洗,杜绝注入。 - 本地起库:`docker-compose up -d`(Neo4j 5.18,`7474` 浏览器 / `7687` Bolt),再在 `application-neo4j.yml` 填 `campus.neo4j.uri/username/password`。 - 前端「知识图谱」页(`/graph`)提供完整可视化:力导向布局渲染「事项—流程—资源—部门—文档」节点与关系,支持缩放/平移/拖拽、按节点类型筛选、名称/ID 搜索定位、悬停高亮邻居、点击查看详情与关联关系(可跳转对端节点)、一键展开「事项子图」;写权限按系统角色(`roleIds` 中的 `km`/`admin`)开放。 - 注意:内存模式下 `GraphManagementService` 的写操作仅存于内存,重启重置;切到 Neo4j 后写入持久化。 3. **向量库(如 Milvus / pgvector / Faiss)**:实现 `VectorSearchService` 接口;`InMemoryVectorSearchService` 目前用 TF-IDF + 余弦相似度模拟。 4. **真实大模型**:`campus.llm.api-key` 已改为读取环境变量 `CAMPUS_LLM_API_KEY`(密钥不入仓库,详见 `backend/.env.example`);`base-url / model` 仍在 `application.yml` 配置。`LlmConfig` 自动装配 `OpenAiLlmClient`(OpenAI 兼容协议);`CAMPUS_LLM_API_KEY` 留空则走 `MockLlmClient` 离线降级(纯检索摘要)。 5. **防刷/合规**:`campus.rate-limit` 控制限流;文档 8.5 要求不上传涉密/个人隐私/内部政策原文到公网模型。 --- ## 6. 测试 ```bash cd backend mvn -Dmaven.compiler.useIncrementalCompilation=false test ``` `CampusQaApplicationTests` 覆盖:上下文装配、登录、/chat 混合问答(含来源/卡片/流程)、/navigate、/navigate/flow、/retrieve/graph/query、无 token 拦截(401)。 `KnowledgeAdminFlowTest`(UC-05)覆盖 6 条用例: | 用例 | 断言要点 | |---|---| | 越权提交 | 师生账号提交变更 → `403` | | 双岗复核全链路 | 提交 → `PENDING` 且自动标记关键变更 → 提交人自审 `403` → 第 1 岗 `HALF_APPROVED` → 重复复核 `409` → 第 2 岗 `APPROVED` 生效 → 正文已更新 → 检索可召回新口径 → 留痕含 SUBMIT/NOTIFY/2×REVIEW_APPROVE/EFFECT → 终态再复核 `409` | | 驳回分支 | 未填原因 `400`;填写后 `REJECTED`,且知识库内容保持原样 | | 撤回分支 | 提交人撤回 → `CANCELLED`,之后复核 `409` | | 404 分支 | 目标不存在 / 变更单不存在 → `404` | | 列表与详情 | 返回 `statusText`、`requiredApprovals=2`、`reviewable` 随复核人变化 | 当前状态:`mvn test` → **Tests run: 12, Failures: 0, Errors: 0**。 端到端验证脚本(可选):`python e2e_check.py <后端端口>`,会真实跑一遍「提交→双岗复核→生效→检索命中→留痕→驳回→问答」并打印结果。 --- ## 7. 已知限制(演示级) - 图谱 / 向量 / 大模型均为**内存等价实现**,重启即重置(种子数据重新加载);图谱也可经 `neo4j` profile 切真实 Neo4j 持久化(见 §5.2)。 - 密码已采用 **BCrypt 加盐哈希**存储与校验(文档 7.2.2),不再明文比对;种子账号默认口令见演示路径。 - JWT 签名密钥与模型 API Key 已移入环境变量(`CAMPUS_JWT_SECRET` / `CAMPUS_LLM_API_KEY`),不再写入仓库(文档 7.2)。 - 事项关联图谱用标题关键词简化匹配,生产可替换为更精确的实体链接。 - 所有 `/api/admin/**` 管理接口均已强制 `ROLE_ADMIN / ROLE_KM` 角色鉴权(`@PreAuthorize` + 方法内二次校验),并移除了原本绕过 UC-05 双岗复核的直写后门(详见《未实现功能清单.md》安全加固项)。 - 在本机 WorkBuddy 沙箱里执行 `npm run dev/build` 时,vite 需要向项目目录写临时 `.mjs` 配置文件会被拦截; 此时改用同目录下的 `vite.config.cjs`(配置内容等价,走 require 路径):`npx vite --config vite.config.cjs`。 CI / 普通终端直接用 `vite.config.js` 即可。后端端口非 8080 时用 `VITE_BACKEND=http://localhost:<端口>` 覆盖代理目标。