From 1f56cc3efbf72c8d602c928680ed65000c0f173e Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Tue, 15 Sep 2026 17:00:29 +0800 Subject: [PATCH] 20260915170028 --- code/fms/CODEBUDDY.md | 185 +++++++++ .../cn/g3soft/fmsapi/FmsApiApplication.class | Bin 0 -> 1180 bytes .../g3soft/fmsapi/config/AuthProperties.class | Bin 0 -> 1134 bytes .../config/FileStorageProperties$Local.class | Bin 0 -> 963 bytes .../config/FileStorageProperties$Oss.class | Bin 0 -> 743 bytes .../fmsapi/config/FileStorageProperties.class | Bin 0 -> 1731 bytes .../g3soft/fmsapi/config/JacksonConfig.class | Bin 0 -> 1127 bytes .../g3soft/fmsapi/config/JwtAuthFilter.class | Bin 0 -> 4601 bytes .../fmsapi/controller/AuthController.class | Bin 0 -> 2802 bytes .../fmsapi/controller/DataController.class | Bin 0 -> 4785 bytes .../fmsapi/controller/FileController.class | Bin 0 -> 6660 bytes .../g3soft/fmsapi/database/OrgContext.class | Bin 0 -> 1716 bytes .../database/OrgDataSourceFactory.class | Bin 0 -> 5023 bytes .../OrgDataSourceManager$PoolEntry.class | Bin 0 -> 1425 bytes .../database/OrgDataSourceManager.class | Bin 0 -> 8785 bytes .../fmsapi/database/OrgDatabaseConfig.class | Bin 0 -> 1917 bytes .../database/OrgDatabaseConfigLoader.class | Bin 0 -> 6468 bytes .../database/OrgDatabaseConfigWatcher.class | Bin 0 -> 6943 bytes .../database/OrgDatabaseProperties$Pool.class | Bin 0 -> 3036 bytes .../database/OrgDatabaseProperties.class | Bin 0 -> 1975 bytes .../database/OrgRoutingDataSource.class | Bin 0 -> 1348 bytes .../exception/AuthenticationException.class | Bin 0 -> 470 bytes .../fmsapi/exception/BusinessException.class | Bin 0 -> 434 bytes .../exception/GlobalExceptionHandler.class | Bin 0 -> 5156 bytes .../exception/SaveObjectException.class | Bin 0 -> 1265 bytes .../ActiveSessionRegistry$AccountKey.class | Bin 0 -> 1790 bytes .../service/ActiveSessionRegistry.class | Bin 0 -> 1793 bytes .../g3soft/fmsapi/service/AuthService.class | Bin 0 -> 5440 bytes .../DataSaveService$AutoCodeFormat.class | Bin 0 -> 2078 bytes .../DataSaveService$AutoCodeRule.class | Bin 0 -> 3113 bytes .../DataSaveService$CodeCandidate.class | Bin 0 -> 1755 bytes .../fmsapi/service/DataSaveService.class | Bin 0 -> 21207 bytes .../g3soft/fmsapi/service/DataService.class | Bin 0 -> 4983 bytes .../g3soft/fmsapi/service/FileService.class | Bin 0 -> 13964 bytes .../fmsapi/service/LoginLogService.class | Bin 0 -> 4120 bytes .../cn/g3soft/fmsapi/utils/ApiResponse.class | Bin 0 -> 3373 bytes .../cn/g3soft/fmsapi/utils/ClientInfo.class | Bin 0 -> 2468 bytes .../fmsapi/utils/ClientInfoExtractor.class | Bin 0 -> 4480 bytes .../fmsapi/utils/DbUtils$ColumnMetadata.class | Bin 0 -> 2268 bytes .../fmsapi/utils/DbUtils$PageResult.class | Bin 0 -> 2267 bytes .../fmsapi/utils/DbUtils$TableMetadata.class | Bin 0 -> 2434 bytes .../cn/g3soft/fmsapi/utils/DbUtils.class | Bin 0 -> 28217 bytes .../fmsapi/utils/IpRegionSearcher.class | Bin 0 -> 4126 bytes .../cn/g3soft/fmsapi/utils/IpUtils.class | Bin 0 -> 1487 bytes .../fmsapi/utils/JwtUtils$TokenClaims.class | Bin 0 -> 1843 bytes .../cn/g3soft/fmsapi/utils/JwtUtils.class | Bin 0 -> 4694 bytes .../cn/g3soft/fmsapi/utils/ParamUtils.class | Bin 0 -> 3413 bytes .../cn/g3soft/fmsapi/utils/StringUtils.class | Bin 0 -> 540 bytes .../g3soft/fmsapi/utils/UuidV7Generator.class | Bin 0 -> 875 bytes .../snowflake/contract/IIdGenerator.class | Bin 0 -> 262 bytes .../snowflake/contract/ISnowWorker.class | Bin 0 -> 259 bytes .../contract/IdGeneratorException.class | Bin 0 -> 1157 bytes .../contract/IdGeneratorOptions.class | Bin 0 -> 935 bytes .../contract/OverCostActionArg.class | Bin 0 -> 901 bytes .../utils/snowflake/core/SnowWorkerM1.class | Bin 0 -> 4342 bytes .../utils/snowflake/core/SnowWorkerM2.class | Bin 0 -> 1473 bytes .../snowflake/idgen/DefaultIdGenerator.class | Bin 0 -> 3108 bytes .../utils/snowflake/idgen/IdGenerator.class | Bin 0 -> 1322 bytes .../fmsapi/FmsApiApplicationTests.class | Bin 537 -> 537 bytes .../database/OrgDataSourceFactoryTests.class | Bin 2390 -> 2363 bytes .../OrgDataSourceManagerTests$1.class | Bin 1959 -> 1973 bytes .../database/OrgDataSourceManagerTests.class | Bin 7332 -> 7388 bytes .../OrgDatabaseConfigLoaderTests.class | Bin 2456 -> 2498 bytes .../OrgDatabaseConfigWatcherTests$1.class | Bin 1749 -> 1740 bytes .../OrgDatabaseConfigWatcherTests.class | Bin 7181 -> 7213 bytes .../GlobalExceptionHandlerTests.class | Bin 3189 -> 3157 bytes .../service/ActiveSessionRegistryTests.class | Bin 1602 -> 1598 bytes .../fmsapi/service/AuthServiceTests.class | Bin 3864 -> 3828 bytes .../DataSaveServiceTests$JdbcFixture.class | Bin 3910 -> 3894 bytes .../fmsapi/service/DataSaveServiceTests.class | Bin 8820 -> 8884 bytes .../g3soft/fmsapi/utils/JwtUtilsTests.class | Bin 2051 -> 2051 bytes .../snowflake/DefaultIdGeneratorTests.class | Bin 7463 -> 7684 bytes code/fms/fms-api/tools/migration/README.md | 64 ++- .../tools/migration/TreePathRepair.java | 372 ++++++++++++++++++ .../src/components/fms-table/FmsTable.vue | 21 +- .../src/components/ui/tree/tree-node.vue | 24 +- code/fms/fms-vue/src/utils/tree.js | 118 ++++++ .../src/views/base/othercompany/index.vue | 80 +--- .../src/views/base/otherdata/index.vue | 14 +- .../fms/fms-vue/src/views/dashboard/index.vue | 1 - .../menu-management/ModulePickerModal.vue | 7 +- .../views/module/menu-management/index.vue | 36 +- .../src/views/module/menu-management/utils.js | 93 +---- .../views/module/module-management/index.vue | 73 +++- .../views/module/module-management/utils.js | 71 +--- .../fms/fms-vue/tests/components/tree.spec.js | 27 ++ .../tests/unit/module-picker-modal.spec.js | 84 ++++ .../tests/unit/othercompany-utils.spec.js | 110 +----- code/fms/fms-vue/tests/unit/utils.spec.js | 152 ++++++- 89 files changed, 1135 insertions(+), 397 deletions(-) create mode 100644 code/fms/CODEBUDDY.md create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/FmsApiApplication.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/AuthProperties.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/FileStorageProperties$Local.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/FileStorageProperties$Oss.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/FileStorageProperties.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/JacksonConfig.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/JwtAuthFilter.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/AuthController.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/DataController.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/FileController.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgContext.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceFactory.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager$PoolEntry.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfig.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseProperties$Pool.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseProperties.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgRoutingDataSource.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/AuthenticationException.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/BusinessException.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/GlobalExceptionHandler.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/SaveObjectException.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/ActiveSessionRegistry$AccountKey.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/ActiveSessionRegistry.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/AuthService.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeFormat.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeRule.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$CodeCandidate.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataService.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/FileService.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/LoginLogService.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ApiResponse.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfo.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfoExtractor.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils$ColumnMetadata.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils$PageResult.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils$TableMetadata.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpRegionSearcher.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpUtils.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils$TokenClaims.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ParamUtils.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/StringUtils.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/UuidV7Generator.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IIdGenerator.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/ISnowWorker.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IdGeneratorException.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IdGeneratorOptions.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/OverCostActionArg.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/core/SnowWorkerM1.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/core/SnowWorkerM2.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/idgen/DefaultIdGenerator.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/idgen/IdGenerator.class create mode 100644 code/fms/fms-api/tools/migration/TreePathRepair.java create mode 100644 code/fms/fms-vue/tests/unit/module-picker-modal.spec.js diff --git a/code/fms/CODEBUDDY.md b/code/fms/CODEBUDDY.md new file mode 100644 index 00000000..94f078ed --- /dev/null +++ b/code/fms/CODEBUDDY.md @@ -0,0 +1,185 @@ +# CODEBUDDY.md + +This file provides guidance to CodeBuddy Code when working with code in this repository. + +## 仓库结构 + +| 目录 | 说明 | +| --- | --- | +| `fms-api/` | 后端,Spring Boot 4.1 + Java 21,JDBC + Druid,无 ORM | +| `fms-vue/` | 当前前端,Vue 3.5 + Vite 8 + Pinia 4 + pnpm,JS(非 TS) | +| `fms-vue-old1/`、`fms-vue-old2/` | 旧版前端副本,仅作历史参考(`old2` 是权限/登录逻辑的来源),**不要改** | +| `sql/` | 建表 / 视图 / 模块注册脚本,由 `RunSqlFile` 执行 | +| `fms-api/tools/migration/` | 独立单文件 Java 迁移工具,`javac` 手动编译,不走 Maven | +| `fms-api/config/migrations/tools/` | 早期一次性数据迁移程序(历史) | +| `参考组件库/` | `antdv-next`、`shadcn` 源码,前端组件实现的参照 | +| 根目录 `*.md` | 设计文档(中文):`FMS新系统核心表结构设计.md`(权威表结构)、`FMS业务表设计.md`、`开发规范.md`(AI 工作规范)、`Fms旧系统表结构.md` | + +真实数据库是远端 SQL Server 2022(`118.89.70.199:1433`);旧库 `G3HY2025`,新库 `FMS`。 + +## 常用命令 + +### 后端(在 `fms-api/` 下) + +```bash +./start.cmd # 设置 JAVA_HOME=jdk-21 / Maven 3.8.9 后 spring-boot:run +./mvnw spring-boot:run # 等价;需自行准备 JDK 21 +./mvnw test # 全部测试 +./mvnw -Dtest=DataSaveServiceTests test # 单个测试类 +./mvnw -Dtest=JwtUtilsTests#methodName test # 单个测试方法 + +# 连接只支持 TLS 1.0 的旧库时: +mvn "-Dspring-boot.run.jvmArguments=-Djava.security.properties=./config/legacy-tls.security" spring-boot:run +``` + +服务端口 `8088`,`server.servlet.context-path=/api`。 + +### 前端(在 `fms-vue/` 下,用 pnpm) + +```bash +pnpm dev # Vite dev server,端口 5082,/api 代理到 http://127.0.0.1:8088 + # 用 VITE_PROXY_TARGET 可指向另一个后端实例 +pnpm build +pnpm lint # oxlint . --deny-warnings +pnpm lint:fix +pnpm fmt # oxfmt --write . +pnpm fmt:check +pnpm check # lint + fmt:check + build + +pnpm test # vitest run +pnpm test:file tests/views/login.spec.js # 单文件(也可靠目录:tests/stores) +pnpm test:watch +pnpm test:coverage # 只有这个会检查 60% 阈值 +``` + +### 数据库脚本与迁移工具 + +```bash +# 执行 sql/ 下脚本(按 GO 分批,单事务,catalog 必须是 FMS) +cd fms-api +java -Dstdout.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ + tools/migration/RunSqlFile.java ../sql/fms_core.sql --dry-run +# 去掉 --dry-run 执行;其余工具(FieldNameSync / SchemaDumpMarkdown / OtherDataMigrate)见 +# fms-api/tools/migration/README.md,编译时需 -encoding UTF-8 +``` + +## 架构要点 + +### 1. 元数据驱动的「SQL 核心」 + +系统是 SQL 驱动的内部 ERP,模块差异靠**配置数据**表达,不为单个页面复制模块专用 SQL。 + +核心表(定义见 `FMS新系统核心表结构设计.md`,建表见 `sql/fms_core.sql`): + +- `s_module` — 模块树。`b_id` 是业务编码主键(非雪花),`b_parent_id` 是权威父子关系,`b_depth`/`b_path` 是派生字段;`b_module_type` ∈ `module` / `data` / `virtual`;查询来源优先级 `b_query_sql` > `b_view_table`;保存目标 `b_save_table`。 +- `s_field` — 字段业务元数据,主键 `(b_module_id, b_field)`。只存业务类型 `b_type`、名称、多语言键;**不存**数据库类型/长度/精度,需要时现场读表结构。 +- `s_module_schema` — 界面配置,主键 `(b_module_id, b_schema_type)`,`b_schema_type` ∈ `view` / `edit` / `query`,内容整份存 `b_schema_json`。字段分组不再建表,是 JSON 里的 `type: "group"` 节点。 +- `s_user_module_pref` — 个人层覆盖,只存相对系统默认的增量;读取顺序「个人 > 系统默认」。 +- `s_autocode`、`s_relation`、`s_i18n` / `s_i18n_type`、`s_menu`、日志表。 +- 权限:`s_power`(权限点)、`s_user_power`、`s_user_field_power`、`s_user_data_power`。菜单/模块/动作权限统一收在 `s_power`,字段权限和数据范围不进 `s_power`。 + +表结构硬约束:不使用外键、`CHECK`、触发器;树结构(模块树/菜单树/部门树)用「邻接表 + `b_depth`/`b_path` 冗余」,移动节点时同事务更新全部后代。主键类型取决于表:配置表用业务编码,业务子表用 `bigint` 雪花。 + +### 2. 后端通用数据接口(`DataController`) + +前端所有业务数据都走这组通用接口,没有按页面定制的接口: + +- `POST /api/data/loaddata` — 按表/视图 + `search_condition` + `order_by` + `search_columns` 查询 +- `POST /api/data/page` — 分页(`page_no`/`page_size`),内部包一层 `COUNT_BIG(1) OVER()` 拿总数,空页回退单独 count +- `POST /api/data/loaddatabysql` — 执行调用方传入的完整 `SELECT` +- `POST /api/data/saveobjt` — 单个事务内保存多张表的变更请求 +- `POST /api/data/nextid` / `nextcode` — 雪花 ID / 按 `s_autocode` 规则取业务编号 +- `POST /api/data/describe` — 读表或视图列结构 + +`DbUtils`(`fms-api/src/main/java/cn/g3soft/fmsapi/utils/DbUtils.java`)是 SQL 生成与执行的唯一出口: + +- 标识符一律经 `SAFE_IDENTIFIER` 校验并加 `[...]`,排序字段只允许 `field [ASC|DESC]` 或 `CASE ... END`。 +- `validateReadOnlySql()` 强制只读:必须以 `select`/`with` 开头,剔除字符串字面量后禁止 `;`、注释和 `insert|update|delete|merge|drop|...` 关键字。**调用方传入的 SQL 与 `b_query_sql` 同一个入口,都只允许查询**;写入必须走 `DataSaveService`。 +- 通用增删改按 `ResultSetMetaData` 现场解析列,`_` 前缀派生列不参与写入。 + +`DataSaveService` 支持多列业务键(如 `s_field` 的 `(b_module_id, b_field)`),并按 `s_autocode` 生成业务编号。`ParamUtils` 负责从 `Map` 取参。 + +### 3. 多机构数据源与鉴权 + +- `AuthController /auth/login` 用 `orgid + userid + password` 登录(`b_user.b_id` 即账号,密码明文保存),签发 JWT;`ActiveSessionRegistry` 保证同一账号单会话,被顶号返回 `40101`。 +- `JwtAuthFilter` 校验 token + 会话后写入 `OrgContext`(ThreadLocal),异常一律以 HTTP 200 + 业务 code 返回。 +- `OrgDataSourceManager` 按机构码懒加载 Druid 连接池,配置文件 `fms-api/config/dbconfigs/{ORG_ID}.properties`(该目录已 gitignore);`OrgDatabaseConfigWatcher` + 定时核对支持改配置不重启、旧池延迟关闭。**读写业务数据必须经它取连接**,不要直接用注入的默认 `DataSource`(迁移工具除外)。 + +### 4. 前端运行时装配 + +- `src/router/routes.js` 只声明路由表与派生纯数据(`collectMenuRoutes` / `menuRouteOptions` 供菜单管理下拉用);`src/router/index.js` 只建实例 + 守卫。分离是为了避免「页面 → router/index → 守卫 → store」循环依赖,不要把两者合并。 +- 路由分组节点(`/module`)只写 `path + redirect + children`、**不写 component**,子页面才能继续渲染在 `DefaultLayout` 的同一个 `router-view` 上、保持 keep-alive 层级。 +- 守卫里统一 `await` 权限、菜单、语言清单三家 store 的 `load()`;三者都按登录主体(`orgId:userId`)去重、并发复用同一请求、主体切换时 `clear()`。 +- `views/module/**` 是 `meta.adminOnly`,仅管理员账号 `g3soft` 可进(路由守卫拦截 + 侧栏入口只在 `AppSidebar` 渲染),且不进 `s_menu`。 +- 页面缓存:`layouts/DefaultLayout.vue` 里 keep-alive 外层 key = `fullPath`(稳定),内层 key = `fullPath:refreshKey`(双击页签只重挂内层)。**每个页签一个命名包装组件**,详情页按 `cacheKeyOf(fullPath)` 生成 name;包装组件渲染不能读当前路由,`fullPath` 只能经 props 传入。`src/stores/app.js` 的 `visitedTabs` / `routeRefreshKeys` 与之联动。 + +### 5. 模块列表 / 编辑的运行时(元数据渲染) + +业务页面不手写表格和表单,交给通用组件按模块配置渲染: + +``` +s_module_schema.b_schema_json ──schemaRender.js──▶ fms-module-list / fms-module-edit 的行/分组结构 +``` + +- `src/components/fms-module-common/schemaRender.js` — JSON→渲染结构适配层:`loadModuleSchemas`、`buildListConfig`(view→列)、`buildEditRender`(edit→分组+行)、`buildQueryRender`(query→条件)、`applyColumnSettings`(列宽/顺序回写)、`applyQueryLayout`(「保存为通用」回写默认查询布局)。运行时组件不需要感知 JSON 节点差异。 +- `src/components/fms-module-common/queryUtils.js` — 查询条件 AST → SQL 片段。常用条件与高级条件共用一套 AST(条件项自带 `join` 表达且/或),`OPERATORS` 与操作符文案是唯一定义处;`astToSql` 是唯一出口。字段编码和值在这里做 `[...]` 与 `N'...'` 转义。 +- `src/components/fms-module-list/FmsModuleListPage.vue` — 通用列表页容器,props 有 `dataCode`、`fixedSearchCondition`、`rowActions`、`rowSelection`、`editable`、`rowDraggable` 等;个人查询布局偏好走同目录 `useModulePref.js`(`s_user_module_pref`)。 +- `src/components/ui/` — 自研 UI 组件库,统一从 `@/components/ui` 具名导入(含命令式 `Message`、`Modal.confirm`)。 + +### 6. 模块管理页 = 配置中心 + +`src/views/module/module-management/index.vue` 是生产上述配置数据的页面:左模块树 + 右分节面板(基础/字段/列表/表单/查询/自动编码/权限/多语言),底层就是通用 `loadDataApi` / `saveObjectApi`。它按「编排层只持有数据、各面板 `v-model` 各管各的」组织: + +- 每份数据成对持有 `xxx`(当前值,面板绑定)与 `xxx_org`(数据库基线),如 `maindata / maindata_org`、`fields / fields_org`、`view_schema / view_schema_org`。禁止用 `drafts`/`originals` 两个聚合对象。 +- 跨面板副作用(删分组后清理引用、自动编码字段在表单中自动只读)由编排层写幂等 `watch`。 +- 模块配置表用业务键/联合主键,**不调用 `nextIdApi`**,保存时也不做临时 ID → 雪花 ID 转换;`_`/`v_` 前缀列是内部派生字段,提交前剥离。 +- `src/views/module/menu-management/index.vue` 是同类页面(左菜单树 + 右面板),实现时以它和模块管理为参照。 + +### 7. 文件服务 + +`FileController` 支持 `local` 与 `aliyun-oss` 两种存储(`fms.file.storage-type`)。本地上传到 `./data/files/{orgId}/{yyyy}/{MM}/{dd}/`,静态访问 `/api/file/static/{path}`(含路径穿越防护)。前端 `src/services/fileService.js` 按 `initFileConfig()` 拿到的模式自动分流,OSS 走「共享凭证 → XHR 直传 → save-record 回调」。 + +## 编码约定 + +`开发规范.md` 是 AI 工作规范的权威来源,重点: + +- **不写 placeholder / 桩代码 / 待填充 TODO**。 +- **后端接口优先通用**;要新增特殊接口必须先征得用户确认。 +- **改前先读**,只改完成当前需求必需的文件;不擅自回滚或顺手重构无关代码。 +- **能由 SQL 完成的(关联、过滤、排序、分页、聚合、权限过滤、数据范围)在 SQL 层完成**,不要先加载大量数据再在代码里处理。 +- **权限过滤以后端 SQL 为准**:前端隐藏菜单/按钮/字段只是体验,模块操作权限、字段可见/可查询/可导出权限、数据范围都必须在后端查询或写入 SQL 中落实;用户个性化配置只能调布局,不能放宽权限。 +- 固定 SQL 只用于稳定明确的特殊逻辑,且保持通用,不为单个页面复制一套接口。 + +命名分层(`开发规范.md` 有完整示例): + +- 前端 JS 逻辑标识符用 `camelCase`;后端返回字段、数据库字段、与表结构一一对应的业务数据用 `snake_case`。API 封装层是边界:请求体必须保留后端原字段名,不因前端改名而改接口契约。 +- 方法名动词开头、语义完整(`loadModuleConfig` / `handleSave` / `buildSaveData` / `clearModuleData`),不用 `loadMod` / `dealData`。布尔值用 `is/has/can/should` 前缀,ID 用 `xxxId`,集合用复数。 +- Vue 组件文件名 PascalCase;模板中 props / 事件 / `v-model` 用 kebab-case(`:module-data`、`@save-success`、`v-model:main-data`)。 +- 公共函数只在满足条件时抽取(≥2 个真实调用点 / 逻辑较复杂 / 必须保证多处规则一致);只服务某个业务目录的函数放该目录的 `utils.js`,不进全局 `src/utils`;一个业务目录原则上只有一个 `utils.js`。 +- Pinia 统一用 Setup Store(`defineStore('name', () => {}, options)`),一个 store 一个领域;只有确实需要跨刷新保留的字段才配 `persist` 并显式 `pick`(见 `src/stores/app.js`)。 + +表单页面统一主流程与命名,不要改名或调换顺序: + +``` +handleSave() 保存入口(saving 用 try/finally 复位) + → beforeSave() 校验/同步/准备,返回 false 终止 + → buildSaveData() 组装请求,可返回单请求/请求数组/null(可 async) + → 调用 saveObjectApi + → afterSave() 成功后重新加载、更新基线、清草稿 +``` + +编辑页加载命名:`load()` 判断新建/编辑 → `loadNew()` / `loadEdit()`;不区分时用语义明确的 `loadXxx()`。 + +i18n:`module/` 下的管理界面文案固定中文、不接入业务多语言;模块名/字段名/JSON 布局节点等面向客户的资源维护 `s_i18n` 键。语言清单与当前界面语言统一由 `stores/i18n.js` 管理(`s_i18n_type`),默认语言按 `s_i18n_type.b_default` 判断,不取数组第一项。 + +## 测试 + +前端测试约定见 `fms-vue/tests/README.md`(唯一权威): +- 文件一律 `*.spec.js`,与被测源码同名,**一个源码文件最多一个 spec**;`describe` 与用例名用中文。 +- 页面测试 mock `@/services/api` 这一个接缝并用 `mountPage`(`tests/helpers/app.js`);store 测试必须用 `createTestPinia()`,不要手动 `createPinia()`(pinia 4 下 persist 插件会静默失效)。 +- `beforeEach` 必须 `localStorage.clear()` + `sessionStorage.clear()`;异步用 `flushPromises()` + `nextTick()`。 +- 覆盖率门槛 60%(lines/functions/branches/statements),只在 `pnpm test:coverage` 下生效。 + +后端测试在 `fms-api/src/test/java/...`,用 `@SpringBootTest` / 纯 JUnit 类,`mvnw test` 运行。 + +改动后的验收(`开发规范.md`「变更验收」):先全局搜索确认无旧路径/旧导出/旧方法名残留;至少跑受影响模块的测试,涉及公共 store / 布局缓存 / 跨页面工具时补相关页面与状态测试;若被改动前已存在的缺失文件、旧测试或环境问题阻断,在结果中明确记录,不要为通过检查去改无关代码。 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/FmsApiApplication.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/FmsApiApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..9a055d80b86443d581d6ba0ea30d4189d65b4d61 GIT binary patch literal 1180 zcmb7ET~8B16g>llEu}#Du!7$rYANa_`dC6zn_x()5Nb+{@nO21E(6<{&FrlBw|p=V zP5c4=DC3<4rAe#AeYiJs@60*(-ZQg5et!K9ppK^njA7hEu80XtGL(+^NA5>lh5l~m zNCcK)a#Jd4w;0AN>unEthRs0v;fqA~tl#S=JeGcHWt8}d(kH!$9}7Rw${HS6f2aFO zC}FtOW;eD{tE2!A<5Og&izs4-A%CFFu`oMb2G%NI4kZusMJ!;E;rW$JvKtCDNW(Cj z2s^;bP_Hzv#Q8_XHP`kvhSedE?S4{^Wj&4~8F1HK0oSqYVWo&0xXDn}CiIioNEP-B z?~4=a)bD6*{ZgZO0qhE_cpriOPS zF+f3Km##778oDbO=9*H8H)+2k%v-mYVWFu59<{lVZhaBV*+ZEytPKxwsDFpu=aTv; zS4NfHraU(xp)HO`GkE&^k}zxyv;D^q6^mg4h2IgKb*;};o$usg3)l;y~S^73vmjWSl|bC#59A%_$~SoLPBGQo$bCU!;|#M4OVu-!4=w@A#0ktIPfvCRCCt;4UB+TY mP(BC21FX@WJmntZ5s^No)dZukE4q@|byUf_-_Qp6C%`W&=S&U& literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/AuthProperties.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/AuthProperties.class new file mode 100644 index 0000000000000000000000000000000000000000..a5187a9b7819c404841e2fea574be7634298a769 GIT binary patch literal 1134 zcma)*TW=CU6vzLwuux!IkZUjAFLj~Xeeki`gwz_-pf+N7U$z4b!VcLTEFVh~8;uWs z06&!R%t9&jk_chW%yQ2E{N|jqfBydZ4WN$aX&8tl5w~C>!I0~_UtDM4`d#NkyU#tv zka#J4q4pVKcC}?vl>V%zkwzv7%fc$w81hk^5<~919tW-x(q~A@&RaPSLWYWcRBc>! z*-IgZTS??C6i{T?_I#)NB9tBFbcUfj5{@VRj_5k|vFaTMa>N5AIO(nSPu1aMB+fGx zZxSzCs9>ETMFCCj1)MIFpE^gEY^p%`-93i*fjr?1*@p1>`|+^NgHLXIKu_`w>A8cJ z8wh=#J&dcKAPl?lf54<~b-6maU?|$NdMr~|;gVO$zKU9HQJ3Cv)srX3Zr~2N;=01Q zss3V7%I7(j+CnV3X_iIb$}H2etIm~fQVTu)M(AP`mK(9FTgtHUaqR2BmIwvasP6kx zMI%65hYcBYop7Yf+|jtFGWhJYrBu4Nicgd?nm5)th}u0Hpo@kRb7B`rLvL=`wXIeMt^a?TB0omFQuv!txpGch{`z;FCq*cEIzzX8X zV2gIv)E&2RhfWN4ac?H&kkwLV?K?_eX=6YO=uE)cLa;dxzE7M+#Ia9SCrZ?I&QQ9X zK2OAjNTEFSnki^?eLIGS6l>DEM26uJ9@DC?x5$*e%v8RX=?S*4W4cn3O=8-*fk{_- X&Qu@{15cyXMwM)a{;bh&C&aUVtn=-Q literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/FileStorageProperties$Local.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/FileStorageProperties$Local.class new file mode 100644 index 0000000000000000000000000000000000000000..822c502cfc0d5f79ffe51a5a6d1eeb7e286d445b GIT binary patch literal 963 zcmbV~T~8B16o%i~er&sLEmT0o4^+5lF|aWNZ!jbVV>GGQq}coEGF^sjcgf7u__H8^ zn0Vn2@JAWX42>07FEpE+*)#K=InOy~e*FCQ9l&d>2JlewQ4Uc-m0|9ff96rjvn2XB zJQlHGsJ@b!G`kF?c4tsW4Rs$uh+7C5w&E;Gwsbx+(P*ssiHzbr8_6VkD^t-ox#EfF zsr*DJBZXe;<}pth0)2V%Mk79fn3C%(Mc>xzO(1Bqf=?DqC#dRCNZ_ z*bl-S<%d14_*fXBh%BTGpJvsCbkiSh(9b6-7PgTL%Z2^iw4Gz9?F((*<=Xgo!m#mA z9-DUHCR7vj-JlXibJ4_YfA5vpY8$ct!Yc6a?N*qM{fleNdd z8d_w-O9Ad+fjovqERnq$d=CGV4n53*JB7h_agS6sc-OH}s`cj=@Mo#*g9bK;ans$f zKmJ690*O^`vGCe3}YFua9CZ?BvVY0pb&D12O5+0KE@Cc9H Qxmkt+2ep@LPFr;3Y2k$RC*KuVi;XK@yz%^KOJ-_=U3 zD)oU6;G+=7fC^Rf!poT%`;6zzjL**hoC4U!ixw(~VpJ2G8feC7C1@iNc$1ZB-gjo=)5+AyW1VKEoaj7#uM0Kw#>!mnTXU?e z*UG)xb55X<_l{3as9#6@lbaSt`3zNaqyGMw~KUa!(a*SP;1>dJ%oD zn0Sv-N-JnBre(3+ZFZ-M|4I`H zi6;I4f0Xf^ZBq!85B0;&W%rzSo;jEO^Y`a(06W+;5kb^IEQL4{0+}=Wvu!nOw{9I& z&SX^yB;Gi#qqYU2`9j&ikic5iwd!kuS5sE48Q3kys(NnCsax-yhCEW9Z`b9a@3o|_ z92qQ@ysF(WVIZ2sFj59aQb=P|V0j=Nfp|kJ39Od}a_j(X85k2-9nA9}2ux%;;1en2 zFv&1@LBJRc$Uvnnef@hWOk+l1IOxRb_-jiFWJ_0ZKT^Kq*0%&=yWR=Ejg}l&?zfv2 z=^xvbMrf_$P_}(X@4GK!>eL~ZH5eCVqXAbR_5y0lFfu^Z%ux=@LX?GFU|egXkPmfEb~`#-I?| zUn7`DUfZuq-7Nw$eVb-Q*Mq?HhqkMnrYt*w!&L9MuBU9}crN+hmOQ_11ub1IH67|N zp8v_Jc%IVjsN}h_TD>N;cEb*B`=R$$bL#uFd|7kO8DBQ@N-cyM-j{*ele*@3QtG|y zN`JRu2Z7`^-c3T3_rW}CI>KK`lp_n9tB7vyaAv!3Hb567IL>mnM@zwZws;9q`~~CV zC6X5~xy*2xy}cG>t*m7?PAUF~?o1#}UrMb2_>w6@MOBa~BLeT$5V2n4&h6rCcNrd(g9XUOx6TTJFnrfD+G+`*(X&|^x6Oa&DCFpb}2n(NE7gy;R3uBECI zr+G3h+`*))p~sXXQw+--BUoXSz%mke!DltVORVulf}b+1o>yFFxi)CB&O*p=-azsn D;?#pg literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/JacksonConfig.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/config/JacksonConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..e57213b4bdc3f206c52416f5c34fb3bed15be906 GIT binary patch literal 1127 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 P|DC@OSvFAYKT!2Qcvu@# literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/AuthController.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/AuthController.class new file mode 100644 index 0000000000000000000000000000000000000000..4d749e7bb6ecf083ac9d86f3d5dd1a37490f86e5 GIT binary patch literal 2802 zcmbVOYi|@)7=F%nx3k@Dfi4tWl}oYW_5uT95n4oBE>c-2bfHL5aJoC)4xP>{GqWJy zBpSmPVoZoh6Jz{j^b?p6+LU(XR3*_kuvJ@5T}-t+Ds&z64$ zFakS)2wD_GlZc^JVBHn%nx>kXRaD2PujqMCp!J|(8Qvj*mQ3$t9C3k|?G%kd0tvJ$ zNG8#NH3HpvOD*no?HNyF6?ju;eoDd10s{@2^iT78$un$A9WA?trMvFYDkY9rschxj<(2Qgc=G+tM@zS# ze*Vj&rR66-Ehmu17MbR)tC(YQy9G8px%1=7t;Mk;Vc9mkh8_j4C$R%>2=q0$l(#L< zu}xEV)RD3`d$_h0XxB){>CQDHuM6zRHpnuW^2`?u_=XecMW2HHBnGfkpr;uj0>iY6 zh9yvbK{gv}`9jK^=6ISaT{3k~o%OtudYmum7YP&k)w1q-Rb=?s8TU(}neHQxbf3>)B z>zl`Szhs^Ny0h@~?%f35!hs})Ff0(A(F{}IKxV8l%-+Tt$Vgl@WYUl3og@z7UHZzt zAu=-IhN!gXu!0eRj_0AWlcXDmlQ@E-jMpva^8~2C{%}nK;ieoBNKce4&zRRI4cC}9 z^%2XmJx$`DYBw{yO3kzyS!LervXj?k9uC_D!ftJreR8TiKdn1wHR@n_vUXlGCpE{A z`+ykrW(_JGXiU(m0H=*hL1^Ktq*)v_0K2gg%%W4los6R{)imBNF&f}fKvt+Z)0Jy< ztROSt>kMB#!5t@0JN%xiB9SNvc~HeQ-Pl;4&Ksp_N(V#y1joTfVMFR*Ytm1Qd(~8PXR@_3ik{THS-aqm z7TwbwLZ&Te6fMmwJA~H5f8n7?KN)ayrE8{b{(q_lvmCFgTasgPhO<-WbemJJ>(lDA zVHMO+o1L~@k0~vYktvQ{)66oXZ&ia^czb=@*0NRCzs*|wuIs6g%ur1ag#_MPcyIyUeCnW5Hn z>jUgsK(bSOo!YmA{r4o{;O}^E38VLDDGKS`F0}K#mA1EGEyLZ!fVZFr8HT%?0S}>% ze7kW5doYE4eiX+mo(56SXPm;gAK45}lQY8e%a}k8E!24)XZaPS=5eI>6{Eh*IEP6_ zex9~B`U#dgKBSJy*#(|W@vI9i&yZ1YvGVKu8(S6T`C}eM$$SZ)@T)CCSn#edKF5-G}>+|SpftbYIoTReLJ literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/DataController.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/DataController.class new file mode 100644 index 0000000000000000000000000000000000000000..4e9c4f4bedd393c88a2c4818c37b3e08b9218da7 GIT binary patch literal 4785 zcmcgvYjYb{8Geqm%G%1tjvW#znuNrp#&Mi(X-Qn-*wk^-;2=ARmD(g}3oGqfSzF#! zc2`bZF6C07gj*@M7AP%+89p%sGfbMm43sZ?;CE0y@DrF8c+T!hT19eEGQfCN>phqE zJn!>9?|aVq%Rhhj2LRJ}J&P35DijSFY!TSLVyqi_#c;~{+2V>-3Iw(sw;el}5=f66 zT;dR1gKY=tH;T5|OABfh&F=*w%qQML7w>ja)#saT$V(g=*1#;3qmlMMySTenM= zz;M28t{?S<6XS6cS@fY_#dZw?7!)|r%_M=Iiffp1eS!TW`Ov3YU{~}xqdIXg@twE* zfJ?jwJ5}t`@Lt>}Fx0k$oa*b-ReQnmtFGe{z3SIWCCm2(9*ylMIO7uk*+%Hu`!w7y z-P%^idt#$-wIY4qqhT-JFQ8P7GS}PpZeB>QWMKAd7{Uhx`a`0Pb*n-4Xgi=5vYTTG z>j4a__@IUn926L7^JJ5WgzD3bz>Xx;kl=%hJvbzw{q>Kpy!ES>{`UL}Z{2uRl73h+ z{-D5_B!z{*vz_up6r{T*wFeIgWJ+$$3AW;4Jfh;m8jj#7DQ-~#)m*kH?&0Lpk>cwD zNv^Hp(U#npudNj=FN<*;(=dVKRGjSui$Rk<}k0~tcG(qU$-6RiyXrB4U;H#cB1N1 z4+?aC%{6NkYu3!-2`s8u((nmfWbSR^_Bq2d)}D}AB{0A^ELc}-wr819>;;Y_B_!3I zP(!Sbq>FKrRPJRBPvR*elHAR>Cd2VS5_ddwwyg+A4nC>jQ!DXBRo;KnFl=cm#3_6tTT#E!w_KW7Ce~ z21a1B{s`)n0npd(W=t1Oqf8KViNUIklAc4WWTQVw(hG)y~xKRnN z*fcuUUL-cH=3JvKNL^Ju%DO6}&-MBfg;)kzU{@=;jcOg?k=CKd+x3uWiPLzB2D7KG zm#k_?nu=!_$S?)`s};R)KHunL6z8nqife|tv;xZ`y553ac8s9rk+omkp~o)Q?}m`r zR3qDx-z25Wm8K@7w@31>SJwTiETGHe*t+I=tNJyos26R=OiJaP>j$)6m6eui4QmHo z*5x)NHxKTDt?Fwq(;~y}7nB)p`N0X-+~9&eUYzi09zwv1(^$B;!QcqI`2QjNZU`n# zdbAg3VFS;s8b+Y{;zO$eO6qv>8{A@J;-0Poe*U8p&% zl8dfwhO+F{C1GxHMQ$ZdZB168=JpkpClVrTKC>KRo8)AuGXc#lO$vTnAuvj-jAC5K zo=*DOeQ>+-Y*a3&>T+MPxf>>3h&Pq+;K4G_3jZ$PQ}0;__E^13OXWuQbX5xC)PNo)X}*Urt^K zm*t4j-$Fff6WeZK$FJF*M$^Xr(8jJ1q!!zdHUrdXYn`Jb`?BR4TduQ3;d=w0=6@N% zX5ce?Quu7>TOT_=$LHD05I#PSFR+i-AAv9O`O+mk$JhOvLdsc*~Y=u%#{DHyZK&W(TC->WFljWM-r!MIXhNp@qxJ!5xcA4=hvl2iVK z@t4t;Q{Ke*{NbDU$mDM2=Tfqhw=g3q|2<|ezJZU=r;jQ-mEEsn*Wsw|Hu5PfJ*S9Y z{_F2-3Kgz|3Liz5OC6{0le{5LG1VU9O>LT&of#bCC1D1qc#J=ZMP3wSCQXsfy@dB| zyg-g%t+xP9%3)CGs(Y8_R=AHQ6|YzGFzg)@3HUuv@!tYZC*P2 z-a%HyocIUsYfqvdbRhMI9Z7w%u280bn#h5rLxT&qaA8iFBDX^gZZo+|$nExAhTKZ3 z$?lYt%GjQpxFj2H<7u9RU&CNdN#BN%!kaK9l{uw_&>;#4JnXBOM=#D|nDmXY%#5Re z6S#o$q_R%rFd<1IC4EZ``j%q)RL(5mCDPYRTaw5$t?a|gBvMHbDaT79qrM-pPZHUW z)H@hcVT!kLfK;}tv{{up5&Gkfgs#*HCEj$HFau%X99$-;H*qDVB3V73N@UYO!^wfz l$!0dmmV2n4Kk0xog;&Tx3O@~nGRXg4<@amkD5|(W`!{CUt^)u7 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/FileController.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/controller/FileController.class new file mode 100644 index 0000000000000000000000000000000000000000..5bdd23f5111ccf903affe0532d2d0e8c0d26c04a GIT binary patch literal 6660 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 HebfI28ySY7 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgContext.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgContext.class new file mode 100644 index 0000000000000000000000000000000000000000..05b8fe4f944c8dbcd66a8a198edf752b58b7d615 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager.class new file mode 100644 index 0000000000000000000000000000000000000000..c3c80f8ddd4699547759021404f25974d86c61d3 GIT binary patch literal 8785 zcmbtZ30zdyx&MDKaACMA5=AknXk1vGh_;DfYzS%t1za#%jcIb7xxmOUm)yDI(loDI z()ijYX&citX||@dHf@q(YEZOk-j?=tdHcR^71OuxtG4~0d+*F#U<1R`&@kAtQC8KHEHj_@IJv+SJNDXuw!(nJBEJYb~4RdtN#XP~fXfo2jA!YSB zk-njnF&vM?497TRq|C?;yMHUky;j#v!D zU~bZ~hm1u0{U*WD;hNBs$pvVyV^i&|zS@FQ6%f?vs7HgK$QT|@92e}Uomqwn*vk-h zao(3sMxD5ojBNJ@tz~G!It>vWH)6eDWnP%%e1gCf%!^t>!)eFt=xaNaqR$B0ePjeY zt~Fq`smBJT^*ppy!v?|4GpcujaH|whwj~|=c$g@9hmLpRCcy$ewtkaLcu-E=Rq!?tnUAla3a&3My@na7Uj9mG%Kf8k2@S5!8ck*lV;`@X()qYZ%SA zMaLGrOAyY*9zi{$2hA5D91k&IMPhb39*Yc9jghTtn2TLRei_{s-JiF<#d3EDDR_fk_N-(|zuJi5DaR>G?KiTGx zb;MNhJrEpx-^k-GVQj;Db?jH^t|+QvrE=vvbsWT9f>4Ul#?5sdWw;ynXfSjfLR8R{ z*Tbiarp#QafwU8|jwXAoqk@Wz@NPKiil-b`2vbKN`YFT_Bat?lO%_jB*KQ>ep3#`q z3J}+E7<9;DBN{b_so~`lR%fFHHI+&anKq9YLQ;dJV;J|+zjGqwC=0Yc%id;02bjxz z6;6zKS#ouIm8w!Y9A9~+VXK)VI*uw+6cX$j7;KxT0K`0|I93s~E$s|2_Qj)ydfb+o zSJ~`79Us8`bi85P8a8bwt~9cKW@lEpXJ>W`(fUDrNW+J9d;}k59-P9A^6OyDF>$|O z5#5G75L%LuO=a+IEHDi4Wpq8XnSd0zXe14YJ^1kD*I#L=x$pimjN+Vz^E$?Gfl!!y2m|6#!)D6Z6x2{ALFWXR zUT1riy}zmBb$p9~%d28w=5WG@(!LWkFs;1#P*Z|m6SPh;k!OQT=U%<^ z=;y{if3{JLPCPXJ@C%ntjL@K23udhJ8#;ayzeNt?$v$fmVyCr79kqW0en-de;`g{U z!E#>EmdBLWW(^gq|3Jqd;*WUq$Q4Qc4T|-}%(nHsGI!_t6a1-$KhyE&_>0-!60-*u zdx1g_&m-H+%;fGaEc$2Z#0oWkrEblCE!aP6*S{uS2t}33eyHP;%43V&tTgeOx7AD- zEa4x-WgTy+h+5)U-SI>so(kh{@pl^jUdKPE)I2|z`aSH@9p?J=S&xY`;MNiy{v5l_n+rVh@FXT;8l^w#kB0{i8!K5lT%U~dGWHKdyN z6bUQU(v-SKu+pVjLbV5o3@MRPO~SgAiO!N`a^%kXMk!(AWemaUpmKQAOQi_-$vb&Z zEA5TjG)sqD{;^yw1w7BWyp#i)$9H=c?UN^Qxc<+!C5VY`%v z4H~v%M2zfoWVh+*oqI#=R*Zbl>xw7Mp7hWm)83=t=;qLT9dVQ?a3|fkRP#b1Ie3?ykWMqG zJwSyJak+Kv7EMVPb+@-ASVSw&o?^hAwz<_zIkt72s-0&gi7zJpY=a>0S&VCQ;(l%J zm(J3u@*L|)#;~Nicon&w3_{w>+%yCmrB68=f!>sCus)+A}ct-VvF?1+qtjg$i6+2P?uWk)B$|ByS$+qUI8_8QRWMsot zFy4DXkYa=w{CGQEFi}ljE?^br_p=UK6BWw4dTjYtLQyBEcwp2PJx## z8837Vt2SVtg8EU*9*pn?W1E(9eAr~iU})G%((lMzSr5_GZK`lUcPPKjvK*q#81`Hx zRW6(670Y(sKIGlH+$@{Qv3Fh(UMR<@a_O3;bll4=J!;DiUEV1-mGd8pxoaQi_k`sR z*{jJuUEVAEGYvww+K(rXSc7Jy%hfZ_H~PHRGnu5j=AM2KRK35+WA}C=8B3U{Rb7@f zm>%{!hQYQE@{v4$KBDDSIQ2H9e^%p>*=ds{5 zEIFg7i~3G6m#abULRdz=mdkZ~(!Kq%Ko;^zR3DM6742uZI>g_N^=GlHVH7L7IS4nB zbS-#+Yb@j~skx`VX%riu#GLv@4sIUB<`MpG9^t-1_smLi3q(-L&h=8P$7)ux5o~nN zuJ`EsXY(YflttvBl&$o|92IcKBB_!kq;);1ltHc{qDi%eW?9CGB9Th|i0bHZjpF(i z?ow7LttZR3j$*4~#S@=OKt4Ku(>zFzT-hN|K* zycR;^NLA4&F7o}wQM~aaDjU`FbE9}u{VM+(HMR;L~2zTYKqVg8piPJg{UfW z>8SPY>I_O+imQsh4gT?nOpJgd6m0=Pv5bIGznk!~51c+I!H*C)M;ZFn+Yj*lLtJ@3 zYIxONhY!)pAK{LVQZ)}UhGdPK-@Hk#% zH}w@fK|guQHP}I(ay`#o$6#4R>Qxev8|i$lboGd==ZNagn)6K`nrco3f&%XTCZ3@Y z3(3KKK9v_yEzNxE8Fm9ou3$k(A@wT#fImOszL17855kO^8eYDFat7xWtFK^LCPGvC|LZkTOs!+{2{ADiCdTH!A^87pj z{Dpw?)n+BE%{t$5SFQrCT!`0Y1G1=8nWI2;SAB*C$&QN0t9dfr>~BUfu2@>qu>U-M zwC@7`5vX|;|2l^MX3dq0PaZd?P=+rv1fO=fsc{qS>MS>_u|-q}EyEId2en+_dj_@u zXN@NSo@~ literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfig.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..4aefdc8f6b1bed5c09d1983fea83397b51fa11cd GIT binary patch literal 1917 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 H>>&3a=-IO# literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgRoutingDataSource.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgRoutingDataSource.class new file mode 100644 index 0000000000000000000000000000000000000000..31944e402a08512f6503e663c86d8d7abe5795fb GIT binary patch literal 1348 zcma)6QBRXG6g}Mr8*{)w5Qn0mh}%GlV0;-dA)txLPz1C0?pqm!tsVV3g@2`qLF0oz zz#nD2{WjSW7VxFF?Y-yR)7#$u{`2z}fGxa8BZhbqi42A?%<$6KIuBb`>H47+P5If_ z6MkH)2D~Pm2u<4tl5bT*XGP$4XzL)_Vi?|3TDeyY@qD2&f)qxQNM~>h8HUwB^ZG_; z4jn&ihN7h+vZ@Fre{bvirtK(QKiPy&)?wZiTGXXwSSw%4(t~#qr8LGco?3ui3V$@s=?S zzT>AoG!Z|*!zAW?X%-lk{tw1b8jy65j6r2$$nCeaQ%zY>p{h3Ime$4zU)zvjj%ZGV zb%Kk_ZN4u%53aROq&Ga7D-VzI zq+g!}vK5I+Vwqw3@+b!%%9lQdR6CSbQ-gQ~QB40ukiR9ukeY|CHoGg`k*Vzo-%n0j z5|-|~?KjGwAUCK7Rz0Ub!JvK&I*+KoRC`u6g*~Se6bC{=N-T{#Z%KN(&6d$pyAl?t zPh<2CFdC^-$v&dhFxkgs<7DYmpmC8_oGc?dRQwM1l_oJfq0zg6Q7qBu2`B(hktfT1 z-%*qYuhYu=QpImbUSRYK&HM3FefU%tU!a%-p^6dH0?F}A6|!-%SQJkr)MlTwdl{G(L}NXE=qX~x2wQz-=-g3h$ z;e;Whc%*BSi7V+}wDU_7$6gD&h-kuoG1@d^LMwy@bb3BKID7_sqO5_KqW=$K>1Kpg b%C;A{g*8IHvW_+>btrE#0$zB7Vgua|w_AC{ literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/BusinessException.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/BusinessException.class new file mode 100644 index 0000000000000000000000000000000000000000..13e0bdd382b6120c091ee38f30505b31a83de03b GIT binary patch literal 434 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 DGa0{i literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/SaveObjectException.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/exception/SaveObjectException.class new file mode 100644 index 0000000000000000000000000000000000000000..b1e3913486cb112310b629557708a4f6ece996b8 GIT binary patch literal 1265 zcmb7E&rcIU6#k~&cH6R0SfJ&HAgDmQMOFn*s0ksOG%0E<;XW)wS){vUyG8Ub@#0Ox zMH5K?6AyaB#J@ulkNyEheY0EIN*WUnv+vED_rCAVH?RHr>*FT?OSqjtghBiE`PJFG zmuIgJzaPCxK|xGKT!V@P!^k$@17HS|f`IJabvGSshO0D}xM+u0EN46K-d%1=@dqhRdI=-zje#zu{<5ua2XQ}DFRy&wHmJw+`wOOjUE~9{#&a?Ua?~* z`85V*+1()Ybjfza!+LdHc&q1%xE9}8s*MRR%kD!qB@S z{4ICmG52^?_`)L=sbzyVN$yhc1Yx)sY9Gzzi&W~lZYX+FEJcPGy30Pdp04oS07k_v zN^WT$YC|%Y-MVLq2eur9p$=m*Cu=ZC&#cg#N1@AMBTtvZ5m=fCvN13k^GQT-g?29} zGe&mE(Alu^0a2rHjHEGpjI=R!4E>PKBDhN5OpupG0={Qg=yLm zTENW$PqIe>Px40tZw5K)GEaV=LaAhE4bYmQML#V}&{wu+l9ezq;|R(3u{FHxQqq@4R M;wDAsgJu{00Ehwt7ytkO literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/ActiveSessionRegistry$AccountKey.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/ActiveSessionRegistry$AccountKey.class new file mode 100644 index 0000000000000000000000000000000000000000..1e7ed09e13024b637bf82f5c022d8e332f06696d GIT binary patch literal 1790 zcmbtUTTc^F5dKbSyKPx4R6s!lFCe`j3*rrnXvG*B%OyZ~(5LP8&@Qxl>h87)|H%hQ zBqlug1N>3O*$Z?@k)S;6nR8}m<~!faocZrmx@uIF zEv^Z(!Vq234c%U5=t*aG2hS`7XAS&VM)98OEgiVdDHa%`u408;!+t_)pZ<7oX zeSOI&o+j`N&n3K&@e+#+6P>;{=~3tnu6SJhU<~o{X?%vGU^;Z8Ee^Qi8Lp~ErOvI% zk`M>2X1=@N0gdmtbJ*nXxuuzU(-vmXWu>lKR#4%$(dX_ULG*KP>8v7h(J;8_hU8T4 zMCbWSc+D`?Nx&t9B)nl5zuc5+;<3jZjgAmKw-AjA8d0=MIdg<$lynh{(R!CoYb1L} zCbK`lvbkeO*_mVX9g!-8ao3#DI)({a?|G&q@6(B#+|bVdq#Hbh2egleNbRU-QW5Gy zFc)c_q+=>&PtXs1qir{W?3wro12ljpFK{0OzFx_F6jMIWC6Z1)V%gjYZvV}H*t7BB z$r_I^O&4%zMHn4Ghu$+v3Y{EEk?eO!ml2Bi5gBFWG#^3dzNUh0L+`0xDzFKh8*sZgL^3jkGNCOoWP75_MY9Ej$n~5`xy=!)7 zgYv@v;6L;Q2^y73Jo3PgLOrv)a-24mC_H%Q&Ye5=o_p@x>%af`<4*wVxapt(yM&^P zDV(xUeIWLQHxNPJyR-E``r5*&RTU_`X2D);_DYzxu#g%hS`9ou4E!XDWT3r`vG|pU zcQ?hzK?!yl6}TnLxR}M9g(W}m`q$!cM|(TNSd5ex%V=Nu(p&en+Lv7!$0`i&%D#$q zbYNjtOzL9`bDg8;46;(fX$#9Q=eDu#`(YC3ujPS*DrzSAvlcEbX1>$(yE;-qf8}{i zvsb|@c(sJrT%5zag_&c3$kQkxZPzEAWn4}=%Qejj|GCP+8+fyXw_Lo9^T#TZL9$R% z@eN|!OG|R!pn1o|yJ&FVmkhmdLg*I+T5xd*i!8GIF%bjOTR-V;8l&anvN5f&=31yA zCVLjjUDXeSP9kaHhvTJL?fmDeOfr=hVr|7jaUOTu8B>GaVFP0!1de7x{YOjI$qY4$8kg;D?3wn2B)RrxU)Px$i9 zX*tf`ypgM$QtyV_--t*IrIr!lPpdd9ZY^@K0g1=fzB0 zhWcP6IZ=kAFyL%qNh-HxtT&~pIj_36fJe`-jJe1q!-f2QnJd_l=cr3${p{8J{it$Y9g literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/AuthService.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/AuthService.class new file mode 100644 index 0000000000000000000000000000000000000000..653f5ecd1b49d4fbacc88943e3c6d8a6b86ae9a5 GIT binary patch literal 5440 zcmcIodw3jG76092H?zB$Bqg+=TS{M6NYclGP-v5y(xfc}(gu^=dC{>Cmh)Pk5Em+g0<>3Qc#Rn>iuOdE3A1XdS|M9En@6OC_CT#2%`+a`N z&YXMhx%a%!J$Ige{m?T2mg4U*l%P~WNJSWB0uwf9o3unm%cc|UJsb3-BT%;3$Qn+Q zKxu7VcMK6k6~t7OLmfrl=~zZKEfC_nUtnTwUE9cB-ehAGm8eoMLB%B9`J+U7mmOJLfW$pzvWQ%iM`>k8A7JoT(&BsIq{vjUgaHb z;6fD_p;n;M^*~{o+8Flo{sCu;>&!J#Oh&zmIhZR@l`+#sw#`fzG_Nn#H*X=51!)^2 zn9m(f?tgUno&&EQ+%f$4{lgDF7Q@B3M8R8BT#CyCYKzSAUv{H=Ej?*kDVK{tZd|5V zQ*50vF@2ilXs)X>x|8U2oPosEd`PW{=+HOjblOn{`sKJn!9o>_(7=Ekv1I#zbdi=3 z2PK(p;z zXo?s;wM@lwtPqHDt9-I82!=k_H5Tg}WatNy-0f-=t+<9>%d=RiImup~aemRb>|M7e zf;Itlc<;6o58r<3zFjBx?u}p-Gq`7+L3ty%PQ~>y2pMzD4C6ipodVNGYC>x!qo=h@ zr=vOg^36%zHERT2%(N4`4i7)Q^Td6R9p8T7#8Zcc_uYH^!8?!d+H;q40Ep<*3w9+ebbU9HP#XG_bb%zkfBJ<697$GpA? zJt~s2;6)tMGos8LUB!B^-ViRr=m|vve8JE+Vt{~J`VA0vC+O2@1pEr_IZAP|FPhG9qz08sr zHls9xcL|gjsd6mAZ7SY__cBWda?H|&L9Tv|2F&SWg68y55!}IW*OEyymvti8%4Cr7 z7r{2x3P*Y`g6#xND{Z7AxQp{S8bg+v531OK4^bdn0{HAkR~VVdA69WMK0>px zEOcv`9L@GWRs~;r*$MPWYwT3RU(a4Hl_^Zb~iz7?&G(RB5T2i;RIfke4jDlxXd=+04INz1U){{BQaJD2? zGNZM$zRXDTWb;}8sV$+}wasgr>sTn`9Sm5bU+*?-qbH*;&1OwjWtIW*zJSttho)9Q zCpvW7%vnjjk^X2gQ;fMPW|+Pz*WaUCt2OQ+dpOzCb!(O($38OT^cvjs!ZQ?=k?xDT zhuyYhyiGtLOZ}s+AiOL(XFTz}wWIUP%hlFYp&<>nV~ix3y>d2^KSXI%%WEM7WX6uK z+o1Irc>P+&xK&TJTWPuFgb|Ev5hW@t+0l3t0;0Cbu1{KjRL~PsX*2os)OCIX!M4G# zOLC{E?NxaRmsz@<%P=r%M*C~aK;CCp1vZ!b76-=6Fn<^{jYr^O{LE}>Y@$#L&T1vA z3zI3?M_zsu#R~m`t>D)@yS#|sm`QYA-&UBTrB>=ruSp|VEW<23G+wmRNM|)CXE8$e z1ncZbz~nDLXTprnv11kOv$tNUW%4{NOPZNnKl_02Ca>E2ccq}U1=jkix$!)HCj}hm zYacx3d=y!`Cj;$1+MiynrJwyaG|$|_k26mGPqO=Q$fL~8_4FH#6U>cRr(4Zd7_x3n z2;BeX%fRA++{Pn2VGqa~?s^$Z?8*BQHX}+m9VSYm)4MKGb+t9iZmlK5ypxqX>>fY7 z5KiXl3WWIN?GplD=ctVDZ}43r+sGmuyRsr2uWE0=g*oQUozLgEQ-W{7cewrl#1rn; z-{ezH0~2wCPt_y9QGAOdfp6n^ip9kx`@yR@QJ2jbgz_XR261jfcrM|FvikivFCLDU z4dQ|ZC9VwNO@inDWTNXNy0m(ph=&G|IEV%N;!5cd8cTQq2e$)ZmeE?0Nu|@f*U14`9Z85gw~*vdnz6y ze{~SO4HX<_If%)j~xm1$ymX*;wlD5D}IcXcp0m33hnp@t`hpz~LAF1|;9T!@5Kryle5spcQy8W|FKuWr5HtL&*v~J6SytJMtI7;- zuYv+o-Qbm;nu^z41*A7vjUys2+)G5f#$PG^#AgZqTqx0h;rOro#puM{ydV9oEczdL Cl=X%H literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeFormat.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeFormat.class new file mode 100644 index 0000000000000000000000000000000000000000..f70dc29582204d02cd7a9c2e24a4439676590c50 GIT binary patch literal 2078 zcmbtUVN(-V6g@8_Y@myT0JhRrYeh{Kb*r|u8fvRhBu+#}96Hl}dL$3Bn(U^#FO2@D ze$W}2&hVi>pg*e9^L96|)Rf8451V)2&Aa#9bKiOQufGre1n>ZlCy~RLg}jXd#sx|* z<(_mq(%*HTt7Z_k1jd&=-_!R6#wyjV2^2A5VbaD&um$ckeRubE6zphsryI$h=SC{r z^P0+Cm0E7fJ++za7a#O>uoAS?S`c=nX4YP)cD&aD<;D@@rVc%Sw`M@j*qAm5lP#&! zAmg5~Q8w<0NcChWbr2fwoQ-*$6_|?DZ++!A)z4l_w*^GKOqQS6IEMv+q7D)OfhU#5 zDJ!p4Kdje)d}iZwoF_VEKQ5#D#xh<08IfugFSvB7u$5XUtUnbo-@h>RR;| zfqRvVKkfb~o_cjlAfM2jZg{@h=y$hO_(E=XNW9z#nzFMcL(l9}XI{5GQn-8S##5Y6 zlx_#DXT12X(kf)Sxyq3Hh^1N=n9s7yuol5_*`MID?u zK3^=X30yvE!jQBBQWn+)&YyggmzQ(^Uvb*x_}udxL!5xL%4SZ*D+g1&aujhj=L)#Q z-$m{^@eWvU{(x|n_F*~K_A%vL-$%)r&+lXQ9Z%(OnZM%@fY7)?`)lT46t1Ge)mR3V z|I;$Krj`XW5-}EA20M=`?U_7}f`?3r;C!TubI@V1Dd;9wlg4rm@GUUZfYaN)n=&n9OM$ro`QH&}q7R}kE=O(Z#>y z6`c35P9juDGYWQRSn$+n!R4XBrO|@t4_lcnd-kM84$3aTCCePOcr#vaQ6)wN=E98v p2YNJiqQ!4$mD!tRYO9EE@g45s9%@*L{WVU-zv!Xwmi?jJJe$Y=ln literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeRule.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$AutoCodeRule.class new file mode 100644 index 0000000000000000000000000000000000000000..8da50337932659d681e2c602fb248e71f849b548 GIT binary patch literal 3113 zcmbVN>r&fB6#h0~%NRL;xiqFBgoXrLK%6v9X(O7J;8IK+LV=Pt>20wWti+aFNoLwU zNS~seX=du_3_tn+eTF_ur)O8bh}vO1{8*i%vpRe9oy)g>|9koufHjokxP$=>gE}IJ zGK{|DCp=r>_HlMs6dkw35M8xw%YVo)kj@+oA%-ChaUH|Z8SWSD?D0L%Ir6hdRgc%K ztS8(Pt0=NL?(;o?Wo z%)s(;_iY_lm5(bt;qU)c6ViH3$1JW>0MF;Hf50ns`c<|ix8BilLpmgib=MWPzaw1B zDaoCiI&MiPy}6^@bYE#bYR3^t{j?KRtXY zioTh7&hSe*WN)^{#Vq-5x2?WR-5+HR7zTs%jT9_fY}c!Y!d2y!z3dcu<$$}EEO*m5 z=$9=@&dLSLNi+FG_+_WGL(N?kzHsTViFA)Ds|n3BOosCo$|rNqPFr(JxouI`?>Tk1 zC^jrPpcDU_-`jFXsd=`A=WjZmuYo+${Wd7|NQ+HIA32Wi`7W;osJs}q8BSV!UA{%_T$?Z}_ zc=H9vc~!5O-Axy=G#w>n<%Cystr`(t*k!%KJuhr%PR@(&L&DL|&_=s6^t^2gR~;#+ z?es8X86H?htuiB7PYrx~E%)w4}TXl%xwlNn(5@iQ|Mzogcx6^n5^X|6l-@N#=|{!HmTQG-IiOgt6SfsF50M zVBDCDG%#hPq76(NQ(6NvMrx>mIb$l`z`T*t8(1(>i3W_vNwhh6xzV3UO`;oAn#31TnnV~?n#8CnO`;r9n#37bnnWrw=Hxwt z1U@Exd6)u9*fN!%<3X1r=n4c~c%YjNbccbiE07Z~Mwihi^!qY7cS*`0)r?at1HY5n zjZY6gY2q8Bi)O8d`(5DIzT8*vs9Bm-k}^3lWAPOC-{e0PeA7;E4C@4+Dd-VKFH{Ih zKnqfq-Xx@m@dvc?07aT7GRj;p=R|+b$)3)m{W(webRO@|`AWMEVa!wKRiT6VYR|~i z{Y9R|r<7yaKv^nIE88=_k>O44(}{eeXXLs5BH!v6dA`5MnVyjs`is2O)7j|9If4z9 zna{APWFC)6N}31=utk4zumkD~h%cbMfb;^&3kWZuyMXKpm e>|q~Y-~i9?CB6cuuaZ=o81gv8G>Q;7M(RJxcwX!P literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$CodeCandidate.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataSaveService$CodeCandidate.class new file mode 100644 index 0000000000000000000000000000000000000000..89938570910811fa5219345d9904dbf46b8f3468 GIT binary patch literal 1755 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 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataService.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/DataService.class new file mode 100644 index 0000000000000000000000000000000000000000..631a4e27162a2e9e0e921d448a5e6a178885387f GIT binary patch literal 4983 zcmbVQ3tJS|8GeuKGRr!EE=t6TVuBU~T~q57g9?NSK~zwTHLV?X2N+pq)|pvU+9tiF z&81Cx(KeUHHf@^pVjGhhB-+OGnl`;(e?eoO{)arZ@0l5P2No}nj}JRD-#O>|-tYY` z=N#Yu-=#MI?83j}n1>Pt5fxFC3M@FLjcdt_W)3I!4j$7}u0ZK_!!+Dhfs%&CfilEU zrXa3jK2(AFl$jjf;8;U$a%jxavPRO;?QtWeCp$D(>-XOU;%WJ131 z7TS77*Bq)X5U}nBnu|ivI1s~~0%hZde&VP}(&MPdDg~=mydMn$E3f0(K8+Cy*jmU4 z^y$ZQhOMW4&;lC+5FYG3TDIl7j)i@VVGZ-q(KS0Yax`U`X+y?TLTXmAR^ln6E#F2A zNr9MUr*-@2;Heny7O2Rt^hlQLHmLX@HZrzcC)s0`-7>N;FkP0QOGP*C6NpHuKyD6P zJ_iMlug6K4$*!)(o0MVwJ{s)Pom|FkQLsng4zHC3X3SANy<2len0;~dpd^NU#AsV5 zoH+W>ui$`+eK;u4c%2*%ialgSAkk1n@q8S@{R$4N_%MzzOa% zE{Q-A==N9$L@|7Xd=oY6A!oYtaTFg@ps5%{iWy%p=4W?A2F2^!U2*6bQZTGy1O{7$ zH`P;}54kd~Wpeu7A$I5nvRyQzC5BOfsB5_zQ?Ul2z#M=`68! zD@|W3d+387xC0t>uy%T^l$IILY(str!iYO!NbJR>KF6_wT`@MjlghlD)>}y2z(<}x zxP9c$1dIJFkjD@56^N;}X_`Bu6;^i4;C z&v3l`3E_NiZE20nkRn9cS<<&Ezc`7>{(Ty{w-XbD_2WR2A#f7s` z-##IAvj9xD3Xf}YOQ%JbKvQw5${G2?VQ9bLic;I#p9C_qwBLq%w;o>>$B?0C(gJE) zAJVu%1|6%^^jfJb#zN8fRZ6f?Z$F@$-2(UB9^R1JpcZ*(U^)6%flfmot>fp79xD&Pb>hqHVNd<#1e@fN>` z63T(bH5Z^vp?nXYRn1M4xTCk(7uBvkzb!KTUK6=OlrgGZ#zo7p+!c%1iIr2ht0~bqg{G#&I`4P=6h1JC zP3Ji$jJqBRR-v3|t9VPRK?6b5d$_B8eReiFYlloy6{Kk;~{gd=b6dqBYUP-Wsmo|2hUraEPA| z1fL%jm_j-)J|@MPDOh>QPAPH2b2_EuL`YJgr4^M#y8&gqe^y~5Gi(!cYcpfE11k^Atm{iO2A6 zyiT@u@QQGWQIC32_d_99O1k~A*En{A_+O@C-Vv!(mh8J!sqop|)SQ@@!YAIu#I{IH z1=*5gh5A|S#~f4Nc;AUBBYj oJp4M}?ta7X-|{a;UBBnwTlfpV5}eb=8}VORlz+oN@GsQ-A52$DkN^Mx literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/FileService.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/FileService.class new file mode 100644 index 0000000000000000000000000000000000000000..78a29caf58a6b4983ebe00fd0955c898fbf5b3cf GIT binary patch literal 13964 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 MdTo^!(z?k1UkCc*6aWAK literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/LoginLogService.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/service/LoginLogService.class new file mode 100644 index 0000000000000000000000000000000000000000..f7efd3a6e74dce913ab88625a1652402790c7e92 GIT binary patch literal 4120 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 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfo.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..178049c4473788e58002bbdd66b0455c8b547d96 GIT binary patch literal 2468 zcmc&$U31%15IxtiEypOe<20deLqg~WwvtxF1zK<@v}r>P?gvfC1Mni-H;GzDGLjsI z-@)JDfzIGzCJ+1oeiXyGl3bgq2zu*GuJ_)(dse%9chBUnzfb=Ja2MYek-?0Htd1P= z0_Eq{iDmXJ=g@p6yRO$0$Zy$>9o!L^snz#qQNXN*qK>zq3#@e=^YEtc9t7sWv2P7* za~Rlt-`wuo(g|A5fh&-)2LhG$#PM$6+0J28xjLt#r2G`^LEG(Gf$cga|C?;H{(73q-TOLfsMFn%|S^rMpm)ev*euy`#sqYJ9qnec3wj&=8 zk2})avpRiNsiL}~zi)ZAI>*XvaAdOzms7k48~Tw9j@;f;%d?JUz@8B}UrTtVu)SLX zi_-;~Dx6>^Pd9OV#eUj#hhA6i*ec%Ar15X4hUcS=$EbYIbpt=}tU(l!U%=-AJNBSC z>HTKh+hZBGZ`1YpH$F`IxB04POPm$1Ij%C-0@o5(m1~)6wUkGzh_CRqhPIAJcud`7 zAibdz*vHb0a*D&_{B*_<3%r~zi|w4aFQge6KD3-(U;0!xKnZG!<~v;3wJ8^0zTw^tw5KcGgShR ztwCA_xfo<#UUSWpRzG4;D#1-k^?A)W#e2YS+`b;QstHZvsOL$eA1A=CsdA-(Z{Sv3 z-Yv@T*^Kp582^P|ifrP~t4F?#PZ)qoH%B6-5KMZUlF5^bH)s3~?W}}yu}3mSB6uzx zxRMAirvoo0f)~<(FC>DO(t$4}f~)DkD~aIcbl|l_@M;=x7N3SK*2ETNgRKxv`*_yX z1V?ioO?foq(S%3y{TVGTvOgZ8Nc0_i!T-PRVH@}H06X{+Pk3MC{Zl-{9;*KUEj!cw literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfoExtractor.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/ClientInfoExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..334490a1b5f8d753ed87cad3cb4c727d7b6ec991 GIT binary patch literal 4480 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.~2z>9X3*8Q$kK9H<%zfFY)xmvlCCwH_@xWL(X0fckwO% bv)o4&TX=xS{8psp9PVI;fPI1=v5V6GEDi?aS32(!;VK?r+siQak z8?SW6&d4~!g+IU_<@9-XH?lM|1HIUs^B&H5&i5Jq{qN`>0AFJ}j}*oXq)lXy6_|Wt zKeMf_?RKnf+4lTBf$WmwI%-*9tWw<_M-Jl#@+L086sWdct8+8(9xLndL16bBtFN4H zV6C>k({ygj?#OK!^t(zR?R!53^w11lXg#ax1gfqf-!M_Yq(DY_%I*q?buBHKcoP>H zL60z-9>onUo-%Psi*s7MeVQ$F!KnYu3_IX zaRpZiyKe{kE8de6?q z>9y_duI)Se9ShTH-(i{y=V$N8Kq~1IdE@0cUpM7Ay^EDo z1|S^yhCn%X78Wqk0>P0i4Of!F0ow7D3p9p=uN!k1*SbJ{$LsrTdC$>(FyozD)a%88 z-IRe^^8#gnZM`yj5jdHHZJ)gBc`8u8-HVDIWAoA8%|z5*t3^6s^jj?K?f?`AL(Nt{ntPL5%UkFi6t-i9-?30*l{pUFnCA zlGB+#zIz5X1?G*>vYIz=NWuJUI!WJ&y)L*d@)U9&=;4}W}x>y7ofK2>B!K{ dFo$K_;lC|+v4RJoJVn_pG%<}0JjAz{egRQ2@nrx2 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils$TableMetadata.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils$TableMetadata.class new file mode 100644 index 0000000000000000000000000000000000000000..3ea51b9c89d42da5d61763abaed5355a7039563e GIT binary patch literal 2434 zcmb_dZBrXn6n<_(*npc7B1LH{eUa8AysTL3+ak6EE3|<+Qu&~cGdIaCY)N+0-A!lu z*&pH`@PnO^afT260DqL@bF-VW8A1xvehBAq_B`kPocr&8NB;o0hezW`A#Gs9L*LQl zUaYIdlO_symsaXtv(s{ewHJ|c_6;Bdo$fdGqTMr?XFmI0<9q3IGDGFl z_-}8N)Y~YmuFF($!1#5SaZ})8GQ>pdh1b7a@h5@HJ!=EDMc~)dzrKxhxY0Xa$FHmV zPLnff##drV4+__4o0nn33qk{*3Cs@PP%caFKIPXuFAPFowxbpXS$rjMtMAm<$Ct$J zyB-q)27fvJ@)M)@W*m2M&%m0A3f2X#9?!7faYLu2tf-W+D=t~B=Uw1svKx;3+&fTK zq_`p7MpFfIRnI%i1+sAgxLY!`_9;_fiO2+ zSNUC&iZh?ovkI1e+ekZ3r>L{l4+eB=Uys5EW$#qIfo2 z!%s&#uL_aH5-bLwQ_S!hlL#i-OG$syDP_uk!5EfMCblS8p+Vr>*}w&S9H*1?oIeZr z8pBMn2N`_Q@#Nw2Vt(TMd1EID3ZP$SPoSBcKzOW3UD*Z7vwOT=Yy2lp|9 O??^XEAE1hD%=`}!>r?~) literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/DbUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..1f8e2769650e0c93f93986244b8b4b03879da0a3 GIT binary patch literal 28217 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 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpRegionSearcher.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpRegionSearcher.class new file mode 100644 index 0000000000000000000000000000000000000000..e1bc47f15393f474223f08ebd24e03c6e6cd1549 GIT binary patch literal 4126 zcma)9YjjlA75>gVawn4uB!iKMAOS>@$zud5N&*y!Mh6o}NsNY|ax=M^3{37F?!Ci9 zN(B{)iWRMo@{S0NTGXHzf(Y2w)vndmfBn-x?H@Zp*XmmR?OH1R&Yejzu`$bJ<=%7m z*=L`9zWwdJPyX`VXIBBN#+N~O;8oyL;YWc$@fK~n7D;MGJhHxXiym_X3YI5~gtJn> zTVBzoARtg}TJeaTvJytT+tPaV9j3K45;HA5k}xCnNzJyKHK%8TZkuT2@+Q z;%|ihMjA~cE*Gq}ENxd1)8UnmSt@4ZP9mhGQo7M4;4kl3Q_)t4GR##lPsM!PB`|LS zyKTBs$=o|Wu#}ZD@nsj%! z{7#oov`@~dx`}kg08&>mk?OD;b%m%xwSpQI5!@rNFlI#J_u6K+6Y1`?wNxUKb`nWD z5>0Jzi88!SAYhN2WWkT#X|alXK}fwxGcFKv*V)PLC0iKGcwD#Y0$3_gHxlQv;r+@o z2Tx@BU&jw}2_wz&7-x#?5@`sr-{h5JR%69K5%Y5+G%@>~y;(jbsP^+R2 z%LPhCX&f~iom&Vf+qGm`U*9codwDcRS~t0J8g)fjiJvN1r7{=SF#TjW^SvZ95=OU4 zw8lx3yu`^StD87e@qmg4WitC?Nz>L9JT#mO?$)h6mbpXgOzJ^2AgW?59w9@tuCA7J zDrM=mt&_>q^S2vWLHi1thONoWTL0kAn4WUT;Q-bPG+sac{`D8GW)2RN#bg~wu@02| zYv`pLN1rbnL0VP-f*Nfu%UpRabN*!J_&{dhz|Cu~XO5lxYVRSMxN++2S9@QU*!)bz z2DI=fx@DOJG@S2Ou3F^5FxJyYhdp)Ao#*Le0u3YXj2HMb=Wkv(`(u0zq8%F*bg0;b zC&rY|taGG5!d}Dux4QLnV*pRFUSy9LWIkvrIuT=4O4yPTmZ2r1sUIkcT�(=D_|N zS6-EMAg-bZ35v4$A#nFN=;X652Uq~~X4WVG*H4|j`O)c&duWDf#qK>4`d$?V$UNT; z&0>XGHjeE)#ZK%JC?cQO2DLCXt>Wj{BT#6kJ8idnNc6_!+Q_M&5%6qs{n)4C zSv*Ht*{NiLJ#M`0s_4i&OwNW(`TZ&m;FpYbO0#VJQNvEe4ZVxe`+=}U6}%uYXS6!3 zOZRr_R)cBvYEB+0Um`E67oAI^f^5V#=D~NN7jA0fxLMz zN}btW?df2NEa=QNvG7E#U~GRP6bVweG#k*dHv(=W=yKlNXiei>PCmARP(G=}(PCSh zv{deNGd83RC(*07CF}&xyxK5KN0W7tN1n%hw=zOnL>m!DnoZlO=d@#`V-5`#WUogO zyiuy;Xea$=RVZ%KogTBRS(BG877LmdJeG*^k7ZzP-okM?#$7z}mf???NqOn;lfe#+ zOdW^mG|AlebdbH56Ri8QTZ_zg?>_XbqM!e!U``c|AafKU*C^uO&k|z$A#;vst56K)wO<D5|AYMImn-sr z4Z$mQV~c{;VtmMTGC&{kEo8sSh6Dd03SP=2l0hQ*_z@I@o*W2YK;@s^QRNUeOIepI zqegdA6;&A`ss=(&_QO{-&?EuhQdxBgNgueJTg=09tQdi?hW>$6D#9X6Cb>#UujyDy z81JKIEh>3=Tfr48`CQ9qAGO1Dt&(63;5?mOi7ABT97l9E2ZkJd%vk{m%UlGCJcWhd zNC?_}tN6G@X%?k+2}vtHp};S=1_;WIp9R75AR$nQOE4Tt4Z=EtzlR&c#kGUjzGBuf z%nZ-+Z63taxw)d;XJcjP7lYWluKKES3V!c7z&%4xL>Q_!#!VF)jzqDZe^M28P>mU74YF(MP<|zC=PI_yKgMA~zXGVr4c&`srX83$HCHO8+ zdae)Cq_nvBZA^9tbKV(VUV<tCwBV#71o64m&yXG@in9*o^&*2|F$hLuZ0?Gc|hf2U7J* zY{fsh>ISbu|7KeB(Sr>L&~^zf;8PO&0jAj?M}FG%kS7;8D#G6|SJ@oK> ze1^-6_iCKL=eQzkkqEjhg3F?1nEI{!|EOJjgUJf6ehWpxHGWJNUqfX=9rOl*!P42& zRtZTj(yE@~ literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpUtils.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/IpUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..8e8373710a159be00fb6b8bd393f6c9b97d4e882 GIT binary patch literal 1487 zcma)6O;a035Pc&Fb|scb$THYq;g7_82!!}ZV#nAHwqlH8fhmZw%c&d&X%GvmU1fI# z&Mj5BwC0u?3xT&2mUfzfY}uuD zXgfiv(t4t$iIjlRcK2QH&^2KRB%c*Glyr)f^*+3Zw1JF;0b~Wn_vF6xLs<%xf8eOF zv>S%4(h8T&NU@>5ZmWPb%udBM=nYbb# zrnTp57T(7P0zE;?u?c9rTMl2IMPXu$e%F2P#W%&WV=Ff#jV2}pvQMkUxAdCcf`uua ze?J-9P)#pX_v&?Dr%<#ogA!|F2lrXx{Z2`ro0wyO*;}W-Ic>gR;X{1H24D@O6TC%R z)Ezi3>J;g!?W(nQb6fde$Zdz3Y}KntXG{9FJ|8O+;jYa*uT=lPK^NH%Bqw6C z(h|6%Yw=Ev*sae9%0FpS-5BrYNFZi2bpK{3Yx}FRbqpc1s={5bzAk;)RH5<-C$;Id z{hC^~HE!#8$Y%5eV2&*w;~)zRV-dG0@9-2u5^=7aXlm|Kvp^{*dkVim{M<45l&cBvBunD9Y*toHyADu z%qyJBCb=0p!p&DO5D7so;O_#VvrO0=Jf{D{VVk1l9B-BG{1U}~)!=rxy Dw2n(| literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils$TokenClaims.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils$TokenClaims.class new file mode 100644 index 0000000000000000000000000000000000000000..752775dcdc304227029cb37fcd6e0479ddacdb26 GIT binary patch literal 1843 zcmb7E*-{fh6g>@u2{4fmBktmYngn6QU4sjtma*beiae+xX=(jLnBZWbM>|6JP>oi<{)p;gs zDyRz#&UwBU-V*396qkpP#*l?!8zZm=nlT$WqZ!gtYft%=dX8b| zZS2FiKsr>BwZMF#x{FZC#lHhEzz1v`#32H&xq59;)uq7sT>@7O&Ji0&af}Je_ieYq z%olctl8Elf%3E0r%f*)h*9!@KT(+HPipv56Q8uGh&zFze%@r9exhoCkm#eCp+gNr3 z&zxiBK)B{H>giq8l;n9N!!=cZ>IQC8hB6>TzR;u89V%4>_9YWaBr4RO6p4Qc3_n-x zpeFBoCh|;|WHW|LY}#X~!v{)-794?z|ImTW=@LACz->kG>ohJ3j7MZGceq&I zV`I7ALy+ z!1e15sVA#Sy=%A1+qOA2T>8|KccrccUMo~V(uD6%>!czk&#MG%5AT1xlFc6272lUZ z2U4=ZBZ(JoVP4?$Rt0)sb~CcDC~$21t87t}K4v)z`uMK;ISn{>xXYPI(UF`FlomKP zFu_~Ld3sGbK$$OnhbT?0!z!IyN2WBrj_gC)5&4*$!3Ji4=)ZW=|FfX$<2O7H+4MmAG&crq!aO5b2@mryE3 z6s%0Q9!cZO&Wv+Cjk7y3PGO3H_Te0+J8}l+Df4VwM4T>d3~6S$vP|qMsguS9T*75s V!7XZYL`Y)>x7nCGSioHz{0+r^k+1*& literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/JwtUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..f2ae6fa9576737f90b1f0c8476ff70059a8f5667 GIT binary patch literal 4694 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 z z4v<(si5&_^t=*^5?$c;?!7Hh$3P~#oxt?OZfQw#*T+y*YYhb;?a6E{5STkHC+p;zB z5SqA!a3XmR=}-Wu>dhlAI2E?l2iNL>EA^&)Cn3q)Q6vo`yj0KGyF~CFtwMMoA8>5p W-3OO>nz(T3 z!jCfE0TMJWoF;Q$=bkh7_VerQ9l#m(EF_?r&@(WQWUvQ(${n8vJ?Ex9@Lb7|JQjhF zCk$G#)U>dIlnEjw;bY;%Quq^R z-g2*e2K^%JcnoVb5qQ_}sO?2}yzNsaUkhFCH+dvfyh!S@FR1CD_CIx%p-`+X=bEcl zODzUt%KdmI@u$p?ZAk78>wLVBwCkSihn-s<@sTIJh~%wC7)P#mDbzddALASgAj_~YC(`If50lg tz|O{<=B<&pyny$ znV;bz5kw_EfR7@~+9?7gB-WC5=Ire3nKSeK$JcKFig*%72;m464JxJ>k|)L+LpKep zqaQR+c*|p$+7_1Zo-u^8xkemO#3G1mn1=R8c(w0(yvs1t>NyU#yqf6piZD&#`Zr4| zF!omrNz6nrtKkY#3~Mb*?`*pEv8Nw*UE@^fJx`dfUhj!^qwtbj+%Y`cAs&at{k?+< z!$R37>2i8WczwMp4Sr}?ZM(Y_M;g~6nA31w5}f~cC_~iZXI|O1I>b$`#8?d|k`W-M zPh=1oy*O5ITS}N^P_w0y6e^7{gR*P4IYXi>EWY3CHn~$X znkFH$WxHjV4Z{)ApMZ+@TFR9z|DPsXlMS#{N_ftTjr$I@v7Z(TKt8OR8xPoVnf!B2h_$8NkSUSMv@gKpN5E#b@Dc8 zXSA#9Bd`x-gm8!ca^8R&P_TfzWNASV?qP$x7pFOOxP zM25IW@rY{5Q_LJziG7~#@Vcuac; Fg`eZZ!AbxC literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IIdGenerator.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IIdGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..2ea9b9227b71994230baa21dd06050c912657ab6 GIT binary patch literal 262 zcmb7c8wicDDw|U=J zkLT?UV2X}Fg)r7uCL{0SAmgJ~r&N|9&As&2UE*9FjMUDCLg^rv%l*PwQ>fqyff}Kk zsk4%~vWeX6GNS`QSaVrBn-Hq~!HN*guG*YJ>Z})N5hj1&zO+vX7j9iyXfsc9L+Je~ c4c|5m0;BmqfY`8(24}&k#=FTA@m)dt1+cP7cmMzZ literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/ISnowWorker.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/ISnowWorker.class new file mode 100644 index 0000000000000000000000000000000000000000..9a5ea458403a76aba968e62ae2f0d64ab91457d0 GIT binary patch literal 259 zcmb79Y|_xf>S!kEa%_Gx&yfckwAa1 zE=tzQ?Bsg8CmjT0L${C>0?om2C6LZTnif?} foc{wf`mn?ToXq6_!F(;mtP@rd?>0}$cLUuwzQIVL literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IdGeneratorException.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/IdGeneratorException.class new file mode 100644 index 0000000000000000000000000000000000000000..81bfb7edbc0b71b8d908d2037227ff9ec1766916 GIT binary patch literal 1157 zcmbu7OK;Oa6ot>^W#Wb=O-Nt70&O8p5ngnKf+7&2s8V={R@tzcI1|Su_Eh5uWy?=u z6~uxcz>h-Qaje8dK|;co$DZ-I-?={HpTEBU0Pq}58yRFR&RYT5(r9r-N4_6pCx9QmN%$$bn!e zcl}VjnFM{I&ds+oRJ+pU;~rPOnJ0@mJ@O-lXWjp^Z8KPbh$8OAM$7Tg>&k1PM{@9v zD;@|f6q)8-K8Z+QPeri^ouT=UN%k0uLFAnz8<6A5BYJH`=nVFmoG4eE_$HB^RS7;e z&Ep<6>4!5%9@HrcG;Y)EnC{H1wx(cT=^=w18qMDc66IiFm!3*95pLoZ%?zMO3IpiS zJ=V64rm&U?HYwL)!py#q5Dfb^?hxrNG4hNiGi+%U)?UW06Sn*pY=gdaj%_5^a)LcY zmQKKG9X*=j`ZBjlZPj9KVkd=D{|SS g{I*g?_T0$EUYv{BgMAz%X@V<`RzHeKD8-bjz>TGM2dIWCwC-j%O@f(E$6 zaoN=4d*U@=&*#&D^bJtqxVivb?>FE&#|;A%dTQ1=mVQeGx~1kG36*H6Au-LjT~975 z^~7L8ky2YZV$v7BYtGAHLXTaNV5j{*5^hn~YPZ%WMNXEA5VeJ>`@$DfsijX{a$Pm| z9r?sHbk_g;in{rBROsO`8jB2=`Ci(Qs3=9AWRE;49eL6-@+4p6Nv+6}IOJn=lje!+ zO**|noOVFN`UqwvLZqz~oDPw<;vvphg;a>O$euTWi&iGY6|0#38sgfAsMrQs3puj) zkU*AJ9tE0&Rg`H}Xl+oVP255QcX1z8%H)fdsL@UHkR+k8h|XK0W1jio-|1LoBK(U} mon!(Y{spH;#A++zwYKQJ@2(ftp literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/OverCostActionArg.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/contract/OverCostActionArg.class new file mode 100644 index 0000000000000000000000000000000000000000..b35764b188ffa1885112f8d83e6900ce43adfa40 GIT binary patch literal 901 zcmbtST~8BH5Iwh_w63Lmw1SADh_nUVL|=?338|l z_N_wZRbT?UqmZmr4|147E{nW_TW}P%edG3?55r^Y9uJ1zdEkz0&>y-(6J8$oy)*6l zp|O$YTeo$gqk1^ByS@!Vvm5mk^4GZYo0IZ}>FUqK zmG*vvPxU|{RS&z|Z@L*6{c$un(ox4d>hsHNGxWXwffohxKK_`pC&5tRb@M-**{00P ziOeZGu_8|+Y2LMA<#6oi-;xyOdt;f0C)(V&qL2=*wG}^TdlGg(d675JR!7`6-wsES zuipkDYhvW&rk=`AfroT5!Jj4rC6`Y`Q&5aEuO`mCi8#~lIMdiTb2{$x^aH;ALXw?; zz1laZTJj3nTJ8$B1#eG&rrn+)UbmMyul`=0q>Dz++*D;v!KW9pklN}nIe6U pJseIDv`&3yu}nHW(Aw`~$ixxX=Iq literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/core/SnowWorkerM1.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/core/SnowWorkerM1.class new file mode 100644 index 0000000000000000000000000000000000000000..aed62c84e1f9b3bd8245817a8d8d275af1d78ecb GIT binary patch literal 4342 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 BZ9V`1 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/idgen/DefaultIdGenerator.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/utils/snowflake/idgen/DefaultIdGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..81ba7abd4444d163c161063af5d2aba8b88d1b9c GIT binary patch literal 3108 zcmbVOTT>KA6#g0(W?2SU5X1|RRWX1NmW!9j0xGNM5)jA|5RvGx+rY>&v(C&Sn8cX- z?I|fAQd+TMiYgzHJeb6It>iIP$uG!H$WvZYl^08MdS~{6kV4(so$Ws7^w;N{KK-5f z{n5Q&035<+9^@cbf=fmo@)^8ml?BBgQ?wcX;M7?ju^93@RZX?J8FK3zMkTlzx+9u@ zrrp%1E&ueqsl-)(!ct?V-_-Q;(=la^`y;w$8A` ztS85&j~j+=G+M6=CdlZw8 zsPmkG_2p42LWxTc;Xx%fN!To73%0IEMtI4z_&h^tBw-j_v&7N?H5OCNWM2B*sAech zfjU76mtTYJ?eZ4{Fz&j>dn|y7plMMoH zr;HZ`oE+xo(nxO9Gc+d?`S*h>tI-;L+19?!E?--l0NW*_5lxhg)Dm(jTt>-gmf=SW zL*+ajW>D86iZ!NMvota-MYAa8iuzE)>Qi!~jbX=%&Ftg#r9x@TgtA-49ieGpVD%Mo z$}?H*A@oUjMMggc81}CVr$%SE=0D7*l|;ug)JpAH$wEo3vn7}Bq%bb#C_hgZWc4Vl?k0HL*t8-QwikX-elMG`AlSAMgo8-nZ6b<3n~6t{6rH!d(# zr@UN)8rY;8Z!&Dl=A|dih?0fbMPZgjP*0le5oRU%xa7e*cvr%Ef>_^YsLj;0sx9c$ zG3`EQb8RnmPTg2y_%gc_I)-)>#}q9Z<7REYuFoanfvie#8fKPc#ln%LIA6njBFo-x z{DEwKcOZgFEEZNRPDLY!H0tZYm|~jTl<*P5&Q&q3<*I~FR$S>7&N)%hjkDvzHiYQ? zgdsvdkDx+gkz5)R%hEpwa+cwu8Q9dW+hC2qq3AxO@v?&3DEzhYClr^X{PI;4i{Tw? zynM~kS2_BcuN=Yllz=R2&v-X3JMuaqcagAW(G~5gx`KS~fi9XhTt%Vyt6Ihuv18RW zNL4qG=RI%_F98l(ZQgar0?}KM0&GnI))HWMbrP_fOxkV0_UdJn3c%`n*qg@L=f0lY zZeNzRBdrZwMG+wcO6$C-UAi3y4t^_mx1N4E^kZ3L@;W%)cpFE4#IYMFzK`Q#HbgUm zI_UZu8_F?!2jLs=bhtLVe#XR2FgDD_*hwPXTuc+z7pSFY37vo!Zj{oCSq1I25pDGP zv>#ikAy#7;HJHG5M6m<2WIspmFBedUi)8sZn(z&!>pQgK7OlFAc08b$fPcgGl{^|^r!dSE>Mk?n(Qf3?JdgHM$v3JE zoTYu~L!%hxDGyORf(D(katq%hPNQ6$BZE|0eIFB0T9#|enA=;BTu@X}|4@l!7>4*3jpJMZWl{h#yYndU+S+b+1KyK%UrRTz1LYmIU49^J z+3E;oueII?C48=Fyh(;xGS8JZ8CZB2SQ*$RS}6h9920r9*clmEobyvsH|BCP zDzgLCfB=XDs$yVd-~_VtfixpXL~A<(L@&m;{Y6K=TGD!dcx$zFg delta 112 zcmbQqGLwb()W2Q(7#J8F8AR9_m?qk2F|#wUOcYlVWMp7*&QD3@Vqj+A z&8W->+7#Y}sEPWu&2ollS&cHEwHlq$NKadBK6#&xglTR=jOlDyc1pwq{ B4#xlh diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceFactoryTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceFactoryTests.class index 50c335c909248d4a7ea296ebc63961d2ffc45813..6f18a08a038583f2e4336bd97dd64d8a8ab3dd38 100644 GIT binary patch delta 885 zcmY*YOHUI~6#njXX6|%4EoJ0Y-VbT9yjp<@Eh>mqkb)pp9*S6?iH%?@Q({asjVl*! zXzq`2&885WXySr}iCg2=pW#M4Q;3N($vNlV?>pz*^WB*rX4$O&_UF@Q0Fy}Tkl^5u z4Jc5H@lKUPV;C%YJH!Xoqv_x_>=Few)ULy!!NZ{E_OrS4em;}7ba+wCVH&uE8u6VC z3sstBbz)Hpu?F!@intmX2#U+ue`rc9k16bkDkYDYuC|#p{{CB*!fS zQ@DMe9dt6N87sMekQTq>E}Ik#Aa39eVx&mMil;KW@syR<5x}g0yHFXF?cJ=E=17Qk zwNBSDhXn)ouqa|`FR6OJ=#FrjdMdkzAd8?NAWCI(V}J(H#uD0qw;oJ986$bo zn@7XN3zvd6(HllD-aH!p54@^T{H7Y`&CIuNzxU3Qas$pNJsu);LUtR`FG<7mqZ1xC`)n8?x7#BN_qA@Yw6PsiMnVINwkvzWByv2lu{ol%k=t%1sQ z;IufDnmnE8=ICMwiqCSXn2KDr$AI`K_vki;d^m?; zQKZ!CojC7?ToC}zY zF{sOlWQw4j!5qgdL(v^)(`ijO_f}hDw>Oeo&Qc=o!#oy5yBd-iZ1F{FAg~{Zu+~-J zpH4f=cPE_nnCmCzxSe5iOYS4lM^^;~PFyQd=4zVupzr3*2K`+#;DML^zP}Dc{UiYe zWR-A)IM=I?C?#UcB)JKnJGliX%5S;!e{$$6`k zH`eT5LWH&1jbaBoV-JfVPZ%9sNTs(Pn zMGu!FZj=g~Ij7=0XWYbOdB~5Hiaw5bHcY)_IO{ZAP;rrSKF%G-{kWuJki+grrTnH| zqLgI}tGLYBT4KkY@K_;dnnw~eTv0K~n>%-x zwi?Aa6_BQFNUDThsP+$cr2LkGHYt9CdAxI!_mPfi(t8uPh$BVEHDob|Ca1K`}lu(T3DuNoER(F{W-z zTxzZ{apRV)BrqhBNZk5A+`H19dhS4+0udf4+PLP{bn+jbTXI z`amet$6+uO=+ecf?8mh==lQ$&VwfQ~QmIyIqw>B!o(O{>A`n2(gpLLQjz$JO+r1<| z=|3YT;%Mb)VaO3rhiqhKKp=snY+;#j3hf+i3x+#8cbLi5Ew*(fxG2e z$$nN=a?T2zLy|PEJF5zFL10*6f`nOAsW?UiE-6T8-7Py+zs|yCfiZ=PDBS;i9OD8L z3LJdmR5r?vqP#LBFr~1~gq`%$6RWoC9!Rh-BQUGRWT-is$l{vPe_a;!q|Q;2?Rtyc zHeSfLMyIp_`*kr}t(G01qU@5aQrWG|thqHB7`=lw$6_(H0*W*`93k>yiU!H6jMt&A_s$mailrooHj<`T*QBPSR*PB38>%>Sq*owjCOTR;T|4P%A`(e Lcl#C`XCU diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.class index 4b8e906a87411c50103900b27f1d04108f51122e..cae9c258f0d64924b48acda45942dd67ccb1c121 100644 GIT binary patch literal 7388 zcmbVR33waD75>L|w2HitL~#m zHZ27Ty<2+Gma8o-&;q4EK^Rhw-jvdl-q+Xrpa(taK@0t7SCS=LPVCyBwX2zVGw=QL z-v8c=pL+3;#{jg6#sJDtuAoB2EcgWGj%Y`OBtc4=4gYOt%tg;p&fkh zHPco^@6?jokZ$$qw!@_ZNqsciA}~-_N{do$_2HSV*6PO+0o9C0n^Kx>kD69gs(+@6 z8Y~m29JLHbZ#R=MV@O~@Rt*>FUdJ+$L#_4weyk9v=-b)b7r-jiDp;*z4b}=Y&rB78 z(}r|s91nr5rPd2a%tT0w8-s)}6t&VuG?X&UcxZ=w9hatI^#WDtq@`<-VQnz3=M9`q z0~=MGg(g}a(~Nje)^d7;hiRdK5{{6Hb<)vh52h2TPU$ex)D0>&N|5H5Ne`j+VY;AB z05{`o1zS{{gL4IznbuH`@R_(TOgBhLWb26ctbm zdzTisb-HWa3>RI69u+T_e<uOWNY`95HP0V;`N1F`W0hT-Wsh9Ke8rgDNh@B}L@b zH%wh8h|c-b8pKT0Aab)XkL8 zr*LBWmN_bE(A&dW_d5x^oy*8(ny?vfQSny1jj(7gJ4r%v$0fHD@S(cWE^^A5r_d

