Merge remote-tracking branch 'base-version/main' into dev

This commit is contained in:
chenbowen
2025-11-28 11:07:42 +08:00
40 changed files with 1006 additions and 365 deletions

View File

@@ -0,0 +1,63 @@
package com.zt.plat.module.system.api.iwork;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.iwork.dto.*;
import com.zt.plat.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* RPC 服务 - iWork 集成
*/
@FeignClient(name = ApiConstants.NAME, contextId = "iWorkIntegrationApi")
@Tag(name = "RPC 服务 - iWork 集成")
public interface IWorkIntegrationApi {
String PREFIX = ApiConstants.PREFIX + "/integration/iwork";
// ----------------- 认证 / 会话 -----------------
@PostMapping(PREFIX + "/auth/register")
@Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret")
CommonResult<IWorkAuthRegisterRespDTO> register(@RequestBody IWorkAuthRegisterReqDTO reqDTO);
@PostMapping(PREFIX + "/auth/token")
@Operation(summary = "申请 iWork Token独立接口")
CommonResult<IWorkAuthTokenRespDTO> acquireToken(@RequestBody IWorkAuthTokenReqDTO reqDTO);
// ----------------- 流程类能力 -----------------
@PostMapping(PREFIX + "/user/resolve")
@Operation(summary = "根据外部标识获取 iWork 用户编号")
CommonResult<IWorkUserInfoRespDTO> resolveUser(@RequestBody IWorkUserInfoReqDTO reqDTO);
@PostMapping(PREFIX + "/workflow/create")
@Operation(summary = "发起 iWork 流程")
CommonResult<IWorkOperationRespDTO> createWorkflow(@RequestBody IWorkWorkflowCreateReqDTO reqDTO);
@PostMapping(PREFIX + "/workflow/void")
@Operation(summary = "作废 / 干预 iWork 流程")
CommonResult<IWorkOperationRespDTO> voidWorkflow(@RequestBody IWorkWorkflowVoidReqDTO reqDTO);
// ----------------- 人力组织分页接口 -----------------
@PostMapping(PREFIX + "/hr/subcompany/page")
@Operation(summary = "获取 iWork 分部列表")
CommonResult<IWorkHrSubcompanyPageRespDTO> listSubcompanies(@RequestBody IWorkOrgPageReqDTO reqDTO);
@PostMapping(PREFIX + "/hr/department/page")
@Operation(summary = "获取 iWork 部门列表")
CommonResult<IWorkHrDepartmentPageRespDTO> listDepartments(@RequestBody IWorkOrgPageReqDTO reqDTO);
@PostMapping(PREFIX + "/hr/job-title/page")
@Operation(summary = "获取 iWork 岗位列表")
CommonResult<IWorkHrJobTitlePageRespDTO> listJobTitles(@RequestBody IWorkOrgPageReqDTO reqDTO);
@PostMapping(PREFIX + "/hr/user/page")
@Operation(summary = "获取 iWork 人员列表")
CommonResult<IWorkHrUserPageRespDTO> listUsers(@RequestBody IWorkOrgPageReqDTO reqDTO);
}

View File

