# 就业援助模块开发问题记录
> 记录开发过程中遇到的各种问题及解决方案,供后续新模块开发参考
---
## 1. SQL 执行问题
### 1.1 达梦不支持行内 COMMENT 语法
**错误:** `Syntax error (-2007)`
**原因:** 达梦(DM8)原生模式下不支持 MySQL 的行内 `COMMENT 'xxx'` 语法。
**错误写法:**
```sql
CREATE TABLE FOO (
ID VARCHAR(36) NOT NULL COMMENT '主键ID' -- ❌ DM不支持
);
```
**正确写法:**
```sql
CREATE TABLE FOO (
ID VARCHAR(36) NOT NULL
);
COMMENT ON TABLE FOO IS '表名';
COMMENT ON COLUMN FOO.ID IS '主键ID';
```
**注意:** `.docs/sql/` 目录下的参考文件(如 `重点关注人员信息.sql`)使用的是 MySQL 语法,要在 DM 上执行需先转换。
---
### 1.2 SQL 执行到错误的模式(Schema)
**错误:** `Table or view not found`
**原因:** 在 DM 管理工具中连接时,SQL 可能执行到了错误的 schema 下,导致表和数据不在预期的 schema 中。
**解决:** 执行前确认当前 schema,或者使用 `SET SCHEMA 模式名` 切换到正确模式。
---
### 1.3 达梦保留字需用双引号包围
**错误:** `Invalid column name [name]`
**原因:** `NAME`、`DESCRIPTION`、`STATUS` 是达梦保留字,在 SQL 中直接使用会报错。
**解决:** 用双引号包围保留字,且需要注意**大小写**——达梦默认以**大写**存储对象名,所以双引号内也需大写:
```sql
-- ✅ 正确
INSERT INTO sys_permission (id, "NAME", "DESCRIPTION", "STATUS") VALUES (..., 'xxx', 'xxx', 1);
-- ❌ 错误(大小写不匹配)
INSERT INTO sys_permission (id, "name", "description", "status") VALUES (...);
```
---
### 1.4 视图 SQL 需使用 DM 原生函数
**错误:** 视图创建成功后查询报错
**原因:** DM 原生模式下 `TIMESTAMPDIFF` + `CURDATE()` 是 MySQL 函数
**解决:** 使用 DM 原生函数:
```sql
-- ❌ MySQL 语法
TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age
-- ✅ DM 原生语法
FLOOR(MONTHS_BETWEEN(CURRENT_DATE, birth_date) / 12) AS age
```
---
## 2. 菜单权限问题
### 2.1 权限 status = NULL 导致按钮不显示
**现象:** 数据库中有权限记录,角色授权也正确,但前端按钮不显示。
**根因:** 后端 `SysPermissionController` 查询权限时过滤条件为 `status = 1`。但插入 SQL 中 `status` 字段为 `NULL`,导致权限码不被返回给前端。
**参考:** `V20260604_9__fix_permission_status.sql`
**解决:** 插入按钮权限时 `status` 必须设为 `1`:
```sql
-- ✅ 正确
INSERT INTO sys_permission (..., status, internal_or_external)
VALUES (..., 1, 0); -- status = 1
-- ❌ 错误
INSERT INTO sys_permission (..., status, internal_or_external)
VALUES (..., NULL, 0); -- status = NULL
```
**修复已有数据:**
```sql
UPDATE sys_permission SET status = 1
WHERE perms LIKE '模块名:%' AND (status IS NULL OR status != 1);
```
---
### 2.2 v-auth 权限指令不生效
**现象:** 权限已插入、角色已关联、status=1,但 `v-auth` 仍然隐藏按钮
**排查步骤:**
1. 在控制台查看 `permissionStore.getPermCodeList` 是否包含目标权限码
2. 如果不在列表中,检查 `status` 是否为 `1`
3. 重启后端服务(清除缓存)
4. 重新登录(前端重新加载权限列表)
---
## 3. 前端组件模式
### 3.1 列表页必须使用 useListPage + 手动搜索表单
**参考模块:** `FocusPersonnelList.vue`
**正确模式:**
```vue
```
**❌ 错误模式(不要用):**
```ts
// 不要用 formConfig.schemas 方式(前端的 useSearchForm: true)
const [registerTable] = useTable({
formConfig: { schemas: [...] }, // ❌
});
```
---
### 3.2 Modal 组件必须使用 ref 方式
**参考模块:** `FocusPersonnelModal.vue`
**正确模式:**
```vue
```
**❌ 错误模式(不要用):**
```ts
const [registerModal, { openModal }] = useModal(); // ❌
```
---
### 3.3 详情页组件模式
**参考模块:** `FocusPersonnelDetail.vue`
**正确模式:**
```vue
```
---
### 3.4 字典翻译必须使用 useDict
**现象:** 列表显示数值而非文字(如性别显示 "1" 而非 "男性")
**解决:** 使用 `useDict` 钩子加载字典:
```ts
import { useDict } from '/@/hooks/dictionary/useDict';
const { getDictText } = useDict(['JobSeekerStatus', 'JobSeekerCategory', ...]);
```
模板中使用 `getDictText` 翻译:
```vue
{{ getDictText('JobSeekerStatus', text) || text }}
```
性别用硬编码映射即可:
```ts
const genderMap: Record = { '1': '男性', '2': '女性' };
```
---
### 3.5 selectedRowKeys 判空保护
**错误:** `Cannot read properties of undefined (reading 'length')`
**原因:** 表格未初始化时 `selectedRowKeys` 为 `undefined`,直接访问 `.length` 报错
**解决:**
```vue
:disabled="!selectedRowKeys || selectedRowKeys.length === 0"
```
---
## 4. 达梦函数兼容性速查表
| 功能 | MySQL 语法 | DM 语法 |
|------|-----------|---------|
| 当前日期 | `CURDATE()` | `CURRENT_DATE` |
| 年龄计算 | `TIMESTAMPDIFF(YEAR, birth, CURDATE())` | `FLOOR(MONTHS_BETWEEN(CURRENT_DATE, birth) / 12)` |
| 标识符引用 | `` `name` `` | `"NAME"`(双引号,大写) |
| 行内注释 | `COMMENT 'xxx'` | 不支持,用 `COMMENT ON` |
| 删除视图 | `DROP VIEW IF EXISTS v_name` | 支持 |
| 删除表 | `DROP TABLE IF EXISTS t_name` | 支持 |
| 字符串拼接 | `CONCAT('%', val, '%')` | `CONCAT('%', val, '%')` |
| 分页 | `LIMIT ? OFFSET ?` | `LIMIT ? OFFSET ?` |
---
## 5. 开发流程检查清单
### 新建模块时逐项检查:
- [ ] 建表SQL:去掉行内 `COMMENT`,改用 `COMMENT ON`
- [ ] 建表SQL:使用 `DROP TABLE IF EXISTS` 支持重复执行
- [ ] 视图SQL:使用 `FLOOR(MONTHS_BETWEEN(...))` 替代 `TIMESTAMPDIFF`
- [ ] 视图SQL:使用 `DROP VIEW IF EXISTS` 支持重复执行
- [ ] 菜单SQL:`status` 设为 `1`(非 NULL)
- [ ] 菜单SQL:保留字 `NAME`/`DESCRIPTION`/`STATUS` 用大写双引号
- [ ] 菜单SQL:先 DELETE 清理旧数据再 INSERT(兼容重复执行)
- [ ] Mapper XML:年龄用 `FLOOR(MONTHS_BETWEEN(...))` 计算
- [ ] Mapper XML:不使用反引号
- [ ] 前端列表页:使用 `useListPage` + 手动 `` 搜索
- [ ] 前端Modal:使用 `ref` 方式 + ``,非 `useModal`
- [ ] 前端详情:使用 `` + `watch` 加载
- [ ] 前端字典:使用 `useDict` 加载字典翻译
- [ ] 导出按钮:保留 `v-auth` 权限控制
- [ ] selectedRowKeys:添加 `!selectedRowKeys` 判空