# mjava-minglei 明磊业务集成服务。工程基于 Spring Boot 构建,通过统一的 `malk:mjava` 客户端对接钉钉和宜搭,同时提供明磊内部外部接口的转发能力。 ## 1. 技术栈 | 技术 | 版本或用途 | | --- | --- | | Java | JDK 8 | | Spring Boot | 2.7.18 | | Spring MVC | REST Controller、参数绑定和异常处理 | | Spring Validation | `@Valid`、`@NotBlank`、`@NotEmpty` 等请求校验 | | Maven | 项目构建和依赖管理 | | `com.malk:mjava` | 统一的钉钉、宜搭客户端和数据模型,当前版本 0.0.3 | | Jackson / Fastjson | JSON 请求绑定、序列化和宜搭字段数据组装 | | RestTemplate | 调用明磊外部 HTTP 接口 | | Logback + SLF4J | 控制台、应用文件和错误文件日志 | | JUnit 5 + Mockito | 单元测试和客户端调用测试 | ## 2. 工程架构 工程采用典型的 Controller - Service - Impl 分层结构: ```text MjavaMingleiApplication | v Controller 层 接收 HTTP 请求、校验参数、返回 HTTP 响应 | v Service 接口层 定义业务能力和模块边界 | v ServiceImpl 层 编排宜搭、钉钉或外部 T100 调用 | +--> malk YDClient / DDClient +--> DingTalk Open Platform +--> 明磊外部 T100 HTTP 接口 ``` ### 2.1 目录职责 ```text src/main/java/com/malk/minglei ├── MjavaMingleiApplication.java Spring Boot 启动类 ├── config Bean、配置属性、HTTP 日志过滤器 ├── controller REST 接口和局部异常处理 ├── dto 请求模型和统一响应模型 └── service ├── *.java 业务接口 └── impl 业务实现和外部系统调用 src/main/resources ├── application.yml 服务、钉钉、宜搭、外部接口配置 └── logback-spring.xml 日志输出和滚动策略 src/test/java 单元测试和集成上下文测试 ``` ### 2.2 配置和客户端装配 `MjavaYidaConfiguration` 注册两套独立的宜搭客户端: - `coordinationOfficeYidaClient`:明磊锂能协调办公应用。 - `masterDataYidaClient`:明磊锂能应用主数据。 两套客户端使用各自的 `appType` 和 `systemToken`,业务实现通过 `@Qualifier` 选择,避免多个 `YDClient` Bean 注入时产生歧义。钉钉客户端由 malk 包提供,宜搭客户端内部通过 `YDClientImpl` 调用表单和流程接口。 ## 3. 业务模块 ### 3.1 宜搭统一流程发起 相关类:`YidaProcessController`、`YidaProcessService`、`YidaProcessServiceImpl`、`YidaProcessHandler`、`BomYidaProcessHandler`。 统一入口根据请求体的 `type` 分发处理器。`type` 的业务含义是目标宜搭表单的 `formUuid`,不是自定义的业务名称。例如 BOM 表单使用: ```text FORM-04001902BF9C40E6859102569FE139179LWH ``` 分发前会去除首尾空格并转为大写匹配。新增表单时,应新增一个 `YidaProcessHandler` 实现类,并让 `getType()` 返回该表单的 `formUuid`;表单 ID、流程编码和应用凭证由实现类或配置管理,不由调用方传入。 处理流程如下: 1. Controller 使用 `@Valid` 校验请求体。 2. `YidaProcessServiceImpl` 根据规范化后的表单 ID 查找处理器。 3. 具体处理器组装 `YidaOperationParam`。 4. 通过对应应用的 `YDClient.operateData(..., FORM_OPERATION.start)` 发起宜搭流程。 5. Controller 返回宜搭流程实例 ID。 ### 3.2 明磊主数据表单查询 相关类:`MingleiDocumentService`、`MingleiDocumentServiceImpl`、`YidaProcessController`。 主数据查询固定使用主数据应用客户端和目标表单 ID。支持两种查询方式: - 分页查询:返回宜搭查询结果中的 `records`、`totalCount`、`pageNumber`。 - 精准查询:条件优先级为 `dept_id` > `user_id` > `dept_name`。传入 `user_id` 时,先调用钉钉通讯录接口取得员工所属部门,再按部门 ID 查询。查询成功后仅提取一级至四级部门主管和部门分管领导字段对应的钉钉 `userId`。 ### 3.3 部门成员查询 `MingleiDocumentServiceImpl.listDepartmentUserIds` 先取得钉钉访问令牌,再按 `type` 决定查询范围:`0` 获取指定部门及所有子部门,`1` 仅获取指定部门;随后逐个读取部门成员并按首次出现顺序去重,返回用户 ID 列表。 ### 3.4 明磊外部 T100 接口转发 相关类:`MingleiPortController`、`MingleiPortService`、`MingleiPortServiceImpl`。 该模块是本服务提供的本地触发入口,收到精简的 `head` 和 `detail` 后,由服务端补齐固定的外层协议结构,再通过 `RestTemplate` 调用明磊外部接口。连接超时为 10 秒,读取超时为 30 秒,目标 URL 和接入密钥通过配置注入。 ## 4. REST 接口 ### 4.1 宜搭流程发起 ```http POST /api/yida/process-instances Content-Type: application/json ``` 请求示例: ```json { "type": "FORM-04001902BF9C40E6859102569FE139179LWH", "userId": "dingTalkUserId", "dataTitle": "流程标题", "fields": { "textField_xxx": "字段值" } } ``` `userId` 也可使用请求别名 `originatorUserId`。`fields` 的键必须是目标宜搭表单真实存在的字段 ID。成功返回 HTTP `201 Created`: ```json { "code": 200, "isSuccess": true, "result": { "processInstanceId": "宜搭流程实例 ID" } } ``` 该接口使用统一的 `ApiResponse` 包装格式,流程实例 ID 位于 `result.processInstanceId`。 ### 4.2 分页查询主数据单据 ```http GET /api/yida/minglei/forms/instances?page=1&size=20 ``` `page` 从 1 开始,`size` 范围为 1 至 100。成功响应为宜搭查询结果的服务端整理结构: ```json { "records": [], "totalCount": 0, "pageNumber": 1 } ``` ### 4.3 精准查询主数据单据 ```http POST /api/yida/minglei/forms/instances/query Content-Type: application/json ``` 请求示例: ```json { "dept_id": "123456", "user_id": "dingTalkUserId", "dept_name": "明磊锂能技术部" } ``` 返回示例: ```json { "employeeField_mszdqrl8": "一级部门主管 userId", "employeeField_mszdqrl9": "二级部门主管 userId", "employeeField_mszdqrla": "三级部门主管 userId", "employeeField_mszdqrlb": "四级部门主管 userId", "employeeField_mszdqrl3": "部门分管领导 userId" } ``` ### 4.4 查询部门及子部门全部成员 ```http POST /api/yida/minglei/departments/users Content-Type: application/json ``` 请求体: ```json { "dept_Id": "123456", "type": 0 } ``` `type` 为必填字段:`0` 表示穿透查询子部门,`1` 表示仅查询指定部门。 成功响应统一为 `code`、`isSuccess`、`result` 三层结构: ```json { "code": 200, "isSuccess": true, "result": [ "449856319", "715357119" ] } ``` ### 4.5 转发供应商价格审批接口 ```http POST /api/minglei/ports/supplier-price-approvals Content-Type: application/json ``` 请求体只需要提供协议中的 `head` 和 `detail`: ```json { "head": { "pmdidocno": "CH3", "pmdi001": "N", "pmdi034": "605188", "pmdidocdt": "2026-08-06", "pmdi004": "517022", "pmdi015": "2026-08-06", "pmdi016": "2027-06-01", "pmdi003": "A043", "pmdi002": "210721017", "pmdiud001": "N", "pmdistus": "I" }, "detail": [ { "pmdj011": "0.5000", "pmdj002": "524000059517022000000", "pmdj030": "" } ] } ``` 服务端会自动补齐 `type`、`host`、`service`、`datakey`、`payload` 等固定协议字段,并返回外部接口的原始 JSON 响应。 ### 4.6 接收 ERP 推送宜搭数据 ```http POST /api/erp/yida/push Content-Type: application/json ``` 当前阶段只接收并记录 ERP 推送数据,不调用宜搭、不发起流程、不落库。`type` 必须非空,当前允许任意单据类型;例如 `cpmp402` 表示核价通知流程。 请求体: ```json { "type": "cpmp402", "mainData": { "rq": "", "bm": "", "tjr": "ERP_USER_001", "gs": "", "t100id": "", "ent": "", "t100bm": "" }, "detailData": [ { "hjdh": "", "gysbh": "", "gysmc": "", "t100id": "", "ent": "", "yy": "" } ] } ``` `mainData` 为固定主数据结构,其中 `tjr` 必填;`detailData` 必须存在,但不校验具体 JSON 类型,可传数组或对象(包括外层再包一层 `detailData` 的对象结构)。接口成功返回基座 `McR.success()`,Service 会记录请求和响应 JSON 日志。 ## 5. 统一响应和异常处理 新增接口优先使用 `ApiResponse`,成功响应约定为: ```json { "code": 200, "isSuccess": true, "result": {} } ``` 部门成员接口使用专用的 `DepartmentUserIdsResponse`。历史单据查询接口为兼容已有调用方,保留当前响应结构;流程发起接口使用 `ApiResponse`。 异常处理分为两层: - Controller 局部处理请求体解析和参数校验失败,返回 HTTP 400。 - `GlobalExceptionHandler` 处理配置缺失,返回 HTTP 412;未分类的外部调用异常记录错误日志并返回 HTTP 502。 ## 6. 配置说明 配置文件为 `src/main/resources/application.yml`。生产环境建议通过环境变量注入,不要把真实密钥提交到代码仓库。 | 配置项 | 环境变量 | 用途 | | --- | --- | --- | | `server.port` | `SERVER_PORT` | HTTP 服务端口,默认 80 | | `spring.profiles.active` | `SPRING_PROFILES_ACTIVE` | Spring Profile,默认 `dev` | | `dingtalk.app-key` | `DINGTALK_APP_KEY` | 钉钉应用 Key | | `dingtalk.app-secret` | `DINGTALK_APP_SECRET` | 钉钉应用 Secret | | `minglei.yida.coordination-office.app-type` | `YIDA_COORDINATION_OFFICE_APP_TYPE` | 协调办公应用编码 | | `minglei.yida.coordination-office.system-token` | `YIDA_COORDINATION_OFFICE_SYSTEM_TOKEN` | 协调办公应用 System Token | | `minglei.yida.master-data.app-type` | `YIDA_MASTER_DATA_APP_TYPE` | 主数据应用编码 | | `minglei.yida.master-data.system-token` | `YIDA_MASTER_DATA_SYSTEM_TOKEN` | 主数据应用 System Token | | `minglei.port.supplier-price-approval-url` | `MINGLEI_SUPPLIER_PRICE_APPROVAL_URL` | 外部 T100 接口地址 | | `minglei.port.supplier-price-approval-key` | `MINGLEI_SUPPLIER_PRICE_APPROVAL_KEY` | 外部接口接入密钥 | | `logging.file.path` | `LOGGING_FILE_PATH` | 日志目录,默认 `logs` | ### 日志说明 Logback 同时输出: - 控制台日志。 - `${LOGGING_FILE_PATH}/application.log` 应用日志。 - `${LOGGING_FILE_PATH}/error.log` 错误日志。 - `archive` 子目录中的按日期、大小滚动归档日志。 `HttpRequestResponseLoggingFilter` 会记录请求方法、URI、状态码、耗时、请求体和响应体,单个载荷最多记录 10KB,并对 `password`、`secret`、`token`、`authorization`、`accessToken`、`systemToken`、`key` 等字段脱敏。日志目录必须对运行账号可写,否则可能导致 Logback 初始化失败。 ## 7. 启动和构建 环境要求:JDK 8、Maven 3.6+,并确保能够访问项目依赖仓库及内部 malk 依赖。 ```powershell # 根据实际环境填写,不要把真实凭证提交到 README 或代码仓库 $env:SERVER_PORT = "8080" $env:DINGTALK_APP_KEY = "" $env:DINGTALK_APP_SECRET = "" $env:YIDA_COORDINATION_OFFICE_APP_TYPE = "" $env:YIDA_COORDINATION_OFFICE_SYSTEM_TOKEN = "" $env:YIDA_MASTER_DATA_APP_TYPE = "" $env:YIDA_MASTER_DATA_SYSTEM_TOKEN = "" mvn clean package mvn spring-boot:run ``` 打包后也可以运行: ```powershell java -jar target\mjava-minglei-1.0.0-SNAPSHOT.jar ``` ## 8. 测试 测试代码位于 `src/test/java`,覆盖: - Spring Boot 上下文加载。 - `ApiResponse` 和部门用户响应的 JSON 结构。 - 宜搭主数据查询条件、部门成员查询和负责人 ID 解析。 - 宜搭流程处理器按表单 ID 分发。 - 外部 T100 请求的固定协议组装和响应转发。 执行测试: ```powershell mvn test ``` ## 9. 扩展宜搭表单流程 新增可发起的宜搭流程时,按以下步骤实现: 1. 新建 `XxxYidaProcessHandler`,实现 `YidaProcessHandler`。 2. 定义该表单真实的 `FORM_UUID`,让 `getType()` 返回该表单 ID。 3. 在 `startProcess` 中选择正确的 `YDClient`,组装表单 ID、流程编码和表单字段。 4. 在 `YidaProcessServiceImplTest` 增加表单 ID 分发测试。 5. 同步更新接口文档中的支持类型和请求示例。 调用方只传 `type`、发起人、标题和 `fields`,不传应用凭证、表单 ID 或流程编码之外的服务端配置。不同宜搭应用必须使用对应的具名客户端,避免协调办公和主数据凭证混用。 ## 10. 安全和运维注意事项 - 不要在 README、提交记录或日志中写入真实 App Secret、System Token、接入密钥或访问令牌。 - 宜搭和钉钉接口所需的网络连通性、权限范围和组织数据权限由部署环境保证。 - 当前 Controller 未内置业务鉴权;如需访问控制,应在网关或统一认证层配置。 - 发生“日志文件拒绝访问”时,优先检查 `LOGGING_FILE_PATH` 目录是否存在以及运行账号是否具有写权限。 - 发生宜搭配置不匹配时,确认应用编码、System Token 与处理器选择的客户端属于同一应用。