# EnforcementCode-NOS-2.0 开发说明(非现场执法) ## 背景与术语约定 | 术语 | 含义 | 维护团队 / 仓库 | | ---- | ---- | ----------------- | | **现场执法** | 执法人员到现场对企业进行检查、处罚等业务,**当前团队正在开发的执法码主体项目** | 现有团队 · 现场执法主仓库 | | **非现场执法(NOS)** | 不走到现场也能完成的执法监管场景(企业自查、远程抽查、数据上报等),**新进团队准备开发的业务**;本仓库即为此部分代码 | 新进团队 · 本仓库(EnforcementCode-NOS-2.0) | *后续所有nos表示的都是非现场执法相关的内容。 ### 为什么单独建仓库开发 * 现场执法仓库由现有团队持续迭代。 * 先在独立仓库中完成后端模块与前端 H5 工程,可以避免两边代码冲突、快速迭代。初步开发完成后,再合并入现场执法的代码库里边。 * 后端在开发阶段复用现场执法的启动工程([`zdxt-admin-web-server`](zdxt-web-server/zdxt-admin-web-server))、公共 jar(`zdxt-common-*` / `zdxt-system`)以及部分现场执法的表(以视图方式引入),保证技术栈、权限体系、Sa-Token 认证与主项目一致。 ### 最终合入路径 非现场执法开发完成后,**代码与数据库均需合入现场执法**: * **代码**:[`zdxt-modules/zdxt-enforcement-nos`](zdxt-modules/zdxt-enforcement-nos) 主体拷入现场执法后端仓库的 `zdxt-modules/`下;三个前端工程按照企业端,执法端,监督端,手工合并现场执法的的三端下。 * **数据库**:开发阶段使用 227 上的 `zfjd2.0-nos` 独立库(以视图方式复用现场执法部分表);**部署时合并为一个库**,现场与非现场使用同一个数据库。为避免表名冲突,本仓库所有新增表以 `nos_` 前缀区分。 仓库定位:在不污染现场执法主仓库的前提下,先行独立完成非现场执法相关的后端模块与三个前端 H5 工程的开发,再统一合并。 --- ## 一、代码仓库 ``` http://146.56.199.25:3000/zhuibobo/EnforcementCode-NOS-2.0.git ``` --- ## 二、第三方 Jar 安装 * 使用 [`Libs/install-to-maven.ps1`](Libs/install-to-maven.ps1) 将 `Libs/` 目录下的 `zdxt-common-*.jar`、`zdxt-system-*.jar` 等 jar 包注册到本地 Maven 仓库。 * 后续如有 jar 包更新(版本不变),**重新执行脚本即可**,执行脚本时会直接覆盖本地仓库中的同版本 jar。覆盖后建议在 IDE 中刷新 Maven(IDEA:右键项目 → Maven → Reload Project),确保 IDE 加载到最新的 jar。如果无效清理idea工具的项目缓存。 ### Windows 下使用方法 **前置条件**:已安装 Maven,且 `mvn` 命令已加入系统 PATH。 1. 打开 **PowerShell**(Win + R → 输入 `powershell` → 回车)。 2. 进入 `Libs` 目录并执行脚本: ```powershell cd D:\EnforcementCodeProject\CodeV2\EnforcementCode-NOS-2.0\Libs .\install-to-maven.ps1 ``` 3. 如果提示 **"无法加载文件...因为在此系统上禁止运行脚本"**,先执行以下命令放开执行策略,再重新运行脚本: ```powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned ``` 4. 脚本会逐个安装 jar,安装成功显示绿色 `OK`,失败显示红色 `FAILED`,全部完成后输出 `All done.`。 --- ## 三、数据库 ### 1. 开发数据库 * 开发阶段统一使用 `227` 服务器上的数据库 `zfjd2.0-nos` 作为开发库。 账号密码:`zfjd-nos/zfjd@nos` * 通过 **视图(VIEW)** 的方式从现场执法库引入以下表,仅做数据读取的功能,如需要修改其中的表,或者添加新的表,与现场执法团队进行讨论: ``` efcode_virtual_unit srz_dept srz_user sys_client sys_dept sys_menu sys_post sys_role sys_user sys_user_post sys_user_role ``` * 如果现场执法团队需要使用非现场执法团队建立的表,同样通过 **视图(VIEW)** 的方式从非现场执法库引入,仅做数据读取的功能,如需要修改其中的表,或者添加新的表,与非现场执法团队进行讨论: * 部署测试时,**现场执法**与**非现场执法**两个库会合并为同一个库。 ### 2. 表结构维护 * 所有 DDL/DML 全部以 SQL 形式存放在 [`script/sql/nos`](script/sql/nos) 目录下,方便后续脚本化重建数据库。 * **命名规范**:非现场执法的所有表必须以 `nos_` 开头,方便后续与现场执法表区分。 * **注释规范**:所有表和字段必须添加中文 `comment` 说明。 * **租户排除**:所有 `nos_` 开头的表都需加入 [`application.yml`](zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml) 的多租户排除表配置中: ```yaml # 多租户配置 tenant: # 是否开启 enable: false # 排除表 excludes: - sys_menu - sys_tenant - sys_tenant_package # 在此追加 nos_ 开头的表名 ``` ### 3. 公共字段(所有 nos_ 开头表必须包含) 每张非现场表必须包含以下统一字段,以满足审计、租户、逻辑删除等通用能力: | 字段名 | 类型 | 说明 | | -------------- | -------------- |--------------------| | `id` | varchar(50) | 主键 | | `create_dept` | varchar(100) | 创建部门Id | | `create_by` | varchar(50) | 创建人Id | | `create_time` | datetime | 创建时间 | | `update_by` | varchar(50) | 更新人Id | | `update_time` | datetime | 更新时间 | | `is_delete` | char(1) | 逻辑删除:`0` 否 / `1` 是 | | `bureau_dept_id` | varchar(100) | 执法单位ID | | `dept_id` | varchar(100) | 部门ID | | `organ_id` | varchar(100) | 组织单位ID | > 实体类继承 `TenantEntity` / `BaseEntity` 即可自动包含上述字段。 ``` * bureau_dept_id:执法单位ID(当前用户所在的执法单位的id)=DataFilterUtil.getUserBureauDeptId * dept_id:部门ID(当前用户所在的部门id)=DataFilterUtil.getUserDeptId * organ_id:组织单位ID (当前用户所在的组织单位id)=DataFilterUtil.getUserOrganId * 对应的名称作为可选项,自行决定加不加 ``` --- ## 四、后端 ### 1. 模块位置 * 所有非现场执法的后端代码统一写入: * [`zdxt-modules/zdxt-enforcement-nos`](zdxt-modules/zdxt-enforcement-nos) * 该模块由主工程 [`zdxt-web-server/zdxt-admin-web-server`](zdxt-web-server/zdxt-admin-web-server) 引入并打包启动。 ### 2. 后端模块目录规范 模块根包:`com.zdxt.enforcementnos`,各子目录职责如下: 建表后可以使用代码生成工具,与现场执法团队沟通,将表通过视图引入到现场执法库中或者将sql提供给现场执法团队直接在数据库中创建,然后再生成代码 ``` zdxt-modules/zdxt-enforcement-nos/src/main/java/com/zdxt/enforcementnos/ ├── Main.java # 模块入口占位类(仅用于包扫描错误提示,不包含 main 方法) ├── common/ # 模块内部公共类集合(仅 nos 业务复用) │ ├── commonenum/ # 业务枚举:状态、类型、扣分项等 │ ├── config/ # 模块内业务配置类(例如自定义 ConfigurationProperties、常量配置) │ └── util/ # 业务工具类(状态转换、字段补齐等,区别于全局 zdxt-common-core 工具) ├── config/ # Spring 配置类(@Configuration):Bean 装配、MyBatis-Plus、拦截器等 ├── controller/ # REST 接口层,路由前缀统一 `/enforcementcode/xxx` │ ├── app/ # 移动端(H5)接口 │ │ ├── enforcement/ # 执法端接口 │ │ └── enterprise/ # 企业端接口 │ ├── web/ # PC 管理端接口 │ │ └── supervision/ # 监督端接口 │ └── TestEnfDemoController.java ├── domain/ # 领域对象 │ ├── TestEnfDemo.java # 实体(PO),客户端与服务端交互时,不要直接传递数据库对应的实体,对应数据库 `nos_xxx` 表,继承 TenantEntity / BaseEntity │ ├── bo/ # 业务入参对象,客户端传递给服务器的实体对象(Business Object):新增/修改/查询参数 │ └── vo/ # 出参对象,服务端传递给客户端的实体对象(View Object):返回给前端的结构 ├── mapper/ # MyBatis-Plus Mapper 接口,继承 BaseMapperPlus │ └── TestEnfDemoMapper.java ├── service/ # 业务服务接口(`I*Service`) │ ├── ITestEnfDemoService.java │ └── impl/ # 服务实现类(`*ServiceImpl`) │ └── TestEnfDemoServiceImpl.java ├── exception/ # 模块专属业务异常类(继承 ServiceException) ├── handler/ # 处理器:全局异常处理、枚举字典处理、数据权限处理等 ├── helper/ # 业务辅助类(复杂逻辑抽取,供 service 调用,区别于 util 是带业务上下文的) ├── listener/ # 事件监听:Spring Event、MQ消费、MyBatis-Plus 填充、Excel 导入监听等 ├── quartz/ # 定时任务(Quartz Job),接入公共模块 zdxt-common-quartz ├── runner/ # ApplicationRunner / CommandLineRunner:启动后需要执行一次的初始化逻辑 └── util/ # 顶层通用工具类(不依赖 Spring 上下文) ``` #### 分层写入规定 | 场景 | 应放入的包 | | -------------------------- | --------------------------------------------- | | 接收 HTTP 请求 | `controller` | | 表字段映射 | `domain` (`TestEnfDemo.java`) | | 接收前端入参 | `domain.bo` | | 返回前端出参 | `domain.vo` | | 数据库访问 | `mapper` | | 业务逻辑接口 | `service` | | 业务逻辑实现 | `service.impl` | | 复杂业务子步骤抽取 | `helper` | | Bean 装配 / 拦截器注册 | `config` | | 业务枚举、常量 | `common.commonenum`、`common.config` | | 不依赖 Spring 的工具 | `util`(顶层) | | 依赖 Spring 的业务工具 | `common.util` | | 全局异常 / 字典填充 | `handler` | | 事件监听 | `listener` | | 定时任务 | `quartz` | | 启动后一次性执行逻辑 | `runner` | | 业务级异常 | `exception` | > 约定:不要在 `zdxt-enforcement-nos` 中创建与主工程同名的包名,使用默认的包名com.zdxt.enforcementnos。 ### 3. 服务端口与上下文路径 * 启动 profile:`application.yml,dev,dev-nos-ep`(即 [`application-dev-nos-ep.yml`](zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev-nos-ep.yml)) * 端口:`18005` * `context-path`:`/enf-ep` * 因此后端实际接收路径形如:`http://127.0.0.1:18005/enf-ep/enforcementcode/enfDemo/list` ### 4. 接入方法(接口设计原则) * 现场执法对外提供给非现场执法调用的接口,所需的参数 **必须通过接口参数传递**,不要凭借 token 去查询用户/部门等数据。 * 非现场执法自身的接口,**只验证 token 的有效性**,暂时不再做权限码(`@SaCheckPermission`)等其他权限校验,等后续合并阶段再统一处理。 * 如果非现场执法前端需要调用现场执法的后接口,通过token进行调用. * 如果非现场执法后端需要调用现场执法后端的接口(尽量避免),在前端进入后,先请求一次后端,将 token 提交给后端,后端自行存入当前 session,本次 session 中凭该 token 调用后端接口。 ### 5. clientid 必须一致 后端 Sa-Token 在登录时会把请求 Header 中的 `clientid` 与 token 绑定,之后每次请求都会校验。**前端 axios 拦截器中固定写入的 `clientid` 必须与后端 `sys_client` 表中已注册的指纹完全一致**,否则会出现: ``` 认证失败:客户端ID与Token不匹配 ``` 参考管理端 `e5cd7e4891bf95d1d19206ce24a7b32e`(开发环境),三个前端 H5 工程建议在 `.env.*` 中配置 `VITE_APP_CLIENT_ID`,并在 `http.js` 中通过 `import.meta.env.VITE_APP_CLIENT_ID` 读取。 ### 6. Redis 共享(Token 跨系统互认) * **现场执法**与**非现场执法**两个后端服务必须连接 **同一个 Redis 实例(同一个 host + port + database)**。 * Sa-Token 默认把 `Token` 与登录会话信息存放在 Redis 中。共用 Redis 后,用户在任意一端登录拿到的 Token,在另一端的接口(同样的 `clientid`、同样的 Sa-Token 配置)都能直接通过认证,无需重复登录。 * 由此带来的效果: * 前端在现场执法登录后,凭同一个 Token 既可调用 `/api`(现场执法)也可调用 `/nosapi`(非现场执法); * 非现场执法后端如需反向调用现场执法接口,也能直接复用前端透传的 Token。 * 配置位置:[`application-dev-nos-ep.yml`](zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev-nos-ep.yml) 中的 `spring.data.redis` 配置必须与现场执法环境保持一致(同一台 Redis、同一个 database 索引)。 * **注意事项**: * 两边的 Sa-Token 配置(`token-name`、`token-prefix`、`is-share`、`is-concurrent` 等)需保持一致,否则同一 Token 可能在另一端被识别为非法。 --- ## 五、前端 ### 1. 三个独立工程 非现场执法前端拆分为 **3 个独立的 Vue 3 + Vite H5 工程**,相互不依赖: | 角色 | 工程路径 | 端口 | | ------------ | ------------------------------------------------------------------------------------------- |-------| | 企业端 | [`zdxt-efcode-nos-enterprise-app`](zdxt-web-client/zdxt-efcode-nos-enterprise-app) | 12001 | | 执法端 | [`zdxt-efcode-nos-enforcement-app`](zdxt-web-client/zdxt-efcode-nos-enforcement-app) | 12002 | | 监督端 | [`zdxt-admin-nos-plus-ui`](zdxt-web-client/zdxt-efcode-nos-supervision-app) | 12005 | > 实际端口以各工程 `vite-config/index.js` 与 `vite.config.js` 中 `server.port` 为准。 ### 2. 非现场执法代码统一放入 `nos/` 子目录 为了后续合入现场执法时能一眼辨识哪些是本期非现场执法新增代码,**三个前端工程内所有非现场执法新增的文件必须放入名为 `nos` 的子目录**。现有的 `nos/` 目录清单如下: #### `zdxt-admin-nos-plus-ui`(PC 管理端,参考用) ``` src/views/efcode/nos/ ``` #### `zdxt-efcode-nos-enforcement-app`(执法端 H5) ``` src/api/nos/ src/assets/nos/ src/components/nos/ src/router/nos/ src/views/enforcement/nos/ ``` #### `zdxt-efcode-nos-enterprise-app`(企业端 H5) ``` src/api/nos/ src/assets/nos/ src/components/nos/ src/router/nos/ src/store/nos/ src/util/nos/ src/views/enterprise/nos/ ``` > 约定:凡是本期非现场执法新写的 `*.vue / *.js / *.ts / 图片 / less` 等,都必须放在上述对应目录下的 `nos/` 里,不混入现场执法原有目录。合并时只需拷入主仓库对应目录的 `nos/` 下。 ### 3. 启动命令 ```bash cd zdxt-web-client/zdxt-efcode-nos-enforcement-app npm install npm run dev # 开发模式(加载 vite-env/.env.enforcement-dev) npm run build # 生产打包(加载 vite-env/.env.enforcement-prod) ``` ### 4. 请求前缀约定 | 前缀 | 含义 | 转发目标 | | ---------- | ----------------------------- | ------------------------------------- | | `/api` | 现场执法接口 | 现场执法后台 | | `/nosapi` | 非现场执法接口 | 现场执法后台(合并部署后) / 本地后端 | * **开发期**通过 Vite 代理转发;**生产期**通过 NG(Nginx)代理转发,前缀保持一致。 ### 5. 开发期登录流程 ```mermaid graph TD A[开发时 App 登录页面] -->|Vite 代理转发登录请求| B["现场执法后台 /api 前缀请求(现场执法)"] B -->|返回访问 Token| C[后续接口访问] C --> D["/api 前缀请求(现场执法)"] C --> E["/nosapi 前缀请求(非现场执法)"] D -->|Vite 代理转发| F[现场执法后台] E -->|Vite 代理转发| F ``` ### 6. 部署期免登录流程 从主页面进入非现场页面时,会携带 token,凭借 token 可以调用监督码后台接口获取相关信息: ```mermaid graph TD A[从主页面进入非现场的页面时] -->|携带 token 进入| B["路由守卫拦截,获取 token,存入 $globalStore"] B -->|返回访问 Token| C[后续接口访问] C --> D["/api 前缀请求(现场执法)"] C --> E["/nosapi 前缀请求(非现场执法)"] D -->|NG 代理转发| F[现场执法后台] E -->|NG 代理转发| F ``` --- ## 六、代码生成 * 将设计好的表 **创建到执法码二期的库** 中,或者通过视图引入,使用现有的代码生成模块即可生成统一风格的 CRUD 代码,便于后续维护。 * **包名设置**:现场执法的监督端在代码生成模块中导入表后,点击「编辑」→「生成信息」,将包名修改为: ``` com.zdxt.enforcementnos ``` 这样生成的代码与现有 `zdxt-modules/zdxt-enforcement-nos` 的目录结构一致,可直接拷贝过来使用。 --- ## 七、合入主仓库的步骤 ### 1. 前端 3 个前端工程独立合并,每个都按以下步骤来一次: * [`zdxt-efcode-nos-enterprise-app`](zdxt-web-client/zdxt-efcode-nos-enterprise-app) * [`zdxt-efcode-nos-enforcement-app`](zdxt-web-client/zdxt-efcode-nos-enforcement-app) * [`zdxt-admin-nos-plus-ui`]([zdxt-admin-nos-plus-ui](zdxt-web-client/zdxt-admin-nos-plus-ui)) 操作要点: 1. 拷贝vue和资源文件 2. 同步 `vite-env/.env.*`、`vite-config/index.js` 中的代理与端口配置。 3. 把 `clientid` 改为现场执法环境对应的指纹值。 4. 检查路由前缀(`/enforcement`、`/enterprise`、`/supervision`)是否与现场执法已有路由冲突。 ### 2. 后端 * 将 [`zdxt-modules/zdxt-enforcement-nos`](zdxt-modules/zdxt-enforcement-nos) 模块整体拷贝到现场执法的 `zdxt-modules` 目录下。 * 在现场执法的 `zdxt-web-server/zdxt-admin-web-server/pom.xml` 中加入对该模块的依赖。 * 把 `nos_` 开头的表 SQL([`script/sql/nos`](script/sql/nos))合入现场执法的数据库初始化脚本。 * 把 `application.yml` 中的租户排除表条目合入。 * 验证 Sa-Token 全局拦截器对 `/enforcementcode/**` 路径的放行规则。 ### 3. 合并验证清单 - [ ] 三端 H5 可独立启动并成功登录 - [ ] `/api` 与 `/nosapi` 接口均能正常返回 - [ ] 后端 `nos_` 表无与现场执法表的命名冲突 - [ ] 多租户字段在 `nos_` 表上不会被误注入 - [ ] `clientid` 与 `sys_client` 表数据一致 --- ## 八、目录速查 ``` EnforcementCode-NOS-2.0 ├── Libs/ # 第三方私有 jar ├── script/sql/nos/ # 非现场表 SQL ├── zdxt-modules/zdxt-enforcement-nos/ # 后端业务模块 ├── zdxt-web-server/zdxt-admin-web-server/ # 后端启动工程(端口 18005, ctx /enf-ep) └── zdxt-web-client/ ├── zdxt-admin-nos-plus-ui/ # 监督端(PC,参考用) ├── zdxt-efcode-nos-enterprise-app/ # 企业端 H5 ├── zdxt-efcode-nos-enforcement-app/ # 执法端 H5 ```