&N`_tPFR$& zYSF6vB@(X+bA5?IwiVxw#ry{!|Q8%atbjzFu*A)qJOKzv4leT1H9V2U+ zo_weUa2v|y$46D%DWgKQb;nDT3|iT7w~CM99>xPZ_UbW)-EjM`9yu%*w~lIb|6HUL z)o83ar1ZTi?vv7IxYCYc>Cqmh1vjbe(Bs-M1)mUDKP|02wZf(r)v4M2DEA}7ygbeu z0gT~c1&^pWflqQ)>0EY7RzNX}q|3VLa7YqjDD4>W5RHkm45hQmYHlB!82*0hxy#Vw zQ}MXWIu)|SRq!c+&8IYSa*0{HK7c3i8CgPq*7f)QV-9yG zR!OwJ!0q)2*#ps!FAB_)4&tr6iv@#e9rNQ$0zGLfzHKBr7-_MOF!NeRb*m*5TEAgS z^E&>nZ&|--vfZEmVbXA_+FEte9ut+oRJd>LO+@KqIG!`F*4s+V$@EMlgW z&>Tlr8*A$hmO!?kDVq@$JVWOyX7cWop6qs6NHYGnRD2uX$yLd@wJs^i?gjI+OtgCS zBWayn)0)*Kp9;oTAmJ1*?q;JqwEuJvqq$eu-Zx__d1P;I{(Hi*+NmmNA%i^pH$;eXNk_ zpE1i!j2Ev9f2ZR2_yZFvV<)WHPNx|)Vul{|76_jN={%q%W-bY=K=ob{ zG&S!xY(pl>WYTop6vNE^zXG$SrFl1}kZWVvNLD~>)rks_hB(%^P8O1tNTjila{ zP7LZ+pG>XtSYa}q_iL6Rzh@gOoMD4AZ=PX-myBX_vE>Cvn=XCSW3WA}8}076ON6W= zV}@nZAy`99@65#7C*hq!1=h__FRFA}ucJi{cWNouLIwZHRYLB0DxY#mtofvED|o4B zRK7=G=@ght1i0ENk8iv~SMR9dM22;{&mk@&C+wN#aWlo0n8(L6*yb#QQ+RHt?hKpJ z9!+LG1_GV9Hm)TGqgw4rfUI3dWoKT^_7kjZ3gNy8)O1gEXVNSgiDcGA_vqAR^ii?7 zJSjRh&#Ng4{`E7APED=i;ai%J21P}BpU2b!o)<_;T59^_@ zYaXZ2^1xNn_VI5|7uJheA#cSCwPcj#e{I+_52sVD1$iKwX9`-11pAJq^upoO@AQ^D zxoqJPF*~z73iOiMxW#HmGO1f`-K*P*SV@{HSx)C;+AB^cohlSjOXrx5D7VU;tl&tZ zh_wQ%C+$TkSQWv`o|=;nWzcwgfaN?!mhr45|9F;!z#sYMhpL3(#LhmUENx6I^UPFwFZl@jGrq|i%iF<9N0E^g!GVc`9GW-VFqCBLUe& zc+TdxE&MzeThW4cUeSd4b^zz&GF*TWUd^Q0dW=_M$N9X9&ug&L1+{^8*I<7Z)OKtT zWrVj}B9VtTJrUmHMezEkgtt5gFP`Bt(&NG<;C045t`Np)de}A^lk>1Jh|Bo z58!C<6%XTh8IN^WWkGlZuLr-rIe1+L?~uXu-Z9*i!TU?bS(p~(P&k|aO#m(;3Ijyp zAW^u4E^;YRI7AdQc~3`dqQpd}RForij*Jzc(VR!)U>=R#6Vd3MCK{5nJlc7QXvi2F zI2G;WqN-6wPL^=SqbeUlIm($BHytz${M+k33(04Mpu`DEf}ogmjTGtT2+7Ch1&G6o z>NH(rlmv7+B}dWE=K)5IBp)wM8neJ`MpZse2J#SfOoXVX2%@Rtq)h%_h5;{W{dq#A z@OkJ2ZaeT0?&xZgzLCL4n&<#`HPQ5spTPa>d7!1Dx+3@>spg^TiVPk-fhP_y6@0p7 z)(Na)|L0nK)xO~8t1ILHU+~H5S>B5$;k%b>cp1av^9 zJU1X~zwBxo_>`;p8{9lxFteB54;9ShcL7A+gcndD=1J{EP|W93>ivYM;u}{M)hHJW e#6tcpXUk%KTOn47)xO!hdU1cm8c`=2Q2jq9DhJa5 literal 7332 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 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoaderTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoaderTests.class index 8dd518fedc8d7b174de6a182fd4b54a76219db9e..b771450edb62f595197c32dde0dea97387236186 100644 GIT binary patch delta 945 zcmYLHTTc^F5dO}#-7V_^r59RmQcH{Uf`y7GauWd+DXq#)6os~IuvpusEy^1q(Zn~? z91}4y$Uh(&0|XK!KKkM>Ffl%$KKS4tAWi|(Y-Z^lHuZiv*!c*c52_3S z8VOnjI*1I0g~;QGKM_gJ`@_)%HI`!#yW+`su7^R>piT_|;X5nQ*yWI?p9ySI!ok^wDOfIfwWmKa^ zLal;2oa8e?8($TMEN0jhoGReb7BEO?Ud`?h#zVuQ@MK7a8J!Bc(9QqqM)E6tI&FVyPR)OR#agL#BatHW zKiX%kHos=&FPKgyz=_DC=v>6fgUl^DTZp1Vl#QmW!B>iS0=RL5z?$CF2h6N|;t~5BK?V(Z%13-z*HuP%^1z`V*0C zR;9WT1{XgNtvoD=Wm#u1m3p|CmJnk^^>KJPON%}_fSq~>#4jVEos#Y$OC;S(T0=rl zg}h5-`AHY@be=7+cVr0ksE(3>Qd}lmIZ8l32FL>j>9TNmd7X-BDblrzWoc>)B|)#} z1LRE@c3~#Ja>Qq}dbd&K+eE$5;oE@|*hbSH93kVG=1sJ0qpjG~0X-En)87IEtgz8n zk18~vhE`jTPMWL$$aB>67?2)ql(&3^P}->eRa_%<0mvA_b;9?5^kKjt7kEF$9w1#9 zT<W>!j$xc+hC(u>%_539 GRQ&^STA|$l delta 822 zcmYLHOHUI~6#njXIx`HnQin%HDG#AgW{QQ1DDqM&Dgp(Shxi^~~*dyq%nm4o=KDlj+sh)uK|4mn-eF$=P%#gC|@+${@!VrW_p}_&D;Q ziy8S5D-&IEAb;3NrQ1{#GeiaRMoV=R!IuYyeH>mWuDhS(0LoUkExDYbiK0T{t;~Zu zg79gmV$c_t78aeQ^sJN8QH_v_8ZpC~y&Scu6KkxuIEZ=<6FM<*d8vpn#Kej>rW@4M zA&w{-MT@kc#L%qbuy`R=)9WK>5uc>#;4y}R?X}JAr}w@+yZhtelke;6IxHOLXhXXg z^VDiFbaHgzgt+f%6~E=IPn)-IPfXc0jSMZ}2i{tjvFK0?c1{c_B>@fn4C+XtFEKcl za2rW*3}Q%p_6&#_rCZZ6f-x1N;-eBGB9|~Oekotf0tLf<=sL-BjpH=V5O9?M2Hu-Y zI!p0+JC$-$Dkj8N)nrp5Uu#&V8BmXmKGKt{N;lVPWZgeB5AyQq7A2dDGdI9r5+Gp@ zoo+DTLjj$9CjfrzB@Zy@vGhM>At7#}=ag6`+rEXOp79No^jqdD1TrYyLXiB5frwFQ zWpOZ)LD;Y&n`i*CXnF@LVH|D9pf!t*T>>;X@ly*dmmx$_S+or?RFL+SIZEbEsehC- zG^2{vlokjz=ti9A#fao7^uXiSUC{_ROXOsV>CMrtBgAb{HI2Ru26hYYbRNu=kz9@* eoToSm7jTiRE5>EQuHZUu;3grik~Svcp!6@*YuW6g_Wd;L%|ywAv5)VT)4A*MQa54@#QYN~M)znpO?%YDy=H;(!cRcQr9_ zX+qx0U*J|Zkl3W|)va;m|8S+=hsKx8J@1}(?!D*D`xf{bnE3qb<0*g}xT8aX%ApzX zz{`;7gd&}0XgFVUYV8#UHJ-S~;bVx2pDY&C!J*3FH(($j_LS&g5Fw5s1H(AOkm`g| zqNmL4bBq`mMS#KU+U>iox{e^m4TPo2_sq3hj`hTrttJgjNu6Kn{_{bcGcY5?-osYy z$Zkn##K3u)x}c7k98m*v(y}J4a?CS~JhG0gtoyW{-PtY`UR3R7yVh_x5@Jt{#^OlH zOQspd_?iUE9ShO#0Ds$>>3r z@FMN5gZ*WaJ+Ke`;{}RdqJjIkL~-rap4_;Iqd}TN?7Y5`N@1w4d;+R8Zqi6 z{Uo7_Y47BGl{gC-9MHhSzPm-5m4S{NmZ8b1AfIB}8>y^P7KZYhN;D37ur-b#r&nYg lF@N9wfPy@!R_N1Epr~LSxB6KTo7n0zMU)A3@?;Xy`U9REb}#?{ delta 769 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 delta 3106 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 delta 1290 zcmZuwZBHCk6n^f$&CKo;*kQNjb$4N*WubILEJ|OXl%gUmZ3U^TRvdN*I(6J#nB9V} ztF13B`i0y-Vq>zqk;M49F;ScNMPs5rpot&!tMS}f0tqpboO{o?=bZaI&$;tU+dpkB zkN*AkI{;JoN`i-BjQ=5x$V;YEawPb6PxOWveA9)T8H5*#3O@pTTH58m%HshE8pDVv z2n7*_A|c8!$`kVFu#6K_ckSxpl8knSX5A=Sx=}9O((S^!l@B6CTIuG0 z`J_Z@Swf5R3`aF)**+{ui_+5@fCJ2 zkiw9JLH>mGcdIy!VM354!}V{VGRj*6!V^@3+gIg4i{ zj4|p#8GOqVC5+EultHnanT@hxFBO7F;hc)|xWIq+BttqTMENAcB)`NWJnkK4Q#|cW zD$|&eaD^e|_B!j0G>cx7qW%RHFJhKI^d`FI7@AzRwvk`gmkU--;7p623;c=qf^rQn zOSsO^d&O+I?zj-Xlj7OD|G>tbI&d@YE`I^#tPR3gdp;c#Pwd`1V z({W_1QdPN_GfJk69K*53KCxz4wn_Ki#F`Lxo&V^&Ou||!ZsTp*p}KUHX^^l0H)dXiv} z$kSM zXvej-8amQLd#H40w0I3Yr>p2&7_MTVjx%BQcNNdvAaA^mi`u0sE(_=R@9|O{^Hp3G zfV?iCLD#lGroSeFRz%6h$i``(L>orXjtO*N4xKLO!3LOp&@!%6u!L6#Z-k!IGHwuq zB)zLwk)aD-qia+;YeZgmC3@rkSh6kxAXx4kvb>qmRy4!E2O^jyf;E`wA!2#!fTi>^ zv8)`>v=3?K>nLiDwo*lTpST-&Uo(zGKJ?d7ITHI!%ZkXMMicH(;sh~4bb4q|y)>RA uDb|M${#z(HC&t-OX6HbeyZge#VB-tiCt>=qc0`y)V3@|Ar&?dCHGkk{T zuH3n0NrJ^_!h$7B{Q`agzkmzlxiCX(!eZ_{kN-LMcTVPvpYq)K-*2Y?@|d@vK{pWK z2qMG~+i^ZPsa>a1N-b~fxSMr`&`h~fu4ftak_4MaI^pra9; zY!tKoCT=nW7BfrpCOWD7?VE*_^}LdIbKF9Y{LONa+qh%kF2_CGmuYQK7PWk&5B&xP zI0g}ypS6Viq0RO@VhGpVTCH5IBplUam}3N^a!7w=kG17CEc7A4kwl!HEZ0`|-@8iw znBxi3@{_(eW#TCVFJ-p35?)6WlMKP-)un|M6VDi;)%v!(?`LUC^Ic zAbk*_WdRIAjZ2nJAFYid&&kS?&*+ZXCt&t>7{wEWzrZ@8n1&a0s%3+y0tjQ8EPkoS z3}(qw9YUl~y$(sEdX?;BSRy7w6J1{&^fvKe%@2u(O$=Q*cAKNbN<03aHgyqetEZC7 z*!Y^qDEET#gBDrIBg+wqnsSbA_1E~6A3e-CCUm=Hot`3~_5%J`36s6BxnYb{B3 SgnEZt425eL7+w;GhQdEC{dE-p delta 742 zcmZvY+fEZv6o&sbo#{-=9$E(|2vRx7X|NV4;(@kU3jv}D2?vQ6MvFA1XxfBU@60?y zv!B9C6DbA~OuY2gm+=Z%1D&=SZ}wh?|6lvt>rDI<=CAXUGXNPZ=}>CUd_;#z2p{A= z<>DW5r9ymt_aOhST02UQ68yE@n|Y59jzy)iRcpxy3X@0v{ z**g5l&;pMH9^;App{&TTx-erPMG%F={$8vx)xaV_Ti;q;-Y}3MnC0qz{?O~n=jyD( zo8aXUEcdS*xfySQD*=w4aZY@P?PCP(Q;1i`=sZH`E7ug<;Ip&gP+1>hr^PjK?Lim1 zIRkolo`MKRe(v=0?auxJ736SGu)iT>^;!LO-1?58I>vXrigmY+d(C9FE%CH-e`z27 z>u1Y#ZEmN<9A!?xWY&;;uk|=B=0cNs;lIqEU1s)Xn@mZMDQ)#zPrZacV{ylO`bymp Xc(oReywLgvS`66+1M&9VEoUzQDh+k7 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/AuthServiceTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/AuthServiceTests.class index 622e41a8e7094792c5c46ab006f03b5a92a2faea..e9bfccb42b1b690f3e3adae1f2b0cbc6ef1987a3 100644 GIT binary patch delta 1489 zcmZ8hTU1n482amv|dg!^ARxhp9>OX@(tB14y+qeJo{r|uBzFhR@ z`qm%*S@;e>8*Xc`!LA^U!+~@L_o#kC_mAt55r6mKXebzCNN)~D!m;fPc5l@I1u8>P zyuYYigN6(RoFfx1hHPVM#6MvK$HFngA6Sxd#2}&7q^!RUUjgjDP$eNSyK?N!mmpbZX8>e1*)V~TJOG9Uvzcu2uMj$Z6%$V|0< zIu;)H2lPp4I?fdyK%as~toT6&*J`{g9F57t=~b@7IHI7R<0y7Bc;t46r+|JGJ06Pc z3&p0VA{vfCS1`yC#E|?ky;%N{zKIRX5+z4HhT|MzjLH_JLe0TA#{?pBR2e9oWN^fc zu~0T%DF+E!HZVHm*iDvea6dpMZv2a7Z8`f zI!mQp3;HBAc{&=JvfB0<$Lkm(ZX+RT?qvpBcu2zx-m+4>EiY)zu6OaCf-4;F<0?aG ziq9R5%fGd~g~dN62AM`-gkN^8xF#4ckfB+s*J(wkNyYbb7t|Z2Rf<{ zf$u+*jk}b;Acj^!Us~+(73p@1KI@@7WOZYK(q@T9(Ue4si8j$Ln#3N_DIO336Wt~r zp2r~@`Yc+a$;7}SLZWd=nixr9%*1S)Fji32;!(;FU`a%~s!g0s;>o`1B%WDBkBJwA zA&ex>FMGtrd0euAVu2d*MoKsFrp4u*z8dkt{~aH#I65ta)d@RItQ^=$&kbn81@x9w zq8>G9px0z8wxJO%WVg%fu5#TPXvtF5ce`_lF6Gy_LH#*Pc!`@d+;0e#OL8gp?Mmzx W`4SXMBTo{`eKpC$Fi)!u3;zN&Z8|Xk delta 1581 zcmZ8hSyUTk6#o7R$v+u}v<_tv+606YNP)1^g_a_PZbYP5XsIoR5FjiWN`|&7m0G3l zOZ;7`b*W3;>P9VT+IlRn^6c?Zk8eKcgXj3rmwAEx5@c#lI=f=15~$I~qI@^p6Ll!~V{J(QwF!|571#F~3N#{bGyMEas)g0tQD@ zBpNYxFxX1VdKl7L^}(3O4VaU{{hW!(IXe?sr{y<8q zD8yayGu1AJVik5cI5skP;>|g^`%W4MjmUV+35HS?95RQGp)!6ePyEW(Whp2_rWveI z;e^8BC!FY%O4pjsY8B~FIch0!{8n+Ef|?Y%K}7~!GVBVrU>nC)aa*dht7yV@QEuDI zxeTX_bn&y&;8NO7g~F3YM32Tec8TxP>pe1dQ_JmvZW()tgyGtlK4d7!$9@$Z2#5=| zUfGRK75Ab`{Ap`H(oNh9^hX9|93=cee>6A|meEW4?%HlTE8{SOGSEL6jKxmsQ-nLh zK(!x5KgThK3~L~&PgxABcoI(uod=xH;5m+G8H!AV_=&ryoaga^_>J!@h(mC^#GtLV z+G$NFIIiMlydnZ}i9Cnb%xveyjNIUS18;I%U~p45H)VPYZ;K!0P{l!c9S+k#E)l_zK?QU8$V~GwL!(9EZD)>Ha;oY!1)t(Gj>`;%R*B@K#Gm5}@q=?; z)>jNX7z*jrQOb3NZZStLveK^zRrQ*j(3`VEGPj*H$=qy9(h^&e_STZLrX)=?Jt$h~ zJ4o}Br1gN^cMaM+DH7Jv*I_v|WRb3>0LVrTSwJowmT)I&CTcd16f@Izl-bOr`?UNd zHY_540YyH|Yw6wvl=yC>gT0pr^tF+ve3#cM@4Y}(UQ182ndaaSZMM-S7j2-kX zSvH!ql2JuQ#%j?G$PiT-o>?yrVI%rbf@3H~ke+oEzJIl%ue8!at=MokRh>q) zx2`nRmg#}(_=zRer(oI`>1cAl6v*i=&EQ-DrqBXprMu` zb;v_C@zy|GHIjETw$cx6JK3#b(p9wIT!n%Z8~^avN56xwag~@mN2f1hfz~2HsBDs{ Q*tL~dl6(x;DJtQ{-!b+}!2kdN diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/DataSaveServiceTests$JdbcFixture.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/service/DataSaveServiceTests$JdbcFixture.class index f561651b5802eec17e60fe29e2a7fd90bb027398..25c40ab43c77edefc1dd51433ae574a62a13e4b7 100644 GIT binary patch delta 1772 zcmZuxYjYD-7=BK&$!<5NZCIMtQVOlLO}lLgEf=*`XsJ|+6od*0Dv+jY8)&m7*~r$t zoJ(_-Ui)|a2LP=YSK)$NhQ#54m%+b1yek~)565~#Yr3}^5sSgw9F0Y-B@FKBnhi1( zhJy59L4^txJ{g=N7kLab&3JEUz>MsOT4tzo62&}MgU^o{GBl2vG%dF|y|t=BMj_QZ z3ng~;huX|oj24QTu^jku4@WU(rT16Tre=#nt|G;cIUI9QmOfKU+`ZzQ!`;VGiK_HX zo2wQ-yXI1z&QXiHVWxX!%ww1(TG%YrZaSZ%0gXau4a@9wA;%&#GUO)*d%~8{WzjHT z#4KC8pW^{Xi^dX0+{$V`$Y|nt2+g9!U8ks6!m$)B!g9C!mZ43?BOJ>qzv2!yF{oCb z5nE?i$#~3%03PL7fewb;Uc+ilT4tL$Fc77yr5oL>;xVj}(aEtIYeczJAey8~C4jXY z>(C{7rFynr9F~Gg2{v*(iB00F)U1@_DUPS{j8HrcN)TH)wjnH9J;lBVdSn=Ow`avR zPl4F&DWQTsjwrT^;~t%oevSc3u6qgtCWE{y9FO#c<30qjlOv9V_}f$JO>*qQb7Ho4 zIc;V)$MbkWM7=Akk{UTC=|eqI9D5L^QyD`>BxxDzo!gYLkD)s4uMVi#3%7!qDqh3u zG7fOOfj0$H7K$dNBkv%s+|#|@iuNaDye-m7h5HcP;sC3Yq&Blh-0+^PRF@A$j6vHP zRCA0WkTp%$+Er7U7?iujh|<6BJu=j+HU+4svF^tv{Sd7zqa7%AW2> zdvu7tsv+Z&_*2!{sPOsp6y}q2T=bw6XtvQQ(rg<$NY-_b?CKy{)IoBjgJeYq$##S; z`g!m!+(=V0L7AJdP#*!)HF*>{S0HDQuWJD(4rEZIr?e6$DaoKz*UFu^Jc9~d3p#Ny zgBo3{cjEdCLet^})8fVYMdXj+L55L0OmWc|T3y&U8MWK&N)jZ3>B8d-smc1%EM2NS zF@_DUTYW0BJVl$Qy4Pe`iniEnH?{E)+!mxU^g3N?Z*!h0t}e@^YN&9BQ>ewVYz$!{AWg+DSQwQ-A8^#1XjT`JNeqmQ}lU>8F>@WPr z{=x6A0RDiWS!I(3!+SVP8CeZS@IIIm9i_;H4~UkC`4Gn`qQ#C79JOol5k4k?oQg1& z*e{^`GWKp7!7F8}YPDBwLycm8t@hSs93u7|J2~&n92OIKMO*C+6i&{&wE>0R%?W%$ vT_v1!DnG?%4xYjngax$SZ%O^q8NYJIukk%8_WzO}r*M(pT?Qlcy^8+>J%NjR delta 1858 zcmZuxS#T6p6g{tJ>7L0;7#bN!fFuaa44plM)ri0tBuD@Wi{Jv2q)moQy2DIQ44{rH z;!b#q3y7e&D+n?q6H1FJmk<0<-2Kz?!w;*d^4GE~Z#QI;6xP(dd-~k_&V9@2K3Do> zY2f0&!=D0Jj$s)R!(wqsUL2I+W(e$z^~GuuF{86)UE5B*-5R(Y5)0WJ@c^q3U(vr@ z(u5_|RWVFR_jbfAz15i~D;zmkOT4NY*W88>aONAj<7Wtd{+x1>6ZW<{WQ$&kW zGSwY5J9Ix>DB+ldX$-E&+|4p(3^W#d%P><$`M{2#hvRz88tAL5W|+g_fmcSDVUn}b z)UHHL)HDoQDGB?)FqcDyBBMfVXH#k!syKY`%cx;c^HVnHsdU0>)h&BQ9Y+DUjCl+t zqDCqyw8{A#g(#A-P_#&8AvzC*iQYk}}<7PC8 zX|9#tThJ_Hl}Nb4tsJY-#NfBObYp{VrISVhLRiDmg0&2Vow~I=ZJANCrzcKFNe8jb zkM-Cf<2HtgqqnlcG!uHv;Aq80@tbRo62fMVEx272xvSVV(dZ5<8t&w{3wMidccW5` z9UL*ViBs;nW%cOb&~Xn#5jBjOiFA(D(lGu0}K%| zZY1Ex0hB24`0)Z>lyOLW;%y+)yo8ss?G%0exf%SErHLarj06H;v}JCQ8xL^7%q$(v3jPdbqt=|u7)MTvfsa2=^l zrYAS)1~HZBUx}v525q|re`r*Q$r38Wf9R-&B0n0m6}@Z;OZ=DHMQQs z^;tA%>H-HZ$YRkLykrb+)Xrjd4oew^u!3+Thm{hx*eH|3tv0=mGHwc^nH)Ba)K})& zN_A@vcSu)tYV$my?IYC$#CPlr zzGp)eFW?7u5kIou@e}(8KTDJG3mzwxa>sB%?8g)I1SyCo@e~=-Mo$xx@C?yDiD(c( zJf6jKR6u@3jJzvRoOwQk16v1ia7~3OY+DWC`3m)w^Eg7}QTyQ|XXZX}p=io>yKV5w zysK>$6yr4oL>O*=Wqgw-sj=n)7o&i}51@3&urj zwOnJs$O|Y#dZu7>^b0JmZLMpYGNLJtrC6rmW);hEi@*juKag;8dD9x`&+A#^jGaG~ za1F;z6kIct=rsB>)Y-JHF1B^E$q`UZYsfxjv>Wb8J1uaqHnNS6QB$NP>B_~M?u#@9 zgQDUEO);#X;{6^N5|%h_!|e)Is;I##frXwvu9-CwZFWk}5M6_S*;IE~ZEI^u;fVvN z@*31CsFSeO3*0acN}FvBaMjuaQBh=LlY(^4AdY*DaP#WpEc-K>`sm}@#Mrwe+f%hpK$CKcPUL!g4#W&~2TZL=kI z(+rAqoy^;3^uCPY>rmsplMj2ifo!I68nH{o-E=)@<7j!~tlh%`1GUpb^tLO{AW?Gw z?WFm%_#T0EQ}i{?ri`3)fW+>CV_Jsew3J%nXu&=O_o R?fXH?31>WUgWe|j;mWK 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 literal 8820 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 N-k`o=dN&T5{sG5okKO

