Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
12
pom.xml
12
pom.xml
@@ -237,8 +237,8 @@
|
|||||||
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
||||||
<config.namespace>dev</config.namespace>
|
<config.namespace>dev</config.namespace>
|
||||||
<config.group>DEFAULT_GROUP</config.group>
|
<config.group>DEFAULT_GROUP</config.group>
|
||||||
<config.username/>
|
<config.username>nacos</config.username>
|
||||||
<config.password/>
|
<config.password>P@ssword25</config.password>
|
||||||
<config.version>1.0.0</config.version>
|
<config.version>1.0.0</config.version>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
@@ -250,8 +250,8 @@
|
|||||||
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
||||||
<config.namespace>prod</config.namespace>
|
<config.namespace>prod</config.namespace>
|
||||||
<config.group>DEFAULT_GROUP</config.group>
|
<config.group>DEFAULT_GROUP</config.group>
|
||||||
<config.username/>
|
<config.username>nacos</config.username>
|
||||||
<config.password/>
|
<config.password>P@ssword25</config.password>
|
||||||
<config.version>1.0.0</config.version>
|
<config.version>1.0.0</config.version>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
@@ -263,8 +263,8 @@
|
|||||||
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
<config.server-addr>172.16.46.63:30848</config.server-addr>
|
||||||
<config.namespace>local</config.namespace>
|
<config.namespace>local</config.namespace>
|
||||||
<config.group>DEFAULT_GROUP</config.group>
|
<config.group>DEFAULT_GROUP</config.group>
|
||||||
<config.username/>
|
<config.username>nacos</config.username>
|
||||||
<config.password/>
|
<config.password>P@ssword25</config.password>
|
||||||
<config.version>1.0.0</config.version>
|
<config.version>1.0.0</config.version>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|||||||
@@ -87,6 +87,7 @@
|
|||||||
<netty.version>4.1.116.Final</netty.version>
|
<netty.version>4.1.116.Final</netty.version>
|
||||||
<mqtt.version>1.2.5</mqtt.version>
|
<mqtt.version>1.2.5</mqtt.version>
|
||||||
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
||||||
|
<okhttp3.version>4.12.0</okhttp3.version>
|
||||||
<!-- 规则引擎 -->
|
<!-- 规则引擎 -->
|
||||||
<liteflow.version>2.15.1</liteflow.version>
|
<liteflow.version>2.15.1</liteflow.version>
|
||||||
<vertx.version>4.5.13</vertx.version>
|
<vertx.version>4.5.13</vertx.version>
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import lombok.Getter;
|
|||||||
public enum DeptSourceEnum {
|
public enum DeptSourceEnum {
|
||||||
|
|
||||||
EXTERNAL(1, "外部部门"), // 系统创建的部门
|
EXTERNAL(1, "外部部门"), // 系统创建的部门
|
||||||
SYNC(2, "同步部门"); // 通过 OrgSyncService 同步的部门
|
SYNC(2, "同步部门"), // 通过 OrgSyncService 同步的部门
|
||||||
|
IWORK(3, "iWork 同步"); // 通过 iWork 同步的部门
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import lombok.Getter;
|
|||||||
public enum UserSourceEnum {
|
public enum UserSourceEnum {
|
||||||
|
|
||||||
EXTERNAL(1, "外部用户"), // 系统创建、注册等方式产生的用户
|
EXTERNAL(1, "外部用户"), // 系统创建、注册等方式产生的用户
|
||||||
SYNC(2, "同步用户"); // 通过 UserSyncService 同步的用户
|
SYNC(2, "同步用户"), // 通过 UserSyncService 同步的用户
|
||||||
|
IWORK(3, "iWork 用户"); // 通过 iWork 全量/单条同步产生的用户
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
package com.zt.plat.module.system.controller.admin.integration.iwork;
|
package com.zt.plat.module.system.controller.admin.integration.iwork;
|
||||||
|
|
||||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthRegisterRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserInfoRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCreateReqVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowVoidReqVO;
|
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
@@ -42,6 +29,7 @@ public class IWorkIntegrationController {
|
|||||||
|
|
||||||
private final IWorkIntegrationService integrationService;
|
private final IWorkIntegrationService integrationService;
|
||||||
private final IWorkOrgRestService orgRestService;
|
private final IWorkOrgRestService orgRestService;
|
||||||
|
private final IWorkSyncService syncService;
|
||||||
|
|
||||||
@PostMapping("/auth/register")
|
@PostMapping("/auth/register")
|
||||||
@Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret")
|
@Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret")
|
||||||
@@ -77,49 +65,63 @@ public class IWorkIntegrationController {
|
|||||||
|
|
||||||
@PostMapping("/hr/subcompany/page")
|
@PostMapping("/hr/subcompany/page")
|
||||||
@Operation(summary = "获取 iWork 分部列表")
|
@Operation(summary = "获取 iWork 分部列表")
|
||||||
public CommonResult<IWorkOrgRespVO> listSubcompanies(@Valid @RequestBody IWorkSubcompanyQueryReqVO reqVO) {
|
public CommonResult<IWorkHrSubcompanyPageRespVO> listSubcompanies(@Valid @RequestBody IWorkSubcompanyQueryReqVO reqVO) {
|
||||||
return success(orgRestService.listSubcompanies(reqVO));
|
return success(orgRestService.listSubcompanies(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/hr/department/page")
|
@PostMapping("/hr/department/page")
|
||||||
@Operation(summary = "获取 iWork 部门列表")
|
@Operation(summary = "获取 iWork 部门列表")
|
||||||
public CommonResult<IWorkOrgRespVO> listDepartments(@Valid @RequestBody IWorkDepartmentQueryReqVO reqVO) {
|
public CommonResult<IWorkHrDepartmentPageRespVO> listDepartments(@Valid @RequestBody IWorkDepartmentQueryReqVO reqVO) {
|
||||||
return success(orgRestService.listDepartments(reqVO));
|
return success(orgRestService.listDepartments(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/hr/job-title/page")
|
@PostMapping("/hr/job-title/page")
|
||||||
@Operation(summary = "获取 iWork 岗位列表")
|
@Operation(summary = "获取 iWork 岗位列表")
|
||||||
public CommonResult<IWorkOrgRespVO> listJobTitles(@Valid @RequestBody IWorkJobTitleQueryReqVO reqVO) {
|
public CommonResult<IWorkHrJobTitlePageRespVO> listJobTitles(@Valid @RequestBody IWorkJobTitleQueryReqVO reqVO) {
|
||||||
return success(orgRestService.listJobTitles(reqVO));
|
return success(orgRestService.listJobTitles(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/hr/user/page")
|
@PostMapping("/hr/user/page")
|
||||||
@Operation(summary = "获取 iWork 人员列表")
|
@Operation(summary = "获取 iWork 人员列表")
|
||||||
public CommonResult<IWorkOrgRespVO> listUsers(@Valid @RequestBody IWorkUserQueryReqVO reqVO) {
|
public CommonResult<IWorkHrUserPageRespVO> listUsers(@Valid @RequestBody IWorkUserQueryReqVO reqVO) {
|
||||||
return success(orgRestService.listUsers(reqVO));
|
return success(orgRestService.listUsers(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/hr/subcompany/sync")
|
// @PostMapping("/hr/subcompany/sync")
|
||||||
@Operation(summary = "同步分部信息至 iWork")
|
// @Operation(summary = "同步分部信息至 iWork")
|
||||||
public CommonResult<IWorkOrgRespVO> syncSubcompanies(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
// public CommonResult<IWorkHrSyncRespVO> syncSubcompanies(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||||
return success(orgRestService.syncSubcompanies(reqVO));
|
// return success(orgRestService.syncSubcompanies(reqVO));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @PostMapping("/hr/department/sync")
|
||||||
|
// @Operation(summary = "同步部门信息至 iWork")
|
||||||
|
// public CommonResult<IWorkHrSyncRespVO> syncDepartments(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||||
|
// return success(orgRestService.syncDepartments(reqVO));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @PostMapping("/hr/job-title/sync")
|
||||||
|
// @Operation(summary = "同步岗位信息至 iWork")
|
||||||
|
// public CommonResult<IWorkHrSyncRespVO> syncJobTitles(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||||
|
// return success(orgRestService.syncJobTitles(reqVO));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @PostMapping("/hr/user/sync")
|
||||||
|
// @Operation(summary = "同步人员信息至 iWork")
|
||||||
|
// public CommonResult<IWorkHrSyncRespVO> syncUsers(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
||||||
|
// return success(orgRestService.syncUsers(reqVO));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ----------------- 同步到本地 -----------------
|
||||||
|
|
||||||
|
@PostMapping("/hr/full-sync")
|
||||||
|
@Operation(summary = "手动触发 iWork 组织/人员全量同步")
|
||||||
|
public CommonResult<IWorkFullSyncRespVO> fullSync(@Valid @RequestBody IWorkFullSyncReqVO reqVO) {
|
||||||
|
return success(syncService.fullSync(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/hr/department/sync")
|
@PostMapping("/hr/single-sync")
|
||||||
@Operation(summary = "同步部门信息至 iWork")
|
@Operation(summary = "按 iWork ID 同步单条组织/人员")
|
||||||
public CommonResult<IWorkOrgRespVO> syncDepartments(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
public CommonResult<IWorkSingleSyncRespVO> singleSync(@Valid @RequestBody IWorkSingleSyncReqVO reqVO) {
|
||||||
return success(orgRestService.syncDepartments(reqVO));
|
return success(syncService.syncSingle(reqVO));
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/hr/job-title/sync")
|
|
||||||
@Operation(summary = "同步岗位信息至 iWork")
|
|
||||||
public CommonResult<IWorkOrgRespVO> syncJobTitles(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
|
||||||
return success(orgRestService.syncJobTitles(reqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/hr/user/sync")
|
|
||||||
@Operation(summary = "同步人员信息至 iWork")
|
|
||||||
public CommonResult<IWorkOrgRespVO> syncUsers(@Valid @RequestBody IWorkOrgSyncReqVO reqVO) {
|
|
||||||
return success(orgRestService.syncUsers(reqVO));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Max;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 全量同步请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkFullSyncReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "起始页码,从 1 开始", example = "1")
|
||||||
|
@Min(1)
|
||||||
|
private Integer startPage = 1;
|
||||||
|
|
||||||
|
@Schema(description = "最大处理页数,null 表示处理至 iWork 返回的末页", example = "10")
|
||||||
|
@Min(1)
|
||||||
|
private Integer maxPages;
|
||||||
|
|
||||||
|
@Schema(description = "每次分页从 iWork 拉取的记录数", example = "100")
|
||||||
|
@Min(1)
|
||||||
|
@Max(500)
|
||||||
|
private Integer pageSize = 100;
|
||||||
|
|
||||||
|
@Schema(description = "同步范围列表,默认同步全部。可选:subcompany、department、jobTitle、user")
|
||||||
|
private List<String> scopes;
|
||||||
|
|
||||||
|
@Schema(description = "是否包含已失效(canceled=1)的记录", example = "false")
|
||||||
|
private Boolean includeCanceled = Boolean.FALSE;
|
||||||
|
|
||||||
|
public Set<IWorkSyncEntityTypeEnum> resolveScopes() {
|
||||||
|
EnumSet<IWorkSyncEntityTypeEnum> defaults = EnumSet.allOf(IWorkSyncEntityTypeEnum.class);
|
||||||
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
Set<IWorkSyncEntityTypeEnum> resolved = scopes.stream()
|
||||||
|
.map(IWorkSyncEntityTypeEnum::fromCode)
|
||||||
|
.filter(java.util.Objects::nonNull)
|
||||||
|
.collect(Collectors.toCollection(() -> EnumSet.noneOf(IWorkSyncEntityTypeEnum.class)));
|
||||||
|
if (resolved.isEmpty()) {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 全量同步响应
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkFullSyncRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "本次处理的总页数")
|
||||||
|
private Integer processedPages;
|
||||||
|
|
||||||
|
@Schema(description = "每次分页请求的条数")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "分部统计信息")
|
||||||
|
private IWorkSyncEntityStatVO subcompanyStat = new IWorkSyncEntityStatVO();
|
||||||
|
|
||||||
|
@Schema(description = "部门统计信息")
|
||||||
|
private IWorkSyncEntityStatVO departmentStat = new IWorkSyncEntityStatVO();
|
||||||
|
|
||||||
|
@Schema(description = "岗位统计信息")
|
||||||
|
private IWorkSyncEntityStatVO jobTitleStat = new IWorkSyncEntityStatVO();
|
||||||
|
|
||||||
|
@Schema(description = "人员统计信息")
|
||||||
|
private IWorkSyncEntityStatVO userStat = new IWorkSyncEntityStatVO();
|
||||||
|
|
||||||
|
@Schema(description = "每个批次的详细统计")
|
||||||
|
private List<IWorkSyncBatchStatVO> batches;
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 部门分页响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "iWork 部门分页响应")
|
||||||
|
public class IWorkHrDepartmentPageRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "响应码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "总条数")
|
||||||
|
private Integer totalSize;
|
||||||
|
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
private Integer totalPage;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Integer pageNumber;
|
||||||
|
|
||||||
|
@Schema(description = "部门数据列表")
|
||||||
|
private List<Department> dataList;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "部门信息")
|
||||||
|
public static class Department {
|
||||||
|
|
||||||
|
@Schema(description = "部门 ID")
|
||||||
|
@JsonProperty("departmentid")
|
||||||
|
private Integer departmentid;
|
||||||
|
|
||||||
|
@Schema(description = "部门编码")
|
||||||
|
@JsonProperty("departmentcode")
|
||||||
|
private String departmentcode;
|
||||||
|
|
||||||
|
@Schema(description = "部门名称")
|
||||||
|
@JsonProperty("departmentname")
|
||||||
|
private String departmentname;
|
||||||
|
|
||||||
|
@Schema(description = "部门标识")
|
||||||
|
@JsonProperty("departmentmark")
|
||||||
|
private String departmentmark;
|
||||||
|
|
||||||
|
@Schema(description = "所属分部 ID")
|
||||||
|
@JsonProperty("subcompanyid1")
|
||||||
|
private Integer subcompanyid1;
|
||||||
|
|
||||||
|
@Schema(description = "所属分部名称")
|
||||||
|
@JsonProperty("subcompanyname")
|
||||||
|
private String subcompanyname;
|
||||||
|
|
||||||
|
@Schema(description = "上级分部 ID")
|
||||||
|
@JsonProperty("supsubcomid")
|
||||||
|
private Integer supsubcomid;
|
||||||
|
|
||||||
|
@Schema(description = "上级分部名称")
|
||||||
|
@JsonProperty("supsubcomname")
|
||||||
|
private String supsubcomname;
|
||||||
|
|
||||||
|
@Schema(description = "父部门 ID")
|
||||||
|
@JsonProperty("parentdeptid")
|
||||||
|
private Integer parentdeptid;
|
||||||
|
|
||||||
|
@Schema(description = "父部门名称")
|
||||||
|
@JsonProperty("parentdeptname")
|
||||||
|
private String parentdeptname;
|
||||||
|
|
||||||
|
@Schema(description = "层级路径")
|
||||||
|
@JsonProperty("alllevel")
|
||||||
|
private String alllevel;
|
||||||
|
|
||||||
|
@Schema(description = "显示顺序")
|
||||||
|
@JsonProperty("showorder")
|
||||||
|
@JsonDeserialize(using = LenientIntegerDeserializer.class)
|
||||||
|
private Integer showorder;
|
||||||
|
|
||||||
|
@Schema(description = "是否有子部门 (0/1)")
|
||||||
|
@JsonProperty("haschild")
|
||||||
|
private String haschild;
|
||||||
|
|
||||||
|
@Schema(description = "是否已失效 (0/1)")
|
||||||
|
@JsonProperty("canceled")
|
||||||
|
private String canceled;
|
||||||
|
|
||||||
|
@Schema(description = "部门类型")
|
||||||
|
@JsonProperty("departmenttype")
|
||||||
|
private String departmenttype;
|
||||||
|
|
||||||
|
@Schema(description = "负责人 ID")
|
||||||
|
@JsonProperty("managerid")
|
||||||
|
private Integer managerid;
|
||||||
|
|
||||||
|
@Schema(description = "负责人名称")
|
||||||
|
@JsonProperty("manager")
|
||||||
|
private String manager;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void putAttribute(String key, Object value) {
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> any() {
|
||||||
|
return attributes == null ? Collections.emptyMap() : attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 岗位分页响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "iWork 岗位分页响应")
|
||||||
|
public class IWorkHrJobTitlePageRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "响应码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "总条数")
|
||||||
|
private Integer totalSize;
|
||||||
|
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
private Integer totalPage;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Integer pageNumber;
|
||||||
|
|
||||||
|
@Schema(description = "岗位数据列表")
|
||||||
|
private List<JobTitle> dataList;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "岗位信息")
|
||||||
|
public static class JobTitle {
|
||||||
|
|
||||||
|
@Schema(description = "岗位 ID")
|
||||||
|
@JsonProperty("jobtitleid")
|
||||||
|
private Integer jobtitleid;
|
||||||
|
|
||||||
|
@Schema(description = "岗位编码")
|
||||||
|
@JsonProperty("jobtitlecode")
|
||||||
|
private String jobtitlecode;
|
||||||
|
|
||||||
|
@Schema(description = "岗位名称")
|
||||||
|
@JsonProperty("jobtitlename")
|
||||||
|
private String jobtitlename;
|
||||||
|
|
||||||
|
@Schema(description = "岗位类型")
|
||||||
|
@JsonProperty("jobtitletype")
|
||||||
|
private String jobtitletype;
|
||||||
|
|
||||||
|
@Schema(description = "所属岗位组 ID")
|
||||||
|
@JsonProperty("jobgroupid")
|
||||||
|
private Integer jobgroupid;
|
||||||
|
|
||||||
|
@Schema(description = "所属岗位组名称")
|
||||||
|
@JsonProperty("jobgroupname")
|
||||||
|
private String jobgroupname;
|
||||||
|
|
||||||
|
@Schema(description = "岗位层级")
|
||||||
|
@JsonProperty("joblevel")
|
||||||
|
private String joblevel;
|
||||||
|
|
||||||
|
@Schema(description = "岗位职责")
|
||||||
|
@JsonProperty("jobfunction")
|
||||||
|
private String jobfunction;
|
||||||
|
|
||||||
|
@Schema(description = "岗位描述")
|
||||||
|
@JsonProperty("description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "上级岗位 ID")
|
||||||
|
@JsonProperty("supjobtitleid")
|
||||||
|
private Integer supjobtitleid;
|
||||||
|
|
||||||
|
@Schema(description = "上级岗位名称")
|
||||||
|
@JsonProperty("supjobtitlename")
|
||||||
|
private String supjobtitlename;
|
||||||
|
|
||||||
|
@Schema(description = "显示顺序")
|
||||||
|
@JsonProperty("showorder")
|
||||||
|
@JsonDeserialize(using = LenientIntegerDeserializer.class)
|
||||||
|
private Integer showorder;
|
||||||
|
|
||||||
|
@Schema(description = "是否已失效 (0/1)")
|
||||||
|
@JsonProperty("canceled")
|
||||||
|
private String canceled;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void putAttribute(String key, Object value) {
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> any() {
|
||||||
|
return attributes == null ? Collections.emptyMap() : attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 分部分页响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "iWork 分部分页响应")
|
||||||
|
public class IWorkHrSubcompanyPageRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "响应码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "总条数")
|
||||||
|
private Integer totalSize;
|
||||||
|
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
private Integer totalPage;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Integer pageNumber;
|
||||||
|
|
||||||
|
@Schema(description = "分部数据列表")
|
||||||
|
private List<Subcompany> dataList;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "分部信息")
|
||||||
|
public static class Subcompany {
|
||||||
|
|
||||||
|
@Schema(description = "分部唯一 ID")
|
||||||
|
@JsonProperty("subcompanyid1")
|
||||||
|
private Integer subcompanyid1;
|
||||||
|
|
||||||
|
@Schema(description = "分部编码")
|
||||||
|
@JsonProperty("subcompanycode")
|
||||||
|
private String subcompanycode;
|
||||||
|
|
||||||
|
@Schema(description = "分部名称")
|
||||||
|
@JsonProperty("subcompanyname")
|
||||||
|
private String subcompanyname;
|
||||||
|
|
||||||
|
@Schema(description = "所属总部 ID")
|
||||||
|
@JsonProperty("companyid")
|
||||||
|
private Integer companyid;
|
||||||
|
|
||||||
|
@Schema(description = "所属总部名称")
|
||||||
|
@JsonProperty("companyname")
|
||||||
|
private String companyname;
|
||||||
|
|
||||||
|
@Schema(description = "上级分部 ID")
|
||||||
|
@JsonProperty("supsubcomid")
|
||||||
|
private Integer supsubcomid;
|
||||||
|
|
||||||
|
@Schema(description = "上级分部名称")
|
||||||
|
@JsonProperty("supsubcomname")
|
||||||
|
private String supsubcomname;
|
||||||
|
|
||||||
|
@Schema(description = "显示顺序")
|
||||||
|
@JsonProperty("showorder")
|
||||||
|
@JsonDeserialize(using = LenientIntegerDeserializer.class)
|
||||||
|
private Integer showorder;
|
||||||
|
|
||||||
|
@Schema(description = "分部描述")
|
||||||
|
@JsonProperty("description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "是否已失效(0/1)")
|
||||||
|
@JsonProperty("canceled")
|
||||||
|
private String canceled;
|
||||||
|
|
||||||
|
@Schema(description = "层级路径")
|
||||||
|
@JsonProperty("alllevel")
|
||||||
|
private String alllevel;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void putAttribute(String key, Object value) {
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> any() {
|
||||||
|
return attributes == null ? Collections.emptyMap() : attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 人力同步响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "iWork 人力同步响应")
|
||||||
|
public class IWorkHrSyncRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "响应码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "同步结果明细")
|
||||||
|
private List<SyncResult> result;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "同步结果项")
|
||||||
|
public static class SyncResult {
|
||||||
|
|
||||||
|
@Schema(description = "操作动作 add/update/delete")
|
||||||
|
@JsonProperty("@action")
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
@Schema(description = "外部编码")
|
||||||
|
@JsonProperty("code")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "执行结果 success/fail")
|
||||||
|
@JsonProperty("result")
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
@JsonProperty("success")
|
||||||
|
private Boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "失败描述")
|
||||||
|
@JsonProperty("message")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void putAttribute(String key, Object value) {
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> any() {
|
||||||
|
return attributes == null ? Collections.emptyMap() : attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.jackson.LenientIntegerDeserializer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 人员分页响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "iWork 人员分页响应")
|
||||||
|
public class IWorkHrUserPageRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "响应码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Schema(description = "是否成功")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Schema(description = "总条数")
|
||||||
|
private Integer totalSize;
|
||||||
|
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
private Integer totalPage;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Integer pageNumber;
|
||||||
|
|
||||||
|
@Schema(description = "人员数据列表")
|
||||||
|
private List<User> dataList;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "人员信息")
|
||||||
|
public static class User {
|
||||||
|
|
||||||
|
@Schema(description = "人员 ID")
|
||||||
|
@JsonProperty("id")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "人员姓名")
|
||||||
|
@JsonProperty("lastname")
|
||||||
|
private String lastname;
|
||||||
|
|
||||||
|
@Schema(description = "登录账号")
|
||||||
|
@JsonProperty("loginid")
|
||||||
|
private String loginid;
|
||||||
|
|
||||||
|
@Schema(description = "工号")
|
||||||
|
@JsonProperty("workcode")
|
||||||
|
private String workcode;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
@JsonProperty("sex")
|
||||||
|
private String sex;
|
||||||
|
|
||||||
|
@Schema(description = "所属分部 ID")
|
||||||
|
@JsonProperty("subcompanyid1")
|
||||||
|
private Integer subcompanyid1;
|
||||||
|
|
||||||
|
@Schema(description = "所属分部名称")
|
||||||
|
@JsonProperty("subcompanyname")
|
||||||
|
private String subcompanyname;
|
||||||
|
|
||||||
|
@Schema(description = "所属部门 ID")
|
||||||
|
@JsonProperty("departmentid")
|
||||||
|
private Integer departmentid;
|
||||||
|
|
||||||
|
@Schema(description = "所属部门名称")
|
||||||
|
@JsonProperty("departmentname")
|
||||||
|
private String departmentname;
|
||||||
|
|
||||||
|
@Schema(description = "所属岗位 ID")
|
||||||
|
@JsonProperty("jobtitleid")
|
||||||
|
private Integer jobtitleid;
|
||||||
|
|
||||||
|
@Schema(description = "所属岗位名称")
|
||||||
|
@JsonProperty("jobtitlename")
|
||||||
|
private String jobtitlename;
|
||||||
|
|
||||||
|
@Schema(description = "手机号码")
|
||||||
|
@JsonProperty("mobile")
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
@Schema(description = "办公电话")
|
||||||
|
@JsonProperty("telephone")
|
||||||
|
private String telephone;
|
||||||
|
|
||||||
|
@Schema(description = "邮箱")
|
||||||
|
@JsonProperty("email")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Schema(description = "直属上级 ID")
|
||||||
|
@JsonProperty("managerid")
|
||||||
|
private Integer managerid;
|
||||||
|
|
||||||
|
@Schema(description = "助理 ID")
|
||||||
|
@JsonProperty("assistantid")
|
||||||
|
private Integer assistantid;
|
||||||
|
|
||||||
|
@Schema(description = "安全级别")
|
||||||
|
@JsonProperty("seclevel")
|
||||||
|
private Integer seclevel;
|
||||||
|
|
||||||
|
@Schema(description = "当前状态")
|
||||||
|
@JsonProperty("status")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "入职日期")
|
||||||
|
@JsonProperty("hiredate")
|
||||||
|
private String hiredate;
|
||||||
|
|
||||||
|
@Schema(description = "离职日期")
|
||||||
|
@JsonProperty("leavedate")
|
||||||
|
private String leavedate;
|
||||||
|
|
||||||
|
@Schema(description = "出生日期")
|
||||||
|
@JsonProperty("birthday")
|
||||||
|
private String birthday;
|
||||||
|
|
||||||
|
@Schema(description = "民族")
|
||||||
|
@JsonProperty("folk")
|
||||||
|
private String folk;
|
||||||
|
|
||||||
|
@Schema(description = "婚姻状况")
|
||||||
|
@JsonProperty("maritalstatus")
|
||||||
|
private String maritalstatus;
|
||||||
|
|
||||||
|
@Schema(description = "文化程度")
|
||||||
|
@JsonProperty("educationlevel")
|
||||||
|
private String educationlevel;
|
||||||
|
|
||||||
|
@Schema(description = "籍贯")
|
||||||
|
@JsonProperty("nativeplace")
|
||||||
|
private String nativeplace;
|
||||||
|
|
||||||
|
@Schema(description = "户口所在地")
|
||||||
|
@JsonProperty("nationality")
|
||||||
|
private String nationality;
|
||||||
|
|
||||||
|
@Schema(description = "证件号码")
|
||||||
|
@JsonProperty("certificatenum")
|
||||||
|
private String certificatenum;
|
||||||
|
|
||||||
|
@Schema(description = "显示顺序")
|
||||||
|
@JsonProperty("dsporder")
|
||||||
|
@JsonDeserialize(using = LenientIntegerDeserializer.class)
|
||||||
|
private Integer dsporder;
|
||||||
|
|
||||||
|
@Schema(description = "系统语言")
|
||||||
|
@JsonProperty("systemlanguage")
|
||||||
|
private String systemlanguage;
|
||||||
|
|
||||||
|
@Schema(description = "账号类型")
|
||||||
|
@JsonProperty("accounttype")
|
||||||
|
private String accounttype;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Object> attributes;
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void putAttribute(String key, Object value) {
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
attributes.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> any() {
|
||||||
|
return attributes == null ? Collections.emptyMap() : attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,15 @@
|
|||||||
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对 iWork 人力组织 REST 请求的响应封装。
|
* @deprecated 请改用强类型的 IWorkHr*RespVO,避免再引用该占位类。
|
||||||
*/
|
*/
|
||||||
@Data
|
@Deprecated(forRemoval = true)
|
||||||
public class IWorkOrgRespVO {
|
@Schema(description = "已废弃,占位用")
|
||||||
|
public final class IWorkOrgRespVO {
|
||||||
|
|
||||||
@Schema(description = "响应中的业务数据(data 字段或整体映射)")
|
private IWorkOrgRespVO() {
|
||||||
private Map<String, Object> payload;
|
throw new UnsupportedOperationException("Use IWorkHr*RespVO instead");
|
||||||
|
}
|
||||||
@Schema(description = "原始响应字符串")
|
|
||||||
private String rawBody;
|
|
||||||
|
|
||||||
@Schema(description = "是否判断为成功")
|
|
||||||
private boolean success;
|
|
||||||
|
|
||||||
@Schema(description = "提示信息")
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
@Schema(description = "响应码")
|
|
||||||
private String code;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 单条同步请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkSingleSyncReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "同步的实体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "user")
|
||||||
|
@NotNull(message = "实体类型不能为空")
|
||||||
|
private IWorkSyncEntityTypeEnum entityType;
|
||||||
|
|
||||||
|
@Schema(description = "iWork 提供的实体主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10001")
|
||||||
|
@NotNull(message = "实体 ID 不能为空")
|
||||||
|
@Min(1)
|
||||||
|
private Long entityId;
|
||||||
|
|
||||||
|
@Schema(description = "缺失时是否自动创建", example = "true")
|
||||||
|
private Boolean createIfMissing = Boolean.TRUE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 单条同步响应
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkSingleSyncRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "同步的实体类型")
|
||||||
|
private IWorkSyncEntityTypeEnum entityType;
|
||||||
|
|
||||||
|
@Schema(description = "实体 ID")
|
||||||
|
private Long entityId;
|
||||||
|
|
||||||
|
@Schema(description = "是否创建了新的记录")
|
||||||
|
private boolean created;
|
||||||
|
|
||||||
|
@Schema(description = "是否对已有记录进行了更新")
|
||||||
|
private boolean updated;
|
||||||
|
|
||||||
|
@Schema(description = "提示信息")
|
||||||
|
private String message;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录一次分页批次执行的统计信息。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkSyncBatchStatVO {
|
||||||
|
|
||||||
|
@Schema(description = "同步的实体类型")
|
||||||
|
private IWorkSyncEntityTypeEnum entityType;
|
||||||
|
|
||||||
|
@Schema(description = "当前批次处理的页码,从 1 开始")
|
||||||
|
private Integer pageNumber;
|
||||||
|
|
||||||
|
@Schema(description = "本批次从 iWork 拉取的记录数量")
|
||||||
|
private Integer pulled;
|
||||||
|
|
||||||
|
@Schema(description = "本批次创建的记录数量")
|
||||||
|
private Integer created;
|
||||||
|
|
||||||
|
@Schema(description = "本批次因已存在而跳过的记录数量")
|
||||||
|
private Integer skippedExisting;
|
||||||
|
|
||||||
|
@Schema(description = "本批次禁用的记录数量")
|
||||||
|
private Integer disabled;
|
||||||
|
|
||||||
|
@Schema(description = "本批次失败的记录数量")
|
||||||
|
private Integer failed;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 同步实体统计信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IWorkSyncEntityStatVO {
|
||||||
|
|
||||||
|
@Schema(description = "从 iWork 拉取的记录数量")
|
||||||
|
private int pulled;
|
||||||
|
|
||||||
|
@Schema(description = "在本系统中新创建的记录数量")
|
||||||
|
private int created;
|
||||||
|
|
||||||
|
@Schema(description = "因已存在而跳过的记录数量")
|
||||||
|
private int skippedExisting;
|
||||||
|
|
||||||
|
@Schema(description = "在本系统中被禁用的记录数量")
|
||||||
|
private int disabled;
|
||||||
|
|
||||||
|
@Schema(description = "同步失败的记录数量")
|
||||||
|
private int failed;
|
||||||
|
|
||||||
|
public void incrementPulled(int delta) {
|
||||||
|
this.pulled += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementCreated(int delta) {
|
||||||
|
this.created += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementSkipped(int delta) {
|
||||||
|
this.skippedExisting += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementDisabled(int delta) {
|
||||||
|
this.disabled += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementFailed(int delta) {
|
||||||
|
this.failed += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.zt.plat.module.system.enums.integration;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 同步支持的实体类型。
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum IWorkSyncEntityTypeEnum {
|
||||||
|
|
||||||
|
SUBCOMPANY("subcompany", "分部 / 公司"),
|
||||||
|
DEPARTMENT("department", "部门"),
|
||||||
|
JOB_TITLE("jobTitle", "岗位"),
|
||||||
|
USER("user", "人员");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
public static IWorkSyncEntityTypeEnum fromCode(String code) {
|
||||||
|
if (code == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (IWorkSyncEntityTypeEnum value : values()) {
|
||||||
|
if (value.code.equalsIgnoreCase(code)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -444,7 +444,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
Integer userSource = user.getUserSource();
|
Integer userSource = user.getUserSource();
|
||||||
|
|
||||||
// 同步用户(SYNC = 2)为内部用户,需要使用E办登录
|
// 同步用户(SYNC = 2)为内部用户,需要使用E办登录
|
||||||
if (userSource != null && userSource.equals(UserSourceEnum.SYNC.getSource())) {
|
if (userSource != null &&
|
||||||
|
(userSource.equals(UserSourceEnum.SYNC.getSource()) ||
|
||||||
|
userSource.equals(UserSourceEnum.IWORK.getSource()))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.zt.plat.module.system.service.integration.iwork;
|
package com.zt.plat.module.system.service.integration.iwork;
|
||||||
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSyncRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
||||||
@@ -12,19 +16,19 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUser
|
|||||||
*/
|
*/
|
||||||
public interface IWorkOrgRestService {
|
public interface IWorkOrgRestService {
|
||||||
|
|
||||||
IWorkOrgRespVO listSubcompanies(IWorkSubcompanyQueryReqVO reqVO);
|
IWorkHrSubcompanyPageRespVO listSubcompanies(IWorkSubcompanyQueryReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO listDepartments(IWorkDepartmentQueryReqVO reqVO);
|
IWorkHrDepartmentPageRespVO listDepartments(IWorkDepartmentQueryReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO listJobTitles(IWorkJobTitleQueryReqVO reqVO);
|
IWorkHrJobTitlePageRespVO listJobTitles(IWorkJobTitleQueryReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO listUsers(IWorkUserQueryReqVO reqVO);
|
IWorkHrUserPageRespVO listUsers(IWorkUserQueryReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO);
|
IWorkHrSyncRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO syncDepartments(IWorkOrgSyncReqVO reqVO);
|
IWorkHrSyncRespVO syncDepartments(IWorkOrgSyncReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO);
|
IWorkHrSyncRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO);
|
||||||
|
|
||||||
IWorkOrgRespVO syncUsers(IWorkOrgSyncReqVO reqVO);
|
IWorkHrSyncRespVO syncUsers(IWorkOrgSyncReqVO reqVO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction for applying iWork entities into local persistence.
|
||||||
|
*/
|
||||||
|
public interface IWorkSyncProcessor {
|
||||||
|
|
||||||
|
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options);
|
||||||
|
|
||||||
|
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options);
|
||||||
|
|
||||||
|
BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options);
|
||||||
|
|
||||||
|
BatchResult syncUsers(List<IWorkHrUserPageRespVO.User> data, SyncOptions options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execution options shared by batch and single sync flows.
|
||||||
|
*/
|
||||||
|
final class SyncOptions {
|
||||||
|
private final boolean includeCanceled;
|
||||||
|
private final boolean allowUpdate;
|
||||||
|
private final boolean createIfMissing;
|
||||||
|
|
||||||
|
private SyncOptions(boolean includeCanceled, boolean allowUpdate, boolean createIfMissing) {
|
||||||
|
this.includeCanceled = includeCanceled;
|
||||||
|
this.allowUpdate = allowUpdate;
|
||||||
|
this.createIfMissing = createIfMissing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SyncOptions full(boolean includeCanceled) {
|
||||||
|
return new SyncOptions(includeCanceled, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SyncOptions single(boolean createIfMissing) {
|
||||||
|
return new SyncOptions(true, true, Boolean.TRUE.equals(createIfMissing));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SyncOptions custom(boolean includeCanceled, boolean allowUpdate, boolean createIfMissing) {
|
||||||
|
return new SyncOptions(includeCanceled, allowUpdate, createIfMissing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIncludeCanceled() {
|
||||||
|
return includeCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowUpdate() {
|
||||||
|
return allowUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreateIfMissing() {
|
||||||
|
return createIfMissing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregated result for a sync batch.
|
||||||
|
*/
|
||||||
|
final class BatchResult {
|
||||||
|
private int pulled;
|
||||||
|
private int created;
|
||||||
|
private int skipped;
|
||||||
|
private int disabled;
|
||||||
|
private int failed;
|
||||||
|
private int updated;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public static BatchResult empty() {
|
||||||
|
return new BatchResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchResult withMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchResult merge(BatchResult other) {
|
||||||
|
if (other == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.pulled += other.pulled;
|
||||||
|
this.created += other.created;
|
||||||
|
this.skipped += other.skipped;
|
||||||
|
this.disabled += other.disabled;
|
||||||
|
this.failed += other.failed;
|
||||||
|
this.updated += other.updated;
|
||||||
|
if (Objects.nonNull(other.message)) {
|
||||||
|
this.message = other.message;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatchResult fromSingle(BatchResult single) {
|
||||||
|
return empty().merge(single);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatchResult singleCreated(String message) {
|
||||||
|
BatchResult result = empty();
|
||||||
|
result.created = 1;
|
||||||
|
result.message = message;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatchResult singleSkipped(String message) {
|
||||||
|
BatchResult result = empty();
|
||||||
|
result.skipped = 1;
|
||||||
|
result.message = message;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BatchResult singleFailed(String message) {
|
||||||
|
BatchResult result = empty();
|
||||||
|
result.failed = 1;
|
||||||
|
result.message = message;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchResult increasePulled(int delta) {
|
||||||
|
this.pulled += delta;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseCreated() {
|
||||||
|
this.created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseSkipped() {
|
||||||
|
this.skipped++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseDisabled() {
|
||||||
|
this.disabled++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseFailed() {
|
||||||
|
this.failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseUpdated() {
|
||||||
|
this.updated++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPulled() {
|
||||||
|
return pulled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSkipped() {
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisabled() {
|
||||||
|
return disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFailed() {
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUpdated() {
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default BatchResult syncSubcompany(IWorkHrSubcompanyPageRespVO.Subcompany data, SyncOptions options) {
|
||||||
|
return syncSubcompanies(Collections.singletonList(data), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
default BatchResult syncDepartment(IWorkHrDepartmentPageRespVO.Department data, SyncOptions options) {
|
||||||
|
return syncDepartments(Collections.singletonList(data), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
default BatchResult syncJobTitle(IWorkHrJobTitlePageRespVO.JobTitle data, SyncOptions options) {
|
||||||
|
return syncJobTitles(Collections.singletonList(data), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
default BatchResult syncUser(IWorkHrUserPageRespVO.User data, SyncOptions options) {
|
||||||
|
return syncUsers(Collections.singletonList(data), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncRespVO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 组织/人员同步服务
|
||||||
|
*/
|
||||||
|
public interface IWorkSyncService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起全量分批同步
|
||||||
|
*/
|
||||||
|
IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 iWork ID 进行单条同步
|
||||||
|
*/
|
||||||
|
IWorkSingleSyncRespVO syncSingle(IWorkSingleSyncReqVO reqVO);
|
||||||
|
}
|
||||||
@@ -6,9 +6,13 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSyncRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgBaseQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgBaseQueryReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgRespVO;
|
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
||||||
@@ -30,8 +34,10 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_BASE_URL_MISSING;
|
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_BASE_URL_MISSING;
|
||||||
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_ORG_IDENTIFIER_MISSING;
|
import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationErrorCodeConstants.IWORK_ORG_IDENTIFIER_MISSING;
|
||||||
@@ -44,8 +50,21 @@ import static com.zt.plat.module.system.service.integration.iwork.IWorkIntegrati
|
|||||||
@Service
|
@Service
|
||||||
public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
||||||
|
|
||||||
private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() {
|
private static final TypeReference<List<IWorkHrSubcompanyPageRespVO.Subcompany>> SUBCOMPANY_LIST_TYPE =
|
||||||
};
|
new TypeReference<>() {
|
||||||
|
};
|
||||||
|
private static final TypeReference<List<IWorkHrDepartmentPageRespVO.Department>> DEPARTMENT_LIST_TYPE =
|
||||||
|
new TypeReference<>() {
|
||||||
|
};
|
||||||
|
private static final TypeReference<List<IWorkHrJobTitlePageRespVO.JobTitle>> JOB_TITLE_LIST_TYPE =
|
||||||
|
new TypeReference<>() {
|
||||||
|
};
|
||||||
|
private static final TypeReference<List<IWorkHrUserPageRespVO.User>> USER_LIST_TYPE =
|
||||||
|
new TypeReference<>() {
|
||||||
|
};
|
||||||
|
private static final TypeReference<List<IWorkHrSyncRespVO.SyncResult>> SYNC_RESULT_LIST_TYPE =
|
||||||
|
new TypeReference<>() {
|
||||||
|
};
|
||||||
private static final okhttp3.MediaType JSON_MEDIA_TYPE = okhttp3.MediaType.get("application/json; charset=UTF-8");
|
private static final okhttp3.MediaType JSON_MEDIA_TYPE = okhttp3.MediaType.get("application/json; charset=UTF-8");
|
||||||
|
|
||||||
private final IWorkProperties properties;
|
private final IWorkProperties properties;
|
||||||
@@ -69,7 +88,7 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO listSubcompanies(IWorkSubcompanyQueryReqVO reqVO) {
|
public IWorkHrSubcompanyPageRespVO listSubcompanies(IWorkSubcompanyQueryReqVO reqVO) {
|
||||||
String path = orgPaths().getSubcompanyPage();
|
String path = orgPaths().getSubcompanyPage();
|
||||||
Map<String, Object> params = buildBaseParams(reqVO);
|
Map<String, Object> params = buildBaseParams(reqVO);
|
||||||
if (StringUtils.hasText(reqVO.getSubcompanyCode())) {
|
if (StringUtils.hasText(reqVO.getSubcompanyCode())) {
|
||||||
@@ -78,11 +97,12 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
if (StringUtils.hasText(reqVO.getSubcompanyName())) {
|
if (StringUtils.hasText(reqVO.getSubcompanyName())) {
|
||||||
params.put("subcompanyname", reqVO.getSubcompanyName());
|
params.put("subcompanyname", reqVO.getSubcompanyName());
|
||||||
}
|
}
|
||||||
return invokeParamsEndpoint(path, params);
|
JsonNode node = invokeParamsEndpoint(path, params);
|
||||||
|
return buildSubcompanyPageResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO listDepartments(IWorkDepartmentQueryReqVO reqVO) {
|
public IWorkHrDepartmentPageRespVO listDepartments(IWorkDepartmentQueryReqVO reqVO) {
|
||||||
String path = orgPaths().getDepartmentPage();
|
String path = orgPaths().getDepartmentPage();
|
||||||
Map<String, Object> params = buildBaseParams(reqVO);
|
Map<String, Object> params = buildBaseParams(reqVO);
|
||||||
if (StringUtils.hasText(reqVO.getDepartmentCode())) {
|
if (StringUtils.hasText(reqVO.getDepartmentCode())) {
|
||||||
@@ -94,11 +114,12 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
if (StringUtils.hasText(reqVO.getSubcompanyId())) {
|
if (StringUtils.hasText(reqVO.getSubcompanyId())) {
|
||||||
params.put("subcompanyid", reqVO.getSubcompanyId());
|
params.put("subcompanyid", reqVO.getSubcompanyId());
|
||||||
}
|
}
|
||||||
return invokeParamsEndpoint(path, params);
|
JsonNode node = invokeParamsEndpoint(path, params);
|
||||||
|
return buildDepartmentPageResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO listJobTitles(IWorkJobTitleQueryReqVO reqVO) {
|
public IWorkHrJobTitlePageRespVO listJobTitles(IWorkJobTitleQueryReqVO reqVO) {
|
||||||
String path = orgPaths().getJobTitlePage();
|
String path = orgPaths().getJobTitlePage();
|
||||||
Map<String, Object> params = buildBaseParams(reqVO);
|
Map<String, Object> params = buildBaseParams(reqVO);
|
||||||
if (StringUtils.hasText(reqVO.getJobTitleCode())) {
|
if (StringUtils.hasText(reqVO.getJobTitleCode())) {
|
||||||
@@ -107,11 +128,12 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
if (StringUtils.hasText(reqVO.getJobTitleName())) {
|
if (StringUtils.hasText(reqVO.getJobTitleName())) {
|
||||||
params.put("jobtitlename", reqVO.getJobTitleName());
|
params.put("jobtitlename", reqVO.getJobTitleName());
|
||||||
}
|
}
|
||||||
return invokeParamsEndpoint(path, params);
|
JsonNode node = invokeParamsEndpoint(path, params);
|
||||||
|
return buildJobTitlePageResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO listUsers(IWorkUserQueryReqVO reqVO) {
|
public IWorkHrUserPageRespVO listUsers(IWorkUserQueryReqVO reqVO) {
|
||||||
String path = orgPaths().getUserPage();
|
String path = orgPaths().getUserPage();
|
||||||
Map<String, Object> params = buildBaseParams(reqVO);
|
Map<String, Object> params = buildBaseParams(reqVO);
|
||||||
if (StringUtils.hasText(reqVO.getWorkCode())) {
|
if (StringUtils.hasText(reqVO.getWorkCode())) {
|
||||||
@@ -138,7 +160,8 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
if (StringUtils.hasText(reqVO.getEmail())) {
|
if (StringUtils.hasText(reqVO.getEmail())) {
|
||||||
params.put("email", reqVO.getEmail());
|
params.put("email", reqVO.getEmail());
|
||||||
}
|
}
|
||||||
return invokeParamsEndpoint(path, params);
|
JsonNode node = invokeParamsEndpoint(path, params);
|
||||||
|
return buildUserPageResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildBaseParams(IWorkOrgBaseQueryReqVO reqVO) {
|
private Map<String, Object> buildBaseParams(IWorkOrgBaseQueryReqVO reqVO) {
|
||||||
@@ -156,47 +179,51 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO) {
|
public IWorkHrSyncRespVO syncSubcompanies(IWorkOrgSyncReqVO reqVO) {
|
||||||
String path = orgPaths().getSyncSubcompany();
|
String path = orgPaths().getSyncSubcompany();
|
||||||
return invokeDataEndpoint(path, reqVO.getData());
|
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||||
|
return buildSyncResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO syncDepartments(IWorkOrgSyncReqVO reqVO) {
|
public IWorkHrSyncRespVO syncDepartments(IWorkOrgSyncReqVO reqVO) {
|
||||||
String path = orgPaths().getSyncDepartment();
|
String path = orgPaths().getSyncDepartment();
|
||||||
return invokeDataEndpoint(path, reqVO.getData());
|
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||||
|
return buildSyncResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO) {
|
public IWorkHrSyncRespVO syncJobTitles(IWorkOrgSyncReqVO reqVO) {
|
||||||
String path = orgPaths().getSyncJobTitle();
|
String path = orgPaths().getSyncJobTitle();
|
||||||
return invokeDataEndpoint(path, reqVO.getData());
|
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||||
|
return buildSyncResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkOrgRespVO syncUsers(IWorkOrgSyncReqVO reqVO) {
|
public IWorkHrSyncRespVO syncUsers(IWorkOrgSyncReqVO reqVO) {
|
||||||
String path = orgPaths().getSyncUser();
|
String path = orgPaths().getSyncUser();
|
||||||
return invokeDataEndpoint(path, reqVO.getData());
|
JsonNode node = invokeDataEndpoint(path, reqVO.getData());
|
||||||
|
return buildSyncResp(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkOrgRespVO invokeParamsEndpoint(String path, Map<String, Object> params) {
|
private JsonNode invokeParamsEndpoint(String path, Map<String, Object> params) {
|
||||||
|
Objects.requireNonNull(params, "查询参数不能为空");
|
||||||
Map<String, Object> payload = new HashMap<>();
|
Map<String, Object> payload = new HashMap<>();
|
||||||
payload.put("params", params == null ? Collections.emptyMap() : params);
|
payload.put("params", params);
|
||||||
return executeJson(path, payload);
|
return executeJson(path, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkOrgRespVO invokeDataEndpoint(String path, Object data) {
|
private JsonNode invokeDataEndpoint(String path, Object data) {
|
||||||
|
Objects.requireNonNull(data, "同步数据不能为空");
|
||||||
Map<String, Object> payload = new HashMap<>();
|
Map<String, Object> payload = new HashMap<>();
|
||||||
payload.put("data", data == null ? Collections.emptyMap() : data);
|
payload.put("data", data);
|
||||||
return executeJson(path, payload);
|
return executeJson(path, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkOrgRespVO executeJson(String path, Map<String, Object> payload) {
|
private JsonNode executeJson(String path, Map<String, Object> payload) {
|
||||||
|
// 统一封装请求体并发送 POST 调用
|
||||||
assertOrgConfigured(path);
|
assertOrgConfigured(path);
|
||||||
Map<String, Object> body = new HashMap<>();
|
Map<String, Object> body = new HashMap<>(payload);
|
||||||
if (payload != null && !payload.isEmpty()) {
|
|
||||||
body.putAll(payload);
|
|
||||||
}
|
|
||||||
body.put("token", buildTokenPayload());
|
body.put("token", buildTokenPayload());
|
||||||
String jsonBody = toJson(body);
|
String jsonBody = toJson(body);
|
||||||
|
|
||||||
@@ -214,7 +241,10 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
log.error("[iWork-Org] 调用 {} 失败,status={} body={}", path, response.code(), responseBody);
|
log.error("[iWork-Org] 调用 {} 失败,status={} body={}", path, response.code(), responseBody);
|
||||||
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, response.code(), responseBody);
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, response.code(), responseBody);
|
||||||
}
|
}
|
||||||
return buildResponse(responseBody);
|
if (!StringUtils.hasText(responseBody)) {
|
||||||
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, "iWork 响应为空");
|
||||||
|
}
|
||||||
|
return parseJson(responseBody);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
log.error("[iWork-Org] 调用 {} 失败", path, ex);
|
log.error("[iWork-Org] 调用 {} 失败", path, ex);
|
||||||
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, ex.getMessage());
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, ex.getMessage());
|
||||||
@@ -222,6 +252,7 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> buildTokenPayload() {
|
private Map<String, String> buildTokenPayload() {
|
||||||
|
// 按 iWork 约定生成 key + ts 结构的鉴权信息
|
||||||
String tokenSeed = StringUtils.trimWhitespace(orgConfig().getTokenSeed());
|
String tokenSeed = StringUtils.trimWhitespace(orgConfig().getTokenSeed());
|
||||||
if (!StringUtils.hasText(tokenSeed)) {
|
if (!StringUtils.hasText(tokenSeed)) {
|
||||||
throw ServiceExceptionUtil.exception(IWORK_ORG_IDENTIFIER_MISSING);
|
throw ServiceExceptionUtil.exception(IWORK_ORG_IDENTIFIER_MISSING);
|
||||||
@@ -242,20 +273,110 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
return hex.toUpperCase(Locale.ROOT);
|
return hex.toUpperCase(Locale.ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IWorkOrgRespVO buildResponse(String responseBody) {
|
// 解析并封装分部分页响应
|
||||||
// 统一解析 iWork 响应,兼容 data 节点和扁平结构
|
private IWorkHrSubcompanyPageRespVO buildSubcompanyPageResp(JsonNode node) {
|
||||||
IWorkOrgRespVO respVO = new IWorkOrgRespVO();
|
ParsedEnvelope envelope = parseEnvelope(node);
|
||||||
respVO.setRawBody(responseBody);
|
IWorkHrSubcompanyPageRespVO respVO = new IWorkHrSubcompanyPageRespVO();
|
||||||
if (!StringUtils.hasText(responseBody)) {
|
respVO.setCode(envelope.code());
|
||||||
respVO.setPayload(Collections.emptyMap());
|
respVO.setMessage(envelope.message());
|
||||||
|
respVO.setSuccess(envelope.success());
|
||||||
|
JsonNode dataNode = envelope.success() ? readNode(envelope.root(), "data") : null;
|
||||||
|
if (dataNode == null) {
|
||||||
|
respVO.setTotalSize(0);
|
||||||
|
respVO.setTotalPage(0);
|
||||||
|
respVO.setPageSize(0);
|
||||||
|
respVO.setPageNumber(0);
|
||||||
|
respVO.setDataList(Collections.emptyList());
|
||||||
return respVO;
|
return respVO;
|
||||||
}
|
}
|
||||||
JsonNode node = parseJson(responseBody);
|
respVO.setTotalSize(readInt(dataNode, 0, "totalSize"));
|
||||||
respVO.setCode(textValue(node, "code"));
|
respVO.setTotalPage(readInt(dataNode, 0, "totalPage", "totalPageCount"));
|
||||||
respVO.setMessage(resolveMessage(node));
|
respVO.setPageSize(readInt(dataNode, 0, "pageSize", "pagesize"));
|
||||||
respVO.setSuccess(isSuccess(node));
|
respVO.setPageNumber(readInt(dataNode, 0, "pageNumber", "page", "curpage"));
|
||||||
JsonNode payloadNode = node.has("data") ? node.get("data") : node;
|
respVO.setDataList(readList(dataNode, "dataList", SUBCOMPANY_LIST_TYPE));
|
||||||
respVO.setPayload(objectMapper.convertValue(payloadNode, MAP_TYPE));
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并封装部门分页响应
|
||||||
|
private IWorkHrDepartmentPageRespVO buildDepartmentPageResp(JsonNode node) {
|
||||||
|
ParsedEnvelope envelope = parseEnvelope(node);
|
||||||
|
IWorkHrDepartmentPageRespVO respVO = new IWorkHrDepartmentPageRespVO();
|
||||||
|
respVO.setCode(envelope.code());
|
||||||
|
respVO.setMessage(envelope.message());
|
||||||
|
respVO.setSuccess(envelope.success());
|
||||||
|
JsonNode dataNode = envelope.success() ? readNode(envelope.root(), "data") : null;
|
||||||
|
if (dataNode == null) {
|
||||||
|
respVO.setTotalSize(0);
|
||||||
|
respVO.setTotalPage(0);
|
||||||
|
respVO.setPageSize(0);
|
||||||
|
respVO.setPageNumber(0);
|
||||||
|
respVO.setDataList(Collections.emptyList());
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
respVO.setTotalSize(readInt(dataNode, 0, "totalSize"));
|
||||||
|
respVO.setTotalPage(readInt(dataNode, 0, "totalPage", "totalPageCount"));
|
||||||
|
respVO.setPageSize(readInt(dataNode, 0, "pageSize", "pagesize"));
|
||||||
|
respVO.setPageNumber(readInt(dataNode, 0, "pageNumber", "page", "curpage"));
|
||||||
|
respVO.setDataList(readList(dataNode, "dataList", DEPARTMENT_LIST_TYPE));
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并封装岗位分页响应
|
||||||
|
private IWorkHrJobTitlePageRespVO buildJobTitlePageResp(JsonNode node) {
|
||||||
|
ParsedEnvelope envelope = parseEnvelope(node);
|
||||||
|
IWorkHrJobTitlePageRespVO respVO = new IWorkHrJobTitlePageRespVO();
|
||||||
|
respVO.setCode(envelope.code());
|
||||||
|
respVO.setMessage(envelope.message());
|
||||||
|
respVO.setSuccess(envelope.success());
|
||||||
|
JsonNode dataNode = envelope.success() ? readNode(envelope.root(), "data") : null;
|
||||||
|
if (dataNode == null) {
|
||||||
|
respVO.setTotalSize(0);
|
||||||
|
respVO.setTotalPage(0);
|
||||||
|
respVO.setPageSize(0);
|
||||||
|
respVO.setPageNumber(0);
|
||||||
|
respVO.setDataList(Collections.emptyList());
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
respVO.setTotalSize(readInt(dataNode, 0, "totalSize"));
|
||||||
|
respVO.setTotalPage(readInt(dataNode, 0, "totalPage", "totalPageCount"));
|
||||||
|
respVO.setPageSize(readInt(dataNode, 0, "pageSize", "pagesize"));
|
||||||
|
respVO.setPageNumber(readInt(dataNode, 0, "pageNumber", "page", "curpage"));
|
||||||
|
respVO.setDataList(readList(dataNode, "dataList", JOB_TITLE_LIST_TYPE));
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并封装人员分页响应
|
||||||
|
private IWorkHrUserPageRespVO buildUserPageResp(JsonNode node) {
|
||||||
|
ParsedEnvelope envelope = parseEnvelope(node);
|
||||||
|
IWorkHrUserPageRespVO respVO = new IWorkHrUserPageRespVO();
|
||||||
|
respVO.setCode(envelope.code());
|
||||||
|
respVO.setMessage(envelope.message());
|
||||||
|
respVO.setSuccess(envelope.success());
|
||||||
|
JsonNode dataNode = envelope.success() ? readNode(envelope.root(), "data") : null;
|
||||||
|
if (dataNode == null) {
|
||||||
|
respVO.setTotalSize(0);
|
||||||
|
respVO.setTotalPage(0);
|
||||||
|
respVO.setPageSize(0);
|
||||||
|
respVO.setPageNumber(0);
|
||||||
|
respVO.setDataList(Collections.emptyList());
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
respVO.setTotalSize(readInt(dataNode, 0, "totalSize"));
|
||||||
|
respVO.setTotalPage(readInt(dataNode, 0, "totalPage", "totalPageCount"));
|
||||||
|
respVO.setPageSize(readInt(dataNode, 0, "pageSize", "pagesize"));
|
||||||
|
respVO.setPageNumber(readInt(dataNode, 0, "pageNumber", "page", "curpage"));
|
||||||
|
respVO.setDataList(readList(dataNode, "dataList", USER_LIST_TYPE));
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并封装同步结果
|
||||||
|
private IWorkHrSyncRespVO buildSyncResp(JsonNode node) {
|
||||||
|
ParsedEnvelope envelope = parseEnvelope(node);
|
||||||
|
IWorkHrSyncRespVO respVO = new IWorkHrSyncRespVO();
|
||||||
|
respVO.setCode(envelope.code());
|
||||||
|
respVO.setMessage(envelope.message());
|
||||||
|
respVO.setSuccess(envelope.success());
|
||||||
|
respVO.setResult(readList(envelope.root(), "result", SYNC_RESULT_LIST_TYPE));
|
||||||
return respVO;
|
return respVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,34 +389,74 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveMessage(JsonNode node) {
|
private ParsedEnvelope parseEnvelope(JsonNode node) {
|
||||||
|
// 校验 iWork 顶层响应并抽取公共字段
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, "iWork 响应缺失");
|
||||||
|
}
|
||||||
|
String code = node.has("code") ? node.get("code").asText() : null;
|
||||||
|
if (!StringUtils.hasText(code)) {
|
||||||
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, "iWork 响应缺少 code 字段");
|
||||||
|
}
|
||||||
|
String message = null;
|
||||||
|
if (node.has("msg")) {
|
||||||
|
message = node.get("msg").asText();
|
||||||
|
} else if (node.has("message")) {
|
||||||
|
message = node.get("message").asText();
|
||||||
|
}
|
||||||
|
boolean success = "1".equals(code);
|
||||||
|
return new ParsedEnvelope(code, message, success, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode readNode(JsonNode parent, String field) {
|
||||||
|
if (parent == null || !parent.has(field)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (node.has("msg")) {
|
JsonNode value = parent.get(field);
|
||||||
return node.get("msg").asText();
|
return value == null || value.isNull() ? null : value;
|
||||||
}
|
|
||||||
if (node.has("message")) {
|
|
||||||
return node.get("message").asText();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSuccess(JsonNode node) {
|
private int readInt(JsonNode parent, int defaultValue, String... fieldNames) {
|
||||||
if (node == null) {
|
if (parent == null || fieldNames == null) {
|
||||||
return false;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
if ("1".equals(textValue(node, "code"))) {
|
for (String field : fieldNames) {
|
||||||
return true;
|
if (!StringUtils.hasText(field)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JsonNode valueNode = readNode(parent, field);
|
||||||
|
if (valueNode == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (valueNode.isNumber()) {
|
||||||
|
return valueNode.intValue();
|
||||||
|
}
|
||||||
|
if (valueNode.isTextual()) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(valueNode.asText());
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
log.warn("[iWork-Org] 字段格式非数值,使用默认值: {}", field);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.warn("[iWork-Org] 字段类型非整数,使用默认值: {}", field);
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
if ("1".equals(textValue(node, "status"))) {
|
return defaultValue;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return "1".equals(textValue(node, "success"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String textValue(JsonNode node, String field) {
|
private <T> List<T> readList(JsonNode parent, String field, TypeReference<List<T>> typeReference) {
|
||||||
return node != null && node.has(field) ? node.get(field).asText() : null;
|
JsonNode arrayNode = readNode(parent, field);
|
||||||
|
if (arrayNode == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (!arrayNode.isArray()) {
|
||||||
|
log.warn("[iWork-Org] 字段应为数组但实际不是,尝试转换: {}", field);
|
||||||
|
}
|
||||||
|
return objectMapper.convertValue(arrayNode, typeReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ParsedEnvelope(String code, String message, boolean success, JsonNode root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertOrgConfigured(String path) {
|
private void assertOrgConfigured(String path) {
|
||||||
@@ -318,7 +479,7 @@ public class IWorkOrgRestServiceImpl implements IWorkOrgRestService {
|
|||||||
|
|
||||||
private String toJson(Object payload) {
|
private String toJson(Object payload) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.writeValueAsString(payload == null ? Collections.emptyMap() : payload);
|
return objectMapper.writeValueAsString(payload);
|
||||||
} catch (JsonProcessingException ex) {
|
} catch (JsonProcessingException ex) {
|
||||||
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, "序列化 JSON 失败: " + ex.getMessage());
|
throw ServiceExceptionUtil.exception(IWORK_ORG_REMOTE_FAILED, "序列化 JSON 失败: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,775 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||||
|
import com.zt.plat.framework.tenant.core.context.TenantContextHolder;
|
||||||
|
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.DeptExternalCodeSaveReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.user.vo.user.UserSaveReqVO;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.dept.DeptExternalCodeDO;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
|
import com.zt.plat.module.system.dal.mysql.dept.PostMapper;
|
||||||
|
import com.zt.plat.module.system.dal.mysql.user.AdminUserMapper;
|
||||||
|
import com.zt.plat.module.system.enums.common.SexEnum;
|
||||||
|
import com.zt.plat.module.system.enums.dept.DeptSourceEnum;
|
||||||
|
import com.zt.plat.module.system.enums.user.UserSourceEnum;
|
||||||
|
import com.zt.plat.module.system.service.dept.DeptExternalCodeService;
|
||||||
|
import com.zt.plat.module.system.service.dept.DeptService;
|
||||||
|
import com.zt.plat.module.system.service.dept.PostService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||||
|
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||||
|
import com.zt.plat.module.system.util.sync.SyncVerifyUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||||
|
|
||||||
|
private static final String SYSTEM_CODE_SUBCOMPANY = "IWORK_SUBCOMPANY";
|
||||||
|
private static final String SYSTEM_CODE_DEPARTMENT = "IWORK_DEPARTMENT";
|
||||||
|
private static final String JOB_CODE_PREFIX = "IWORK_JOB_";
|
||||||
|
private static final String DEFAULT_USER_PASSWORD = "Zgty@9527";
|
||||||
|
private static final int DEFAULT_SORT = 999;
|
||||||
|
|
||||||
|
private final DeptService deptService;
|
||||||
|
private final DeptExternalCodeService deptExternalCodeService;
|
||||||
|
private final PostService postService;
|
||||||
|
private final PostMapper postMapper;
|
||||||
|
private final AdminUserService adminUserService;
|
||||||
|
private final AdminUserMapper adminUserMapper;
|
||||||
|
|
||||||
|
private final Map<String, DeptExternalCodeDO> deptExternalCache = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, PostDO> postCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options) {
|
||||||
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> records = CollUtil.emptyIfNull(data);
|
||||||
|
BatchResult result = BatchResult.empty();
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.increasePulled(records.size());
|
||||||
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> queue = new ArrayList<>(records);
|
||||||
|
int guard = 0;
|
||||||
|
int maxPasses = Math.max(1, queue.size() * 2);
|
||||||
|
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||||
|
int processed = 0;
|
||||||
|
Iterator<IWorkHrSubcompanyPageRespVO.Subcompany> iterator = queue.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
IWorkHrSubcompanyPageRespVO.Subcompany sub = iterator.next();
|
||||||
|
if (shouldSkipByCanceled(sub.getCanceled(), options)) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sub.getSubcompanyid1() == null) {
|
||||||
|
log.warn("[iWork] 分部缺少标识,跳过:{}", sub.getSubcompanyname());
|
||||||
|
result.increaseFailed();
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ParentHolder parentHolder = resolveSubcompanyParent(sub.getSupsubcomid());
|
||||||
|
if (parentHolder.required() && parentHolder.parentId() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean canceled = isCanceledFlag(sub.getCanceled());
|
||||||
|
DeptSaveReqVO saveReq = buildSubcompanySaveReq(sub, parentHolder.parentId(), canceled);
|
||||||
|
try {
|
||||||
|
DeptSyncOutcome outcome = upsertDept(saveReq,
|
||||||
|
SYSTEM_CODE_SUBCOMPANY,
|
||||||
|
sub.getSubcompanyid1().toString(),
|
||||||
|
sub.getSubcompanyname(),
|
||||||
|
canceled,
|
||||||
|
options);
|
||||||
|
applyDeptOutcome(result, outcome, "分部", sub.getSubcompanyname());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[iWork] 同步分部失败: id={} name={}", sub.getSubcompanyid1(), sub.getSubcompanyname(), ex);
|
||||||
|
result.increaseFailed();
|
||||||
|
result.withMessage("同步分部失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
iterator.remove();
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
if (processed == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
for (IWorkHrSubcompanyPageRespVO.Subcompany remaining : queue) {
|
||||||
|
log.warn("[iWork] 分部因父级缺失未同步: id={} name={}", remaining.getSubcompanyid1(), remaining.getSubcompanyname());
|
||||||
|
result.increaseFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options) {
|
||||||
|
List<IWorkHrDepartmentPageRespVO.Department> records = CollUtil.emptyIfNull(data);
|
||||||
|
BatchResult result = BatchResult.empty();
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.increasePulled(records.size());
|
||||||
|
List<IWorkHrDepartmentPageRespVO.Department> queue = new ArrayList<>(records);
|
||||||
|
int guard = 0;
|
||||||
|
int maxPasses = Math.max(1, queue.size() * 2);
|
||||||
|
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||||
|
int processed = 0;
|
||||||
|
Iterator<IWorkHrDepartmentPageRespVO.Department> iterator = queue.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
IWorkHrDepartmentPageRespVO.Department dept = iterator.next();
|
||||||
|
if (shouldSkipByCanceled(dept.getCanceled(), options)) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dept.getDepartmentid() == null) {
|
||||||
|
log.warn("[iWork] 部门缺少标识,跳过:{}", dept.getDepartmentname());
|
||||||
|
result.increaseFailed();
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ParentHolder parentHolder = resolveDepartmentParent(dept);
|
||||||
|
if (parentHolder.required() && parentHolder.parentId() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean canceled = isCanceledFlag(dept.getCanceled());
|
||||||
|
DeptSaveReqVO saveReq = buildDepartmentSaveReq(dept, parentHolder.parentId(), canceled);
|
||||||
|
try {
|
||||||
|
DeptSyncOutcome outcome = upsertDept(saveReq,
|
||||||
|
SYSTEM_CODE_DEPARTMENT,
|
||||||
|
dept.getDepartmentid().toString(),
|
||||||
|
dept.getDepartmentname(),
|
||||||
|
canceled,
|
||||||
|
options);
|
||||||
|
applyDeptOutcome(result, outcome, "部门", dept.getDepartmentname());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[iWork] 同步部门失败: id={} name={}", dept.getDepartmentid(), dept.getDepartmentname(), ex);
|
||||||
|
result.increaseFailed();
|
||||||
|
result.withMessage("同步部门失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
iterator.remove();
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
if (processed == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
for (IWorkHrDepartmentPageRespVO.Department remaining : queue) {
|
||||||
|
log.warn("[iWork] 部门因父级缺失未同步: id={} name={}", remaining.getDepartmentid(), remaining.getDepartmentname());
|
||||||
|
result.increaseFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options) {
|
||||||
|
List<IWorkHrJobTitlePageRespVO.JobTitle> records = CollUtil.emptyIfNull(data);
|
||||||
|
BatchResult result = BatchResult.empty();
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.increasePulled(records.size());
|
||||||
|
for (IWorkHrJobTitlePageRespVO.JobTitle job : records) {
|
||||||
|
if (job == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (shouldSkipByCanceled(job.getCanceled(), options)) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (job.getJobtitleid() == null) {
|
||||||
|
log.warn("[iWork] 岗位缺少标识,跳过:{}", job.getJobtitlename());
|
||||||
|
result.increaseFailed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean canceled = isCanceledFlag(job.getCanceled());
|
||||||
|
Integer status = toStatus(canceled);
|
||||||
|
String code = buildJobCode(job.getJobtitleid());
|
||||||
|
String name = limitLength(StrUtil.blankToDefault(job.getJobtitlename(), code), 50);
|
||||||
|
try {
|
||||||
|
JobSyncOutcome outcome = upsertJobTitle(job, code, name, status, options);
|
||||||
|
applyJobOutcome(result, outcome, name);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[iWork] 同步岗位失败: id={} name={}", job.getJobtitleid(), job.getJobtitlename(), ex);
|
||||||
|
result.increaseFailed();
|
||||||
|
result.withMessage("同步岗位失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BatchResult syncUsers(List<IWorkHrUserPageRespVO.User> data, SyncOptions options) {
|
||||||
|
List<IWorkHrUserPageRespVO.User> records = CollUtil.emptyIfNull(data);
|
||||||
|
BatchResult result = BatchResult.empty();
|
||||||
|
if (records.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.increasePulled(records.size());
|
||||||
|
for (IWorkHrUserPageRespVO.User user : records) {
|
||||||
|
if (user == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean inactive = isInactiveUser(user.getStatus());
|
||||||
|
if (inactive && !options.isIncludeCanceled()) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String username = resolveUsername(user);
|
||||||
|
if (StrUtil.isBlank(username)) {
|
||||||
|
log.warn("[iWork] 人员缺少可用账号,跳过:id={} name={}", user.getId(), user.getLastname());
|
||||||
|
result.increaseFailed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Long deptId = resolveUserDeptId(user);
|
||||||
|
if (deptId == null) {
|
||||||
|
log.warn("[iWork] 人员未找到匹配部门,跳过:id={} name={} deptId={} subcompany={}",
|
||||||
|
user.getId(), user.getLastname(), user.getDepartmentid(), user.getSubcompanyid1());
|
||||||
|
result.increaseFailed();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Long postId = resolveUserPostId(user);
|
||||||
|
CommonStatusEnum status = inactive ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
|
||||||
|
AdminUserDO existing = adminUserMapper.selectByUsername(username);
|
||||||
|
UserSyncOutcome outcome;
|
||||||
|
if (existing == null) {
|
||||||
|
if (!options.isCreateIfMissing()) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
outcome = createUser(user, username, deptId, postId, status);
|
||||||
|
} else {
|
||||||
|
if (!Objects.equals(existing.getUserSource(), UserSourceEnum.IWORK.getSource())) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!options.isAllowUpdate()) {
|
||||||
|
result.increaseSkipped();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
outcome = updateUser(existing, user, username, deptId, postId, status);
|
||||||
|
}
|
||||||
|
applyUserOutcome(result, outcome, user.getLastname(), username);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("[iWork] 同步人员失败: id={} name={}", user.getId(), user.getLastname(), ex);
|
||||||
|
result.increaseFailed();
|
||||||
|
result.withMessage("同步人员失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeptSyncOutcome upsertDept(DeptSaveReqVO desired,
|
||||||
|
String systemCode,
|
||||||
|
String externalCode,
|
||||||
|
String externalName,
|
||||||
|
boolean disabled,
|
||||||
|
SyncOptions options) {
|
||||||
|
DeptExternalCodeDO mapping = getDeptMapping(systemCode, externalCode);
|
||||||
|
if (mapping == null) {
|
||||||
|
if (!options.isCreateIfMissing()) {
|
||||||
|
return new DeptSyncOutcome(SyncAction.SKIPPED, false, null);
|
||||||
|
}
|
||||||
|
Long deptId = deptService.createDept(desired);
|
||||||
|
persistDeptExternalCode(null, deptId, systemCode, externalCode, externalName, desired.getStatus());
|
||||||
|
return new DeptSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(desired.getStatus()), deptId);
|
||||||
|
}
|
||||||
|
Long deptId = mapping.getDeptId();
|
||||||
|
DeptDO existing = deptService.getDept(deptId);
|
||||||
|
if (existing == null) {
|
||||||
|
deptExternalCache.remove(buildDeptCacheKey(systemCode, externalCode));
|
||||||
|
if (!options.isCreateIfMissing()) {
|
||||||
|
return new DeptSyncOutcome(SyncAction.SKIPPED, false, null);
|
||||||
|
}
|
||||||
|
Long recreatedId = deptService.createDept(desired);
|
||||||
|
persistDeptExternalCode(mapping.getId(), recreatedId, systemCode, externalCode, externalName, desired.getStatus());
|
||||||
|
return new DeptSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(desired.getStatus()), recreatedId);
|
||||||
|
}
|
||||||
|
if (!Objects.equals(existing.getDeptSource(), DeptSourceEnum.IWORK.getSource())) {
|
||||||
|
return new DeptSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
|
||||||
|
}
|
||||||
|
if (!options.isAllowUpdate()) {
|
||||||
|
return new DeptSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
|
||||||
|
}
|
||||||
|
desired.setId(existing.getId());
|
||||||
|
mergeDeptDefaults(desired, existing);
|
||||||
|
boolean disabledChanged = CommonStatusEnum.isDisable(desired.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
||||||
|
deptService.updateDept(desired);
|
||||||
|
persistDeptExternalCode(mapping.getId(), existing.getId(), systemCode, externalCode, externalName, desired.getStatus());
|
||||||
|
return new DeptSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JobSyncOutcome upsertJobTitle(IWorkHrJobTitlePageRespVO.JobTitle job,
|
||||||
|
String code,
|
||||||
|
String name,
|
||||||
|
Integer status,
|
||||||
|
SyncOptions options) {
|
||||||
|
PostDO existing = resolvePostByCode(code);
|
||||||
|
boolean disabled = CommonStatusEnum.isDisable(status);
|
||||||
|
if (existing == null) {
|
||||||
|
if (!options.isCreateIfMissing()) {
|
||||||
|
return new JobSyncOutcome(SyncAction.SKIPPED, false, null);
|
||||||
|
}
|
||||||
|
PostSaveReqVO createReq = new PostSaveReqVO();
|
||||||
|
createReq.setCode(code);
|
||||||
|
createReq.setName(name);
|
||||||
|
createReq.setSort(defaultSort(job.getShoworder()));
|
||||||
|
createReq.setStatus(status);
|
||||||
|
createReq.setRemark(StrUtil.blankToDefault(job.getJobfunction(), job.getDescription()));
|
||||||
|
Long postId = postService.createPost(createReq);
|
||||||
|
PostDO created = new PostDO();
|
||||||
|
created.setId(postId);
|
||||||
|
created.setCode(code);
|
||||||
|
created.setName(createReq.getName());
|
||||||
|
created.setSort(createReq.getSort());
|
||||||
|
created.setStatus(status);
|
||||||
|
created.setRemark(createReq.getRemark());
|
||||||
|
postCache.put(buildPostCacheKey(code), created);
|
||||||
|
return new JobSyncOutcome(SyncAction.CREATED, disabled, postId);
|
||||||
|
}
|
||||||
|
if (!options.isAllowUpdate()) {
|
||||||
|
return new JobSyncOutcome(SyncAction.SKIPPED, false, existing.getId());
|
||||||
|
}
|
||||||
|
PostSaveReqVO updateReq = new PostSaveReqVO();
|
||||||
|
updateReq.setId(existing.getId());
|
||||||
|
updateReq.setCode(code);
|
||||||
|
updateReq.setName(name);
|
||||||
|
updateReq.setSort(defaultSort(job.getShoworder(), existing.getSort()));
|
||||||
|
updateReq.setStatus(status);
|
||||||
|
updateReq.setRemark(StrUtil.blankToDefault(job.getJobfunction(), job.getDescription()));
|
||||||
|
boolean disabledChanged = disabled && CommonStatusEnum.isEnable(existing.getStatus());
|
||||||
|
postService.updatePost(updateReq);
|
||||||
|
existing.setName(updateReq.getName());
|
||||||
|
existing.setSort(updateReq.getSort());
|
||||||
|
existing.setStatus(status);
|
||||||
|
existing.setRemark(updateReq.getRemark());
|
||||||
|
postCache.put(buildPostCacheKey(code), existing);
|
||||||
|
return new JobSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserSyncOutcome createUser(IWorkHrUserPageRespVO.User source,
|
||||||
|
String username,
|
||||||
|
Long deptId,
|
||||||
|
Long postId,
|
||||||
|
CommonStatusEnum status) {
|
||||||
|
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||||
|
req.setPassword(DEFAULT_USER_PASSWORD);
|
||||||
|
req.setUserSource(UserSourceEnum.IWORK.getSource());
|
||||||
|
Long userId = adminUserService.createUser(req);
|
||||||
|
return new UserSyncOutcome(SyncAction.CREATED, CommonStatusEnum.isDisable(req.getStatus()), userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserSyncOutcome updateUser(AdminUserDO existing,
|
||||||
|
IWorkHrUserPageRespVO.User source,
|
||||||
|
String username,
|
||||||
|
Long deptId,
|
||||||
|
Long postId,
|
||||||
|
CommonStatusEnum status) {
|
||||||
|
UserSaveReqVO req = buildUserSaveReq(source, username, deptId, postId, status);
|
||||||
|
req.setId(existing.getId());
|
||||||
|
boolean disabledChanged = CommonStatusEnum.isDisable(status.getStatus()) && CommonStatusEnum.isEnable(existing.getStatus());
|
||||||
|
adminUserService.updateUser(req);
|
||||||
|
return new UserSyncOutcome(SyncAction.UPDATED, disabledChanged, existing.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeptSaveReqVO buildSubcompanySaveReq(IWorkHrSubcompanyPageRespVO.Subcompany data,
|
||||||
|
Long parentId,
|
||||||
|
boolean canceled) {
|
||||||
|
DeptSaveReqVO req = new DeptSaveReqVO();
|
||||||
|
req.setName(limitLength(StrUtil.blankToDefault(data.getSubcompanyname(), "未命名分部"), 30));
|
||||||
|
req.setShortName(limitLength(data.getSubcompanyname(), 20));
|
||||||
|
req.setCode(trimToNull(data.getSubcompanycode()));
|
||||||
|
req.setParentId(parentId == null ? DeptDO.PARENT_ID_ROOT : parentId);
|
||||||
|
req.setSort(defaultSort(data.getShoworder()));
|
||||||
|
req.setStatus(toStatus(canceled));
|
||||||
|
req.setIsCompany(Boolean.TRUE);
|
||||||
|
req.setIsGroup(Boolean.FALSE);
|
||||||
|
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeptSaveReqVO buildDepartmentSaveReq(IWorkHrDepartmentPageRespVO.Department data,
|
||||||
|
Long parentId,
|
||||||
|
boolean canceled) {
|
||||||
|
DeptSaveReqVO req = new DeptSaveReqVO();
|
||||||
|
req.setName(limitLength(StrUtil.blankToDefault(data.getDepartmentname(), "未命名部门"), 30));
|
||||||
|
req.setShortName(limitLength(StrUtil.blankToDefault(data.getDepartmentmark(), data.getDepartmentname()), 20));
|
||||||
|
req.setCode(trimToNull(data.getDepartmentcode()));
|
||||||
|
req.setParentId(parentId == null ? DeptDO.PARENT_ID_ROOT : parentId);
|
||||||
|
req.setSort(defaultSort(data.getShoworder()));
|
||||||
|
req.setStatus(toStatus(canceled));
|
||||||
|
req.setIsCompany(Boolean.FALSE);
|
||||||
|
req.setIsGroup(Boolean.FALSE);
|
||||||
|
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserSaveReqVO buildUserSaveReq(IWorkHrUserPageRespVO.User source,
|
||||||
|
String username,
|
||||||
|
Long deptId,
|
||||||
|
Long postId,
|
||||||
|
CommonStatusEnum status) {
|
||||||
|
UserSaveReqVO req = new UserSaveReqVO();
|
||||||
|
req.setUsername(username);
|
||||||
|
req.setNickname(limitLength(StrUtil.blankToDefault(source.getLastname(), username), 30));
|
||||||
|
req.setRemark(buildUserRemark(source));
|
||||||
|
req.setDeptIds(singletonSet(deptId));
|
||||||
|
if (postId != null) {
|
||||||
|
req.setPostIds(singletonSet(postId));
|
||||||
|
}
|
||||||
|
req.setEmail(trimToNull(source.getEmail()));
|
||||||
|
req.setMobile(trimToNull(source.getMobile()));
|
||||||
|
req.setSex(resolveSex(source.getSex()));
|
||||||
|
req.setStatus(status.getStatus());
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeDeptDefaults(DeptSaveReqVO target, DeptDO existing) {
|
||||||
|
target.setCode(StrUtil.blankToDefault(target.getCode(), existing.getCode()));
|
||||||
|
target.setShortName(StrUtil.blankToDefault(target.getShortName(), existing.getShortName()));
|
||||||
|
target.setParentId(target.getParentId() == null ? existing.getParentId() : target.getParentId());
|
||||||
|
target.setSort(target.getSort() == null ? existing.getSort() : target.getSort());
|
||||||
|
target.setStatus(target.getStatus() == null ? existing.getStatus() : target.getStatus());
|
||||||
|
target.setLeaderUserId(existing.getLeaderUserId());
|
||||||
|
target.setPhone(existing.getPhone());
|
||||||
|
target.setEmail(existing.getEmail());
|
||||||
|
target.setTenantId(existing.getTenantId());
|
||||||
|
target.setIsCompany(target.getIsCompany() == null ? existing.getIsCompany() : target.getIsCompany());
|
||||||
|
target.setIsGroup(target.getIsGroup() == null ? existing.getIsGroup() : target.getIsGroup());
|
||||||
|
target.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParentHolder resolveSubcompanyParent(Integer parentExternalId) {
|
||||||
|
if (parentExternalId == null || parentExternalId <= 0) {
|
||||||
|
return new ParentHolder(DeptDO.PARENT_ID_ROOT, false);
|
||||||
|
}
|
||||||
|
Long parentId = resolveDeptId(SYSTEM_CODE_SUBCOMPANY, parentExternalId.toString());
|
||||||
|
return new ParentHolder(parentId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParentHolder resolveDepartmentParent(IWorkHrDepartmentPageRespVO.Department dept) {
|
||||||
|
Integer parentDeptId = dept.getParentdeptid();
|
||||||
|
if (parentDeptId != null && parentDeptId > 0) {
|
||||||
|
Long parentId = resolveDeptId(SYSTEM_CODE_DEPARTMENT, parentDeptId.toString());
|
||||||
|
return new ParentHolder(parentId, true);
|
||||||
|
}
|
||||||
|
Integer subcompanyId = dept.getSubcompanyid1();
|
||||||
|
if (subcompanyId != null && subcompanyId > 0) {
|
||||||
|
Long parentId = resolveDeptId(SYSTEM_CODE_SUBCOMPANY, subcompanyId.toString());
|
||||||
|
return new ParentHolder(parentId, true);
|
||||||
|
}
|
||||||
|
return new ParentHolder(DeptDO.PARENT_ID_ROOT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeptExternalCodeDO getDeptMapping(String systemCode, String externalCode) {
|
||||||
|
if (StrUtil.isBlank(externalCode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String key = buildDeptCacheKey(systemCode, externalCode);
|
||||||
|
DeptExternalCodeDO cached = deptExternalCache.get(key);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
DeptExternalCodeDO mapping = deptExternalCodeService.getBySystemCodeAndExternalCode(systemCode, externalCode);
|
||||||
|
if (mapping != null) {
|
||||||
|
deptExternalCache.put(key, mapping);
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistDeptExternalCode(Long mappingId,
|
||||||
|
Long deptId,
|
||||||
|
String systemCode,
|
||||||
|
String externalCode,
|
||||||
|
String externalName,
|
||||||
|
Integer status) {
|
||||||
|
DeptExternalCodeSaveReqVO req = new DeptExternalCodeSaveReqVO();
|
||||||
|
req.setDeptId(deptId);
|
||||||
|
req.setSystemCode(systemCode);
|
||||||
|
req.setExternalDeptCode(externalCode);
|
||||||
|
req.setExternalDeptName(StrUtil.blankToDefault(externalName, externalCode));
|
||||||
|
req.setStatus(status);
|
||||||
|
req.setRemark("iWork 同步");
|
||||||
|
DeptExternalCodeDO mapping;
|
||||||
|
if (mappingId == null) {
|
||||||
|
Long id = deptExternalCodeService.createDeptExternalCode(req);
|
||||||
|
mapping = buildMapping(id, deptId, req);
|
||||||
|
} else {
|
||||||
|
req.setId(mappingId);
|
||||||
|
deptExternalCodeService.updateDeptExternalCode(req);
|
||||||
|
mapping = buildMapping(mappingId, deptId, req);
|
||||||
|
}
|
||||||
|
deptExternalCache.put(buildDeptCacheKey(systemCode, externalCode), mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeptExternalCodeDO buildMapping(Long id, Long deptId, DeptExternalCodeSaveReqVO req) {
|
||||||
|
DeptExternalCodeDO mapping = new DeptExternalCodeDO();
|
||||||
|
mapping.setId(id);
|
||||||
|
mapping.setDeptId(deptId);
|
||||||
|
mapping.setSystemCode(req.getSystemCode());
|
||||||
|
mapping.setExternalDeptCode(req.getExternalDeptCode());
|
||||||
|
mapping.setExternalDeptName(req.getExternalDeptName());
|
||||||
|
mapping.setStatus(req.getStatus());
|
||||||
|
mapping.setRemark(req.getRemark());
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveDeptId(String systemCode, String externalCode) {
|
||||||
|
DeptExternalCodeDO mapping = getDeptMapping(systemCode, externalCode);
|
||||||
|
return mapping == null ? null : mapping.getDeptId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PostDO resolvePostByCode(String code) {
|
||||||
|
String key = buildPostCacheKey(code);
|
||||||
|
PostDO cached = postCache.get(key);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
PostDO post = postMapper.selectByCode(code);
|
||||||
|
if (post != null) {
|
||||||
|
postCache.put(key, post);
|
||||||
|
}
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveUserDeptId(IWorkHrUserPageRespVO.User user) {
|
||||||
|
if (user.getDepartmentid() != null) {
|
||||||
|
Long deptId = resolveDeptId(SYSTEM_CODE_DEPARTMENT, user.getDepartmentid().toString());
|
||||||
|
if (deptId != null) {
|
||||||
|
return deptId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (user.getSubcompanyid1() != null) {
|
||||||
|
Long deptId = resolveDeptId(SYSTEM_CODE_SUBCOMPANY, user.getSubcompanyid1().toString());
|
||||||
|
if (deptId != null) {
|
||||||
|
return deptId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveUserPostId(IWorkHrUserPageRespVO.User user) {
|
||||||
|
if (user.getJobtitleid() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String code = buildJobCode(user.getJobtitleid());
|
||||||
|
PostDO post = resolvePostByCode(code);
|
||||||
|
if (post != null) {
|
||||||
|
return post.getId();
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(user.getJobtitlename())) {
|
||||||
|
return postService.getOrCreatePostByName(user.getJobtitlename().trim());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyDeptOutcome(BatchResult result, DeptSyncOutcome outcome, String entityLabel, String name) {
|
||||||
|
if (outcome == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
incrementByAction(result, outcome.action());
|
||||||
|
if (outcome.disabled()) {
|
||||||
|
result.increaseDisabled();
|
||||||
|
}
|
||||||
|
result.withMessage(StrUtil.format("{}[{}]{}", entityLabel, StrUtil.blankToDefault(name, "-"), describeAction(outcome.action())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyJobOutcome(BatchResult result, JobSyncOutcome outcome, String name) {
|
||||||
|
if (outcome == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
incrementByAction(result, outcome.action());
|
||||||
|
if (outcome.disabled()) {
|
||||||
|
result.increaseDisabled();
|
||||||
|
}
|
||||||
|
result.withMessage(StrUtil.format("岗位[{}]{}", StrUtil.blankToDefault(name, "-"), describeAction(outcome.action())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyUserOutcome(BatchResult result, UserSyncOutcome outcome, String displayName, String username) {
|
||||||
|
if (outcome == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
incrementByAction(result, outcome.action());
|
||||||
|
if (outcome.disabled()) {
|
||||||
|
result.increaseDisabled();
|
||||||
|
}
|
||||||
|
result.withMessage(StrUtil.format("人员[{}]{}",
|
||||||
|
StrUtil.blankToDefault(displayName, username), describeAction(outcome.action())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void incrementByAction(BatchResult result, SyncAction action) {
|
||||||
|
if (action == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (action) {
|
||||||
|
case CREATED -> result.increaseCreated();
|
||||||
|
case UPDATED -> result.increaseUpdated();
|
||||||
|
case SKIPPED -> result.increaseSkipped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSkipByCanceled(String canceled, SyncOptions options) {
|
||||||
|
return isCanceledFlag(canceled) && !options.isIncludeCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCanceledFlag(String flag) {
|
||||||
|
if (StrUtil.isBlank(flag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String normalized = flag.trim();
|
||||||
|
return "1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "yes".equalsIgnoreCase(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInactiveUser(String statusFlag) {
|
||||||
|
if (StrUtil.isBlank(statusFlag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !"0".equals(statusFlag.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer resolveSex(String sexFlag) {
|
||||||
|
Integer external = parseInteger(sexFlag);
|
||||||
|
Integer converted = SyncVerifyUtil.convertExternalToInternal(external);
|
||||||
|
return converted != null ? converted : SexEnum.UNKNOWN.getSex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer parseInteger(String raw) {
|
||||||
|
if (StrUtil.isBlank(raw)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(raw.trim());
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveUsername(IWorkHrUserPageRespVO.User user) {
|
||||||
|
String candidate = sanitizeUsername(user.getLoginid());
|
||||||
|
if (candidate == null) {
|
||||||
|
candidate = sanitizeUsername(user.getWorkcode());
|
||||||
|
}
|
||||||
|
if (candidate == null && user.getId() != null) {
|
||||||
|
candidate = sanitizeUsername("IWORK" + user.getId());
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sanitizeUsername(String raw) {
|
||||||
|
if (StrUtil.isBlank(raw)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String normalized = raw.replaceAll("[^A-Za-z0-9]", "");
|
||||||
|
if (StrUtil.isBlank(normalized)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return normalized.length() > 30 ? normalized.substring(0, 30) : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Long> singletonSet(Long value) {
|
||||||
|
Set<Long> set = new HashSet<>(1);
|
||||||
|
set.add(value);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer defaultSort(Integer value) {
|
||||||
|
return value == null ? DEFAULT_SORT : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer defaultSort(Integer value, Integer fallback) {
|
||||||
|
return value == null ? (fallback == null ? DEFAULT_SORT : fallback) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer toStatus(boolean disabled) {
|
||||||
|
return disabled ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String limitLength(String value, int maxLength) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.length() > maxLength ? trimmed.substring(0, maxLength) : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildUserRemark(IWorkHrUserPageRespVO.User user) {
|
||||||
|
if (user.getId() == null) {
|
||||||
|
return "iWork 同步";
|
||||||
|
}
|
||||||
|
return StrUtil.format("iWork 同步,源ID={}", user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildJobCode(Integer jobTitleId) {
|
||||||
|
return JOB_CODE_PREFIX + jobTitleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDeptCacheKey(String systemCode, String externalCode) {
|
||||||
|
Long tenantId = TenantContextHolder.getTenantId();
|
||||||
|
return (tenantId == null ? "_" : tenantId.toString()) + "::" + systemCode + "::" + externalCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildPostCacheKey(String code) {
|
||||||
|
Long tenantId = TenantContextHolder.getTenantId();
|
||||||
|
return (tenantId == null ? "_" : tenantId.toString()) + "::POST::" + code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String describeAction(SyncAction action) {
|
||||||
|
return switch (action) {
|
||||||
|
case CREATED -> "已创建";
|
||||||
|
case UPDATED -> "已更新";
|
||||||
|
case SKIPPED -> "已跳过";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ParentHolder(Long parentId, boolean required) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum SyncAction {
|
||||||
|
CREATED,
|
||||||
|
UPDATED,
|
||||||
|
SKIPPED
|
||||||
|
}
|
||||||
|
|
||||||
|
private record DeptSyncOutcome(SyncAction action, boolean disabled, Long deptId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record JobSyncOutcome(SyncAction action, boolean disabled, Long postId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private record UserSyncOutcome(SyncAction action, boolean disabled, Long userId) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkDepartmentQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJobTitlePageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrUserPageRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkJobTitleQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSingleSyncRespVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSyncBatchStatVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSyncEntityStatVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkUserQueryReqVO;
|
||||||
|
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 同步服务实现
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||||
|
|
||||||
|
private final IWorkOrgRestService orgRestService;
|
||||||
|
private final IWorkSyncProcessor syncProcessor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWorkFullSyncRespVO fullSync(IWorkFullSyncReqVO reqVO) {
|
||||||
|
IWorkFullSyncRespVO respVO = new IWorkFullSyncRespVO();
|
||||||
|
respVO.setPageSize(reqVO.getPageSize());
|
||||||
|
List<IWorkSyncBatchStatVO> batchStats = new ArrayList<>();
|
||||||
|
respVO.setBatches(batchStats);
|
||||||
|
|
||||||
|
Set<IWorkSyncEntityTypeEnum> scopes = reqVO.resolveScopes();
|
||||||
|
int processedPages = 0;
|
||||||
|
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
|
||||||
|
if (scopes.contains(IWorkSyncEntityTypeEnum.SUBCOMPANY)) {
|
||||||
|
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats);
|
||||||
|
}
|
||||||
|
if (scopes.contains(IWorkSyncEntityTypeEnum.DEPARTMENT)) {
|
||||||
|
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats);
|
||||||
|
}
|
||||||
|
if (scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE)) {
|
||||||
|
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
|
||||||
|
}
|
||||||
|
if (scopes.contains(IWorkSyncEntityTypeEnum.USER)) {
|
||||||
|
processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats);
|
||||||
|
}
|
||||||
|
respVO.setProcessedPages(processedPages);
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWorkSingleSyncRespVO syncSingle(IWorkSingleSyncReqVO reqVO) {
|
||||||
|
IWorkSingleSyncRespVO respVO = new IWorkSingleSyncRespVO();
|
||||||
|
respVO.setEntityType(reqVO.getEntityType());
|
||||||
|
respVO.setEntityId(reqVO.getEntityId());
|
||||||
|
switch (reqVO.getEntityType()) {
|
||||||
|
case SUBCOMPANY -> processSingleSubcompany(reqVO, respVO);
|
||||||
|
case DEPARTMENT -> processSingleDepartment(reqVO, respVO);
|
||||||
|
case JOB_TITLE -> processSingleJob(reqVO, respVO);
|
||||||
|
case USER -> processSingleUser(reqVO, respVO);
|
||||||
|
default -> throw new IllegalArgumentException("不支持的实体类型: " + reqVO.getEntityType());
|
||||||
|
}
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
|
IWorkSyncEntityStatVO stat,
|
||||||
|
List<IWorkSyncBatchStatVO> batches) {
|
||||||
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> {
|
||||||
|
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
||||||
|
query.setCurpage(page);
|
||||||
|
query.setPagesize(pageSize);
|
||||||
|
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
||||||
|
List<IWorkHrSubcompanyPageRespVO.Subcompany> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options);
|
||||||
|
updateStat(stat, result, dataList.size());
|
||||||
|
return new BatchExecution(result, dataList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeDepartmentFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
|
IWorkSyncEntityStatVO stat,
|
||||||
|
List<IWorkSyncBatchStatVO> batches) {
|
||||||
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> {
|
||||||
|
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
||||||
|
query.setCurpage(page);
|
||||||
|
query.setPagesize(pageSize);
|
||||||
|
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
||||||
|
List<IWorkHrDepartmentPageRespVO.Department> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options);
|
||||||
|
updateStat(stat, result, dataList.size());
|
||||||
|
return new BatchExecution(result, dataList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeJobTitleFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
|
IWorkSyncEntityStatVO stat,
|
||||||
|
List<IWorkSyncBatchStatVO> batches) {
|
||||||
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.JOB_TITLE, batches, (page, pageSize) -> {
|
||||||
|
IWorkJobTitleQueryReqVO query = new IWorkJobTitleQueryReqVO();
|
||||||
|
query.setCurpage(page);
|
||||||
|
query.setPagesize(pageSize);
|
||||||
|
IWorkHrJobTitlePageRespVO pageResp = orgRestService.listJobTitles(query);
|
||||||
|
List<IWorkHrJobTitlePageRespVO.JobTitle> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncJobTitles(dataList, options);
|
||||||
|
updateStat(stat, result, dataList.size());
|
||||||
|
return new BatchExecution(result, dataList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeUserFullSync(IWorkFullSyncReqVO reqVO,
|
||||||
|
IWorkSyncProcessor.SyncOptions options,
|
||||||
|
IWorkSyncEntityStatVO stat,
|
||||||
|
List<IWorkSyncBatchStatVO> batches) {
|
||||||
|
return executePaged(reqVO, IWorkSyncEntityTypeEnum.USER, batches, (page, pageSize) -> {
|
||||||
|
IWorkUserQueryReqVO query = new IWorkUserQueryReqVO();
|
||||||
|
query.setCurpage(page);
|
||||||
|
query.setPagesize(pageSize);
|
||||||
|
IWorkHrUserPageRespVO pageResp = orgRestService.listUsers(query);
|
||||||
|
List<IWorkHrUserPageRespVO.User> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncUsers(dataList, options);
|
||||||
|
updateStat(stat, result, dataList.size());
|
||||||
|
return new BatchExecution(result, dataList.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSingleSubcompany(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||||
|
IWorkHrSubcompanyPageRespVO.Subcompany data = fetchSingleSubcompany(reqVO.getEntityId());
|
||||||
|
if (data == null) {
|
||||||
|
markNotFound(respVO, "分部");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompany(data, buildSingleOptions(reqVO));
|
||||||
|
populateSingleResult(respVO, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSingleDepartment(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||||
|
IWorkHrDepartmentPageRespVO.Department data = fetchSingleDepartment(reqVO.getEntityId());
|
||||||
|
if (data == null) {
|
||||||
|
markNotFound(respVO, "部门");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartment(data, buildSingleOptions(reqVO));
|
||||||
|
populateSingleResult(respVO, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSingleJob(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||||
|
IWorkHrJobTitlePageRespVO.JobTitle data = fetchSingleJob(reqVO.getEntityId());
|
||||||
|
if (data == null) {
|
||||||
|
markNotFound(respVO, "岗位");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncJobTitle(data, buildSingleOptions(reqVO));
|
||||||
|
populateSingleResult(respVO, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processSingleUser(IWorkSingleSyncReqVO reqVO, IWorkSingleSyncRespVO respVO) {
|
||||||
|
IWorkHrUserPageRespVO.User data = fetchSingleUser(reqVO.getEntityId());
|
||||||
|
if (data == null) {
|
||||||
|
markNotFound(respVO, "人员");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IWorkSyncProcessor.BatchResult result = syncProcessor.syncUser(data, buildSingleOptions(reqVO));
|
||||||
|
populateSingleResult(respVO, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executePaged(IWorkFullSyncReqVO reqVO,
|
||||||
|
IWorkSyncEntityTypeEnum type,
|
||||||
|
List<IWorkSyncBatchStatVO> batches,
|
||||||
|
PageExecutor executor) {
|
||||||
|
int startPage = reqVO.getStartPage() == null ? 1 : reqVO.getStartPage();
|
||||||
|
int pageSize = reqVO.getPageSize() == null ? 100 : reqVO.getPageSize();
|
||||||
|
int pagesLimit = reqVO.getMaxPages() == null ? Integer.MAX_VALUE : reqVO.getMaxPages();
|
||||||
|
int processedPages = 0;
|
||||||
|
for (int page = startPage; processedPages < pagesLimit; page++) {
|
||||||
|
BatchExecution execution = executor.execute(page, pageSize);
|
||||||
|
if (execution == null || execution.totalPulled == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
processedPages++;
|
||||||
|
IWorkSyncBatchStatVO batchStat = new IWorkSyncBatchStatVO();
|
||||||
|
batchStat.setEntityType(type);
|
||||||
|
batchStat.setPageNumber(page);
|
||||||
|
batchStat.setPulled(execution.totalPulled);
|
||||||
|
batchStat.setCreated(execution.result.getCreated());
|
||||||
|
batchStat.setSkippedExisting(execution.result.getSkipped());
|
||||||
|
batchStat.setDisabled(execution.result.getDisabled());
|
||||||
|
batchStat.setFailed(execution.result.getFailed());
|
||||||
|
batches.add(batchStat);
|
||||||
|
}
|
||||||
|
return processedPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStat(IWorkSyncEntityStatVO stat, IWorkSyncProcessor.BatchResult result, int pulled) {
|
||||||
|
stat.incrementPulled(pulled);
|
||||||
|
stat.incrementCreated(result.getCreated());
|
||||||
|
stat.incrementSkipped(result.getSkipped());
|
||||||
|
stat.incrementDisabled(result.getDisabled());
|
||||||
|
stat.incrementFailed(result.getFailed());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateSingleResult(IWorkSingleSyncRespVO respVO, IWorkSyncProcessor.BatchResult result) {
|
||||||
|
respVO.setCreated(result.getCreated() > 0);
|
||||||
|
respVO.setUpdated(result.getUpdated() > 0);
|
||||||
|
respVO.setMessage(result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markNotFound(IWorkSingleSyncRespVO respVO, String entityName) {
|
||||||
|
respVO.setCreated(false);
|
||||||
|
respVO.setUpdated(false);
|
||||||
|
respVO.setMessage(StrUtil.format("未在 iWork 中找到{}(ID={})", entityName, respVO.getEntityId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkSyncProcessor.SyncOptions buildFullSyncOptions(IWorkFullSyncReqVO reqVO) {
|
||||||
|
return IWorkSyncProcessor.SyncOptions.full(Boolean.TRUE.equals(reqVO.getIncludeCanceled()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkSyncProcessor.SyncOptions buildSingleOptions(IWorkSingleSyncReqVO reqVO) {
|
||||||
|
return IWorkSyncProcessor.SyncOptions.single(Boolean.TRUE.equals(reqVO.getCreateIfMissing()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkHrSubcompanyPageRespVO.Subcompany fetchSingleSubcompany(Long entityId) {
|
||||||
|
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
||||||
|
query.setCurpage(1);
|
||||||
|
query.setPagesize(1);
|
||||||
|
query.setParams(Collections.singletonMap("subcompanyid", entityId));
|
||||||
|
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
||||||
|
return CollUtil.getFirst(pageResp.getDataList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkHrDepartmentPageRespVO.Department fetchSingleDepartment(Long entityId) {
|
||||||
|
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
||||||
|
query.setCurpage(1);
|
||||||
|
query.setPagesize(1);
|
||||||
|
query.setParams(Collections.singletonMap("departmentid", entityId));
|
||||||
|
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
||||||
|
return CollUtil.getFirst(pageResp.getDataList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkHrJobTitlePageRespVO.JobTitle fetchSingleJob(Long entityId) {
|
||||||
|
IWorkJobTitleQueryReqVO query = new IWorkJobTitleQueryReqVO();
|
||||||
|
query.setCurpage(1);
|
||||||
|
query.setPagesize(1);
|
||||||
|
query.setParams(Collections.singletonMap("jobtitleid", entityId));
|
||||||
|
IWorkHrJobTitlePageRespVO pageResp = orgRestService.listJobTitles(query);
|
||||||
|
return CollUtil.getFirst(pageResp.getDataList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWorkHrUserPageRespVO.User fetchSingleUser(Long entityId) {
|
||||||
|
IWorkUserQueryReqVO query = new IWorkUserQueryReqVO();
|
||||||
|
query.setCurpage(1);
|
||||||
|
query.setPagesize(1);
|
||||||
|
query.setParams(Collections.singletonMap("id", entityId));
|
||||||
|
IWorkHrUserPageRespVO pageResp = orgRestService.listUsers(query);
|
||||||
|
return CollUtil.getFirst(pageResp.getDataList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface PageExecutor {
|
||||||
|
BatchExecution execute(int pageNumber, int pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record BatchExecution(IWorkSyncProcessor.BatchResult result, int totalPulled) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.jackson;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.core.JsonToken;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jackson 反序列化器,允许将诸如 "0.0"、"5" 等字符串形式的数字解析为整数,
|
||||||
|
* 用于兼容 iWork 返回的非标准整型字段。
|
||||||
|
*/
|
||||||
|
public class LenientIntegerDeserializer extends JsonDeserializer<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
|
||||||
|
JsonToken token = parser.currentToken();
|
||||||
|
if (token == null) {
|
||||||
|
token = parser.nextToken();
|
||||||
|
}
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (token.isNumeric()) {
|
||||||
|
return parser.getNumberValue().intValue();
|
||||||
|
}
|
||||||
|
if (token == JsonToken.VALUE_STRING) {
|
||||||
|
String text = parser.getText();
|
||||||
|
if (text == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = text.trim();
|
||||||
|
if (trimmed.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (trimmed.contains(".")) {
|
||||||
|
BigDecimal decimal = new BigDecimal(trimmed);
|
||||||
|
return decimal.intValue();
|
||||||
|
}
|
||||||
|
return Integer.parseInt(trimmed);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return (Integer) ctxt.handleWeirdStringValue(Integer.class, trimmed,
|
||||||
|
"无法将文本转换为整数");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (token == JsonToken.VALUE_NULL) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (Integer) ctxt.handleUnexpectedToken(Integer.class, parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.zt.plat.module.system.service.integration.iwork.impl;
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgQueryReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSyncRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOrgSyncReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkSubcompanyQueryReqVO;
|
||||||
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
|
||||||
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||||
import okhttp3.mockwebserver.MockResponse;
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
@@ -20,7 +22,6 @@ import java.time.Clock;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ class IWorkOrgRestServiceImplTest {
|
|||||||
private IWorkOrgRestService service;
|
private IWorkOrgRestService service;
|
||||||
private IWorkProperties properties;
|
private IWorkProperties properties;
|
||||||
private Clock fixedClock;
|
private Clock fixedClock;
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
@@ -40,7 +42,8 @@ class IWorkOrgRestServiceImplTest {
|
|||||||
|
|
||||||
properties = buildProperties();
|
properties = buildProperties();
|
||||||
fixedClock = Clock.fixed(Instant.ofEpochMilli(1_672_531_200_000L), ZoneOffset.UTC);
|
fixedClock = Clock.fixed(Instant.ofEpochMilli(1_672_531_200_000L), ZoneOffset.UTC);
|
||||||
service = new IWorkOrgRestServiceImpl(properties, new ObjectMapper(), fixedClock);
|
objectMapper = new ObjectMapper();
|
||||||
|
service = new IWorkOrgRestServiceImpl(properties, objectMapper, fixedClock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@@ -50,41 +53,46 @@ class IWorkOrgRestServiceImplTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldListSubcompanies() throws Exception {
|
void shouldListSubcompanies() throws Exception {
|
||||||
mockWebServer.enqueue(jsonResponse("{\"code\":\"1\",\"data\":{\"page\":1}}"));
|
mockWebServer.enqueue(jsonResponse("{\"code\":\"1\",\"data\":{\"totalSize\":1,\"totalPage\":1,\"pageSize\":10,\"pageNumber\":1,\"dataList\":[{\"subcompanyid1\":4,\"subcompanyname\":\"总部\"}]}}"));
|
||||||
|
|
||||||
IWorkOrgQueryReqVO reqVO = new IWorkOrgQueryReqVO();
|
IWorkSubcompanyQueryReqVO reqVO = new IWorkSubcompanyQueryReqVO();
|
||||||
reqVO.setParams(Map.of("curpage", 1));
|
reqVO.setParams(Map.of("curpage", 1));
|
||||||
IWorkOrgRespVO respVO = service.listSubcompanies(reqVO);
|
IWorkHrSubcompanyPageRespVO respVO = service.listSubcompanies(reqVO);
|
||||||
|
|
||||||
assertThat(respVO.isSuccess()).isTrue();
|
assertThat(respVO.isSuccess()).isTrue();
|
||||||
assertThat(respVO.getPayload()).containsEntry("page", 1);
|
assertThat(respVO.getTotalSize()).isEqualTo(1);
|
||||||
|
assertThat(respVO.getDataList()).hasSize(1);
|
||||||
|
assertThat(respVO.getDataList().get(0).getSubcompanyname()).isEqualTo("总部");
|
||||||
|
|
||||||
RecordedRequest request = mockWebServer.takeRequest();
|
RecordedRequest request = mockWebServer.takeRequest();
|
||||||
assertThat(request.getPath()).isEqualTo(properties.getOrg().getPaths().getSubcompanyPage());
|
assertThat(request.getPath()).isEqualTo(properties.getOrg().getPaths().getSubcompanyPage());
|
||||||
String decoded = URLDecoder.decode(request.getBody().readUtf8(), StandardCharsets.UTF_8);
|
String decoded = URLDecoder.decode(request.getBody().readUtf8(), StandardCharsets.UTF_8);
|
||||||
assertThat(decoded).contains("params={\"curpage\":1}");
|
JsonNode bodyNode = objectMapper.readTree(decoded);
|
||||||
String tokenJson = extractField(decoded, "token");
|
assertThat(bodyNode.path("params").path("curpage").asInt()).isEqualTo(1);
|
||||||
assertThat(tokenJson).isNotBlank();
|
JsonNode tokenNode = bodyNode.path("token");
|
||||||
assertThat(tokenJson).contains("\"ts\":\"1672531200000\"");
|
assertThat(tokenNode.path("ts").asText()).isEqualTo("1672531200000");
|
||||||
String expectedKey = DigestUtils.md5DigestAsHex("test-seed1672531200000".getBytes(StandardCharsets.UTF_8)).toUpperCase();
|
String expectedKey = DigestUtils.md5DigestAsHex("test-seed1672531200000".getBytes(StandardCharsets.UTF_8)).toUpperCase();
|
||||||
assertThat(tokenJson).contains("\"key\":\"" + expectedKey + "\"");
|
assertThat(tokenNode.path("key").asText()).isEqualTo(expectedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldSyncDepartments() throws Exception {
|
void shouldSyncDepartments() throws Exception {
|
||||||
mockWebServer.enqueue(jsonResponse("{\"code\":\"1\",\"result\":{}}"));
|
mockWebServer.enqueue(jsonResponse("{\"code\":\"1\",\"result\":[{\"@action\":\"add\",\"code\":\"demo\",\"result\":\"success\"}]}"));
|
||||||
|
|
||||||
IWorkOrgSyncReqVO reqVO = new IWorkOrgSyncReqVO();
|
IWorkOrgSyncReqVO reqVO = new IWorkOrgSyncReqVO();
|
||||||
reqVO.setData(List.of(Map.of("@action", "add", "code", "demo")));
|
reqVO.setData(List.of(Map.of("@action", "add", "code", "demo")));
|
||||||
IWorkOrgRespVO respVO = service.syncDepartments(reqVO);
|
IWorkHrSyncRespVO respVO = service.syncDepartments(reqVO);
|
||||||
|
|
||||||
assertThat(respVO.isSuccess()).isTrue();
|
assertThat(respVO.isSuccess()).isTrue();
|
||||||
assertThat(respVO.getPayload()).containsKey("result");
|
assertThat(respVO.getResult()).hasSize(1);
|
||||||
|
assertThat(respVO.getResult().get(0).getCode()).isEqualTo("demo");
|
||||||
|
|
||||||
RecordedRequest request = mockWebServer.takeRequest();
|
RecordedRequest request = mockWebServer.takeRequest();
|
||||||
assertThat(request.getPath()).isEqualTo(properties.getOrg().getPaths().getSyncDepartment());
|
assertThat(request.getPath()).isEqualTo(properties.getOrg().getPaths().getSyncDepartment());
|
||||||
String decoded = URLDecoder.decode(request.getBody().readUtf8(), StandardCharsets.UTF_8);
|
String decoded = URLDecoder.decode(request.getBody().readUtf8(), StandardCharsets.UTF_8);
|
||||||
assertThat(decoded).contains("data=[{\"@action\":\"add\",\"code\":\"demo\"}]");
|
JsonNode bodyNode = objectMapper.readTree(decoded);
|
||||||
|
assertThat(bodyNode.path("data").isArray()).isTrue();
|
||||||
|
assertThat(bodyNode.path("data").get(0).path("code").asText()).isEqualTo("demo");
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockResponse jsonResponse(String body) {
|
private MockResponse jsonResponse(String body) {
|
||||||
@@ -93,14 +101,6 @@ class IWorkOrgRestServiceImplTest {
|
|||||||
.setBody(body);
|
.setBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractField(String decoded, String key) {
|
|
||||||
return Arrays.stream(decoded.split("&"))
|
|
||||||
.filter(part -> part.startsWith(key + "="))
|
|
||||||
.map(part -> part.substring(key.length() + 1))
|
|
||||||
.findFirst()
|
|
||||||
.orElse("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IWorkProperties buildProperties() {
|
private IWorkProperties buildProperties() {
|
||||||
IWorkProperties properties = new IWorkProperties();
|
IWorkProperties properties = new IWorkProperties();
|
||||||
properties.setBaseUrl(mockWebServer.url("/").toString());
|
properties.setBaseUrl(mockWebServer.url("/").toString());
|
||||||
|
|||||||
Reference in New Issue
Block a user