Ver código fonte

接口对接

ht 4 dias atrás
pai
commit
7c82ec364a
40 arquivos alterados com 577 adições e 9 exclusões
  1. 3 1
      mjava-minglei/README.md
  2. 8 0
      mjava-minglei/src/main/java/com/malk/minglei/MjavaMingleiApplication.java
  3. 8 0
      mjava-minglei/src/main/java/com/malk/minglei/config/DingTalkRosterConfiguration.java
  4. 15 0
      mjava-minglei/src/main/java/com/malk/minglei/config/EmployeeRosterConfiguration.java
  5. 6 0
      mjava-minglei/src/main/java/com/malk/minglei/config/EmployeeRosterProperties.java
  6. 15 0
      mjava-minglei/src/main/java/com/malk/minglei/config/HttpRequestResponseLoggingFilter.java
  7. 23 0
      mjava-minglei/src/main/java/com/malk/minglei/config/MjavaYidaConfiguration.java
  8. 6 0
      mjava-minglei/src/main/java/com/malk/minglei/config/YidaApplicationsProperties.java
  9. 26 0
      mjava-minglei/src/main/java/com/malk/minglei/controller/EmployeeRosterController.java
  10. 33 0
      mjava-minglei/src/main/java/com/malk/minglei/controller/GlobalExceptionHandler.java
  11. 5 0
      mjava-minglei/src/main/java/com/malk/minglei/controller/MingleiPortController.java
  12. 17 1
      mjava-minglei/src/main/java/com/malk/minglei/controller/YidaProcessController.java
  13. 9 3
      mjava-minglei/src/main/java/com/malk/minglei/dto/DepartmentUserQueryRequest.java
  14. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterInfo.java
  15. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterQueryRequest.java
  16. 12 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterSyncResult.java
  17. 16 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/MingleiPortRequest.java
  18. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/StartProcessRequest.java
  19. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/dto/YidaOperationParam.java
  20. 19 0
      mjava-minglei/src/main/java/com/malk/minglei/service/employee/DingTalkEmployeeRosterClient.java
  21. 27 0
      mjava-minglei/src/main/java/com/malk/minglei/service/employee/EmployeeRosterService.java
  22. 25 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/DingTalkEmployeeRosterClientImpl.java
  23. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterRecord.java
  24. 27 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterRepository.java
  25. 11 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterScheduler.java
  26. 42 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterServiceImpl.java
  27. 21 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/ApproveOrderFormPushHandler.java
  28. 7 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/BomYidaProcessHandler.java
  29. 18 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/Cpmp402YidaProcessHandler.java
  30. 21 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/CustomerReleaseFormPushHandler.java
  31. 11 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/DingTalkYidaProcessOpenApiClient.java
  32. 35 4
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MingleiDocumentServiceImpl.java
  33. 21 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MiscReceiptIssueFormPushHandler.java
  34. 18 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MlWorkOrderCraftPriceFormPushHandler.java
  35. 5 0
      mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/YidaProcessServiceImpl.java
  36. 5 0
      mjava-minglei/src/main/java/com/malk/minglei/service/yida/MingleiDocumentService.java
  37. 11 0
      mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaFormPushHandler.java
  38. 6 0
      mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaProcessOpenApiClient.java
  39. 3 0
      mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaProcessService.java
  40. 27 0
      mjava-minglei/src/test/java/com/malk/minglei/service/impl/yida/MingleiDocumentServiceImplTest.java

+ 3 - 1
mjava-minglei/README.md

@@ -185,11 +185,13 @@ Content-Type: application/json
 
 ```json
 {
-  "dept_Id": "123456",
+  "dept_Id": "123456,234567",
   "type": 0
 }
 ```
 
+`dept_Id` 支持单个部门 ID,或多个以英文逗号分隔的部门 ID;返回结果为各部门查询范围内人员的去重合集。
+
 `type` 取值:
 
 | 值 | 说明 |

+ 8 - 0
mjava-minglei/src/main/java/com/malk/minglei/MjavaMingleiApplication.java

