diff --git a/code/fms/.workbuddy-ai/memory/2026-09-17.md b/code/fms/.workbuddy-ai/memory/2026-09-17.md new file mode 100644 index 00000000..3a4a4d08 --- /dev/null +++ b/code/fms/.workbuddy-ai/memory/2026-09-17.md @@ -0,0 +1,17 @@ +# 2026-09-17 + +## 修复:合作伙伴分类删除报「order_by 不能为空」 + +- 现象:`base/othercompany` 删除分类 → 「分类数据检查失败:order_by 不能为空」。 +- 根因:`onCategoryDelete` 的守卫查询调 `pageDataApi`(`/data/page`)**漏传 `orderBy`**; + 后端 `DataService.page` 用 `getRequiredString(params,"order_by")`,`DbUtils.buildPageOrderBy` + 对空值抛「order_by 不能为空」。`/data/loaddata` 的 order_by 是可选的,`/data/page` 是必填 + (分页需要确定性排序),所以同类守卫在 `views/system/user` 用 loaddata 没问题、这里用 page 就炸。 +- 改动:`src/views/base/othercompany/index.vue` 守卫查询加 `orderBy: 'b_id ASC'`; + `tests/views/othercompany.spec.js` 新增回归用例断言 pageDataApi 的入参(含 orderBy)。 + 全套 10 个用例通过;oxlint 干净(oxfmt 全仓库既有 format issues,未动)。 +- 已核对仓库内另 3 处 `pageDataApi` 调用(FileListPanel / useLookupOptions / FmsModuleListPage)都传了 orderBy。 + +## 维护 + +- `MEMORY.md` 超限被截断,已按主题重写精简(合并重复项、删掉过期与可推导内容)。 diff --git a/code/fms/.workbuddy-ai/memory/MEMORY.md b/code/fms/.workbuddy-ai/memory/MEMORY.md index a18a4f6a..fe35a944 100644 --- a/code/fms/.workbuddy-ai/memory/MEMORY.md +++ b/code/fms/.workbuddy-ai/memory/MEMORY.md @@ -1,195 +1,92 @@ # FMS 项目长期约定 -## 目录与文件 +## 目录 / 文档 / 库 -- 后端 `fms-api`,前端 `fms-vue`(Vue 3.5 + Vite 8 + Pinia 4 + pnpm,**纯 JS 无 TS**)。 -- 设计文档集中在仓库根:`FMS新系统核心表结构设计.md`(新库 FMS)、`FMS业务表设计.md`、 - `Fms旧系统表结构.md`(旧库 G3HY2025,**只有表、不含视图**)、`开发规范.md`、`FMS删除规则引擎设计.md`。 -- 建库/注册脚本在 `sql/`:`fms_core.sql`、`fms_business_*.sql`(建表+视图)、`fms_module_*.sql`(模块元数据)。 +- 后端 `fms-api`(Spring Boot + JDK21,JDBC+Druid 无 ORM);前端 `fms-vue`(Vue 3.5 + Vite 8 + Pinia,纯 JS)。 + `fms-vue-old1/old2` 仅供历史参考,**不要改**。 +- 根目录设计文档:`FMS新系统核心表结构设计.md`(权威表结构)、`FMS业务表设计.md`、`开发规范.md`(AI 规范)、 + `Fms旧系统表结构.md`(旧库 G3HY2025,只有表无视图)、`FMS删除规则引擎设计.md`。 +- 建库/模块注册脚本在 `sql/`(`fms_core.sql`、`fms_business_*.sql`、`fms_module_*.sql`)。 +- SQL Server `118.89.70.199:1433`:旧库 `G3HY2025`,新库 **FMS**(新系统一律用 FMS,**生产库,写前先确认**)。 +- 机构→库映射在 `fms-api/config/dbconfigs/{ORG_ID}.properties`(gitignore):`G3HD.properties` → FMS, + `G3HY2025.properties` → G3HY2025。**没有 FMS.properties**。 +- 后端 8088(context-path `/api`),前端 dev 5082(`/api` → 127.0.0.1:8088)。直连接口需先 `/auth/login`。 -## 数据库 +## 元数据核心表(真实列名,已连库核对,别再猜) -- 服务器 `118.89.70.199:1433`;**旧库 `G3HY2025`**、**新库 `FMS`**(新系统一律用 FMS)。 -- 机构与库的映射写在 `fms-api/config/dbconfigs/{ORG_ID}.properties`(该目录被 gitignore)。 - 已存在:`G3HD.properties` → databaseName=**FMS**;`G3HY2025.properties` → databaseName=G3HY2025。 - **没有 FMS.properties**,G3HD 就是指向新库的那份。 -- 后端 `fms-api` 监听 **8088**(context-path `/api`);前端 dev server **5082**(proxy `/api` → 127.0.0.1:8088)。 -- 直接 curl 后端接口会返回 `401 未登录或登录已失效`,需先 `/auth/login`。 +- `s_module`:PK **`b_id`**(无 `b_module_code`;无 `b_route`,那是 `s_menu` 的);本身是树: + `b_parent_id`/`b_depth`/`b_path`;另有 `b_module_type`(module/data/virtual)、`b_view_table`、`b_save_table`、 + `b_key_field`、`b_order_sql`、`b_query_sql`、`b_config_json`、`b_canuse`、`b_xh`、`b_bz`、`b_name`、`b_i18n`。 +- `s_field`:PK **`(b_module_id, b_field)`**,无 `b_id`;`b_type` 实测 `input`/`number`/`checkbox`/`datetime`/`date`。 +- `s_module_schema`:PK `(b_module_id, b_schema_type∈view|edit|query)`,`b_schema_json`。 +- `s_menu`:`b_id`/`b_parent_id`/`b_depth`/`b_path`/`b_menu_type`(directory|page)/`b_route`/`b_icon`;`s_menu_module` 绑菜单与模块。 +- **权限靠 `b_id`**:permissionStore 的 `moduleCodes` 即 `s_module.b_id`,页面 `MODULE_CODE` 必须等于它; + 还要在 `s_menu_module` 里绑菜单,否则非超管看不到授权入口。 +- 列表模块编码统一 `v_`+表名(`v_b_othercompany`),`b_view_table`→视图,`b_save_table`→真实表。 +- schema JSON 形状:`view{columns:[{field,width}|{type:group,title,children}]}`、 + `edit{children:[{field,span,required,readonly,defaultValue}|group]}`、`query{groups:[{conditions}],quick}`。 +- 路由守卫只看 `meta.moduleId`/`meta.adminOnly`;`views/module/**` 是 adminOnly(仅 g3soft)。 -## 元数据驱动的核心约定 +## 树结构规范(模块/菜单/部门/分类树通用) -- 模块树 `s_module`(`b_module_type` ∈ module/data/virtual),字段 `s_field`, - 界面配置 `s_module_schema`(PK = `b_module_id` + `b_schema_type` ∈ view/edit/query,JSON 存在 `b_schema_json`)。 -- **这三张表的真实列名(已连库核对,写脚本时别再猜)**: - - `s_module`:PK 是 **`b_id`**(**没有** `b_module_code` 列);且它**本身就是树** —— - `b_parent_id` / `b_depth` / `b_path`;另有 `b_module_type`(`module` / `data`)、 - `b_view_table` / `b_save_table` / `b_key_field` / `b_order_sql` / `b_query_sql` / `b_config_json` - / `b_canuse` / `b_xh` / `b_bz` / `b_name` / `b_i18n`。**没有 `b_route`**(那是 `s_menu` 的列)。 - - `s_field`:PK 是 **`(b_module_id, b_field)`**,**没有 `b_id`**;列为 - `b_field` / `b_name` / `b_i18n` / `b_type` / `b_canuse` / `b_xh`。`b_type` 实测取值: - `input` / `number` / `checkbox` / `datetime` / `date`。 - - `s_module_schema`:PK `(b_module_id, b_schema_type)`,另有 `b_schema_json` / `b_canuse`。 - - `s_menu`:`b_id` / `b_parent_id` / `b_depth` / `b_path` / `b_name` / `b_i18n` / - `b_menu_type`(`directory` 或 `page`)/ `b_route` / `b_icon` / `b_xh` / `b_canuse`。 - - `s_menu_module`:`b_menu_id` / `b_module_id` / `b_xh` / `b_canuse`。 -- **模块编码写法**:列表模块统一 `v_` + 表名(如 `v_b_contact`),`b_view_table` 指向查询视图, - `b_save_table` 指向真实表。数据模块挂在分组 module 节点下(如 `base`、`system`)。 -- **前端权限靠 `b_id`**:`usePermissionStore` 的 `moduleCodes` 就是 `s_module.b_id`; - `canPower(code, power)` 查 `${b_id}.${power}`。所以页面里写的 `MODULE_CODE` 必须等于模块 `b_id`。 - 另外要记得在 `s_menu_module` 里把菜单绑到模块,否则侧栏入口不会出现在角色的「菜单权限」树中,非超管无法授权。 -- **s_module_schema JSON 真实形状**(与 `schemaRender.js` 一一对应,别写错): - - `view`:`{ schemaVersion, columns: [ {field,width} | {type:'group',title,children} ] }` - - `edit`:`{ schemaVersion, children: [ {field,span,required,readonly,defaultValue} | {type:'group',...} ] }` - - `query`:`{ schemaVersion, groups: [ { conditions: [ {field,operator} ] } ], quick: [field] }` -- **s_menu 无种子数据**,业务菜单由「菜单管理」页面维护。`s_menu_module` 把菜单关联到 data/virtual 模块。 -- 路由守卫只看 `meta.moduleId` / `meta.adminOnly`(`to.matched.some`),**没有 moduleId 的路由不需要模块记录即可访问**。 +`b_parent_id` 是权威关系,`b_depth`(根=0)、`b_path`(`/cn/east/`)是派生字段,移动节点要同事务更新全部后代; +不一致时以 `b_parent_id` 为准。工具在 `src/utils/tree.js`(`buildSortedTree`/`computeTreeMove`/`collectDescendantIds`/ +`applyTreePosition`/`filterTreeByText`/`findTreeNode`),展示层用 `components/fms-tree/FmsTree.vue`(自管虚拟根)。 -## 树结构规范(模块树 / 菜单树 / 部门树通用) +## 后端通用接口(前端业务数据只走这几个) -见 `FMS新系统核心表结构设计.md` 第 1.7 节:**邻接表为主 + `b_depth`/`b_path` 冗余**。 +- `POST /auth/login`:字段是**小写** `orgid`/`userid`/`password`;`orgid=G3HD`。 +- `/data/loaddata`:`view_name`/`search_condition`/**可选** `order_by`/`search_columns`(真 JSON 数组)。 +- `/data/page`:**`order_by` 必填**(`DbUtils.buildPageOrderBy` 会报「order_by 不能为空」), + 分页参数名是 **`page_no`/`page_size`**(不是 pageindex/pagesize)。 +- `/data/saveobjt`:body 是数组,每项 `{table, key_field, deletes, updates, inserts}`,**`key_field` 必填** + (复合主键逗号分隔),顺序 delete→update→insert。 +- 模块元数据不由接口下发,前端读本地 schema JSON,改元数据无需重启后端。 +- `DbUtils` 是 SQL 唯一出口:标识符加 `[]`,`validateReadOnlySql()` 只允许查询。 -- `b_parent_id` 是**权威关系**;`b_depth`、`b_path` 是业务层维护的**派生字段**。 -- 根节点 `b_parent_id` 为 `NULL`,`b_depth` 从 0 起算;`b_path` 形如 `/cn/east/`(前后都带 `/`)。 -- 新增/移动节点时必须在同一事务中同步当前节点及**全部后代**的 `b_depth`/`b_path`。 -- 二者不一致时**以 `b_parent_id` 为准**,并提供按父子关系重建冗余字段的能力。 -- 全项目通用的树工具在 **`fms-vue/src/utils/tree.js`**(`buildSortedTree` / `computeTreeMove` / - `collectDescendantIds` / `applyTreePosition` / `filterTreeByText` 等)。新增一棵树优先复用它。 -- 展示层适配器 **`fms-vue/src/components/fms-tree/FmsTree.vue`**:自管 `loading` 遮罩与 - 「全部分类」虚拟根(空选中键 ⟺ 虚拟根,调用方不需要知道 `rootKey`)。 +## 环境坑(本机特有) -## 主题与样式约定 +- **编译/测试 fms-api 唯一可行命令**(start.cmd 里的 JDK/Maven 路径都是坏的): + ``` + cd /d/workspace/code/fms/fms-api && /d/devtool/jdk/jdk21/bin/java \ + -Dclassworlds.conf="D:/devtool/maven/bin/m2.conf" -Dmaven.home="D:/devtool/maven" \ + -Dmaven.multiModuleProjectDirectory="D:/workspace/code/fms/fms-api" \ + -cp "D:/devtool/maven/boot/plexus-classworlds-2.11.0.jar" \ + org.codehaus.plexus.classworlds.launcher.Launcher -o -Dtest=XxxTests test + ``` + 本地仓库是 `D:\data\maven`(不是 ~/.m2);联网下载去掉 `-o`;`target/` 被 git 跟踪,编译后有噪音。 +- 无 sqlcmd/pyodbc/pymssql:用 `fms-api/tools/migration/RunSqlFile.java` 或一次性 `SqlRunner.java` + + JDBC jar(`~/.m2/.../mssql-jdbc-13.4.0.jre11.jar`)+ `/d/devtool/jdk/jdk17/bin/java`。 + 脚本要按行首 `GO` 分批,路径传绝对路径。 +- `vite build` 被 safe-delete 守卫拦(要删 600+ 文件):用 `--outDir dist-verify --emptyOutDir`; + 清理用 `find dist-verify -type f -delete` 再 `find dist-verify -type d -empty -delete`。 +- `oxfmt --check` 全仓库都报 format issues(版本漂移),**不是自己引入的**,别顺手格式化整个仓库。 +- 本工作区可能被多会话同时编辑,验证失败项先用 `find