y7Z}`Q#ca-i3w0bC zbn(G$i9t2t(Qt|(BAV2QZlD=;I)at0xZaEwj5z=sh!CzG>=eKg^_)f_x z=Zgi7FfLZUMTpKLTa<}(wsE2BDwi89%j9M?f} zdhMKTlcyd_%8qclushmex~Q zS$V?oR95~OOJ&8rx+H?0kzLvfZInTg*Fu>@v=yp&DKjZDifZf-#sMV?IDI|UB1b*t z{Dgo8G!g=QbXY+XWs>EmC>!lkV})YhF`AbTaXKCM2OOM<$BZMKchD9)Muh5JpAe0G zMARRTAE8HP4*F`k;-{y5S1JZhI@J_)#L`%|`>hnrB!cA3=2C YIV^IiV3l|Z){v!W(7z--h#Xq}0tRA_@Bjb+ diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/utils/snowflake/DefaultIdGeneratorTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/utils/snowflake/DefaultIdGeneratorTests.class index e777433db81387f43e40c0464cf99f6dd3bc9676..ff9a76edc0679e3853c219746112ec370b7c6276 100644 GIT binary patch literal 7684 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 diff --git a/code/fms/fms-api/tools/migration/README.md b/code/fms/fms-api/tools/migration/README.md index adbc04ac..d5d678d4 100644 --- a/code/fms/fms-api/tools/migration/README.md +++ b/code/fms/fms-api/tools/migration/README.md @@ -12,6 +12,7 @@ | `RunSqlFile.java` | 通用 | 在 FMS 库执行 `sql/` 下的建表 / 视图 / 索引脚本(按 GO 分批,单事务) | | `OtherDataProbe.java` | 迁移辅助(只读) | `list` 列出旧库 `b_other_bmfl` 的 65 个字典模块(启用/旧库行数/新库已建),输出 `otherdata-modules.txt`;`tables <表名...>` 导出新旧结构 + 旧库数据样例 | | `OtherDataMigrate.java` | **其他数据迁移** | 导入 `base_otherdata` 下 64 个字典模块的数据(雪花主键)、重建 `b_from`/`b_grade`、转换 `b_othercompany` 引用值,输出映射 `otherdata-id-map.tsv`;支持 `--dry-run` | +| `TreePathRepair.java` | 核对 / 修复 | 按 `b_parent_id` 重建树表的 `b_depth` / `b_path`,默认只读报告,`--apply` 才写库 | --- @@ -162,10 +163,10 @@ del FieldNameSync*.class javac -encoding UTF-8 -cp "mssql-jdbc-13.4.0.jre11.jar" SchemaDumpMarkdown.java # 导出新库 FMS 到指定文件 -java -Dstdout.encoding=UTF-8 -cp ".;mssql-jdbc-13.4.0.jre11.jar" SchemaDumpMarkdown "D:\path\FMS表结构.md" FMS +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp ".;mssql-jdbc-13.4.0.jre11.jar" SchemaDumpMarkdown "D:\path\FMS表结构.md" FMS # 只导出表,不含视图 -java -Dstdout.encoding=UTF-8 -cp ".;mssql-jdbc-13.4.0.jre11.jar" SchemaDumpMarkdown "D:\path\FMS表结构.md" FMS --tables-only +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp ".;mssql-jdbc-13.4.0.jre11.jar" SchemaDumpMarkdown "D:\path\FMS表结构.md" FMS --tables-only ``` 参数:`<输出文件绝对路径>` `[数据库名,默认 G3HY2025]` `[--tables-only]` @@ -184,11 +185,11 @@ catalog 必须是 `FMS`;按 `GO` 把脚本拆成批次,在单个事务中依 cd fms-api # 先 dry-run 看批次划分 -java -Dstdout.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ tools/migration/RunSqlFile.java ../sql/fms_business_contact.sql --dry-run # 确认后执行 -java -Dstdout.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ tools/migration/RunSqlFile.java ../sql/fms_business_contact.sql ``` @@ -201,6 +202,61 @@ javac -encoding UTF-8 -cp "mssql-jdbc-13.4.0.jre11.jar" RunSqlFile.java > 含「重建」段落(`drop ... if exists`)的脚本会删除同名表及其数据,执行前确认表中数据已不需要。 +--- + +## TreePathRepair — 树表 b_depth / b_path 核对与修复 + +`b_parent_id` 是树结构的权威关系,`b_depth` / `b_path` 是派生字段(见《FMS新系统核心表结构设计》1.7)。 +本工具按 `b_parent_id` 重算这两列,用于核对和修复派生字段与父子关系不一致的数据。 + +历史背景:模块管理 / 菜单管理的拖拽排序曾有一处缺陷,会把**根节点** `b_path` 的前导 `/` 抹掉 +(`/base/` 被写成 `base/`)并整树落库。前端已修复,本工具用于核对历史数据、必要时重算。 +`b_path` 的约定格式形如 `/base/base_otherdata/`(节点编码前后均带 `/`)。 + +### 行为 + +- **默认只读报告,不写库**;确认无误后加 `--apply` 才在单个事务内更新 `b_depth` / `b_path`,失败整体回滚。 +- 只更新这两列,`b_parent_id` 与其余字段一律不动;父级不存在的节点按根处理(与前端 `buildTree` 一致)。 +- **成环的节点无法派生路径,只报告不修**——父子关系成环是数据问题,需人工先修 `b_parent_id`。 +- 报告会区分「只缺前导 `/`」这一缺陷典型症状,并给出变更样例。 + +### 用法 + +```bash +cd fms-api + +# 只读报告(默认):核对 s_module / s_menu +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ + tools/migration/TreePathRepair.java + +# 核对全部四张树表 +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ + tools/migration/TreePathRepair.java --tables s_module,s_menu,b_dept,b_othercompany_category + +# 确认后执行修复 +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ + tools/migration/TreePathRepair.java --apply + +# 多机构:指向目标机构的连接配置(默认 config/dbconfigs/G3HD.properties) +java -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" \ + tools/migration/TreePathRepair.java --config config/dbconfigs/OTHER.properties +``` + +参数: + +| 参数 | 说明 | +| --- | --- | +| (无) | 只读报告,不写库 | +| `--dry-run` | 同「无参数」,显式表达只读 | +| `--apply` | 执行修复(单事务) | +| `--tables a,b` | 待核对表,默认 `s_module,s_menu`;可选 `s_module` / `s_menu` / `b_dept` / `b_othercompany_category` | +| `--config ` | 连接配置,默认 `config/dbconfigs/G3HD.properties` | + +> 修复只重算派生字段,全部内容都能由 `b_parent_id` 重新推出,因此可安全重复执行; +> 执行后重跑一次只读报告应得到「待更新 0 行」。 + +--- + ## 已有同步记录 | 模块 / 视图 | 同步日期 | 字段数 | 备注 | diff --git a/code/fms/fms-api/tools/migration/TreePathRepair.java b/code/fms/fms-api/tools/migration/TreePathRepair.java new file mode 100644 index 00000000..7fbff819 --- /dev/null +++ b/code/fms/fms-api/tools/migration/TreePathRepair.java @@ -0,0 +1,372 @@ +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * 树表 b_depth / b_path 核对与修复。 + * + * 背景:模块管理 / 菜单管理的拖拽排序曾把根节点 b_path 的前导 / 抹掉 + * (`/base/` 被写成 `base/`),并把整棵树按错误规则重新落库。 + * b_parent_id 是权威关系,b_depth / b_path 是派生字段(见《FMS新系统核心表结构设计》1.7), + * 因此受损数据可以按 b_parent_id 完整重建,本工具就是做这件事。 + * + * 行为: + * - 默认只读报告(不写库);加 --apply 才在单个事务内重建,失败整体回滚。 + * - 只处理 b_depth / b_path 两列,b_parent_id 与其余字段一律不动。 + * - 成环的节点无法派生路径,只报告不修(父子关系是数据问题,应由人工先修)。 + * + * 用法(在 fms-api 目录下运行): + * java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/TreePathRepair.java + * java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/TreePathRepair.java --apply + * java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/TreePathRepair.java \ + * --tables s_module,s_menu,b_dept,b_othercompany_category + */ +public final class TreePathRepair { + + /** 允许操作的表(树结构规范里的四张树表) */ + private static final Set ALLOWED_TABLES = new LinkedHashSet<>(List.of( + "s_module", + "s_menu", + "b_dept", + "b_othercompany_category" + )); + + private static final Pattern IDENTIFIER = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*"); + private static final int PATH_LIMIT = 1000; + private static final int SAMPLE_LIMIT = 30; + + public static void main(String[] args) throws Exception { + boolean apply = false; + List tables = List.of("s_module", "s_menu"); + String configPath = "config/dbconfigs/G3HD.properties"; + + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if ("--apply".equalsIgnoreCase(arg)) { + apply = true; + } else if ("--dry-run".equalsIgnoreCase(arg)) { + apply = false; + } else if ("--tables".equalsIgnoreCase(arg) && i + 1 < args.length) { + tables = List.of(args[++i].split(",")); + } else if ("--config".equalsIgnoreCase(arg) && i + 1 < args.length) { + configPath = args[++i]; + } else { + System.out.println("未知参数: " + arg); + printUsage(); + return; + } + } + + List normalizedTables = new ArrayList<>(); + for (String table : tables) { + String name = table.trim(); + if (!normalizedTables.contains(name)) { + normalizedTables.add(name); + } + } + for (String table : normalizedTables) { + if (!IDENTIFIER.matcher(table).matches() || !ALLOWED_TABLES.contains(table)) { + throw new IllegalArgumentException( + "不允许的表: " + table + "(可选: " + String.join(", ", ALLOWED_TABLES) + ")" + ); + } + } + + Path apiRoot = Path.of("").toAbsolutePath().normalize(); + Path propertiesFile = apiRoot.resolve(configPath).normalize(); + Properties properties = new Properties(); + try (InputStream input = Files.newInputStream(propertiesFile)) { + properties.load(input); + } + + System.out.println(apply ? "模式: 修复(会写库)" : "模式: 只读报告(不写库,加 --apply 才修复)"); + System.out.println("配置文件: " + propertiesFile); + + try (Connection connection = DriverManager.getConnection( + properties.getProperty("url"), + properties.getProperty("username"), + properties.getProperty("password") + )) { + System.out.println("目标库: " + connection.getCatalog()); + System.out.println(); + + int totalChanged = 0; + int totalBlocked = 0; + for (String table : normalizedTables) { + int[] result = repairTable(connection, table, apply); + totalChanged += result[0]; + totalBlocked += result[1]; + System.out.println(); + } + + if (apply) { + System.out.println("=== 修复完成:共更新 " + totalChanged + " 行,跳过 " + totalBlocked + " 行 ==="); + } else { + System.out.println("=== 只读报告:待更新 " + totalChanged + " 行,跳过 " + totalBlocked + " 行 ==="); + if (totalChanged > 0) { + System.out.println("确认无误后加 --apply 执行修复。"); + } + } + } + } + + /** + * @return {待更新行数, 因成环/超长而跳过的行数} + */ + private static int[] repairTable(Connection connection, String table, boolean apply) throws Exception { + Map nodes = loadNodes(connection, table); + Set onCycle = findCycleNodes(nodes); + + Map expected = new LinkedHashMap<>(); + Set blocked = new LinkedHashSet<>(); + Set orphan = new LinkedHashSet<>(); + Set tooLong = new LinkedHashSet<>(); + for (String id : nodes.keySet()) { + resolve(id, nodes, expected, onCycle, blocked, orphan, tooLong); + } + + List changes = new ArrayList<>(); + int depthMismatch = 0; + int pathMismatch = 0; + int missingLeadingSlash = 0; + for (Map.Entry entry : nodes.entrySet()) { + Position position = expected.get(entry.getKey()); + if (position == null) { + continue; + } + Node node = entry.getValue(); + boolean depthDiffers = node.depth() != position.depth(); + boolean pathDiffers = !position.path().equals(node.path()); + if (!depthDiffers && !pathDiffers) { + continue; + } + if (depthDiffers) { + depthMismatch++; + } + if (pathDiffers) { + pathMismatch++; + // 本工具针对的典型症状:路径只是丢了前导 / + if (position.path().equals("/" + node.path())) { + missingLeadingSlash++; + } + } + changes.add(new Change(node, position)); + } + + System.out.println("=== " + table + "(" + nodes.size() + " 行)==="); + System.out.println(" 成环节点(父子关系问题,本工具不修): " + onCycle.size() + + (onCycle.isEmpty() ? "" : " → " + join(limit(onCycle)))); + if (!blocked.isEmpty()) { + System.out.println(" 因父级成环/超长而无法派生: " + blocked.size() + " → " + join(limit(blocked))); + } + if (!orphan.isEmpty()) { + System.out.println(" b_parent_id 指向不存在的节点(按根处理): " + orphan.size() + + " → " + join(limit(orphan))); + } + if (!tooLong.isEmpty()) { + System.out.println(" 派生路径超过 " + PATH_LIMIT + " 字符(跳过): " + tooLong.size() + + " → " + join(limit(tooLong))); + } + System.out.println(" b_path 不一致: " + pathMismatch + " 行(其中「只缺前导 /」: " + missingLeadingSlash + " 行)"); + System.out.println(" b_depth 不一致: " + depthMismatch + " 行"); + System.out.println(" 待更新合计: " + changes.size() + " 行"); + + if (changes.isEmpty()) { + System.out.println(" 无需修复。"); + return new int[]{0, blocked.size()}; + } + + System.out.println(" 变更样例:"); + for (int i = 0; i < Math.min(SAMPLE_LIMIT, changes.size()); i++) { + Change change = changes.get(i); + System.out.printf(" %-28s depth %d→%d path '%s' → '%s'%n", + change.node().id(), + change.node().depth(), + change.position().depth(), + change.node().path(), + change.position().path()); + } + if (changes.size() > SAMPLE_LIMIT) { + System.out.println(" ... 其余 " + (changes.size() - SAMPLE_LIMIT) + " 行略"); + } + + if (!apply) { + return new int[]{changes.size(), blocked.size()}; + } + + String updateSql = "UPDATE dbo." + table + + " SET b_depth = ?, b_path = ? WHERE b_id = ?"; + boolean originalAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + try (PreparedStatement statement = connection.prepareStatement(updateSql)) { + for (Change change : changes) { + statement.setInt(1, change.position().depth()); + statement.setString(2, change.position().path()); + statement.setString(3, change.node().id()); + statement.addBatch(); + } + int[] affected = statement.executeBatch(); + for (int count : affected) { + if (count != 1) { + throw new IllegalStateException("更新行数异常: " + count); + } + } + connection.commit(); + System.out.println(" 已提交 " + changes.size() + " 行。"); + } catch (Exception e) { + connection.rollback(); + throw new IllegalStateException(table + " 修复失败,已回滚: " + e.getMessage(), e); + } finally { + if (originalAutoCommit) { + connection.setAutoCommit(true); + } + } + return new int[]{changes.size(), blocked.size()}; + } + + private static Map loadNodes(Connection connection, String table) throws Exception { + String sql = "SELECT b_id, b_parent_id, b_depth, b_path FROM dbo." + table; + Map nodes = new LinkedHashMap<>(); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + String id = normalize(resultSet.getString("b_id")); + if (id == null) { + throw new IllegalStateException(table + " 存在 b_id 为空的行"); + } + int depth = resultSet.getInt("b_depth"); + String path = resultSet.getString("b_path"); + nodes.put(id, new Node( + id, + normalize(resultSet.getString("b_parent_id")), + depth, + path == null ? "" : path + )); + } + } + return nodes; + } + + /** 找出所有真正位于环上的节点(沿 b_parent_id 上溯,出现重复即为环) */ + private static Set findCycleNodes(Map nodes) { + Set onCycle = new LinkedHashSet<>(); + for (String start : nodes.keySet()) { + Map seen = new LinkedHashMap<>(); + String current = start; + while (current != null && nodes.containsKey(current)) { + Integer previous = seen.get(current); + if (previous != null) { + List chain = new ArrayList<>(seen.keySet()); + for (int i = previous; i < chain.size(); i++) { + onCycle.add(chain.get(i)); + } + break; + } + seen.put(current, seen.size()); + current = nodes.get(current).parentId(); + } + } + return onCycle; + } + + /** 自底向上派生 b_depth / b_path;父级成环或路径超长时返回 null 并记入 blocked */ + private static Position resolve( + String id, + Map nodes, + Map cache, + Set onCycle, + Set blocked, + Set orphan, + Set tooLong + ) { + if (id == null || !nodes.containsKey(id)) { + return null; + } + Position cached = cache.get(id); + if (cached != null) { + return cached; + } + if (onCycle.contains(id)) { + blocked.add(id); + return null; + } + + Node node = nodes.get(id); + int depth; + String path; + if (node.parentId() == null) { + depth = 0; + path = "/" + id + "/"; + } else { + Position parent = resolve(node.parentId(), nodes, cache, onCycle, blocked, orphan, tooLong); + if (parent == null) { + if (nodes.containsKey(node.parentId())) { + // 父级在环上 / 被阻断,本节点同样无法派生 + blocked.add(id); + return null; + } + // 父级不存在:与前端 buildTree 一致,提升为根 + orphan.add(id); + depth = 0; + path = "/" + id + "/"; + } else { + depth = parent.depth() + 1; + path = parent.path() + id + "/"; + } + } + + if (path.length() > PATH_LIMIT) { + tooLong.add(id); + blocked.add(id); + return null; + } + Position position = new Position(depth, path); + cache.put(id, position); + return position; + } + + private static String normalize(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + + private static List limit(Set values) { + return values.stream().limit(SAMPLE_LIMIT).toList(); + } + + private static String join(List values) { + return String.join(", ", values); + } + + private static void printUsage() { + System.out.println("用法: TreePathRepair [--dry-run | --apply] [--tables s_module,s_menu,...] [--config ]"); + System.out.println(" 默认(不带参数)只读报告,不写库;--apply 才在单事务内重建 b_depth / b_path。"); + System.out.println(" 可选表: " + String.join(", ", ALLOWED_TABLES)); + System.out.println(" --config 默认 config/dbconfigs/G3HD.properties(多机构时指向目标机构的配置)。"); + } + + private record Node(String id, String parentId, int depth, String path) { + } + + private record Position(int depth, String path) { + } + + private record Change(Node node, Position position) { + } +} diff --git a/code/fms/fms-vue/src/components/fms-table/FmsTable.vue b/code/fms/fms-vue/src/components/fms-table/FmsTable.vue index f137ebc8..39e1e866 100644 --- a/code/fms/fms-vue/src/components/fms-table/FmsTable.vue +++ b/code/fms/fms-vue/src/components/fms-table/FmsTable.vue @@ -650,7 +650,7 @@ defineExpose({ :show-overflow="showOverflow" :empty-cell-text="''" :area-selection="editable" - :bordered="false" + :bordered="bordered ? 'v' : false" @area-selection-change="handleAreaSelectionChange" @row-order-change="handleRowOrderChange" > @@ -710,9 +710,8 @@ defineExpose({ } } -/* 自绘行分隔线(库 bordered=false 时不画任何边框): - 单元格只画 border-bottom,外框(含四角圆弧描边)由外层 .fms-table--bordered 统一承担, - 避免库渐变线叠加与最后一行/外框的双线问题 */ +/* 外框由 .fms-table--bordered 负责;StkTable 的 v 模式负责竖向分割线, + 本组件只补充库 v 模式关闭的横向行分隔线。 */ .fms-table__inner.stk-table { th, td { @@ -720,6 +719,20 @@ defineExpose({ } } +/* bordered="v" 仍会给 StkTable 根节点绘制方形外框背景,且最后一列会绘制右侧渐变线; + 这些边缘由外层圆角容器提供,去掉库的外边缘后只保留单元格之间的竖线。 */ +.fms-table__inner.stk-table.bordered-v { + border-left: none; + background-image: none; +} + +.fms-table__inner.stk-table.bordered-v { + th:last-child, + td:last-child { + background-image: none; + } +} + .fms-checkbox { display: flex; align-items: center; diff --git a/code/fms/fms-vue/src/components/ui/tree/tree-node.vue b/code/fms/fms-vue/src/components/ui/tree/tree-node.vue index b88e2a42..7f3c692c 100644 --- a/code/fms/fms-vue/src/components/ui/tree/tree-node.vue +++ b/code/fms/fms-vue/src/components/ui/tree/tree-node.vue @@ -182,19 +182,11 @@ function onContextMenu(e) { @dragend="onDragEnd" @contextmenu.stop="onContextMenu" > - - - - - + 比同层非叶子靠左、并与父级标题齐平(缩进不足),层级关系丢失。 + 位置在拖拽手柄之前:占位是「缩进带」的一部分(叶子行它是空白), + 放到手柄之后会让手柄与标题之间空出一个箭头宽,读起来像多余空隙。 --> + + + + + ({ ...row })) + return sortTree(buildTree(copied, idKey, parentKey)) +} + +/** + * 预载节点图标并指定非叶子类型(即使暂无子节点也显示展开箭头,便于新增子节点)。 + * 就地修改并返回原数组。 + * @param {Array} nodes 树节点数组 + * @param {Object} options + * @param {string} options.typeField 类型字段名,如 "b_module_type" + * @param {Array} options.nonLeafValues 取这些类型值时强制非叶子 + * @param {Object} options.iconMap 类型值 → 图标组件 + * @returns {Array} 原数组 + */ +export function decorateTreeNodes(nodes, { typeField, nonLeafValues = [], iconMap = {} }) { + for (const node of nodes) { + if (nonLeafValues.includes(node[typeField])) node.isLeaf = false + const icon = iconMap[node[typeField]] + if (icon) node.icon = icon + if (node.children) decorateTreeNodes(node.children, { typeField, nonLeafValues, iconMap }) + } + return nodes +} + +/** + * 在树中查找节点所在的父数组及索引(就地移动节点时配合 splice 使用)。 + * @param {Array} nodes 树节点数组 + * @param {string|number} id 目标节点 ID + * @returns {{ list: Array, index: number }|null} + */ +export function findNodePosition(nodes, id) { + for (let i = 0; i < nodes.length; i++) { + if (String(nodes[i].b_id) === String(id)) return { list: nodes, index: i } + if (nodes[i].children) { + const found = findNodePosition(nodes[i].children, id) + if (found) return found + } + } + return null +} + +/** + * 向 updatedRows 合并一行变更(同一 b_id 累加字段,避免同节点产生多条 update 请求)。 + * @param {Array} updatedRows 变更行收集器(就地修改) + * @param {Object} row 含 b_id 的行 + */ +export function addRowUpdate(updatedRows, row) { + const existing = updatedRows.find((item) => String(item.b_id) === String(row.b_id)) + if (existing) Object.assign(existing, row) + else updatedRows.push({ ...row }) +} + +/** + * 按父节点派生行的 b_depth / b_path(b_parent_id 是权威关系,二者为派生字段)。 + * b_path 非空且无默认值,新增时必须随行写入。 + * @param {Object} row 目标行(就地修改,b_id 必须是最终编码) + * @param {Object|null} parentNode 树中的父节点;null 表示根节点 + */ +export function applyTreePosition(row, parentNode) { + row.b_depth = parentNode ? Number(parentNode.b_depth ?? 0) + 1 : 0 + row.b_path = parentNode ? `${String(parentNode.b_path || '')}${row.b_id}/` : `/${row.b_id}/` +} + +/** + * 重排某组兄弟的 b_xh(10 起步、步长 10),把变化的行收集到 updatedRows。 + * @param {Array} list 兄弟节点数组(就地修改 b_xh) + * @param {Array} updatedRows 变化行收集器 + */ +export function renumberSiblings(list, updatedRows) { + list.forEach((sibling, index) => { + const nextXh = (index + 1) * 10 + if (Number(sibling.b_xh) !== nextXh) { + sibling.b_xh = nextXh + addRowUpdate(updatedRows, { b_id: sibling.b_id, b_xh: nextXh }) + } + }) +} + +/** + * 递归重建整树的 b_depth / b_path(b_parent_id 为权威关系,二者为派生字段)。 + * 仅在值实际变化时收集,避免打开即脏;父子关系变化后调用即可整树收敛。 + * @param {Array} nodes 树节点数组(就地修改) + * @param {Array} updatedRows 变化行收集器 + * @param {string} [parentPath] 父节点路径,根层传空串 + * @param {number} [depth] 当前层级 + */ +export function rebuildTreePositions(nodes, updatedRows, parentPath = '', depth = 0) { + for (const node of nodes) { + // 根层没有 parentPath,必须显式补前导 /:b_path 的约定是节点编码前后均带 / + const path = parentPath ? `${parentPath}${node.b_id}/` : `/${node.b_id}/` + if (Number(node.b_depth) !== depth || String(node.b_path ?? '') !== path) { + node.b_depth = depth + node.b_path = path + addRowUpdate(updatedRows, { b_id: node.b_id, b_depth: depth, b_path: path }) + } + if (node.children?.length) rebuildTreePositions(node.children, updatedRows, path, depth + 1) + } } \ No newline at end of file diff --git a/code/fms/fms-vue/src/views/base/othercompany/index.vue b/code/fms/fms-vue/src/views/base/othercompany/index.vue index 47731883..f6aff343 100644 --- a/code/fms/fms-vue/src/views/base/othercompany/index.vue +++ b/code/fms/fms-vue/src/views/base/othercompany/index.vue @@ -27,7 +27,17 @@ import { escapeSqlText } from '@/components/fms-module-common/queryUtils' import FmsTree from '@/components/fms-tree/FmsTree.vue' import { loadDataApi, pageDataApi, saveObjectApi } from '@/services/api' import { usePermissionStore } from '@/stores/permissions' -import { buildTree, collectAllKeys, filterTreeByText, findTreeNode, sortTree } from '@/utils/tree' +import { + addRowUpdate, + applyTreePosition, + buildSortedTree, + collectAllKeys, + filterTreeByText, + findNodePosition, + findTreeNode, + rebuildTreePositions, + renumberSiblings, +} from '@/utils/tree' const MODULE_CODE = 'v_b_othercompany' // 右侧列表模块 const CATEGORY_TABLE = 'b_othercompany_category' // 左侧分类树表 @@ -36,25 +46,6 @@ const ALL_CATEGORY_KEY = '__fms_tree_root__' /* ===================== 分类树纯函数 ===================== */ -/** - * 必须在浅拷贝上构建:buildTree 会就地往行上挂 children, - * 直接构建会累积出重复子节点、并污染唯一数据源 categories。 - */ -function buildCategoryTree(rows) { - const tree = buildTree((rows || []).map((row) => ({ ...row }))) - sortTree(tree) - return tree -} - -/** - * 派生 b_depth / b_path(b_parent_id 是权威关系,二者为派生字段)。 - * b_path 非空且无默认值,新增时必须随行写入。 - */ -function applyCategoryPosition(row, parentNode) { - row.b_depth = parentNode ? Number(parentNode.b_depth ?? 0) + 1 : 0 - row.b_path = parentNode ? `${String(parentNode.b_path || '')}${row.b_id}/` : `/${row.b_id}/` -} - /** 同级下一个排序号(最大 b_xh + 10,无同级时取 10) */ function nextSiblingXh(rows, parentId) { const normalizedParentId = parentId == null ? null : String(parentId) @@ -73,49 +64,6 @@ function hasChildCategory(rows, id) { return (rows || []).some((row) => row.b_parent_id != null && String(row.b_parent_id) === targetId) } -/** 查找节点所在的父数组与索引 */ -function findNodePosition(nodes, id) { - for (let i = 0; i < nodes.length; i++) { - if (String(nodes[i].b_id) === String(id)) return { list: nodes, index: i } - if (nodes[i].children) { - const found = findNodePosition(nodes[i].children, id) - if (found) return found - } - } - return null -} - -/** 同一 b_id 的字段合并进一行,避免同节点产生多条 update */ -function addRowUpdate(updatedRows, row) { - const existing = updatedRows.find((item) => String(item.b_id) === String(row.b_id)) - if (existing) Object.assign(existing, row) - else updatedRows.push({ ...row }) -} - -/** 重排兄弟 b_xh(10 起步、步长 10),只收集实际变化的行 */ -function renumberSiblings(list, updatedRows) { - list.forEach((sibling, index) => { - const nextXh = (index + 1) * 10 - if (Number(sibling.b_xh) !== nextXh) { - sibling.b_xh = nextXh - addRowUpdate(updatedRows, { b_id: sibling.b_id, b_xh: nextXh }) - } - }) -} - -/** 整树重建 b_depth / b_path(b_parent_id 为权威关系),只收集实际变化的行 */ -function rebuildTreePositions(nodes, updatedRows, parentPath = '', depth = 0) { - for (const node of nodes) { - const path = parentPath ? `${parentPath}${node.b_id}/` : `/${node.b_id}/` - if (Number(node.b_depth) !== depth || String(node.b_path ?? '') !== path) { - node.b_depth = depth - node.b_path = path - addRowUpdate(updatedRows, { b_id: node.b_id, b_depth: depth, b_path: path }) - } - if (node.children?.length) rebuildTreePositions(node.children, updatedRows, path, depth + 1) - } -} - /* ================= 权限 ================= */ const permissionStore = usePermissionStore() const canCreateRow = computed(() => permissionStore.canPower(MODULE_CODE, 'create')) @@ -135,7 +83,7 @@ const categoryById = computed( ) /** 树是 categories 的派生视图(在浅拷贝上构建) */ -const categoryTree = computed(() => buildCategoryTree(categories.value)) +const categoryTree = computed(() => buildSortedTree(categories.value)) /** 分类没有 b_code,匹配「名称 + 编码」两列 */ const filteredCategoryTree = computed(() => @@ -424,7 +372,7 @@ async function onCategorySubmit(payload) { const parentNode = categoryParentId.value ? findTreeNode(categoryTree.value, categoryParentId.value) : null - applyCategoryPosition(row, parentNode) + applyTreePosition(row, parentNode) await saveObjectApi([{ table: CATEGORY_TABLE, key_field: 'b_id', inserts: [row] }]) selectedCategoryKey.value = String(row.b_id) } @@ -500,7 +448,7 @@ async function onCategoryDrop({ dragKey, dropKey, dropPosition }) { if (dragId === dropId) return // 树只是 categories 的派生视图,所以一贯「在副本上算差异 → 落库 → 重载」 - const tree = buildCategoryTree(categories.value) + const tree = buildSortedTree(categories.value) const dragPos = findNodePosition(tree, dragId) const dropPos = findNodePosition(tree, dropId) if (!dragPos || !dropPos) return diff --git a/code/fms/fms-vue/src/views/base/otherdata/index.vue b/code/fms/fms-vue/src/views/base/otherdata/index.vue index cadb7fbc..e5cdb135 100644 --- a/code/fms/fms-vue/src/views/base/otherdata/index.vue +++ b/code/fms/fms-vue/src/views/base/otherdata/index.vue @@ -28,7 +28,7 @@ import { FmsModuleListPage } from '@/components/fms-module-list' import FmsTree from '@/components/fms-tree/FmsTree.vue' import { loadDataApi } from '@/services/api' import { usePermissionStore } from '@/stores/permissions' -import { buildTree, collectAllKeys, filterTreeByText, sortTree } from '@/utils/tree' +import { buildSortedTree, collectAllKeys, filterTreeByText } from '@/utils/tree' /** 根模块编码:本页所有分类与数据模块都挂在此模块下 */ const ROOT_MODULE_ID = 'base_otherdata' @@ -53,16 +53,8 @@ const categoryKeyword = ref('') const moduleById = computed(() => new Map(modules.value.map((item) => [String(item.b_id), item]))) -/** - * 树数据:把根之下的所有子孙按 b_parent_id 组织成树。 - * 必须在浅拷贝上构建:buildTree 会就地往行上挂 children,直接构建会污染 modules。 - */ -const categoryTree = computed(() => { - const rows = modules.value.map((row) => ({ ...row, children: undefined })) - const tree = buildTree(rows) - sortTree(tree) - return tree -}) +/** 树数据:把根之下的所有子孙按 b_parent_id 组织成树(在浅拷贝上构建,不污染 modules) */ +const categoryTree = computed(() => buildSortedTree(modules.value)) /** 搜索过滤:命中名称 / 编码,保留命中节点的子树 */ const filteredCategoryTree = computed(() => diff --git a/code/fms/fms-vue/src/views/dashboard/index.vue b/code/fms/fms-vue/src/views/dashboard/index.vue index 68514595..b0dfd789 100644 --- a/code/fms/fms-vue/src/views/dashboard/index.vue +++ b/code/fms/fms-vue/src/views/dashboard/index.vue @@ -860,7 +860,6 @@ function flattenTree(nodes, depth = 0) { diff --git a/code/fms/fms-vue/src/views/module/menu-management/ModulePickerModal.vue b/code/fms/fms-vue/src/views/module/menu-management/ModulePickerModal.vue index 9f050678..9d559ee0 100644 --- a/code/fms/fms-vue/src/views/module/menu-management/ModulePickerModal.vue +++ b/code/fms/fms-vue/src/views/module/menu-management/ModulePickerModal.vue @@ -7,7 +7,7 @@ * 落库只登记 data / virtual 模块编码:module 类型只是模块树上的分类,不参与权限, * 确定时统一过滤掉;勾选草稿(含分类节点)仅存在于弹层内部。 */ -import { computed, ref, watch } from 'vue' +import { ref, watch } from 'vue' import { Modal } from '@/components/ui' import FmsTree from '@/components/fms-tree/FmsTree.vue' @@ -39,9 +39,6 @@ function isBindable(key) { return type == null || type === 'data' || type === 'virtual' } -/** 初始展开:只展开第一层根节点,深层分类按需点开,避免一次铺开整棵树 */ -const rootKeys = computed(() => (props.treeData || []).map((node) => node.b_id)) - function onOk() { emit( 'confirm', @@ -60,10 +57,10 @@ function onOk() { @ok="onOk" >