@@ -4,10 +4,18 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
+/**
+ * 明磊业务集成服务启动入口。
+ */
 @EnableScheduling
 @SpringBootApplication
 public class MjavaMingleiApplication {
 
+    /**
+     * 启动 Spring Boot 应用。
+     *
+     * @param args 命令行启动参数
+     */
     public static void main(String[] args) {
         SpringApplication.run(MjavaMingleiApplication.class, args);
     }

+ 8 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/DingTalkRosterConfiguration.java

@@ -5,9 +5,17 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.http.client.SimpleClientHttpRequestFactory;
 import org.springframework.web.client.RestTemplate;
 
+/**
+ * 钉钉花名册调用相关的客户端配置。
+ */
 @Configuration
 public class DingTalkRosterConfiguration {
 
+    /**
+     * 创建用于调用钉钉花名册接口的 HTTP 客户端。
+     *
+     * @return 已配置连接和读取超时时间的 RestTemplate
+     */
     @Bean("dingTalkRestTemplate")
     public RestTemplate dingTalkRestTemplate() {
         SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

+ 15 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/EmployeeRosterConfiguration.java

@@ -11,10 +11,19 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
 
 import javax.sql.DataSource;
 
+/**
+ * 员工花名册独立数据源和 JDBC 访问对象配置。
+ */
 @Configuration
 @EnableConfigurationProperties(EmployeeRosterProperties.class)
 public class EmployeeRosterConfiguration {
 
+    /**
+     * 根据员工花名册配置创建独立数据源。
+     *
+     * @param properties 员工花名册配置项
+     * @return 员工花名册数据源
+     */
     @Bean("employeeRosterDataSource")
     @ConditionalOnExpression("'${minglei.employee-roster.datasource.url:}' != ''")
     public DataSource employeeRosterDataSource(EmployeeRosterProperties properties) {
@@ -33,6 +42,12 @@ public class EmployeeRosterConfiguration {
         return dataSource;
     }
 
+    /**
+     * 创建访问员工花名册库表的 JdbcTemplate。
+     *
+     * @param dataSource 员工花名册数据源
+     * @return 员工花名册 JDBC 操作模板
+     */
     @Bean("employeeRosterJdbcTemplate")
     @ConditionalOnBean(name = "employeeRosterDataSource")
     public JdbcTemplate employeeRosterJdbcTemplate(@Qualifier("employeeRosterDataSource") DataSource dataSource) {

+ 6 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/EmployeeRosterProperties.java

@@ -2,6 +2,9 @@ package com.malk.minglei.config;
 
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
+/**
+ * 员工花名册同步和本地存储配置。
+ */
 @ConfigurationProperties(prefix = "minglei.employee-roster")
 public class EmployeeRosterProperties {
 
@@ -64,6 +67,9 @@ public class EmployeeRosterProperties {
         this.datasource = datasource;
     }
 
+    /**
+     * 员工花名册独立数据源配置。
+     */
     public static class DataSourceProperties {
 
         private String url;

+ 15 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/HttpRequestResponseLoggingFilter.java

@@ -32,6 +32,12 @@ public class HttpRequestResponseLoggingFilter extends OncePerRequestFilter {
             "((?:password|secret|token|authorization|accessToken|systemToken|key)=)[^&]*",
             Pattern.CASE_INSENSITIVE);
 
+    /**
+     * 跳过不适合读取请求体的二进制或表单文件上传请求。
+     *
+     * @param request 当前 HTTP 请求
+     * @return true 表示跳过日志过滤
+     */
     @Override
     protected boolean shouldNotFilter(HttpServletRequest request) {
         String contentType = request.getContentType();
@@ -39,6 +45,15 @@ public class HttpRequestResponseLoggingFilter extends OncePerRequestFilter {
                 || contentType.startsWith("application/octet-stream"));
     }
 
+    /**
+     * 记录请求和响应摘要,并对敏感字段做脱敏处理。
+     *
+     * @param request 当前 HTTP 请求
+     * @param response 当前 HTTP 响应
+     * @param filterChain 过滤器链
+     * @throws ServletException Servlet 处理异常
+     * @throws IOException IO 处理异常
+     */
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
             throws ServletException, IOException {

+ 23 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/MjavaYidaConfiguration.java

@@ -17,21 +17,44 @@ import org.springframework.context.annotation.Import;
 
 import java.lang.reflect.Field;
 
+/**
+ * 宜搭和钉钉开放平台客户端配置。
+ */
 @Configuration
 @EnableConfigurationProperties({DDConf.class, YidaApplicationsProperties.class})
 @Import({DDImplClient.class, DDImplClient_Contacts.class, DDImplClient_Personnel.class})
 public class MjavaYidaConfiguration {
 
+    /**
+     * 创建协同办公应用的宜搭普通表单客户端。
+     *
+     * @param dingTalkClient 钉钉开放平台客户端
+     * @param applications 宜搭应用配置
+     * @return 协同办公应用宜搭客户端
+     */
     @Bean("coordinationOfficeYidaClient")
     public YDClient coordinationOfficeYidaClient(DDClient dingTalkClient, YidaApplicationsProperties applications) {
         return createYidaClient(dingTalkClient, applications.getCoordinationOffice());
     }
 
+    /**
+     * 创建主数据应用的宜搭普通表单客户端。
+     *
+     * @param dingTalkClient 钉钉开放平台客户端
+     * @param applications 宜搭应用配置
+     * @return 主数据应用宜搭客户端
+     */
     @Bean("masterDataYidaClient")
     public YDClient masterDataYidaClient(DDClient dingTalkClient, YidaApplicationsProperties applications) {
         return createYidaClient(dingTalkClient, applications.getMasterData());
     }
 
+    /**
+     * 创建协同办公应用的宜搭流程客户端。
+     *
+     * @param dingTalkClient 钉钉开放平台客户端
+     * @return 协同办公应用宜搭流程客户端
+     */
     @Bean("coordinationOfficeYidaProcessClient")
     public YDClient_Process coordinationOfficeYidaProcessClient(DDClient dingTalkClient) {
         YDClient_ProcessImpl yidaProcessClient = new YDClient_ProcessImpl();

+ 6 - 0
mjava-minglei/src/main/java/com/malk/minglei/config/YidaApplicationsProperties.java

@@ -2,6 +2,9 @@ package com.malk.minglei.config;
 
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
+/**
+ * 宜搭应用凭据配置集合。
+ */
 @ConfigurationProperties(prefix = "minglei.yida")
 public class YidaApplicationsProperties {
 
@@ -24,6 +27,9 @@ public class YidaApplicationsProperties {
         this.masterData = masterData;
     }
 
+    /**
+     * 单个宜搭应用的访问凭据。
+     */
     public static class ApplicationCredentials {
 
         private String appType;

+ 26 - 0
mjava-minglei/src/main/java/com/malk/minglei/controller/EmployeeRosterController.java

@@ -17,6 +17,9 @@ import javax.validation.Valid;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 
+/**
+ * 员工花名册同步和查询接口。
+ */
 @Validated
 @RestController
 @RequestMapping("/api/yida/minglei/employee-roster")
@@ -24,21 +27,44 @@ public class EmployeeRosterController {
 
     private final EmployeeRosterService employeeRosterService;
 
+    /**
+     * 创建员工花名册控制器。
+     *
+     * @param employeeRosterService 员工花名册服务
+     */
     public EmployeeRosterController(EmployeeRosterService employeeRosterService) {
         this.employeeRosterService = employeeRosterService;
     }
 
+    /**
+     * 按指定部门同步钉钉员工花名册。
+     *
+     * @param departmentId 钉钉部门 ID
+     * @param type 查询范围,0 表示包含子部门,1 表示仅当前部门
+     * @return 员工花名册同步结果
+     */
     @PostMapping("/sync")
     public ResponseEntity<ApiResponse<EmployeeRosterSyncResult>> sync(@RequestParam(name = "departmentId", defaultValue = "1") @Min(1) long departmentId,
                                                                       @RequestParam(name = "type", defaultValue = "0") @Min(0) @Max(1) int type) {
         return ResponseEntity.ok(ApiResponse.success(employeeRosterService.sync(departmentId, type)));
     }
 
+    /**
+     * 按默认配置完整同步员工花名册。
+     *
+     * @return 员工花名册同步结果
+     */
     @PostMapping("/sync-full")
     public ResponseEntity<ApiResponse<EmployeeRosterSyncResult>> syncFull() {
         return ResponseEntity.ok(ApiResponse.success(employeeRosterService.syncFull()));
     }
 
+    /**
+     * 根据钉钉用户 ID 查询本地员工花名册信息。
+     *
+     * @param request 员工花名册查询请求
+     * @return 员工花名册信息
+     */
     @PostMapping("/getUserRoster")
     public ResponseEntity<ApiResponse<EmployeeRosterInfo>> getUserRoster(@Valid @RequestBody EmployeeRosterQueryRequest request) {
         return ResponseEntity.ok(ApiResponse.success(employeeRosterService.findByUserId(request.getUserID())));

+ 33 - 0
mjava-minglei/src/main/java/com/malk/minglei/controller/GlobalExceptionHandler.java

@@ -14,28 +14,55 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+/**
+ * REST 接口全局异常处理器。
+ */
 @RestControllerAdvice
 public class GlobalExceptionHandler {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
+    /**
+     * 处理缺少必要运行配置导致的请求失败。
+     *
+     * @param exception 配置状态异常
+     * @return 预条件失败响应
+     */
     @ExceptionHandler(IllegalStateException.class)
     public ResponseEntity<Map<String, String>> handleConfiguration(IllegalStateException exception) {
         return ResponseEntity.status(HttpStatus.PRECONDITION_FAILED)
                 .body(Collections.singletonMap("message", exception.getMessage()));
     }
 
+    /**
+     * 处理本地参数校验或业务参数不支持的异常。
+     *
+     * @param exception 参数异常
+     * @return 请求参数错误响应
+     */
     @ExceptionHandler(IllegalArgumentException.class)
     public ResponseEntity<Map<String, String>> handleInvalidArgument(IllegalArgumentException exception) {
         return ResponseEntity.badRequest()
                 .body(Collections.singletonMap("message", defaultText(exception.getMessage(), "Request parameter is invalid")));
     }
 
+    /**
+     * 处理请求体字段校验失败或 JSON 解析失败的异常。
+     *
+     * @param exception 请求体解析或校验异常
+     * @return 请求参数错误响应
+     */
     @ExceptionHandler({MethodArgumentNotValidException.class, HttpMessageNotReadableException.class})
     public ResponseEntity<Map<String, String>> handleBadRequest(Exception exception) {
         return ResponseEntity.badRequest().body(Collections.singletonMap("message", "Request validation failed"));
     }
 
+    /**
+     * 处理宜搭 SDK 调用失败。
+     *
+     * @param exception 宜搭客户端异常
+     * @return 外部服务调用失败响应
+     */
     @ExceptionHandler(McException.class)
     public ResponseEntity<Map<String, String>> handleYidaClient(McException exception) {
         LOGGER.error("Yida client request failed code={} source={}", exception.getCode(), exception.getSource(), exception);
@@ -46,6 +73,12 @@ public class GlobalExceptionHandler {
         return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(response);
     }
 
+    /**
+     * 处理未被更具体规则捕获的异常。
+     *
+     * @param exception 未知异常
+     * @return 外部服务调用失败响应
+     */
     @ExceptionHandler(Exception.class)
     public ResponseEntity<Map<String, String>> handleUnexpected(Exception exception) {
         LOGGER.error("Yida client request failed", exception);

+ 5 - 0
mjava-minglei/src/main/java/com/malk/minglei/controller/MingleiPortController.java

@@ -31,6 +31,11 @@ public class MingleiPortController {
 
     private final MingleiPortService mingleiPortService;
 
+    /**
+     * 创建明磊外部单据接口控制器。
+     *
+     * @param mingleiPortService 明磊外部单据接口服务
+     */
     public MingleiPortController(MingleiPortService mingleiPortService) {
         this.mingleiPortService = mingleiPortService;
     }

+ 17 - 1
mjava-minglei/src/main/java/com/malk/minglei/controller/YidaProcessController.java

@@ -26,6 +26,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 宜搭流程发起、单据查询和部门成员查询接口。
+ */
 @Validated
 @RestController
 @RequestMapping("/api/yida")
@@ -34,6 +37,12 @@ public class YidaProcessController {
     private final YidaProcessService yidaProcessService;
     private final MingleiDocumentService mingleiDocumentService;
 
+    /**
+     * 创建宜搭业务接口控制器。
+     *
+     * @param yidaProcessService 宜搭流程服务
+     * @param mingleiDocumentService 明磊单据查询服务
+     */
     public YidaProcessController(YidaProcessService yidaProcessService, MingleiDocumentService mingleiDocumentService) {
         this.yidaProcessService = yidaProcessService;
         this.mingleiDocumentService = mingleiDocumentService;
@@ -75,6 +84,13 @@ public class YidaProcessController {
         return ResponseEntity.badRequest().body(Collections.singletonMap("message", "Request validation failed"));
     }
 
+    /**
+     * 分页查询明磊普通表单实例。
+     *
+     * @param page 页码,从 1 开始
+     * @param size 每页记录数
+     * @return 宜搭表单实例分页结果
+     */
     @GetMapping("/minglei/forms/instances")
     public ResponseEntity<Map<String, Object>> queryMingleiDocuments(@RequestParam(name = "page", defaultValue = "1") @Min(1) int page,
                                                                       @RequestParam(name = "size", defaultValue = "20") @Min(1) @Max(100) int size) {
@@ -89,7 +105,7 @@ public class YidaProcessController {
      */
     @PostMapping("/minglei/departments/users")
     public ResponseEntity<DepartmentUserIdsResponse> listDepartmentUserIds(@Valid @RequestBody DepartmentUserQueryRequest request) {
-        List<String> userIds = mingleiDocumentService.listDepartmentUserIds(request.getDepartmentId(), request.getType());
+        List<String> userIds = mingleiDocumentService.listDepartmentUserIds(request.getDepartmentIds(), request.getType());
         return ResponseEntity.ok(DepartmentUserIdsResponse.success(userIds));
     }
 

+ 9 - 3
mjava-minglei/src/main/java/com/malk/minglei/dto/DepartmentUserQueryRequest.java

@@ -7,6 +7,8 @@ import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * 按部门查询钉钉成员的请求模型。
@@ -14,7 +16,7 @@ import javax.validation.constraints.Pattern;
 public class DepartmentUserQueryRequest {
 
     @NotBlank
-    @Pattern(regexp = "[1-9]\\d*", message = "dept_Id must be a positive integer")
+    @Pattern(regexp = "\\s*[1-9]\\d*\\s*(,\\s*[1-9]\\d*\\s*)*", message = "dept_Id must be comma-separated positive integers")
     @JsonProperty("dept_Id")
     private String departmentId;
 
@@ -23,8 +25,12 @@ public class DepartmentUserQueryRequest {
     @Max(value = 1, message = "type must be 0 or 1")
     private Integer type;
 
-    public long getDepartmentId() {
-        return Long.parseLong(departmentId);
+    public List<Long> getDepartmentIds() {
+        List<Long> departmentIds = new ArrayList<Long>();
+        for (String value : departmentId.split(",")) {
+            departmentIds.add(Long.parseLong(value.trim()));
+        }
+        return departmentIds;
     }
 
     public void setDepartmentId(String departmentId) {

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterInfo.java

@@ -1,5 +1,8 @@
 package com.malk.minglei.dto;
 
+/**
+ * 员工花名册查询结果。
+ */
 public class EmployeeRosterInfo {
 
     private Integer id;

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterQueryRequest.java

@@ -4,6 +4,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 
 import javax.validation.constraints.NotBlank;
 
+/**
+ * 员工花名册查询请求。
+ */
 public class EmployeeRosterQueryRequest {
 
     @NotBlank

+ 12 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/EmployeeRosterSyncResult.java

@@ -1,5 +1,8 @@
 package com.malk.minglei.dto;
 
+/**
+ * 员工花名册同步结果统计。
+ */
 public class EmployeeRosterSyncResult {
 
     private final long departmentId;
@@ -8,6 +11,15 @@ public class EmployeeRosterSyncResult {
     private final int fieldCount;
     private final int syncedCount;
 
+    /**
+     * 创建员工花名册同步结果。
+     *
+     * @param departmentId 同步的钉钉部门 ID
+     * @param includeSubDepartments 是否包含子部门
+     * @param userCount 本次处理的用户数
+     * @param fieldCount 本次查询的花名册字段数
+     * @param syncedCount 实际写入本地库的记录数
+     */
     public EmployeeRosterSyncResult(long departmentId, boolean includeSubDepartments, int userCount, int fieldCount, int syncedCount) {
         this.departmentId = departmentId;
         this.includeSubDepartments = includeSubDepartments;

+ 16 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/MingleiPortRequest.java

@@ -29,14 +29,27 @@ public class MingleiPortRequest {
         return detail;
     }
 
+    /**
+     * 返回单据编号,便于日志和外部调用跟踪。
+     *
+     * @return 单据编号;请求头为空时返回 null
+     */
     public String getDocumentNumber() {
         return head == null ? null : head.pmdidocno;
     }
 
+    /**
+     * 返回明细行数,便于日志记录。
+     *
+     * @return 明细行数量;明细为空时返回 0
+     */
     public int getDetailCount() {
         return detail == null ? 0 : detail.size();
     }
 
+    /**
+     * 供应商价格审批单头信息。
+     */
     @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
     public static class Head {
 
@@ -74,6 +87,9 @@ public class MingleiPortRequest {
         private String pmdistus;
     }
 
+    /**
+     * 供应商价格审批单明细信息。
+     */
     @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
     public static class Detail {
 

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/StartProcessRequest.java

@@ -7,6 +7,9 @@ import javax.validation.constraints.NotEmpty;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+/**
+ * 兼容旧接口的宜搭流程发起请求模型。
+ */
 public class StartProcessRequest {
 
     @NotBlank

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/dto/YidaOperationParam.java

@@ -2,6 +2,9 @@ package com.malk.minglei.dto;
 
 import com.malk.server.aliwork.YDParam;
 
+/**
+ * 宜搭数据操作参数扩展模型。
+ */
 public class YidaOperationParam extends YDParam {
 
     private String title;

+ 19 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/employee/DingTalkEmployeeRosterClient.java

@@ -3,9 +3,28 @@ package com.malk.minglei.service.employee;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 钉钉智能人事花名册接口客户端。
+ */
 public interface DingTalkEmployeeRosterClient {
 
+    /**
+     * 查询可用的花名册字段分组。
+     *
+     * @param accessToken 钉钉访问令牌
+     * @param agentId 应用 AgentId
+     * @return 花名册字段分组列表
+     */
     List<Map> listRosterFieldGroups(String accessToken, Number agentId);
 
+    /**
+     * 批量查询指定用户的花名册字段值。
+     *
+     * @param accessToken 钉钉访问令牌
+     * @param userIds 钉钉用户 ID 列表
+     * @param agentId 应用 AgentId
+     * @param fieldCodes 需要查询的字段编码
+     * @return 用户花名册记录列表
+     */
     List<Map> listEmployeeRoster(String accessToken, List<String> userIds, Number agentId, List<String> fieldCodes);
 }

+ 27 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/employee/EmployeeRosterService.java

@@ -3,13 +3,40 @@ package com.malk.minglei.service.employee;
 import com.malk.minglei.dto.EmployeeRosterInfo;
 import com.malk.minglei.dto.EmployeeRosterSyncResult;
 
+/**
+ * 员工花名册同步和查询服务。
+ */
 public interface EmployeeRosterService {
 
+    /**
+     * 按指定部门和查询范围同步员工花名册。
+     *
+     * @param departmentId 钉钉部门 ID
+     * @param type 查询范围,0 表示包含子部门,1 表示仅当前部门
+     * @return 同步统计结果
+     */
     EmployeeRosterSyncResult sync(long departmentId, int type);
 
+    /**
+     * 使用配置中的默认部门完整同步员工花名册。
+     *
+     * @return 同步统计结果
+     */
     EmployeeRosterSyncResult syncFull();
 
+    /**
+     * 根据钉钉用户 ID 查询员工花名册。
+     *
+     * @param userId 钉钉用户 ID
+     * @return 员工花名册信息,不存在时返回 null
+     */
     EmployeeRosterInfo findByUserId(String userId);
 
+    /**
+     * 根据员工工号查询员工花名册。
+     *
+     * @param employeeNo 员工工号
+     * @return 员工花名册信息,不存在时返回 null
+     */
     EmployeeRosterInfo findByEmployeeNo(String employeeNo);
 }

+ 25 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/DingTalkEmployeeRosterClientImpl.java

@@ -18,6 +18,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 基于钉钉开放接口和 SDK 的智能人事花名册客户端实现。
+ */
 @Service
 public class DingTalkEmployeeRosterClientImpl implements DingTalkEmployeeRosterClient {
 
@@ -27,12 +30,25 @@ public class DingTalkEmployeeRosterClientImpl implements DingTalkEmployeeRosterC
     private final RestTemplate restTemplate;
     private final DDClient_Personnel personnelClient;
 
+    /**
+     * 创建钉钉花名册客户端实现。
+     *
+     * @param restTemplate 钉钉接口专用 HTTP 客户端
+     * @param personnelClient 钉钉智能人事客户端
+     */
     public DingTalkEmployeeRosterClientImpl(@Qualifier("dingTalkRestTemplate") RestTemplate restTemplate,
                                             DDClient_Personnel personnelClient) {
         this.restTemplate = restTemplate;
         this.personnelClient = personnelClient;
     }
 
+    /**
+     * 查询钉钉花名册字段分组,并转换为 Map 列表。
+     *
+     * @param accessToken 钉钉访问令牌
+     * @param agentId 应用 AgentId
+     * @return 花名册字段分组列表
+     */
     @Override
     public List<Map> listRosterFieldGroups(String accessToken, Number agentId) {
         Map<String, Object> requestBody = new LinkedHashMap<String, Object>();
@@ -56,6 +72,15 @@ public class DingTalkEmployeeRosterClientImpl implements DingTalkEmployeeRosterC
         return toMapList(extractGroupList(result));
     }
 
+    /**
+     * 批量查询指定用户的花名册数据。
+     *
+     * @param accessToken 钉钉访问令牌
+     * @param userIds 钉钉用户 ID 列表
+     * @param agentId 应用 AgentId
+     * @param fieldCodes 需要查询的字段编码
+     * @return 用户花名册记录列表
+     */
     @Override
     public List<Map> listEmployeeRoster(String accessToken, List<String> userIds, Number agentId, List<String> fieldCodes) {
         if (userIds == null || userIds.isEmpty()) {

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterRecord.java

@@ -1,5 +1,8 @@
 package com.malk.minglei.service.impl.employee;
 
+/**
+ * 员工花名册持久化记录。
+ */
 public class EmployeeRosterRecord {
 
     private String dingtalkId;

+ 27 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterRepository.java

@@ -12,18 +12,33 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.List;
 
+/**
+ * 员工花名册本地数据库访问组件。
+ */
 @Repository
 public class EmployeeRosterRepository {
 
     private final ObjectProvider<JdbcTemplate> jdbcTemplateProvider;
     private final EmployeeRosterProperties properties;
 
+    /**
+     * 创建员工花名册仓储组件。
+     *
+     * @param jdbcTemplateProvider 员工花名册 JDBC 模板提供器
+     * @param properties 员工花名册配置
+     */
     public EmployeeRosterRepository(@Qualifier("employeeRosterJdbcTemplate") ObjectProvider<JdbcTemplate> jdbcTemplateProvider,
                                     EmployeeRosterProperties properties) {
         this.jdbcTemplateProvider = jdbcTemplateProvider;
         this.properties = properties;
     }
 
+    /**
+     * 批量新增或更新员工花名册记录。
+     *
+     * @param records 待写入的员工花名册记录
+     * @return 实际提交写入的记录数
+     */
     public int upsertAll(List<EmployeeRosterRecord> records) {
         if (records == null || records.isEmpty()) {
             return 0;
@@ -37,10 +52,22 @@ public class EmployeeRosterRepository {
         return records.size();
     }
 
+    /**
+     * 根据钉钉用户 ID 查询员工花名册。
+     *
+     * @param dingtalkId 钉钉用户 ID
+     * @return 员工花名册信息,不存在时返回 null
+     */
     public EmployeeRosterInfo findByDingtalkId(String dingtalkId) {
         return findOne("dingtalk_id", dingtalkId);
     }
 
+    /**
+     * 根据员工工号查询员工花名册。
+     *
+     * @param employeeNo 员工工号
+     * @return 员工花名册信息,不存在时返回 null
+     */
     public EmployeeRosterInfo findByEmployeeNo(String employeeNo) {
         return findOne("employee_no", employeeNo);
     }

+ 11 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterScheduler.java

@@ -7,6 +7,9 @@ import org.slf4j.LoggerFactory;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+/**
+ * 员工花名册定时同步任务。
+ */
 @Component
 public class EmployeeRosterScheduler {
 
@@ -14,10 +17,18 @@ public class EmployeeRosterScheduler {
 
     private final EmployeeRosterService employeeRosterService;
 
+    /**
+     * 创建员工花名册定时任务。
+     *
+     * @param employeeRosterService 员工花名册服务
+     */
     public EmployeeRosterScheduler(EmployeeRosterService employeeRosterService) {
         this.employeeRosterService = employeeRosterService;
     }
 
+    /**
+     * 每日执行员工花名册全量同步。
+     */
     @Scheduled(cron = "0 0 0 * * ?")
     public void syncFullEmployeeRoster() {
         try {

+ 42 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/employee/EmployeeRosterServiceImpl.java

@@ -18,6 +18,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+/**
+ * 员工花名册同步与查询服务实现。
+ */
 @Service
 public class EmployeeRosterServiceImpl implements EmployeeRosterService {
 
@@ -34,6 +37,16 @@ public class EmployeeRosterServiceImpl implements EmployeeRosterService {
     private final EmployeeRosterRepository repository;
     private final EmployeeRosterProperties properties;
 
+    /**
+     * 创建员工花名册服务实现。
+     *
+     * @param dingTalkClient 钉钉基础客户端
+     * @param contactsClient 钉钉通讯录客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param rosterClient 钉钉花名册客户端
+     * @param repository 员工花名册本地仓储
+     * @param properties 员工花名册同步配置
+     */
     public EmployeeRosterServiceImpl(DDClient dingTalkClient,
                                      DDClient_Contacts contactsClient,
                                      DDConf dingTalkConfiguration,
@@ -48,6 +61,13 @@ public class EmployeeRosterServiceImpl implements EmployeeRosterService {
         this.properties = properties;
     }
 
+    /**
+     * 从钉钉同步指定部门范围内的员工花名册,并写入本地表。
+     *
+     * @param departmentId 钉钉部门 ID
+     * @param type 查询范围,0 表示包含子部门,1 表示仅当前部门
+     * @return 本次同步统计结果
+     */
     @Override
     public EmployeeRosterSyncResult sync(long departmentId, int type) {
         if (departmentId < 1) {
@@ -84,17 +104,33 @@ public class EmployeeRosterServiceImpl implements EmployeeRosterService {
         return new EmployeeRosterSyncResult(departmentId, type == INCLUDE_CHILD_DEPARTMENTS, records.size(), fieldCodes.size(), syncedCount);
     }
 
+    /**
+     * 使用配置中的默认部门和范围同步员工花名册。
+     *
+     * @return 本次同步统计结果
+     */
     public EmployeeRosterSyncResult syncDefaultDepartment() {
         long departmentId = properties.getDepartmentId();
         int type = properties.isIncludeSubDepartments() ? INCLUDE_CHILD_DEPARTMENTS : CURRENT_DEPARTMENT_ONLY;
         return sync(departmentId, type);
     }
 
+    /**
+     * 从根部门开始完整同步员工花名册。
+     *
+     * @return 本次同步统计结果
+     */
     @Override
     public EmployeeRosterSyncResult syncFull() {
         return sync(ROOT_DEPARTMENT_ID, INCLUDE_CHILD_DEPARTMENTS);
     }
 
+    /**
+     * 根据钉钉用户 ID 查询本地员工花名册记录。
+     *
+     * @param userId 钉钉用户 ID
+     * @return 员工花名册信息,不存在时返回 null
+     */
     @Override
     public EmployeeRosterInfo findByUserId(String userId) {
         if (userId == null || userId.trim().isEmpty()) {
@@ -103,6 +139,12 @@ public class EmployeeRosterServiceImpl implements EmployeeRosterService {
         return repository.findByDingtalkId(userId.trim());
     }
 
+    /**
+     * 根据员工工号查询本地员工花名册记录。
+     *
+     * @param employeeNo 员工工号
+     * @return 员工花名册信息,不存在时返回 null
+     */
     @Override
     public EmployeeRosterInfo findByEmployeeNo(String employeeNo) {
         if (employeeNo == null || employeeNo.trim().isEmpty()) {

+ 21 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/ApproveOrderFormPushHandler.java

@@ -22,6 +22,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 销售订单审批 ERP 推送到宜搭流程的处理器。
+ */
 @Service
 public class ApproveOrderFormPushHandler implements YidaFormPushHandler {
 
@@ -84,6 +87,13 @@ public class ApproveOrderFormPushHandler implements YidaFormPushHandler {
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties applications;
 
+    /**
+     * 创建销售订单审批推送处理器。
+     *
+     * @param yidaProcessOpenApiClient 宜搭流程 OpenAPI 客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public ApproveOrderFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
                                        DDConf dingTalkConfiguration,
                                        YidaApplicationsProperties applications) {
@@ -92,11 +102,22 @@ public class ApproveOrderFormPushHandler implements YidaFormPushHandler {
         this.applications = applications;
     }
 
+    /**
+     * 返回销售订单审批支持的 ERP 推送类型。
+     *
+     * @return 支持的 type 列表
+     */
     @Override
     public List<String> getTypes() {
         return Collections.singletonList(TYPE);
     }
 
+    /**
+     * 将销售订单审批数据转换为宜搭流程表单并发起流程。
+     *
+     * @param request ERP 推送请求
+     * @return 宜搭流程实例 ID 或成功标识
+     */
     @Override
     public String push(ErpYidaPushRequest request) {
         validateConfiguration();

+ 7 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/BomYidaProcessHandler.java

@@ -27,6 +27,13 @@ public class BomYidaProcessHandler implements YidaProcessHandler {
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties.ApplicationCredentials coordinationOfficeConfiguration;
 
+    /**
+     * 创建 BOM 流程处理器。
+     *
+     * @param yidaClient 协同办公宜搭客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public BomYidaProcessHandler(@Qualifier("coordinationOfficeYidaClient") YDClient yidaClient,
                                  DDConf dingTalkConfiguration,
                                  YidaApplicationsProperties applications) {

+ 18 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/Cpmp402YidaProcessHandler.java

@@ -52,6 +52,13 @@ public class Cpmp402YidaProcessHandler implements YidaProcessHandler {
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties applications;
 
+    /**
+     * 创建包材核价单流程处理器。
+     *
+     * @param yidaClient 协同办公宜搭客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public Cpmp402YidaProcessHandler(@Qualifier("coordinationOfficeYidaClient") YDClient yidaClient,
                                      DDConf dingTalkConfiguration,
                                      YidaApplicationsProperties applications) {
@@ -60,11 +67,22 @@ public class Cpmp402YidaProcessHandler implements YidaProcessHandler {
         this.applications = applications;
     }
 
+    /**
+     * 返回包材核价单支持的流程类型。
+     *
+     * @return 支持的 type
+     */
     @Override
     public String getType() {
         return TYPE;
     }
 
+    /**
+     * 将包材核价单数据转换为宜搭流程表单并发起流程。
+     *
+     * @param request 宜搭流程发起请求
+     * @return 宜搭流程实例 ID
+     */
     @Override
     public String startProcess(YidaProcessStartRequest request) {
         validateConfiguration();

+ 21 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/CustomerReleaseFormPushHandler.java

@@ -22,6 +22,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 客户放行审批 ERP 推送到宜搭流程的处理器。
+ */
 @Service
 public class CustomerReleaseFormPushHandler implements YidaFormPushHandler {
 
@@ -58,6 +61,13 @@ public class CustomerReleaseFormPushHandler implements YidaFormPushHandler {
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties applications;
 
+    /**
+     * 创建客户放行审批推送处理器。
+     *
+     * @param yidaProcessOpenApiClient 宜搭流程 OpenAPI 客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public CustomerReleaseFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
                                           DDConf dingTalkConfiguration,
                                           YidaApplicationsProperties applications) {
@@ -66,11 +76,22 @@ public class CustomerReleaseFormPushHandler implements YidaFormPushHandler {
         this.applications = applications;
     }
 
+    /**
+     * 返回客户放行审批支持的 ERP 推送类型。
+     *
+     * @return 支持的 type 列表
+     */
     @Override
     public List<String> getTypes() {
         return Collections.singletonList(TYPE);
     }
 
+    /**
+     * 将客户放行审批数据转换为宜搭流程表单并发起流程。
+     *
+     * @param request ERP 推送请求
+     * @return 宜搭流程实例 ID 或成功标识
+     */
     @Override
     public String push(ErpYidaPushRequest request) {
         validateConfiguration();

+ 11 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/DingTalkYidaProcessOpenApiClient.java

@@ -18,10 +18,21 @@ public class DingTalkYidaProcessOpenApiClient implements YidaProcessOpenApiClien
 
     private final DDClient dingTalkClient;
 
+    /**
+     * 创建宜搭流程 OpenAPI 客户端。
+     *
+     * @param dingTalkClient 钉钉开放平台客户端
+     */
     public DingTalkYidaProcessOpenApiClient(DDClient dingTalkClient) {
         this.dingTalkClient = dingTalkClient;
     }
 
+    /**
+     * 使用钉钉访问令牌调用宜搭流程发起接口。
+     *
+     * @param body 宜搭流程发起请求体
+     * @return 钉钉 OpenAPI 原始响应
+     */
     @Override
     public DDR_New startProcess(Map<String, Object> body) {
         return DDR_New.doPost(YIDA_OPEN_API_BASE_URL + START_PROCESS_ENDPOINT, dingTalkClient.initTokenHeader(), null, body);

+ 35 - 4
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MingleiDocumentServiceImpl.java

@@ -23,6 +23,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+/**
+ * 明磊主数据宜搭表单查询服务实现。
+ */
 @Service
 public class MingleiDocumentServiceImpl implements MingleiDocumentService {
 
@@ -50,6 +53,16 @@ public class MingleiDocumentServiceImpl implements MingleiDocumentService {
     private final YidaApplicationsProperties.ApplicationCredentials masterDataConfiguration;
     private final ObjectMapper objectMapper;
 
+    /**
+     * 创建明磊主数据表单查询服务。
+     *
+     * @param yidaClient 主数据宜搭客户端
+     * @param dingTalkClient 钉钉基础客户端
+     * @param dingTalkContactsClient 钉钉通讯录客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     * @param objectMapper JSON 序列化组件
+     */
     public MingleiDocumentServiceImpl(@Qualifier("masterDataYidaClient") YDClient yidaClient,
                                       DDClient dingTalkClient,
                                       DDClient_Contacts dingTalkContactsClient,
@@ -115,18 +128,36 @@ public class MingleiDocumentServiceImpl implements MingleiDocumentService {
      */
     @Override
     public List<String> listDepartmentUserIds(long departmentId, int type) {
+        return listDepartmentUserIds(Collections.singletonList(departmentId), type);
+    }
+
+    @Override
+    public List<String> listDepartmentUserIds(List<Long> requestedDepartmentIdList, int type) {
         String accessToken = dingTalkClient.getAccessToken();
         require(accessToken, "Unable to obtain DingTalk access token");
 
+        Set<Long> requestedDepartmentIds = new LinkedHashSet<Long>();
+        if (requestedDepartmentIdList != null) {
+            for (Long departmentId : requestedDepartmentIdList) {
+                if (departmentId != null) {
+                    requestedDepartmentIds.add(departmentId);
+                }
+            }
+        }
+
         Set<Long> departmentIds = new LinkedHashSet<Long>();
-        departmentIds.add(departmentId);
 
         // 仅 type 为 0 时查询子部门,type 为 1 时保留当前部门。
         if (type == 0) {
-            List<Long> childDepartmentIds = dingTalkContactsClient.getDepartmentId_all(accessToken, false, departmentId);
-            if (childDepartmentIds != null) {
-                departmentIds.addAll(childDepartmentIds);
+            for (Long departmentId : requestedDepartmentIds) {
+                departmentIds.add(departmentId);
+                List<Long> children = dingTalkContactsClient.getDepartmentId_all(accessToken, false, departmentId.longValue());
+                if (children != null) {
+                    departmentIds.addAll(children);
+                }
             }
+        } else {
+            departmentIds.addAll(requestedDepartmentIds);
         }
 
         Set<String> userIds = new LinkedHashSet<String>();

+ 21 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MiscReceiptIssueFormPushHandler.java

@@ -22,6 +22,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 杂收发料单 ERP 推送到宜搭流程的处理器。
+ */
 @Service
 public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
 
@@ -70,6 +73,13 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties applications;
 
+    /**
+     * 创建杂收发料单推送处理器。
+     *
+     * @param yidaProcessOpenApiClient 宜搭流程 OpenAPI 客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public MiscReceiptIssueFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
                                            DDConf dingTalkConfiguration,
                                            YidaApplicationsProperties applications) {
@@ -78,11 +88,22 @@ public class MiscReceiptIssueFormPushHandler implements YidaFormPushHandler {
         this.applications = applications;
     }
 
+    /**
+     * 返回杂收发料单支持的 ERP 推送类型。
+     *
+     * @return 支持的 type 列表
+     */
     @Override
     public List<String> getTypes() {
         return Collections.singletonList(TYPE);
     }
 
+    /**
+     * 将杂收发料单数据转换为宜搭流程表单并发起流程。
+     *
+     * @param request ERP 推送请求
+     * @return 宜搭流程实例 ID 或成功标识
+     */
     @Override
     public String push(ErpYidaPushRequest request) {
         validateConfiguration();

+ 18 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/MlWorkOrderCraftPriceFormPushHandler.java

@@ -75,6 +75,13 @@ public class MlWorkOrderCraftPriceFormPushHandler implements YidaFormPushHandler
     private final DDConf dingTalkConfiguration;
     private final YidaApplicationsProperties applications;
 
+    /**
+     * 创建工单工艺工价审定推送处理器。
+     *
+     * @param yidaProcessOpenApiClient 宜搭流程 OpenAPI 客户端
+     * @param dingTalkConfiguration 钉钉应用配置
+     * @param applications 宜搭应用配置
+     */
     public MlWorkOrderCraftPriceFormPushHandler(YidaProcessOpenApiClient yidaProcessOpenApiClient,
                                                 DDConf dingTalkConfiguration,
                                                 YidaApplicationsProperties applications) {
@@ -83,11 +90,22 @@ public class MlWorkOrderCraftPriceFormPushHandler implements YidaFormPushHandler
         this.applications = applications;
     }
 
+    /**
+     * 返回工单工艺工价审定支持的 ERP 推送类型。
+     *
+     * @return 支持的 type 列表
+     */
     @Override
     public List<String> getTypes() {
         return Collections.singletonList(TYPE);
     }
 
+    /**
+     * 将工单工艺工价审定数据转换为宜搭流程表单并发起流程。
+     *
+     * @param request ERP 推送请求
+     * @return 宜搭流程实例 ID 或成功标识
+     */
     @Override
     public String push(ErpYidaPushRequest request) {
         validateConfiguration();

+ 5 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/impl/yida/YidaProcessServiceImpl.java

@@ -19,6 +19,11 @@ public class YidaProcessServiceImpl implements YidaProcessService {
 
     private final Map<String, YidaProcessHandler> handlers;
 
+    /**
+     * 创建宜搭流程分发服务。
+     *
+     * @param handlers 所有宜搭流程处理器
+     */
     public YidaProcessServiceImpl(List<YidaProcessHandler> handlers) {
         Map<String, YidaProcessHandler> handlerMap = new LinkedHashMap<String, YidaProcessHandler>();
         for (YidaProcessHandler handler : handlers) {

+ 5 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/yida/MingleiDocumentService.java

@@ -5,6 +5,9 @@ import com.malk.minglei.dto.MingleiDocumentQueryRequest;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 明磊宜搭普通表单单据查询服务。
+ */
 public interface MingleiDocumentService {
 
     /**
@@ -32,4 +35,6 @@ public interface MingleiDocumentService {
      * @return 去重后的钉钉 userid 列表
      */
     List<String> listDepartmentUserIds(long departmentId, int type);
+
+    List<String> listDepartmentUserIds(List<Long> departmentIds, int type);
 }

+ 11 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaFormPushHandler.java

@@ -9,7 +9,18 @@ import java.util.List;
  */
 public interface YidaFormPushHandler {
 
+    /**
+     * 返回当前处理器支持的 ERP 推送类型。
+     *
+     * @return 支持的 type 列表
+     */
     List<String> getTypes();
 
+    /**
+     * 将 ERP 推送请求转换并写入对应宜搭表单。
+     *
+     * @param request ERP 推送请求
+     * @return 宜搭返回的数据实例 ID 或处理结果
+     */
     String push(ErpYidaPushRequest request);
 }

+ 6 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaProcessOpenApiClient.java

@@ -9,5 +9,11 @@ import java.util.Map;
  */
 public interface YidaProcessOpenApiClient {
 
+    /**
+     * 调用宜搭 OpenAPI 发起流程实例。
+     *
+     * @param body 宜搭流程发起请求体
+     * @return 钉钉 OpenAPI 原始响应
+     */
     DDR_New startProcess(Map<String, Object> body);
 }

+ 3 - 0
mjava-minglei/src/main/java/com/malk/minglei/service/yida/YidaProcessService.java

@@ -2,6 +2,9 @@ package com.malk.minglei.service.yida;
 
 import com.malk.minglei.dto.YidaProcessStartRequest;
 
+/**
+ * 宜搭流程实例发起服务。
+ */
 public interface YidaProcessService {
 
     /**

+ 27 - 0
mjava-minglei/src/test/java/com/malk/minglei/service/impl/yida/MingleiDocumentServiceImplTest.java

@@ -69,4 +69,31 @@ class MingleiDocumentServiceImplTest {
         assertEquals(Arrays.asList("user-1", "user-2"), userIds);
         verify(contactsClient, never()).getDepartmentId_all("access-token", false, 10L);
     }
+
+    @Test
+    void listDepartmentUserIdsCombinesMultipleDepartmentsUsingTheRequestedType() {
+        DDClient dingTalkClient = mock(DDClient.class);
+        DDClient_Contacts contactsClient = mock(DDClient_Contacts.class);
+        given(dingTalkClient.getAccessToken()).willReturn("access-token");
+        given(contactsClient.getDepartmentId_all("access-token", false, 10L)).willReturn(Arrays.asList(11L));
+        given(contactsClient.getDepartmentId_all("access-token", false, 20L)).willReturn(Arrays.asList(21L));
+        given(contactsClient.listDepartmentUserId("access-token", 10L)).willReturn(Arrays.asList("user-1", "user-shared"));
+        given(contactsClient.listDepartmentUserId("access-token", 11L)).willReturn(Arrays.asList("user-2"));
+        given(contactsClient.listDepartmentUserId("access-token", 20L)).willReturn(Arrays.asList("user-shared", "user-3"));
+        given(contactsClient.listDepartmentUserId("access-token", 21L)).willReturn(Arrays.asList("user-4"));
+
+        MingleiDocumentServiceImpl service = new MingleiDocumentServiceImpl(
+                mock(YDClient.class),
+                dingTalkClient,
+                contactsClient,
+                mock(DDConf.class),
+                new YidaApplicationsProperties(),
+                new ObjectMapper());
+
+        List<String> userIds = service.listDepartmentUserIds(Arrays.asList(10L, 20L), 0);
+
+        assertEquals(Arrays.asList("user-1", "user-shared", "user-2", "user-3", "user-4"), userIds);
+        verify(contactsClient).getDepartmentId_all("access-token", false, 10L);
+        verify(contactsClient).getDepartmentId_all("access-token", false, 20L);
+    }
 }