# 职业指导服务模块 — 开发问题记录与避坑指南 > 记录日期:2026-06-09 > 模块路径:就业一湛通服务平台 > 职业指导服务 > 涉及子模块:公开文件、专家库、服务管理 --- ## 目录 1. [后端路径不一致导致 API 404](#1-后端路径不一致导致-api-404) 2. [数据库视图不存在导致全页面崩溃](#2-数据库视图不存在导致全页面崩溃) 3. [列非空约束导致新增失败](#3-列非空约束导致新增失败) 4. [Flyway SQL 缺少角色授权](#4-flyway-sql-缺少角色授权) 5. [菜单 ID 冲突](#5-菜单-id-冲突) 6. [前端级联错误 (rowSelection) 的实际根因](#6-前端级联错误-rowselection-的实际根因) 7. [Vite import.meta.glob 热更新缓存问题](#7-vite-importmetaglob-热更新缓存问题) 8. [表单隐藏字段未注册导致提交缺字段](#8-表单隐藏字段未注册导致提交缺字段) --- ## 1. 后端路径不一致导致 API 404 ### 问题现象 ``` No static resource careerguidancedocument/careerGuidanceDocument/list. No static resource careerguidanceexpert/careerGuidanceExpert/list. ``` Spring MVC 返回 "No static resource",表示没有 `@RequestMapping` 匹配请求路径。 ### 根因 三个子模块的 `@RequestMapping` 使用了 **三种不同风格**: | 模块 | 实际代码 | 前端请求路径 | 是否匹配 | |---|---|---|---| | Service | `/careerguidanceservice/careerGuidanceService` | `/careerguidanceservice/careerGuidanceService/list` | ✅ | | Document | `/zjrs/careerguidancedocument` | `/careerguidancedocument/careerGuidanceDocument/list` | ❌ 多了一级 `/zjrs/` | | Expert | `/careerguidanceexpert` | `/careerguidanceexpert/careerGuidanceExpert/list` | ❌ 缺少类名层级 | ### 修复 统一路径格式为 **`/{模块名}/{Controller类名}/{action}`**: ```java // Document @RequestMapping("/careerguidancedocument/careerGuidanceDocument") // Expert @RequestMapping("/careerguidanceexpert/careerGuidanceExpert") // Service (已正确) @RequestMapping("/careerguidanceservice/careerGuidanceService") ``` ### 教训 ✅ **代码生成/新建 Controller 时,必须统一 `@RequestMapping` 命名规范** ✅ 建议格式:**全小写模块名 + 首字母大写的 Controller 类名**(对应 Jeecg 默认代码生成器规则) ✅ 前后端 API 路径必须严格一致,建议先定好规范再开发 --- ## 2. 数据库视图不存在导致全页面崩溃 ### 问题现象 ``` 无效的表或视图名[V_CAREER_GUIDANCE_SERVICE] SQL: SELECT * FROM V_CAREER_GUIDANCE_SERVICE ``` ### 根因 Mapper XML 中查询了 `V_CAREER_GUIDANCE_SERVICE` 视图,但该视图未在数据库中创建。 ### 级联影响 ``` 视图不存在 → 后端 API 报错 → 前端接收错误响应 → 组件 setup 阶段数据加载失败 → 级联报错 "Cannot read properties of undefined (reading 'rowSelection')" → Vue render 崩溃 "Cannot read properties of null (reading 'flags')" ``` 前端 `rowSelection` 报错是**表象**,实际根因是后端 API 异常。 ### 修复 执行视图创建 SQL: ```sql CREATE OR REPLACE VIEW V_CAREER_GUIDANCE_SERVICE AS SELECT ... FROM CAREER_GUIDANCE_SERVICE cgs LEFT JOIN PERSONAL_INFO pi ON cgs.PERSONAL_ID = pi.ID; ``` ### 教训 ✅ **建表 SQL 和视图 SQL 必须一起执行**,缺一不可 ✅ 前端报错时要先排查后端 API 是否正常工作(浏览器 F12 → Network → 查看接口响应) ✅ 前端的 `rowSelection` / `flags` / `component` 等 Vue 内部报错,通常都是级联错误,**不要直接在前端代码中加 try-catch 掩盖,应先排查后端** --- ## 3. 列非空约束导致新增失败 ### 问题现象 ``` 违反列[CATEGORY_ID]非空约束 INSERT INTO career_guidance_document (id, name, file_url, sort_no, ...) VALUES (?, ?, ?, ?, ...) ``` SQL INSERT 语句中**没有 `CATEGORY_ID` 列**。 ### 根因 前端表单的 `formSchema` 中没有注册 `categoryId` 字段。虽然前端代码调用了 `setCategoryId()`,但该字段不在 schema 中,提交时被表单框架过滤掉了。 ### 修复 在 `formSchema` 中添加隐藏字段: ```typescript { label: '所属目录', field: 'categoryId', component: 'Input', show: false, // 不在 UI 显示 } ``` ### 教训 ✅ **表单中使用的所有字段必须在 `formSchema` 中注册**,即使是隐藏字段 ✅ 使用 `show: false` 隐藏字段而不移除 ✅ 数据库 NOT NULL 列必须有对应的前端提交逻辑 --- ## 4. Flyway SQL 缺少角色授权 ### 问题现象 菜单在 `sys_permission` 表中存在,但用户登录后看不到菜单。 ### 根因 Flyway 迁移脚本 `V20260609_1__menu_insert_CareerGuidance.sql` **只插入了菜单记录,没有插入角色授权**(`sys_role_permission`)。 ```sql -- 只有 INSERT INTO sys_permission -- 缺少 INSERT INTO sys_role_permission ``` ### 修复 补充角色授权 SQL: ```sql INSERT INTO sys_role_permission (ID, ROLE_ID, PERMISSION_ID) VALUES ('xxx', 'f6817f48af4fb3af11b9e8bf182f618b', '菜单ID'); ``` ### 教训 ✅ **菜单 SQL 必须包含两步:插入菜单表 + 插入角色授权表** ✅ 测试账号的 ROLE_ID 通常是固定的(如 admin: `f6817f48af4fb3af11b9e8bf182f618b`) ✅ 建议将菜单 + 授权写在一个 SQL 文件中,避免遗漏 --- ## 5. 菜单 ID 冲突 ### 问题现象 ``` -- 终版 SQL 注释 -- 修复说明:之前使用的 ID (如 178060100000031) 已被"见习岗位管理"占用 ``` ### 根因 Flyway SQL 使用的菜单 ID(`178060100000003` 等)与已有模块的菜单 ID 冲突,导致插入失败或覆盖。 ### 修复 更换新的 ID 号段(`178060300000000` 等),并统一在回滚 SQL 中清理两种号段: ```sql DELETE FROM sys_role_permission WHERE PERMISSION_ID IN ('1780601...', '1780603...'); DELETE FROM sys_permission WHERE ID IN ('1780601...', '1780603...'); ``` ### 教训 ✅ **新增模块的菜单 ID 必须使用不重复的号段** ✅ 建议记录项目中已使用到的最大的 ID 号段,新模块在此基础上递增 ✅ 保留回滚 SQL(移除 SQL)以便重来 --- ## 6. 前端级联错误 (rowSelection) 的实际根因 ### 问题现象 ``` TypeError: Cannot read properties of undefined (reading 'rowSelection') at setup (CareerGuidanceServiceList.vue:24:64) ``` ### 实际根因 不是 `rowSelection` 代码有问题,而是 **后端 API 先报错**(视图不存在或路径不匹配),组件在 `setup` 阶段获取数据失败,导致后续渲染全部崩溃。 ### 排查方法 1. 打开浏览器 F12 → Console 2. 看完整的错误堆栈,**不要只看第一行** 3. 切换到 Network 标签,看对应 API 的响应状态码和内容 4. 后端也要看日志,定位真正的异常 ### 教训 ✅ **前端错误往上查(看组件树),后端错误往下查(看调用栈)** ✅ Vue 的 `setup` 阶段报错往往是后端数据加载失败导致的 ✅ **不要**在前端加 try-catch 掩盖 `rowSelection` 的访问,这不能解决根因 --- ## 7. Vite import.meta.glob 热更新缓存问题 ### 问题现象 重启前端后,新增的页面组件无法加载,路由跳转失败。 ### 根因 Vite 的 `import.meta.glob('../views/**/*.{vue,tsx}')` **只在服务启动时静态扫描**一次。如果在开发过程中新增了 `.vue` 文件,Vite 可能不会自动刷新缓存。 ### 修复 ```bash pnpm clean:cache && pnpm dev ``` ### 教训 ✅ **新增 Vue 文件后,必须重启 Vite 开发服务器** ✅ 先执行 `pnpm clean:cache` 清理缓存再重启 ✅ 生产环境 `build` 时不会出现此问题 --- ## 8. 表单隐藏字段未注册导致提交缺字段 ### 问题现象 表单提交后,数据库报 NOT NULL 约束错误,但前端看起来字段值已设置。 ### 根因 使用 `setFieldsValue({ categoryId: xxx })` 设置了字段值,但该字段不在 `formSchema` 中注册。表单框架(`useForm`)在 `validate()` 或提交时,**只收集 schema 中注册的字段值**,未注册的字段会被丢弃。 ### 修复 ```typescript // 在 formSchema 中添加隐藏字段 { label: '所属目录', field: 'categoryId', component: 'Input', show: false, // 隐藏 } ``` ### 教训 ✅ **所有提交到后端的字段必须在 `formSchema` 中注册** ✅ 使用 `show: false` 隐藏不需要 UI 展示的字段 ✅ 如果字段不需要校验,不写 `required` 即可 --- ## 通用开发检查清单 ### 后端启动前 - [ ] 数据库表是否已创建(含所有字段、索引) - [ ] 数据库视图是否已创建 - [ ] 菜单 SQL 是否包含角色授权 - [ ] 菜单 ID 是否与已有模块冲突 - [ ] `@RequestMapping` 路径是否符合统一规范 - [ ] 新 Controller/Mapper 是否被 Spring 扫描到(包路径正确) - [ ] 后端是否重新编译(mvn compile) ### 前端启动前 - [ ] API 路径是否与后端 `@RequestMapping` 一致 - [ ] `formSchema` 是否包含所有提交字段(包含隐藏字段) - [ ] 列字段名(`dataIndex`)是否与后端返回字段名一致 - [ ] Vite 缓存是否清理(新增文件后必须重启) - [ ] 路由组件路径是否被 `import.meta.glob` 覆盖 ### 调试排查顺序 ``` 页面崩溃 → F12 → Network 看 API 响应是否有错误 ↓ 有错误 → 修复后端(SQL / 路径 / 逻辑) ↓ 无错误 → Console 看具体报错堆栈 ↓ Vue 内部错误 → 大概率是后端数据问题导致的级联崩溃 ↓ 业务逻辑错误 → 排查具体代码 ```