+
diff --git a/code/fms/fms-vue/src/views/module/menu-management/index.vue b/code/fms/fms-vue/src/views/module/menu-management/index.vue index 8dd31464..34103337 100644 --- a/code/fms/fms-vue/src/views/module/menu-management/index.vue +++ b/code/fms/fms-vue/src/views/module/menu-management/index.vue @@ -21,12 +21,18 @@ import { FolderTree, Save, RefreshCw } from '@lucide/vue' import { Button, Splitter, SplitterPanel, Modal, Message, Spin } from '@/components/ui' import { loadDataApi, saveObjectApi } from '@/services/api' import { - buildTree, - sortTree, - findTreeNode, + addRowUpdate, + applyTreePosition, + buildSortedTree, + decorateTreeNodes, expandAncestors, + findNodePosition, + findTreeNode, insertTreeNode, + rebuildTreePositions, removeTreeNode, + renumberSiblings, + sortTree, } from '@/utils/tree' import { cloneData, diffRow, diffRows } from '@/utils/dataChanges' import { createTempId } from '@/utils/common' @@ -36,14 +42,10 @@ import MenuTreePanel from './MenuTreePanel.vue' import MenuBasicPanel from './MenuBasicPanel.vue' import ModulePickerModal from './ModulePickerModal.vue' import { - addRowUpdate, - applyMenuPosition, + MENU_NON_LEAF_TYPES, + MENU_TYPE_ICON_MAP, buildMenuI18nKey, - findNodePosition, - markDirectoryNonLeaf, menuRowOf, - rebuildTreePositions, - renumberSiblings, } from './utils' import { useI18nStore } from '@/stores/i18n' @@ -158,7 +160,11 @@ async function loadMenuTree() { menuTreeLoading.value = true try { const res = await loadDataApi('s_menu', '1 = 1', 'b_xh ASC, b_id ASC') - menuTreeData.value = markDirectoryNonLeaf(sortTree(buildTree(res.data || []))) + menuTreeData.value = decorateTreeNodes(buildSortedTree(res.data || []), { + typeField: 'b_menu_type', + nonLeafValues: MENU_NON_LEAF_TYPES, + iconMap: MENU_TYPE_ICON_MAP, + }) unsavedMenuIds.clear() } catch (e) { Message.error('加载菜单树失败:' + (e?.message || e)) @@ -221,7 +227,7 @@ async function onMenuDrop({ dragKey, dropKey, dropPosition }) { } // 只有目录能承载子菜单:页面/外链是末端节点,放入会形成「页面挂子菜单」的非法层级。 // 必须在移除被拖节点之前拦截,否则中途 return 会让节点凭空消失。 - if (dropPosition === 'inside' && dropNode.b_menu_type !== 'directory') { + if (dropPosition === 'inside' && !MENU_NON_LEAF_TYPES.includes(dropNode.b_menu_type)) { Message.warning('只能将菜单放入目录下') return } @@ -330,7 +336,7 @@ async function addMenu(parentId, menuTypeValue = 'page') { } // b_depth / b_path 是 b_parent_id 的派生字段(b_path 非空且无默认值,缺失会新增失败)。 // 此处先按父级填一次占位值;保存前会用最终菜单编码重算(临时 id 不落库)。 - applyMenuPosition(newMenu, parentNode) + applyTreePosition(newMenu, parentNode) insertTreeNode(menuTreeData.value, newMenu, { sort: sortTree }) const ancestors = expandAncestors(menuTreeData.value, newId) if (ancestors.length) { @@ -611,10 +617,10 @@ async function beforeSave() { // 否则会把 /-1/ 这类临时路径写进库。父级不在树中(数据异常)时无法派生,保持原值不动。 const parentId = menuData.b_parent_id ? String(menuData.b_parent_id) : null if (!parentId) { - applyMenuPosition(menuData, null) + applyTreePosition(menuData, null) } else { const parentNode = findTreeNode(menuTreeData.value, parentId) - if (parentNode) applyMenuPosition(menuData, parentNode) + if (parentNode) applyTreePosition(menuData, parentNode) } // 受管 i18n 键:菜单统一 menu. 前缀 menuData.b_i18n = buildMenuI18nKey(menuCode) @@ -828,7 +834,7 @@ async function loadModuleOptions() { const res = await loadDataApi('s_module', 'b_canuse = 1', 'b_path ASC, b_xh ASC, b_id ASC') const rows = res.data || [] moduleRows.value = rows - moduleTree.value = sortTree(buildTree(rows.map((row) => ({ ...row })))) + moduleTree.value = buildSortedTree(rows) moduleOptionsLoaded = true } catch (e) { // Silently fail; modules will be empty diff --git a/code/fms/fms-vue/src/views/module/menu-management/utils.js b/code/fms/fms-vue/src/views/module/menu-management/utils.js index e5b8c294..8a3990a5 100644 --- a/code/fms/fms-vue/src/views/module/menu-management/utils.js +++ b/code/fms/fms-vue/src/views/module/menu-management/utils.js @@ -1,7 +1,7 @@ /** * 菜单管理(menu-management)页面级纯函数。 - * 只放无 Vue 状态依赖的逻辑:菜单类型转换、树节点定位、排序号重排、目录节点标记。 - * 树本身的通用查找/过滤/插入/移除见 @/utils/tree,不在此重复。 + * 只放无 Vue 状态依赖的逻辑:菜单类型选项、菜单行提取、多语言资源键。 + * 树本身的通用查找/过滤/插入/移除/位置派生见 @/utils/tree,不在此重复。 */ import { ExternalLink, FileText, Folder } from '@lucide/vue' @@ -19,21 +19,8 @@ export const MENU_TYPE_ICON_MAP = { external: ExternalLink, } -/** - * 目录节点强制非叶子(即使暂无子菜单也显示展开箭头,便于新增子菜单), - * 并预载类型图标到 node.icon。就地修改并返回原数组。 - * @param {Array} nodes 树节点数组 - * @returns {Array} 原数组 - */ -export function markDirectoryNonLeaf(nodes) { - for (const node of nodes) { - if (node.b_menu_type === 'directory') node.isLeaf = false - const icon = MENU_TYPE_ICON_MAP[node.b_menu_type] - if (icon) node.icon = icon - if (node.children) markDirectoryNonLeaf(node.children) - } - return nodes -} +/** 菜单树里能承载子节点的类型:页面 / 外链是末端节点 */ +export const MENU_NON_LEAF_TYPES = ['directory'] /** s_menu 主表列(树节点 → 主表草稿行时按此提取,剔除 children/isLeaf/icon 等树注入字段) */ const MENU_ROW_FIELDS = [ @@ -67,18 +54,6 @@ export function menuRowOf(node) { return row } -/** - * 按父节点派生菜单行的 b_depth / b_path(b_parent_id 是权威关系,二者为派生字段)。 - * 根节点 b_depth = 0,b_path 形如 /system/user/(节点编码前后均带 /)。 - * b_path 非空且无默认值,新增时必须随行写入。 - * @param {Object} row s_menu 行(就地修改,b_id 必须是最终菜单编码) - * @param {Object|null} parentNode 树中的父节点;null 表示根节点 - */ -export function applyMenuPosition(row, parentNode) { - row.b_depth = parentNode ? Number(parentNode.b_depth ?? 0) + 1 : 0 - row.b_path = parentNode ? `${String(parentNode.b_path || '')}${row.b_id}/` : `/${row.b_id}/` -} - /* ================= 多语言资源键(s_i18n,主键 b_key + b_locale) ================= */ /** @@ -91,63 +66,3 @@ export function buildMenuI18nKey(menuId) { const normalizedMenuId = String(menuId || '').trim() return normalizedMenuId ? `menu.${normalizedMenuId}` : null } - -/** - * 在树中查找节点所在的父数组及索引。 - * @param {Array} nodes 树节点数组 - * @param {string|number} id 目标节点 ID - * @returns {{ list: Array, index: number }|null} - */ -export function findNodePosition(nodes, id) { - for (let i = 0; i < nodes.length; i++) { - if (String(nodes[i].b_id) === String(id)) return { list: nodes, index: i } - if (nodes[i].children) { - const found = findNodePosition(nodes[i].children, id) - if (found) return found - } - } - return null -} - -/** 向 updatedRows 合并一行变更(同一 b_id 累加字段,避免同节点产生多条 update 请求) */ -export function addRowUpdate(updatedRows, row) { - const existing = updatedRows.find((item) => String(item.b_id) === String(row.b_id)) - if (existing) Object.assign(existing, row) - else updatedRows.push({ ...row }) -} - -/** - * 重排某组兄弟的 b_xh(10 起步、步长 10),并把变化的行收集到 updatedRows。 - * @param {Array} list 兄弟节点数组(就地修改 b_xh) - * @param {Array} updatedRows 变化行收集器 - */ -export function renumberSiblings(list, updatedRows) { - list.forEach((s, i) => { - const nextXh = (i + 1) * 10 - if (s.b_xh !== nextXh) { - s.b_xh = nextXh - addRowUpdate(updatedRows, { b_id: s.b_id, b_xh: nextXh }) - } - }) -} - -/** - * 递归重建菜单树的 b_depth / b_path(b_parent_id 为权威关系,二者为派生字段)。 - * 根节点 b_depth = 0,b_path 形如 /system/user/(节点编码前后均带 /)。 - * 仅在值实际变化时收集,避免打开即脏;父子关系变化后调用即可整树收敛。 - * @param {Array} nodes 树节点数组(就地修改) - * @param {Array} updatedRows 变化行收集器 - * @param {string} [parentPath] 父节点路径 - * @param {number} [depth] 当前层级 - */ -export function rebuildTreePositions(nodes, updatedRows, parentPath = '', depth = 0) { - for (const node of nodes) { - const path = `${parentPath}${node.b_id}/` - if (Number(node.b_depth) !== depth || String(node.b_path ?? '') !== path) { - node.b_depth = depth - node.b_path = path - addRowUpdate(updatedRows, { b_id: node.b_id, b_depth: depth, b_path: path }) - } - if (node.children?.length) rebuildTreePositions(node.children, updatedRows, path, depth + 1) - } -} diff --git a/code/fms/fms-vue/src/views/module/module-management/index.vue b/code/fms/fms-vue/src/views/module/module-management/index.vue index de659091..90ed6c60 100644 --- a/code/fms/fms-vue/src/views/module/module-management/index.vue +++ b/code/fms/fms-vue/src/views/module/module-management/index.vue @@ -34,14 +34,21 @@ import { import { Button, Splitter, SplitterPanel, Tabs, Modal, Message, Spin } from '@/components/ui' import { loadDataApi, saveObjectApi } from '@/services/api' import { - buildTree, - sortTree, - findTreeNode, + addRowUpdate, + applyTreePosition, + buildSortedTree, + decorateTreeNodes, expandAncestors, + findNodePosition, + findTreeNode, insertTreeNode, + rebuildTreePositions, removeTreeNode, + renumberSiblings, + sortTree, } from '@/utils/tree' import { + MODULE_NON_LEAF_TYPES, MODULE_TYPE_ICON_MAP, MODULE_TYPE_LABELS, SCHEMA_TYPES, @@ -54,13 +61,9 @@ import { collectManagedI18nItems, collectSchemaGroupKeys, createDefaultSchema, - decorateModuleTree, - findNodePosition, moduleRowOf, parseSchema, - rebuildTreePositions, reconcileI18nRows, - renumberSiblings, schemaToJsonText, syncModuleItemI18nKeys, } from './utils' @@ -504,7 +507,11 @@ async function loadModuleTree() { moduleTreeLoading.value = true try { const res = await loadDataApi('s_module', '1 = 1', 'b_xh ASC, b_id ASC') - moduleTreeData.value = decorateModuleTree(sortTree(buildTree(res.data || []))) + moduleTreeData.value = decorateTreeNodes(buildSortedTree(res.data || []), { + typeField: 'b_module_type', + nonLeafValues: MODULE_NON_LEAF_TYPES, + iconMap: MODULE_TYPE_ICON_MAP, + }) unsavedModuleIds.clear() } catch (e) { Message.error('加载模块树失败:' + (e?.message || e)) @@ -518,13 +525,9 @@ async function addModule(parentId, newType) { // 未落库节点先用本地临时负数 id 占位,仅用于树 key / 未保存判定,不写入数据库。 const newId = createTempId() const parentNode = parentId ? findTreeNode(moduleTreeData.value, String(parentId)) : null - const depth = parentNode ? Number(parentNode.b_depth || 0) + 1 : 0 - const path = parentNode ? `${String(parentNode.b_path || '')}${newId}/` : `/${newId}/` const newModule = { b_id: newId, b_parent_id: parentId ? String(parentId) : null, - b_depth: depth, - b_path: path, b_name: '', b_i18n: null, b_module_type: newType, @@ -538,8 +541,9 @@ async function addModule(parentId, newType) { b_xh: 10, b_bz: null, // 模块类型强制非叶子,即使暂无子模块也显示折叠箭头 - isLeaf: newType === 'module' ? false : undefined, + isLeaf: MODULE_NON_LEAF_TYPES.includes(newType) ? false : undefined, } + applyTreePosition(newModule, parentNode) insertTreeNode(moduleTreeData.value, newModule, { sort: sortTree }) const ancestors = expandAncestors(moduleTreeData.value, newId) if (ancestors.length) { @@ -653,9 +657,27 @@ async function onModuleDrop({ dragKey, dropKey, dropPosition }) { const dropPos = findNodePosition(moduleTreeData.value, dropId) if (!dragPos || !dropPos) return + const dragNode = dragPos.list[dragPos.index] const dropNode = dropPos.list[dropPos.index] + // 落点不能是被拖节点自身或它的后代:父子关系成环后树与 b_path 都会失真 + if (findTreeNode([dragNode], dropId)) { + Message.warning('不能把模块移动到它自己的下级') + return + } + // 只有 module 类型能承载子模块:data / virtual 是叶子,放入会形成非法层级。 + // 必须在移除被拖节点之前拦截,否则中途 return 会让节点凭空消失。 + if (dropPosition === 'inside' && !MODULE_NON_LEAF_TYPES.includes(dropNode.b_module_type)) { + Message.warning('只能把模块放入模块类型的节点下') + return + } + // 未保存的模块尚未落库,其临时 id 不能作为持久化的 b_parent_id + if (dropPosition === 'inside' && unsavedModuleIds.has(dropId)) { + Message.warning('请先保存目标模块,再调整层级') + return + } const dropParentId = dropNode.b_parent_id ?? null const [moved] = dragPos.list.splice(dragPos.index, 1) + const movedParentId = moved.b_parent_id ?? null const updatedRows = [] const renumberSets = new Set() @@ -681,11 +703,30 @@ async function onModuleDrop({ dragKey, dropKey, dropPosition }) { renumberSets.forEach((list) => renumberSiblings(list, updatedRows)) // 父子关系变化后整树重建 b_depth / b_path,仅收集实际变化的行 rebuildTreePositions(moduleTreeData.value, updatedRows) + // b_parent_id 是权威关系,必须与本节点的派生字段同批落库, + // 否则会留下「路径按新父级、父级列还是旧值」的不一致。 + // 单独补一条:renumberSiblings / rebuildTreePositions 只收集实际变化的字段值, + // 它俩不负责父级列本身。 + if (String(moved.b_parent_id ?? '') !== String(movedParentId ?? '')) { + addRowUpdate(updatedRows, { b_id: moved.b_id, b_parent_id: moved.b_parent_id ?? null }) + } - // 若当前编辑的节点排序号/父级变化,同步到主表草稿 + // 排序即时落库(下方 saveObjectApi),草稿与基线需一起同步: + // 只改 maindata 会让已落库的 b_xh / b_parent_id / b_depth / b_path 被 + // hasUnsavedChanges 误判为未保存改动。未保存草稿节点例外: + // 它还没有基线可比,改动留在草稿里等保存流程处理。 if (maindata.value) { - const draftRow = updatedRows.find((row) => String(row.b_id) === String(maindata.value.b_id)) - if (draftRow) Object.assign(maindata.value, draftRow) + const draftId = String(maindata.value.b_id) + const draftUnsaved = unsavedModuleIds.has(draftId) + const syncBaseline = !draftUnsaved && !!maindata_org.value + const draftRow = updatedRows.find((row) => String(row.b_id) === draftId) + if (draftRow) { + for (const [field, value] of Object.entries(draftRow)) { + if (field === 'b_id') continue + maindata.value[field] = value + if (syncBaseline) maindata_org.value[field] = value + } + } } if (!updatedRows.length) return diff --git a/code/fms/fms-vue/src/views/module/module-management/utils.js b/code/fms/fms-vue/src/views/module/module-management/utils.js index 2c4c058c..9d950ebf 100644 --- a/code/fms/fms-vue/src/views/module/module-management/utils.js +++ b/code/fms/fms-vue/src/views/module/module-management/utils.js @@ -15,6 +15,9 @@ export const MODULE_TYPE_LABELS = { virtual: '虚拟模块', } +/** 模块树里能承载子模块的类型:data / virtual 是叶子节点 */ +export const MODULE_NON_LEAF_TYPES = ['module'] + /** 工具栏 / 右键「新增」菜单项(key 即 b_module_type) */ export const MODULE_ADD_OPTIONS = [ { key: 'module', label: '新增模块', icon: FolderPlus }, @@ -51,20 +54,6 @@ export { schemaToJsonText, } from '@/utils/schemaJson' -/** - * 模块类型装饰:module 类型默认可展开(显示折叠箭头,即使暂无子模块),并附加类型图标。 - * 就地修改并返回原数组。 - */ -export function decorateModuleTree(nodes) { - for (const node of nodes) { - if (node.b_module_type === 'module') node.isLeaf = false - const icon = MODULE_TYPE_ICON_MAP[node.b_module_type] - if (icon) node.icon = icon - if (node.children) decorateModuleTree(node.children) - } - return nodes -} - /** 从树节点提取纯模块行(剔除 children/isLeaf/icon 等树注入字段),供未落库模块切回时重建主表草稿 */ export function moduleRowOf(node) { const row = {} @@ -72,60 +61,6 @@ export function moduleRowOf(node) { return row } -/** - * 在树中查找节点所在的父数组及索引。 - * @returns {{ list: Array, index: number }|null} - */ -export function findNodePosition(nodes, id) { - for (let i = 0; i < nodes.length; i++) { - if (String(nodes[i].b_id) === String(id)) return { list: nodes, index: i } - if (nodes[i].children) { - const found = findNodePosition(nodes[i].children, id) - if (found) return found - } - } - return null -} - -/** 向 updatedRows 合并一行变更(同一 b_id 累加字段,避免同节点产生多条 update 请求) */ -function addRowUpdate(updatedRows, row) { - const existing = updatedRows.find((item) => String(item.b_id) === String(row.b_id)) - if (existing) Object.assign(existing, row) - else updatedRows.push({ ...row }) -} - -/** - * 重排某组兄弟的 b_xh(10 起步、步长 10),把变化的行收集到 updatedRows。 - * @param {Array} list 兄弟节点数组(就地修改 b_xh) - * @param {Array} updatedRows 变化行收集器 - */ -export function renumberSiblings(list, updatedRows) { - list.forEach((sibling, index) => { - const nextXh = (index + 1) * 10 - if (sibling.b_xh !== nextXh) { - sibling.b_xh = nextXh - addRowUpdate(updatedRows, { b_id: sibling.b_id, b_xh: nextXh }) - } - }) -} - -/** - * 递归重建模块树的 b_depth / b_path(b_parent_id 为权威关系,二者为派生字段)。 - * 根节点 b_depth = 0,b_path 形如 /sea/container/(节点编码前后均带 /)。 - * 仅在值实际变化时收集,避免打开即脏;父子关系变化后调用即可整树收敛。 - */ -export function rebuildTreePositions(nodes, updatedRows, parentPath = '', depth = 0) { - for (const node of nodes) { - const path = `${parentPath}${node.b_id}/` - if (Number(node.b_depth) !== depth || String(node.b_path ?? '') !== path) { - node.b_depth = depth - node.b_path = path - addRowUpdate(updatedRows, { b_id: node.b_id, b_depth: depth, b_path: path }) - } - if (node.children?.length) rebuildTreePositions(node.children, updatedRows, path, depth + 1) - } -} - /* ================= 多语言资源键(s_i18n,主键 b_key + b_locale) ================= */ export function buildModuleItemI18nKey(prefix, moduleCode, itemCode) { diff --git a/code/fms/fms-vue/tests/components/tree.spec.js b/code/fms/fms-vue/tests/components/tree.spec.js index 73aac03a..bb2695f5 100644 --- a/code/fms/fms-vue/tests/components/tree.spec.js +++ b/code/fms/fms-vue/tests/components/tree.spec.js @@ -88,3 +88,30 @@ describe('Tree 右键菜单', () => { wrapper.unmount() }) }) + +describe('Tree 行内元素顺序', () => { + /** + * 回归点:箭头槽(非叶为真箭头、叶子为等宽占位)必须排在拖拽手柄之前。 + * 放到手柄之后时,叶子行的手柄与标题之间会空出一整个箭头宽,读起来像多余空隙; + * 排在前面则这段空白归入左侧缩进带,手柄紧跟标题(见 tree-node.vue 模板注释)。 + */ + const rowClasses = (row) => [...row.element.children].map((el) => el.className) + + it('拖拽开启时:箭头槽在拖拽手柄之前', () => { + const wrapper = mount(Tree, { props: { treeData, fieldNames, draggable: true } }) + const parentRow = wrapper.findAll('.tree-node')[0] + expect(rowClasses(parentRow)).toEqual(['tree-node-switcher', 'tree-node-grip', 'tree-node-content']) + }) + + it('叶子行的占位与手柄顺序一致,保证标题横坐标与非叶兄弟相同', () => { + const wrapper = mount(Tree, { + props: { treeData, fieldNames, draggable: true, defaultExpandedKeys: ['1'] }, + }) + const childRow = wrapper.findAll('.tree-node')[1] + expect(rowClasses(childRow)).toEqual([ + 'tree-node-switcher is-placeholder', + 'tree-node-grip', + 'tree-node-content', + ]) + }) +}) diff --git a/code/fms/fms-vue/tests/unit/module-picker-modal.spec.js b/code/fms/fms-vue/tests/unit/module-picker-modal.spec.js new file mode 100644 index 00000000..227d2bee --- /dev/null +++ b/code/fms/fms-vue/tests/unit/module-picker-modal.spec.js @@ -0,0 +1,84 @@ +/** + * 模块绑定弹层(ModulePickerModal):模块树默认全部收起。 + * + * 回归点:弹层曾用 default-expanded-keys 展开第一层根节点,一打开就铺开整棵树; + * 现在不设该 prop,初始只渲染根节点,子节点由用户按需逐层点开。 + * 折叠态下子树不渲染(tree-node.vue 用 v-if 而非 v-show),因此可直接数 treeitem。 + */ +import { beforeEach, describe, expect, it } from 'vitest' +import { flushPromises, mount } from '@vue/test-utils' +import { nextTick } from 'vue' +import ModulePickerModal from '@/views/module/menu-management/ModulePickerModal.vue' + +const TREE = [ + { + b_id: 'base', + b_module_type: 'module', + b_name: '基础资料', + children: [ + { + b_id: 'base_otherdata', + b_module_type: 'module', + b_name: '其他数据', + children: [{ b_id: 'b_port', b_module_type: 'data', b_name: '港口' }], + }, + ], + }, + { + b_id: 'system', + b_module_type: 'module', + b_name: '系统', + children: [{ b_id: 'b_user', b_module_type: 'data', b_name: '用户' }], + }, +] + +const MODULE_TYPES = { + base: 'module', + base_otherdata: 'module', + b_port: 'data', + system: 'module', + b_user: 'data', +} + +async function mountModal(props = {}) { + const wrapper = mount(ModulePickerModal, { + props: { + open: true, + treeData: TREE, + checkedKeys: [], + moduleTypes: MODULE_TYPES, + ...props, + }, + // Modal 用 Teleport 挂到 body;打桩后内容留在组件树内,便于断言 + global: { stubs: { teleport: true } }, + }) + await flushPromises() + await nextTick() + return wrapper +} + +beforeEach(() => { + localStorage.clear() + sessionStorage.clear() +}) + +describe('模块绑定弹层:树默认收起', () => { + it('初始只渲染根节点,没有任何节点处于展开态', async () => { + const wrapper = await mountModal() + + const items = wrapper.findAll('[role="treeitem"]') + expect(items).toHaveLength(TREE.length) + expect(items.map((item) => item.attributes('aria-expanded'))).toEqual([ + 'false', + 'false', + ]) + }) + + it('折叠态下子节点不渲染', async () => { + const wrapper = await mountModal() + + expect(wrapper.text()).toContain('基础资料') + expect(wrapper.text()).not.toContain('其他数据') + expect(wrapper.text()).not.toContain('港口') + }) +}) diff --git a/code/fms/fms-vue/tests/unit/othercompany-utils.spec.js b/code/fms/fms-vue/tests/unit/othercompany-utils.spec.js index 23c149c0..c02c43b6 100644 --- a/code/fms/fms-vue/tests/unit/othercompany-utils.spec.js +++ b/code/fms/fms-vue/tests/unit/othercompany-utils.spec.js @@ -1,11 +1,9 @@ /** - * 往来单位分类树页面级纯函数。 + * 往来单位分类树的页面级纯函数(仍内联在 views/base/othercompany/index.vue)。 * - * 页面单文件自包含后(分类树逻辑内联在 views/base/othercompany/index.vue), - * 纯函数不再单独导出,测试通过挂载页面组件访问