@@ -0,0 +1,18 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork 注册授权请求 DTO供其他模块通过 Feign 调用 system-server 时使用)
*/
@Data
public class IWorkAuthRegisterReqDTO {
@Schema(description = "iWork 应用编码", requiredMode = Schema.RequiredMode.REQUIRED)
private String appCode;
@Schema(description = "iWork 网关地址", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String baseUrl;
}

View File

@@ -0,0 +1,18 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork 注册授权响应 DTO
*/
@Data
public class IWorkAuthRegisterRespDTO {
@Schema(description = "服务端公钥(Base64)")
private String publicKey;
@Schema(description = "服务端下发的 secret")
private String secret;
}

View File

@@ -0,0 +1,15 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork Token 申请请求 DTO
*/
@Data
public class IWorkAuthTokenReqDTO {
@Schema(description = "应用编码", requiredMode = Schema.RequiredMode.REQUIRED)
private String appCode;
}

View File

@@ -0,0 +1,18 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork Token 响应 DTO
*/
@Data
public class IWorkAuthTokenRespDTO {
@Schema(description = "访问令牌")
private String accessToken;
@Schema(description = "过期时间(秒)")
private Long expiresIn;
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* iWork 部门分页响应 DTO
*/
@Data
public class IWorkHrDepartmentPageRespDTO {
@Schema(description = "总条数")
private Long total;
@Schema(description = "当前页数据")
private List<Item> list;
@Data
public static class Item {
@Schema(description = "部门编号")
private String id;
@Schema(description = "部门名称")
private String name;
}
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* iWork 岗位分页响应 DTO
*/
@Data
public class IWorkHrJobTitlePageRespDTO {
@Schema(description = "总条数")
private Long total;
@Schema(description = "当前页数据")
private List<Item> list;
@Data
public static class Item {
@Schema(description = "岗位编号")
private String id;
@Schema(description = "岗位名称")
private String name;
}
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* iWork 分部分页响应 DTO
*/
@Data
public class IWorkHrSubcompanyPageRespDTO {
@Schema(description = "总条数")
private Long total;
@Schema(description = "当前页数据")
private List<Item> list;
@Data
public static class Item {
@Schema(description = "分部编号")
private String id;
@Schema(description = "分部名称")
private String name;
}
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* iWork 人员分页响应 DTO
*/
@Data
public class IWorkHrUserPageRespDTO {
@Schema(description = "总条数")
private Long total;
@Schema(description = "当前页数据")
private List<Item> list;
@Data
public static class Item {
@Schema(description = "人员编号")
private String id;
@Schema(description = "人员名称")
private String name;
}
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork 操作结果响应 DTO
*/
@Data
public class IWorkOperationRespDTO {
@Schema(description = "是否成功")
private Boolean success;
@Schema(description = "iWork 返回的操作编号或实例编号")
private String operationId;
@Schema(description = "提示信息")
private String message;
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork 人力组织分页查询通用请求 DTO
*/
@Data
public class IWorkOrgPageReqDTO {
@Schema(description = "页码", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer pageNo;
@Schema(description = "每页大小", example = "20", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer pageSize;
@Schema(description = "关键字过滤")
private String keyword;
}

View File

@@ -0,0 +1,15 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 根据外部标识解析 iWork 用户请求 DTO
*/
@Data
public class IWorkUserInfoReqDTO {
@Schema(description = "外部系统中的用户唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
private String externalUserCode;
}

View File

@@ -0,0 +1,18 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* iWork 用户解析响应 DTO
*/
@Data
public class IWorkUserInfoRespDTO {
@Schema(description = "iWork 用户编号")
private String userId;
@Schema(description = "iWork 用户名称")
private String userName;
}

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 发起 iWork 流程请求 DTO
*
* 与 IWorkWorkflowCreateReqVO 字段一一对应,便于 Feign 调用。
*/
@Data
public class IWorkWorkflowCreateReqDTO {
@Schema(description = "用印申请人iWork 人员 ID", example = "1001")
private String jbr;
@Schema(description = "用印部门 ID", example = "2001")
private String yybm;
@Schema(description = "用印单位(分部 ID", example = "3001")
private String fb;
@Schema(description = "申请时间,格式 yyyy-MM-dd", example = "2025-01-01")
private String sqsj;
@Schema(description = "用印去向")
private String yyqx;
@Schema(description = "用印依据附件 URL")
private String yyfkUrl;
@Schema(description = "用印事由或内容摘要")
private String yysy;
@Schema(description = "用印材料附件 URL必填")
private String xyywjUrl;
@Schema(description = "用印材料附件文件名(必填)")
private String xyywjFileName;
@Schema(description = "用印事项")
private String yysx;
@Schema(description = "业务系统单据编号(用于派生流程标题)", example = "DJ-2025-0001")
private String ywxtdjbh;
}

View File

@@ -0,0 +1,21 @@
package com.zt.plat.module.system.api.iwork.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 作废 / 干预 iWork 流程请求 DTO
*/
@Data
public class IWorkWorkflowVoidReqDTO {
@Schema(description = "iWork 实例编号", requiredMode = Schema.RequiredMode.REQUIRED)
private String instanceId;
@Schema(description = "操作人 iWork 用户编号", requiredMode = Schema.RequiredMode.REQUIRED)
private String operatorUserId;
@Schema(description = "作废原因")
private String reason;
}

View File

@@ -61,6 +61,7 @@ public interface ErrorCodeConstants {
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
ErrorCode USER_PASSWORD_MODIFY_FORBIDDEN = new ErrorCode(1_002_003_012, "该用户来源不支持修改密码");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "当前上级部门已存在同名子部门");

View File

@@ -0,0 +1,24 @@
package com.zt.plat.module.system.enums.user;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 密码处理策略,用于区分本地账户与外部同步账户的密码存储/校验方式。
*/
@AllArgsConstructor
@Getter
public enum PasswordStrategyEnum {
/**
* 本地创建或注册用户,使用 Spring Security {@code PasswordEncoder}BCrypt
*/
LOCAL_BCRYPT("LOCAL_BCRYPT"),
/**
* iWork 同步的 MD5 密文,直接按大写 MD5 存储及校验。
*/
IWORK_MD5("IWORK_MD5");
private final String label;
}

View File

@@ -3,6 +3,9 @@ package com.zt.plat.module.system.enums.user;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 用户来源枚举
*
@@ -12,9 +15,9 @@ import lombok.Getter;
@Getter
public enum UserSourceEnum {
EXTERNAL(1, "外部用户"), // 系统创建、注册等方式产生的用户
SYNC(2, "同步用户"), // 通过 UserSyncService 同步的用户
IWORK(3, "iWork 用户"); // 通过 iWork 全量/单条同步产生的用户
EXTERNAL(1, "外部用户", PasswordStrategyEnum.LOCAL_BCRYPT), // 系统创建、注册等方式产生的用户
SYNC(2, "同步用户", PasswordStrategyEnum.LOCAL_BCRYPT), // 通过 UserSyncService 同步的用户
IWORK(3, "iWork 用户", PasswordStrategyEnum.IWORK_MD5); // 通过 iWork 全量/单条同步产生的用户
/**
* 类型
@@ -24,5 +27,28 @@ public enum UserSourceEnum {
* 名字
*/
private final String name;
/**
* 默认密码策略
*/
private final PasswordStrategyEnum passwordStrategy;
public static UserSourceEnum of(Integer source) {
if (source == null) {
return null;
}
return Arrays.stream(values())
.filter(item -> Objects.equals(item.source, source))
.findFirst()
.orElse(null);
}
public static PasswordStrategyEnum resolvePasswordStrategy(Integer source) {
UserSourceEnum matched = of(source);
return matched == null ? PasswordStrategyEnum.LOCAL_BCRYPT : matched.getPasswordStrategy();
}
public boolean isExternal() {
return this == EXTERNAL;
}
}