Merge branch 'refs/heads/zt-test' into test
# Conflicts: # zt-module-infra/zt-module-infra-server/src/main/resources/application.yaml
This commit is contained in:
@@ -86,4 +86,10 @@ public interface DeptApi {
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步部门")
|
||||
CommonResult<Boolean> syncDept(@RequestBody DeptSaveReqDTO syncReqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -64,4 +64,10 @@ public interface PostApi {
|
||||
return CollectionUtils.convertMap(list, PostRespDTO::getId);
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步岗位")
|
||||
CommonResult<Boolean> syncPost(@RequestBody PostSaveReqDTO syncReqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,15 @@ public class DeptSaveReqDTO {
|
||||
@Schema(description = "部门编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "部门编码", example = "ZT001")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "部门名称", example = "ZT")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "部门简称", example = "技术")
|
||||
private String shortName;
|
||||
|
||||
@Schema(description = "父部门 ID", example = "1024")
|
||||
private Long parentId;
|
||||
|
||||
@@ -36,6 +42,15 @@ public class DeptSaveReqDTO {
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举0 开启 1 关闭", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "是否集团", example = "false")
|
||||
private Boolean isGroup;
|
||||
|
||||
@Schema(description = "是否公司", example = "false")
|
||||
private Boolean isCompany;
|
||||
|
||||
@Schema(description = "部门来源类型", example = "1")
|
||||
private Integer deptSource;
|
||||
|
||||
@Schema(description = "外部系统标识,用于建立编码映射", example = "ERP")
|
||||
private String externalSystemCode;
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.zt.plat.module.system.api.esp;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.collection.CollectionUtils;
|
||||
import com.zt.plat.module.system.api.dept.dto.*;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 部门")
|
||||
public interface EspApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/dept";
|
||||
|
||||
// === 以下为补全的接口方法 ===
|
||||
@PostMapping(PREFIX + "/create")
|
||||
@Operation(summary = "新增部门")
|
||||
CommonResult<Long> createDept(@RequestBody DeptSaveReqDTO createReqVO);
|
||||
|
||||
@PutMapping(PREFIX + "/update")
|
||||
@Operation(summary = "修改部门")
|
||||
CommonResult<Boolean> updateDept(@RequestBody DeptSaveReqDTO updateReqVO);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete")
|
||||
@Operation(summary = "删除部门")
|
||||
CommonResult<Boolean> deleteDept(@RequestParam("id") Long id);
|
||||
|
||||
@PostMapping(PREFIX + "/list-all")
|
||||
@Operation(summary = "获得部门列表")
|
||||
CommonResult<List<DeptDetailRespDTO>> getDeptList(@RequestBody DeptListReqDTO reqVO);
|
||||
|
||||
@GetMapping(PREFIX + "/simple-list")
|
||||
@Operation(summary = "获得部门精简信息列表")
|
||||
CommonResult<List<DeptSimpleRespDTO>> getSimpleDeptList();
|
||||
|
||||
@GetMapping(PREFIX + "/simple-company-list")
|
||||
@Operation(summary = "获得公司精简信息列表")
|
||||
CommonResult<List<DeptSimpleRespDTO>> getSimpleCompanyList();
|
||||
|
||||
@GetMapping(PREFIX + "/all-company-list")
|
||||
@Operation(summary = "获得所有公司精简信息列表")
|
||||
CommonResult<List<DeptSimpleRespDTO>> getAllCompanyList();
|
||||
|
||||
@GetMapping(PREFIX + "/get")
|
||||
@Operation(summary = "获得部门信息")
|
||||
@Parameter(name = "id", description = "部门编号", example = "1024", required = true)
|
||||
CommonResult<DeptRespDTO> getDept(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/list")
|
||||
@Operation(summary = "获得部门信息数组")
|
||||
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
|
||||
CommonResult<List<DeptRespDTO>> getDeptList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@Operation(summary = "校验部门是否合法")
|
||||
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
|
||||
CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得指定编号的部门 Map
|
||||
*
|
||||
* @param ids 部门编号数组
|
||||
* @return 部门 Map
|
||||
*/
|
||||
default Map<Long, DeptRespDTO> getDeptMap(Collection<Long> ids) {
|
||||
List<DeptRespDTO> list = getDeptList(ids).getCheckedData();
|
||||
return CollectionUtils.convertMap(list, DeptRespDTO::getId);
|
||||
}
|
||||
|
||||
@GetMapping(PREFIX + "/list-child")
|
||||
@Operation(summary = "获得指定部门的所有子部门")
|
||||
@Parameter(name = "id", description = "部门编号", example = "1024", required = true)
|
||||
CommonResult<List<DeptRespDTO>> getChildDeptList(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/company-dept-info")
|
||||
@Operation(summary = "获得指定用户的公司部门信息")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Set<CompanyDeptInfoRespDTO>> getCompanyDeptInfoListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步部门")
|
||||
CommonResult<Boolean> syncDept(@RequestBody DeptSaveReqDTO syncReqDTO);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zt.plat.module.system.api.esp.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "RPC 服务 - 推送外部系统配置信息 Response DTO")
|
||||
@Data
|
||||
public class EspDto {
|
||||
|
||||
@Schema(description = "部门名称,模糊匹配", example = "ZT")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "是否公司", example = "false")
|
||||
private Boolean isCompany;
|
||||
|
||||
@Schema(description = "是否集团", example = "false")
|
||||
private Boolean isGroup;
|
||||
|
||||
@Schema(description = "部门编号集合,支持多部门查询", example = "[\"1001\", \"1002\"]")
|
||||
private List<String> ids;
|
||||
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.zt.plat.framework.common.biz.system.permission.PermissionCommonApi;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.api.permission.dto.*;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -50,4 +51,9 @@ public interface PermissionApi extends PermissionCommonApi {
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Set<Long>> getUserRoleIdListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@GetMapping(PREFIX + "/user-data-permission-level")
|
||||
@Operation(summary = "获得用户的数据权限级别")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<DataScopeEnum> getUserDataPermissionLevel(@RequestParam("userId") Long userId);
|
||||
|
||||
}
|
||||
@@ -104,6 +104,12 @@ public interface AdminUserApi extends AutoTransable<AdminUserRespDTO> {
|
||||
@Parameter(name = "ids", description = "用户编号数组", example = "3,5", required = true)
|
||||
CommonResult<Boolean> validateUserList(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步用户")
|
||||
CommonResult<Boolean> syncUser(@RequestBody AdminUserSaveReqDTO syncReqDTO);
|
||||
|
||||
@Override
|
||||
@FeignIgnore
|
||||
default List<AdminUserRespDTO> selectByIds(List<?> ids) {
|
||||
|
||||
@@ -50,4 +50,10 @@ public class AdminUserSaveReqDTO {
|
||||
@Schema(description = "密码", example = "123456")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "工号", example = "A00123")
|
||||
private String workcode;
|
||||
|
||||
@Schema(description = "用户来源类型", example = "1")
|
||||
private Integer userSource;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zt.plat.module.system.api.userdept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.api.userdept.dto.UserDeptRespDTO;
|
||||
import com.zt.plat.module.system.api.userdept.dto.UserDeptSaveReqDTO;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户-部门关系 Feign API
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 用户部门关系")
|
||||
public interface UserDeptApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/user-dept";
|
||||
|
||||
@PostMapping(PREFIX + "/create")
|
||||
@Operation(summary = "新增用户部门关系")
|
||||
CommonResult<Long> createUserDept(@RequestBody UserDeptSaveReqDTO reqVO);
|
||||
|
||||
@PutMapping(PREFIX + "/update")
|
||||
@Operation(summary = "修改用户部门关系")
|
||||
CommonResult<Boolean> updateUserDept(@RequestBody UserDeptSaveReqDTO reqVO);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete")
|
||||
@Operation(summary = "删除用户部门关系")
|
||||
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserDept(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/get")
|
||||
@Operation(summary = "通过ID查询用户部门关系")
|
||||
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
|
||||
CommonResult<UserDeptRespDTO> getUserDept(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/list-by-user-id")
|
||||
@Operation(summary = "通过用户ID查询用户部门关系列表")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<List<UserDeptRespDTO>> getUserDeptListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@GetMapping(PREFIX + "/list-by-dept-id")
|
||||
@Operation(summary = "通过部门ID查询用户部门关系列表")
|
||||
@Parameter(name = "deptId", description = "部门编号", example = "1", required = true)
|
||||
CommonResult<List<UserDeptRespDTO>> getUserDeptListByDeptId(@RequestParam("deptId") Long deptId);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete-by-user-id")
|
||||
@Operation(summary = "通过用户ID删除用户部门关系")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserDeptByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete-by-dept-id")
|
||||
@Operation(summary = "通过部门ID删除用户部门关系")
|
||||
@Parameter(name = "deptId", description = "部门编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserDeptByDeptId(@RequestParam("deptId") Long deptId);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步用户部门关系")
|
||||
CommonResult<Boolean> syncUserDept(@RequestBody UserDeptSaveReqDTO syncReqDTO);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zt.plat.module.system.api.userdept.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户部门关系 Response DTO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "RPC 服务 - 用户部门关系 Response DTO")
|
||||
@Data
|
||||
public class UserDeptRespDTO {
|
||||
|
||||
@Schema(description = "关系编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "备注", example = "主部门")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.zt.plat.module.system.api.userdept.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户部门关系创建/修改 Request DTO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "RPC 服务 - 用户部门关系创建/修改 Request DTO")
|
||||
@Data
|
||||
public class UserDeptSaveReqDTO {
|
||||
|
||||
@Schema(description = "关系编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", example = "1", required = true)
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "部门编号", example = "100", required = true)
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "备注", example = "主部门")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.zt.plat.module.system.api.userpost;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.system.api.userpost.dto.UserPostRespDTO;
|
||||
import com.zt.plat.module.system.api.userpost.dto.UserPostSaveReqDTO;
|
||||
import com.zt.plat.module.system.enums.ApiConstants;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户-岗位关系 Feign API
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
@Tag(name = "RPC 服务 - 用户岗位关系")
|
||||
public interface UserPostApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/user-post";
|
||||
|
||||
@PostMapping(PREFIX + "/create")
|
||||
@Operation(summary = "新增用户岗位关系")
|
||||
CommonResult<Long> createUserPost(@RequestBody UserPostSaveReqDTO reqVO);
|
||||
|
||||
@PutMapping(PREFIX + "/update")
|
||||
@Operation(summary = "修改用户岗位关系")
|
||||
CommonResult<Boolean> updateUserPost(@RequestBody UserPostSaveReqDTO reqVO);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete")
|
||||
@Operation(summary = "删除用户岗位关系")
|
||||
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserPost(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/get")
|
||||
@Operation(summary = "通过ID查询用户岗位关系")
|
||||
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
|
||||
CommonResult<UserPostRespDTO> getUserPost(@RequestParam("id") Long id);
|
||||
|
||||
@GetMapping(PREFIX + "/list-by-user-id")
|
||||
@Operation(summary = "通过用户ID查询用户岗位关系列表")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<List<UserPostRespDTO>> getUserPostListByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@GetMapping(PREFIX + "/list-by-post-id")
|
||||
@Operation(summary = "通过岗位ID查询用户岗位关系列表")
|
||||
@Parameter(name = "postId", description = "岗位编号", example = "1", required = true)
|
||||
CommonResult<List<UserPostRespDTO>> getUserPostListByPostId(@RequestParam("postId") Long postId);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete-by-user-id")
|
||||
@Operation(summary = "通过用户ID删除用户岗位关系")
|
||||
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserPostByUserId(@RequestParam("userId") Long userId);
|
||||
|
||||
@DeleteMapping(PREFIX + "/delete-by-post-id")
|
||||
@Operation(summary = "通过岗位ID删除用户岗位关系")
|
||||
@Parameter(name = "postId", description = "岗位编号", example = "1", required = true)
|
||||
CommonResult<Boolean> deleteUserPostByPostId(@RequestParam("postId") Long postId);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@PostMapping(PREFIX + "/sync")
|
||||
@Operation(summary = "同步用户岗位关系")
|
||||
CommonResult<Boolean> syncUserPost(@RequestBody UserPostSaveReqDTO syncReqDTO);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.api.userpost.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户岗位关系 Response DTO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "RPC 服务 - 用户岗位关系 Response DTO")
|
||||
@Data
|
||||
public class UserPostRespDTO {
|
||||
|
||||
@Schema(description = "关系编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "岗位编号", example = "100")
|
||||
private Long postId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.zt.plat.module.system.api.userpost.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户岗位关系创建/修改 Request DTO
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Schema(description = "RPC 服务 - 用户岗位关系创建/修改 Request DTO")
|
||||
@Data
|
||||
public class UserPostSaveReqDTO {
|
||||
|
||||
@Schema(description = "关系编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", example = "1", required = true)
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "岗位编号", example = "100", required = true)
|
||||
private Long postId;
|
||||
}
|
||||
@@ -127,8 +127,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在");
|
||||
ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期");
|
||||
ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用");
|
||||
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量");
|
||||
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁");
|
||||
ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量:{}次");
|
||||
ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁,请于{}分钟后再试");
|
||||
|
||||
// ========== 租户信息 1-002-015-000 ==========
|
||||
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在");
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.zt.plat.module.system.enums.permission;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.zt.plat.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据范围枚举类
|
||||
@@ -33,6 +35,26 @@ public enum DataScopeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* Jackson 序列化时输出整数 code,兼容旧客户端
|
||||
*/
|
||||
@JsonValue
|
||||
public Integer getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public static DataScopeEnum findByScope(Integer scope) {
|
||||
if (scope == null) {
|
||||
return null;
|
||||
}
|
||||
for (DataScopeEnum value : values()) {
|
||||
if (Objects.equals(value.scope, scope)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
|
||||
@@ -137,7 +137,12 @@
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Seata 达梦数据库补丁,必须最先引入以覆盖 seata-all 中的类 -->
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-seata-dm</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 分布式事务 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.seata</groupId>
|
||||
@@ -190,6 +195,10 @@
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.zt.plat.module.system.api.databus;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
|
||||
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
|
||||
import com.zt.plat.module.databus.api.dto.CursorPageResult;
|
||||
import com.zt.plat.module.databus.api.provider.DatabusUserDeptProviderApi;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* Databus 用户-部门关系数据提供者 API 实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
public class DatabusUserDeptProviderApiImpl implements DatabusUserDeptProviderApi {
|
||||
|
||||
@Resource
|
||||
private UserDeptMapper userDeptMapper;
|
||||
|
||||
@Override
|
||||
public CommonResult<CursorPageResult<DatabusUserDeptData>> getPageByCursor(CursorPageReqDTO reqDTO) {
|
||||
// 多查一条判断是否有更多数据
|
||||
int limit = reqDTO.getBatchSize() != null ? reqDTO.getBatchSize() : 100;
|
||||
|
||||
// 查询用户部门关系
|
||||
List<UserDeptDO> list = userDeptMapper.selectPageByCursorWithUserSource(
|
||||
reqDTO.isFirstPage() ? null : reqDTO.getCursorTime(),
|
||||
reqDTO.isFirstPage() ? null : reqDTO.getCursorId(),
|
||||
reqDTO.getTenantId(),
|
||||
limit + 1
|
||||
);
|
||||
|
||||
// 判断是否有更多
|
||||
boolean hasMore = list.size() > limit;
|
||||
if (hasMore) {
|
||||
list = list.subList(0, limit);
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(CursorPageResult.empty());
|
||||
}
|
||||
|
||||
// 转换为同步数据
|
||||
List<DatabusUserDeptData> dataList = list.stream()
|
||||
.map(this::convertToData)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 获取最后一条数据的游标
|
||||
UserDeptDO last = list.get(list.size() - 1);
|
||||
|
||||
// 首次查询时返<E697B6><E8BF94><EFBFBD>总数
|
||||
Long total = null;
|
||||
if (reqDTO.isFirstPage()) {
|
||||
// 统计用户部门关系
|
||||
total = userDeptMapper.countWithUserSource(reqDTO.getTenantId());
|
||||
}
|
||||
|
||||
return success(CursorPageResult.of(
|
||||
dataList,
|
||||
last.getCreateTime(),
|
||||
last.getId(),
|
||||
hasMore,
|
||||
total
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<DatabusUserDeptData> getById(Long id) {
|
||||
UserDeptDO userDept = userDeptMapper.selectById(id);
|
||||
if (userDept == null) {
|
||||
return success(null);
|
||||
}
|
||||
return success(convertToData(userDept));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<DatabusUserDeptData>> getListByIds(List<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
List<UserDeptDO> list = userDeptMapper.selectBatchIds(ids);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
return success(list.stream()
|
||||
.map(this::convertToData)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> count(Long tenantId) {
|
||||
// 统计用户部门关系
|
||||
return success(userDeptMapper.countWithUserSource(tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 UserDeptDO 转换为 DatabusUserDeptData
|
||||
*/
|
||||
private DatabusUserDeptData convertToData(UserDeptDO userDept) {
|
||||
return DatabusUserDeptData.builder()
|
||||
.id(userDept.getId())
|
||||
.userId(userDept.getUserId())
|
||||
.deptId(userDept.getDeptId())
|
||||
.tenantId(userDept.getTenantId())
|
||||
.remark(userDept.getRemark())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.zt.plat.module.system.api.databus;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
|
||||
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
|
||||
import com.zt.plat.module.databus.api.dto.CursorPageResult;
|
||||
import com.zt.plat.module.databus.api.provider.DatabusUserPostProviderApi;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* Databus 用户-岗位关系数据提供者 API 实现
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
public class DatabusUserPostProviderApiImpl implements DatabusUserPostProviderApi {
|
||||
|
||||
@Resource
|
||||
private UserPostMapper userPostMapper;
|
||||
|
||||
@Override
|
||||
public CommonResult<CursorPageResult<DatabusUserPostData>> getPageByCursor(CursorPageReqDTO reqDTO) {
|
||||
// 多查一条判断是否有更多数据
|
||||
int limit = reqDTO.getBatchSize() != null ? reqDTO.getBatchSize() : 100;
|
||||
|
||||
// ⚠️ 使用关联查询,只查询 userSource = 2 的用户的岗位关系
|
||||
List<UserPostDO> list = userPostMapper.selectPageByCursorWithUserSource(
|
||||
reqDTO.isFirstPage() ? null : reqDTO.getCursorTime(),
|
||||
reqDTO.isFirstPage() ? null : reqDTO.getCursorId(),
|
||||
reqDTO.getTenantId(),
|
||||
limit + 1
|
||||
);
|
||||
|
||||
// 判断是否有更多
|
||||
boolean hasMore = list.size() > limit;
|
||||
if (hasMore) {
|
||||
list = list.subList(0, limit);
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(CursorPageResult.empty());
|
||||
}
|
||||
|
||||
// 转换为同步数据
|
||||
List<DatabusUserPostData> dataList = list.stream()
|
||||
.map(this::convertToData)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 获取最后一条数据的游标
|
||||
UserPostDO last = list.get(list.size() - 1);
|
||||
|
||||
// 首次查询时返回总数
|
||||
Long total = null;
|
||||
if (reqDTO.isFirstPage()) {
|
||||
// ⚠️ 只统计 userSource = 2 的用户的岗位关系
|
||||
total = userPostMapper.countWithUserSource(reqDTO.getTenantId());
|
||||
}
|
||||
|
||||
return success(CursorPageResult.of(
|
||||
dataList,
|
||||
last.getCreateTime(),
|
||||
last.getId(),
|
||||
hasMore,
|
||||
total
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<DatabusUserPostData> getById(Long id) {
|
||||
UserPostDO userPost = userPostMapper.selectById(id);
|
||||
if (userPost == null) {
|
||||
return success(null);
|
||||
}
|
||||
return success(convertToData(userPost));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<DatabusUserPostData>> getListByIds(List<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
List<UserPostDO> list = userPostMapper.selectBatchIds(ids);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
return success(list.stream()
|
||||
.map(this::convertToData)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> count(Long tenantId) {
|
||||
// ⚠️ 只统计 userSource = 2 的用户的岗位关系
|
||||
return success(userPostMapper.countWithUserSource(tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 UserPostDO 转换为 DatabusUserPostData
|
||||
*/
|
||||
private DatabusUserPostData convertToData(UserPostDO userPost) {
|
||||
return DatabusUserPostData.builder()
|
||||
.id(userPost.getId())
|
||||
.userId(userPost.getUserId())
|
||||
.postId(userPost.getPostId())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,6 +54,9 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
|
||||
// 构建游标查询条件
|
||||
LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// ⚠️ 只同步 userSource = 2 的用户
|
||||
queryWrapper.eq(AdminUserDO::getUserSource, 2);
|
||||
|
||||
// 游标条件:create_time > cursorTime OR (create_time = cursorTime AND id > cursorId)
|
||||
if (!reqDTO.isFirstPage()) {
|
||||
queryWrapper.and(w -> w
|
||||
@@ -100,6 +103,8 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
|
||||
Long total = null;
|
||||
if (reqDTO.isFirstPage()) {
|
||||
LambdaQueryWrapper<AdminUserDO> countWrapper = new LambdaQueryWrapper<>();
|
||||
// ⚠️ 只统计 userSource = 2 的用户
|
||||
countWrapper.eq(AdminUserDO::getUserSource, 2);
|
||||
if (reqDTO.getTenantId() != null) {
|
||||
countWrapper.eq(AdminUserDO::getTenantId, reqDTO.getTenantId());
|
||||
}
|
||||
@@ -143,6 +148,8 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
|
||||
@Override
|
||||
public CommonResult<Long> count(Long tenantId) {
|
||||
LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
// ⚠️ 只统计 userSource = 2 的用户
|
||||
queryWrapper.eq(AdminUserDO::getUserSource, 2);
|
||||
if (tenantId != null) {
|
||||
queryWrapper.eq(AdminUserDO::getTenantId, tenantId);
|
||||
}
|
||||
|
||||
@@ -107,4 +107,13 @@ public class DeptApiImpl implements DeptApi {
|
||||
return success(BeanUtils.toBean(companyDeptInfos, CompanyDeptInfoRespDTO.class));
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> syncDept(DeptSaveReqDTO syncReqDTO) {
|
||||
DeptSaveReqVO reqVO = BeanUtils.toBean(syncReqDTO, DeptSaveReqVO.class);
|
||||
deptService.syncDept(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
|
||||
import com.zt.plat.module.system.service.dept.PostService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -20,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Slf4j
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class PostApiImpl implements PostApi {
|
||||
@@ -49,6 +51,7 @@ public class PostApiImpl implements PostApi {
|
||||
|
||||
@Override
|
||||
public CommonResult<PostRespDTO> getPost(Long id) {
|
||||
log.error("cccccccc"+id);
|
||||
PostDO post = postService.getPost(id);
|
||||
return success(BeanUtils.toBean(post, PostRespDTO.class));
|
||||
}
|
||||
@@ -72,4 +75,11 @@ public class PostApiImpl implements PostApi {
|
||||
return success(BeanUtils.toBean(list, PostRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> syncPost(PostSaveReqDTO syncReqDTO) {
|
||||
PostSaveReqVO reqVO = BeanUtils.toBean(syncReqDTO, PostSaveReqVO.class);
|
||||
postService.syncPost(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.permission.dto.*;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO;
|
||||
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||
import com.zt.plat.module.system.service.permission.PermissionService;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -65,6 +66,11 @@ public class PermissionApiImpl implements PermissionApi {
|
||||
return success(permissionService.getUserRoleIdListByUserIdFromCache(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<DataScopeEnum> getUserDataPermissionLevel(Long userId) {
|
||||
return success(permissionService.getUserDataPermissionLevel(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> hasAnyPermissions(Long userId, String... permissions) {
|
||||
return success(permissionService.hasAnyPermissions(userId, permissions));
|
||||
|
||||
@@ -149,4 +149,11 @@ public class AdminUserApiImpl implements AdminUserApi {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> syncUser(AdminUserSaveReqDTO syncReqDTO) {
|
||||
UserSaveReqVO reqVO = BeanUtils.toBean(syncReqDTO, UserSaveReqVO.class);
|
||||
userService.syncUser(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.zt.plat.module.system.api.userdept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.userdept.dto.UserDeptRespDTO;
|
||||
import com.zt.plat.module.system.api.userdept.dto.UserDeptSaveReqDTO;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import com.zt.plat.module.system.service.userdept.UserDeptService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 用户-部门关系 API 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
public class UserDeptApiImpl implements UserDeptApi {
|
||||
|
||||
@Resource
|
||||
private UserDeptService userDeptService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> createUserDept(UserDeptSaveReqDTO reqVO) {
|
||||
UserDeptDO userDept = BeanUtils.toBean(reqVO, UserDeptDO.class);
|
||||
Long id = userDeptService.createUserDept(userDept);
|
||||
return success(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> updateUserDept(UserDeptSaveReqDTO reqVO) {
|
||||
UserDeptDO userDept = BeanUtils.toBean(reqVO, UserDeptDO.class);
|
||||
userDeptService.updateUserDept(userDept);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserDept(Long id) {
|
||||
userDeptService.deleteUserDept(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<UserDeptRespDTO> getUserDept(Long id) {
|
||||
UserDeptDO userDept = userDeptService.getUserDept(id);
|
||||
return success(BeanUtils.toBean(userDept, UserDeptRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<UserDeptRespDTO>> getUserDeptListByUserId(Long userId) {
|
||||
List<UserDeptDO> list = userDeptService.getValidUserDeptListByUserIds(List.of(userId));
|
||||
return success(BeanUtils.toBean(list, UserDeptRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<UserDeptRespDTO>> getUserDeptListByDeptId(Long deptId) {
|
||||
List<UserDeptDO> list = userDeptService.getValidUserDeptListByDeptIds(List.of(deptId));
|
||||
return success(BeanUtils.toBean(list, UserDeptRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserDeptByUserId(Long userId) {
|
||||
userDeptService.deleteUserDeptByUserId(userId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserDeptByDeptId(Long deptId) {
|
||||
// 需要实现此方法,暂时返回成功
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> syncUserDept(UserDeptSaveReqDTO syncReqDTO) {
|
||||
UserDeptDO userDept = BeanUtils.toBean(syncReqDTO, UserDeptDO.class);
|
||||
userDeptService.syncUserDept(userDept);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zt.plat.module.system.api.userpost;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.api.userpost.dto.UserPostRespDTO;
|
||||
import com.zt.plat.module.system.api.userpost.dto.UserPostSaveReqDTO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 用户-岗位关系 API 实现类
|
||||
*
|
||||
* @author ZT
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
public class UserPostApiImpl implements UserPostApi {
|
||||
|
||||
@Resource
|
||||
private UserPostMapper userPostMapper;
|
||||
|
||||
@Override
|
||||
public CommonResult<Long> createUserPost(UserPostSaveReqDTO reqVO) {
|
||||
UserPostDO userPost = BeanUtils.toBean(reqVO, UserPostDO.class);
|
||||
userPostMapper.insert(userPost);
|
||||
return success(userPost.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> updateUserPost(UserPostSaveReqDTO reqVO) {
|
||||
UserPostDO userPost = BeanUtils.toBean(reqVO, UserPostDO.class);
|
||||
userPostMapper.updateById(userPost);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserPost(Long id) {
|
||||
userPostMapper.deleteById(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<UserPostRespDTO> getUserPost(Long id) {
|
||||
UserPostDO userPost = userPostMapper.selectById(id);
|
||||
return success(BeanUtils.toBean(userPost, UserPostRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<UserPostRespDTO>> getUserPostListByUserId(Long userId) {
|
||||
List<UserPostDO> list = userPostMapper.selectListByUserId(userId);
|
||||
return success(BeanUtils.toBean(list, UserPostRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<UserPostRespDTO>> getUserPostListByPostId(Long postId) {
|
||||
List<UserPostDO> list = userPostMapper.selectListByPostIds(List.of(postId));
|
||||
return success(BeanUtils.toBean(list, UserPostRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserPostByUserId(Long userId) {
|
||||
userPostMapper.deleteByUserId(userId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> deleteUserPostByPostId(Long postId) {
|
||||
userPostMapper.delete(UserPostDO::getPostId, postId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CommonResult<Boolean> syncUserPost(UserPostSaveReqDTO syncReqDTO) {
|
||||
if (syncReqDTO.getId() == null) {
|
||||
return success(false);
|
||||
}
|
||||
|
||||
UserPostDO existing = userPostMapper.selectById(syncReqDTO.getId());
|
||||
UserPostDO userPost = BeanUtils.toBean(syncReqDTO, UserPostDO.class);
|
||||
|
||||
if (existing != null) {
|
||||
userPostMapper.updateById(userPost);
|
||||
log.info("[syncUserPost] 用户岗位关系同步-更新成功, id={}, userId={}, postId={}",
|
||||
userPost.getId(), userPost.getUserId(), userPost.getPostId());
|
||||
} else {
|
||||
userPostMapper.insert(userPost);
|
||||
log.info("[syncUserPost] 用户岗位关系同步-创建成功, id={}, userId={}, postId={}",
|
||||
userPost.getId(), userPost.getUserId(), userPost.getPostId());
|
||||
}
|
||||
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -143,6 +143,7 @@ public class AuthController {
|
||||
|
||||
@PostMapping("/sms-login")
|
||||
@PermitAll
|
||||
@TenantIgnore
|
||||
@Operation(summary = "使用短信验证码登录")
|
||||
public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
|
||||
return success(authService.smsLogin(reqVO));
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.zt.plat.module.system.controller.admin.dept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.dept.IEspService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 部门推送消息")
|
||||
@RestController
|
||||
@RequestMapping("/system/esp")
|
||||
@Validated
|
||||
public class EspController
|
||||
{
|
||||
|
||||
@Resource
|
||||
private IEspService espService;
|
||||
@Resource
|
||||
private DeptService deptService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建部门推送消息")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:create')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody EspSaveRespVo createReqVO) {
|
||||
Long id = espService.createDeptPushMsg(createReqVO);
|
||||
return success(id);
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改部门推送消息")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:update')")
|
||||
public CommonResult<Boolean> update(@Valid @RequestBody EspSaveRespVo updateReqVO) {
|
||||
espService.updateDeptPushMsg(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除部门推送消息")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:delete')")
|
||||
public CommonResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
espService.deleteDeptPushMsg(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取部门推送消息详情")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:query')")
|
||||
public CommonResult<EspSaveRespVo> get(@RequestParam("id") Long id) {
|
||||
DeptPushMsgDO entity = espService.getDeptPushMsgDetails(id);
|
||||
EspSaveRespVo respVO = BeanUtils.toBean(entity, EspSaveRespVo.class);
|
||||
fillDeptInfo(List.of(respVO));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询部门推送消息")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:query')")
|
||||
public CommonResult<PageResult<EspSaveRespVo>> page(@Valid EspPageReqVO reqVO) {
|
||||
PageResult<DeptPushMsgDO> pageResult = espService.getDeptExternalCodePage(reqVO);
|
||||
PageResult<EspSaveRespVo> result = BeanUtils.toBean(pageResult, EspSaveRespVo.class);
|
||||
fillDeptInfo(result.getList());
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-dept")
|
||||
@Operation(summary = "根据部门部门推送消息")
|
||||
@Parameter(name = "deptId", description = "部门编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:esp-external-code:query')")
|
||||
public CommonResult<List<EspSaveRespVo>> listByDept(@RequestParam("deptId") Long deptId) {
|
||||
List<DeptPushMsgDO> list = espService.getPushMsgByDeptId(deptId);
|
||||
List<EspSaveRespVo> respList = BeanUtils.toBean(list, EspSaveRespVo.class);
|
||||
fillDeptInfo(respList);
|
||||
return success(respList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void fillDeptInfo(List<EspSaveRespVo> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<Long> deptIds = list.stream()
|
||||
.map(EspSaveRespVo::getDeptId)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
if (deptIds == null || deptIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, DeptDO> deptMap = deptService.getDeptList(deptIds).stream()
|
||||
.collect(Collectors.toMap(DeptDO::getId, dept -> dept, (left, right) -> left));
|
||||
list.forEach(item -> {
|
||||
DeptDO dept = deptMap.get(item.getDeptId());
|
||||
if (dept != null) {
|
||||
item.setDeptName(dept.getName());
|
||||
item.setDeptCode(dept.getCode());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 部门外部组织编码映射分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EspPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "部门编号", example = "1024")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "外部系统标识", example = "ERP")
|
||||
private String systemCode;
|
||||
|
||||
@Schema(description = "外部组织编码", example = "100200")
|
||||
private String externalDeptCode;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 部门外消息推送创建/修改 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EspSaveRespVo extends DeptExternalCodeBaseVO {
|
||||
|
||||
@Schema(description = "映射编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "所属部门名称", example = "技术部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "所属部门编码", example = "DEPT_001")
|
||||
private String deptCode;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "最后更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.dept;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 部门推送消息 DO
|
||||
*/
|
||||
@TableName("system_dept_push_msg")
|
||||
@KeySequence("system_dept_push_msg_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeptPushMsgDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 本系统部门 ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 外部系统标识
|
||||
*/
|
||||
private String systemCode;
|
||||
|
||||
/**
|
||||
* 外部系统组织编码
|
||||
*/
|
||||
private String externalDeptCode;
|
||||
|
||||
/**
|
||||
* 外部系统组织名称
|
||||
*/
|
||||
private String externalDeptName;
|
||||
|
||||
/**
|
||||
* 映射状态
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -127,12 +127,17 @@ public interface DeptMapper extends BaseMapperX<DeptDO> {
|
||||
|
||||
/**
|
||||
* 根据部门编码查询部门
|
||||
* <p>
|
||||
* 注意:如果存在多条相同编码的记录,只返回第一条
|
||||
*
|
||||
* @param code 部门编码
|
||||
* @return 部门信息
|
||||
*/
|
||||
default DeptDO selectByCode(String code) {
|
||||
return selectOne(DeptDO::getCode, code);
|
||||
List<DeptDO> list = selectList(new LambdaQueryWrapperX<DeptDO>()
|
||||
.eq(DeptDO::getCode, code)
|
||||
.last("LIMIT 1"));
|
||||
return CollUtil.isNotEmpty(list) ? list.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.zt.plat.module.system.dal.mysql.dept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 部门推送消息接口Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface EspMapper extends BaseMapperX<DeptPushMsgDO> {
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param reqVO 消息推送VO
|
||||
* @return PageResult
|
||||
*/
|
||||
default PageResult<DeptPushMsgDO> selectPage(EspPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<DeptPushMsgDO>()
|
||||
.eqIfPresent(DeptPushMsgDO::getDeptId,reqVO.getDeptId())
|
||||
.eqIfPresent(DeptPushMsgDO::getSystemCode, reqVO.getSystemCode())
|
||||
.likeIfPresent(DeptPushMsgDO::getExternalDeptCode, reqVO.getExternalDeptCode())
|
||||
.eqIfPresent(DeptPushMsgDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(DeptPushMsgDO::getId));
|
||||
}
|
||||
|
||||
|
||||
default DeptPushMsgDO selectBySystemCodeAndDeptId(String systemCode, Long deptId) {
|
||||
return selectOne(new LambdaQueryWrapperX<DeptPushMsgDO>()
|
||||
.eq(DeptPushMsgDO::getSystemCode, systemCode)
|
||||
.eq(DeptPushMsgDO::getDeptId, deptId));
|
||||
}
|
||||
|
||||
default DeptPushMsgDO selectBySystemCodeAndExternalCode(String systemCode, String externalDeptCode) {
|
||||
return selectOne(new LambdaQueryWrapperX<DeptPushMsgDO>()
|
||||
.eq(DeptPushMsgDO::getSystemCode, systemCode)
|
||||
.eq(DeptPushMsgDO::getExternalDeptCode, externalDeptCode));
|
||||
}
|
||||
|
||||
default List<DeptPushMsgDO> selectListByDeptId(Long deptId) {
|
||||
return selectList(DeptPushMsgDO::getDeptId, deptId);
|
||||
}
|
||||
|
||||
default int deleteByDeptId(Long deptId) {
|
||||
return delete(DeptPushMsgDO::getDeptId, deptId);
|
||||
}
|
||||
|
||||
default List<DeptPushMsgDO> selectListBySystemCode(String systemCode) {
|
||||
return selectList(DeptPushMsgDO::getSystemCode, systemCode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,10 @@ import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,4 +32,44 @@ public interface UserPostMapper extends BaseMapperX<UserPostDO> {
|
||||
default void deleteByUserId(Long userId) {
|
||||
delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 游标分页查询用户-岗位关系(只查询 userSource = 2 的用户)
|
||||
* @param cursorTime 游标时间
|
||||
* @param cursorId 游标ID
|
||||
* @param tenantId 租户ID(可选)
|
||||
* @param limit 限制数量
|
||||
* @return 用户岗位关系列表
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"SELECT up.* FROM system_user_post up " +
|
||||
"INNER JOIN system_users u ON up.user_id = u.id " +
|
||||
"WHERE u.user_source = 2 " +
|
||||
"AND up.deleted = 0 " +
|
||||
"<if test='tenantId != null'> AND up.tenant_id = #{tenantId} </if>" +
|
||||
"<if test='cursorTime != null'>" +
|
||||
" AND (up.create_time > #{cursorTime} " +
|
||||
" OR (up.create_time = #{cursorTime} AND up.id > #{cursorId}))" +
|
||||
"</if>" +
|
||||
"ORDER BY up.create_time ASC, up.id ASC " +
|
||||
"LIMIT #{limit}" +
|
||||
"</script>")
|
||||
List<UserPostDO> selectPageByCursorWithUserSource(@Param("cursorTime") LocalDateTime cursorTime,
|
||||
@Param("cursorId") Long cursorId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* 统计用户-岗位关系数量(只统计 userSource = 2 的用户)
|
||||
* @param tenantId 租户ID(可选)
|
||||
* @return 数量
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"SELECT COUNT(*) FROM system_user_post up " +
|
||||
"INNER JOIN system_users u ON up.user_id = u.id " +
|
||||
"WHERE u.user_source = 2 " +
|
||||
"AND up.deleted = 0 " +
|
||||
"<if test='tenantId != null'> AND up.tenant_id = #{tenantId} </if>" +
|
||||
"</script>")
|
||||
Long countWithUserSource(@Param("tenantId") Long tenantId);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -45,4 +48,44 @@ public interface UserDeptMapper extends BaseMapperX<UserDeptDO> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 游标分页查询用户-部门关系(只查询 userSource = 2 的用户)
|
||||
* @param cursorTime 游标时间
|
||||
* @param cursorId 游标ID
|
||||
* @param tenantId 租户ID(可选)
|
||||
* @param limit 限制数量
|
||||
* @return 用户部门关系列表
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"SELECT ud.* FROM system_user_dept ud " +
|
||||
"INNER JOIN system_users u ON ud.user_id = u.id " +
|
||||
"WHERE u.user_source = 2 " +
|
||||
"AND ud.deleted = 0 " +
|
||||
"<if test='tenantId != null'> AND ud.tenant_id = #{tenantId} </if>" +
|
||||
"<if test='cursorTime != null'>" +
|
||||
" AND (ud.create_time > #{cursorTime} " +
|
||||
" OR (ud.create_time = #{cursorTime} AND ud.id > #{cursorId}))" +
|
||||
"</if>" +
|
||||
"ORDER BY ud.create_time ASC, ud.id ASC " +
|
||||
"LIMIT #{limit}" +
|
||||
"</script>")
|
||||
List<UserDeptDO> selectPageByCursorWithUserSource(@Param("cursorTime") LocalDateTime cursorTime,
|
||||
@Param("cursorId") Long cursorId,
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* 统计用户-部门关系数量(只统计 userSource = 2 的用户)
|
||||
* @param tenantId 租户ID(可选)
|
||||
* @return 数量
|
||||
*/
|
||||
@Select("<script>" +
|
||||
"SELECT COUNT(*) FROM system_user_dept ud " +
|
||||
"INNER JOIN system_users u ON ud.user_id = u.id " +
|
||||
"WHERE u.user_source = 2 " +
|
||||
"AND ud.deleted = 0 " +
|
||||
"<if test='tenantId != null'> AND ud.tenant_id = #{tenantId} </if>" +
|
||||
"</script>")
|
||||
Long countWithUserSource(@Param("tenantId") Long tenantId);
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import java.util.List;
|
||||
@Data
|
||||
public class SmsSendMessage {
|
||||
|
||||
public static final String TOPIC = "SMS_SEND_TOPIC"; // 重点:需要增加消息对应的 Topic
|
||||
public static final String TOPIC = "SMS_SEND_TOPIC_TEST"; // 重点:需要增加消息对应的 Topic
|
||||
/**
|
||||
* 短信日志编号
|
||||
*/
|
||||
|
||||
@@ -174,4 +174,15 @@ public interface DeptService {
|
||||
* @return 部门列表
|
||||
*/
|
||||
List<DeptDO> searchDeptTree(String keyword);
|
||||
|
||||
/**
|
||||
* 回填缺失的部门编码(不触发事件)。
|
||||
*
|
||||
* @param deptIds 需要回填的部门 ID 列表
|
||||
*/
|
||||
void backfillMissingCodesWithoutEvent(Collection<Long> deptIds);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
void syncDept(DeptSaveReqVO syncReqVO);
|
||||
}
|
||||
|
||||
@@ -900,4 +900,64 @@ public class DeptServiceImpl implements DeptService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@DataPermission(enable = false)
|
||||
public void backfillMissingCodesWithoutEvent(Collection<Long> deptIds) {
|
||||
if (CollUtil.isEmpty(deptIds)) {
|
||||
return;
|
||||
}
|
||||
List<DeptDO> targets = deptMapper.selectBatchIds(deptIds);
|
||||
for (DeptDO dept : targets) {
|
||||
if (dept == null || StrUtil.isNotBlank(dept.getCode())) {
|
||||
continue;
|
||||
}
|
||||
Integer source = ObjectUtil.defaultIfNull(dept.getDeptSource(), DeptSourceEnum.EXTERNAL.getSource());
|
||||
try {
|
||||
String code = generateDeptCode(dept.getParentId(), source);
|
||||
validateDeptCodeUnique(dept.getId(), code);
|
||||
updateDeptCode(dept.getId(), code);
|
||||
} catch (Exception ex) {
|
||||
log.warn("[iWork] 回填部门编码失败 id={} name={} msg={}", dept.getId(), dept.getName(), ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, allEntries = true)
|
||||
@DataPermission(enable = false)
|
||||
public void syncDept(DeptSaveReqVO syncReqVO) {
|
||||
if (syncReqVO.getId() == null) {
|
||||
log.warn("[syncDept] 同步部门失败,ID 不能为空");
|
||||
return;
|
||||
}
|
||||
// 标准化父部门 ID
|
||||
syncReqVO.setParentId(normalizeParentId(syncReqVO.getParentId()));
|
||||
// 默认部门来源
|
||||
if (syncReqVO.getDeptSource() == null) {
|
||||
syncReqVO.setDeptSource(DeptSourceEnum.EXTERNAL.getSource());
|
||||
}
|
||||
|
||||
// 检查部门是否存在
|
||||
DeptDO existingDept = deptMapper.selectById(syncReqVO.getId());
|
||||
|
||||
// 转换为 DO
|
||||
DeptDO dept = BeanUtils.toBean(syncReqVO, DeptDO.class);
|
||||
|
||||
if (existingDept != null) {
|
||||
// 部门存在,执行更新
|
||||
deptMapper.updateById(dept);
|
||||
log.info("[syncDept] 部门同步-更新成功, deptId={}, deptName={}", dept.getId(), dept.getName());
|
||||
} else {
|
||||
// 部门不存在,执行插入
|
||||
deptMapper.insert(dept);
|
||||
log.info("[syncDept] 部门同步-创建成功, deptId={}, deptName={}", dept.getId(), dept.getName());
|
||||
}
|
||||
|
||||
// 注意:不发布变更事件,避免循环同步
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.zt.plat.module.system.service.dept;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.DeptMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.dept.EspMapper;
|
||||
import com.zt.plat.module.system.dal.redis.RedisKeyConstants;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 部门推送消息接口ServiceImpl实现类
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class EspServiceImpl implements IEspService {
|
||||
|
||||
@Resource
|
||||
private EspMapper espMapper;
|
||||
@Resource
|
||||
private DeptMapper deptMapper;
|
||||
@Resource
|
||||
private CacheManager cacheManager;
|
||||
|
||||
@Override
|
||||
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#createReqVO.deptId", beforeInvocation = false)
|
||||
public Long createDeptPushMsg(EspSaveRespVo createReqVO) {
|
||||
|
||||
//请求校验
|
||||
normalizeRequest(createReqVO);
|
||||
//冲突禁用-映射
|
||||
disableActiveMappingIfConflict(createReqVO.getDeptId(), createReqVO.getSystemCode(), createReqVO.getExternalDeptCode());
|
||||
validateForCreateOrUpdate(null, createReqVO.getDeptId(), createReqVO.getSystemCode(),
|
||||
createReqVO.getExternalDeptCode());
|
||||
|
||||
DeptPushMsgDO entity = BeanUtils.toBean(createReqVO, DeptPushMsgDO.class);
|
||||
if (entity.getStatus() == null) {
|
||||
entity.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
espMapper.insert(entity);
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDeptPushMsg(EspSaveRespVo updateReqVO) {
|
||||
normalizeRequest(updateReqVO);
|
||||
DeptPushMsgDO exists = validateExists(updateReqVO.getId());
|
||||
disableActiveMappingIfConflict(updateReqVO.getDeptId(), updateReqVO.getSystemCode(), updateReqVO.getExternalDeptCode());
|
||||
validateForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getDeptId(), updateReqVO.getSystemCode(),
|
||||
updateReqVO.getExternalDeptCode());
|
||||
|
||||
DeptPushMsgDO updateObj = BeanUtils.toBean(updateReqVO, DeptPushMsgDO.class);
|
||||
// 保持原有的状态默认值逻辑
|
||||
if (updateObj.getStatus() == null) {
|
||||
updateObj.setStatus(exists.getStatus() == null ? CommonStatusEnum.ENABLE.getStatus() : exists.getStatus());
|
||||
}
|
||||
espMapper.updateById(updateObj);
|
||||
evictCacheSafely(exists.getDeptId());
|
||||
evictCacheSafely(updateObj.getDeptId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDeptPushMsg(Long id) {
|
||||
DeptPushMsgDO exists = validateExists(id);
|
||||
espMapper.deleteById(id);
|
||||
evictCacheSafely(exists.getDeptId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeptPushMsgDO getDeptPushMsgDetails(Long id) {
|
||||
return espMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<DeptPushMsgDO> getDeptExternalCodePage(EspPageReqVO reqVO) {
|
||||
|
||||
return espMapper.selectPage(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(cacheNames = RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST, key = "#deptId")
|
||||
public List<DeptPushMsgDO> getPushMsgByDeptId(Long deptId) {
|
||||
return espMapper.selectListByDeptId(deptId);
|
||||
}
|
||||
|
||||
/* @Override
|
||||
public DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode) {
|
||||
if (StrUtil.hasEmpty(systemCode, externalDeptCode)) {
|
||||
return null;
|
||||
}
|
||||
return espMapper.selectBySystemCodeAndExternalCode(systemCode.trim(), externalDeptCode.trim());
|
||||
}*/
|
||||
|
||||
/* @Override
|
||||
public DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId) {
|
||||
if (StrUtil.isBlank(systemCode) || deptId == null) {
|
||||
return null;
|
||||
}
|
||||
return espMapper.selectBySystemCodeAndDeptId(systemCode.trim(), deptId);
|
||||
}*/
|
||||
|
||||
/* @Override
|
||||
public Long getDeptPushMsgDetails(Long deptId, String systemCode, String externalDeptCode,
|
||||
String externalDeptName, Integer status) {
|
||||
|
||||
if (StrUtil.hasEmpty(systemCode, externalDeptCode) || deptId == null) {
|
||||
return null;
|
||||
}
|
||||
String normalizedSystemCode = systemCode.trim();
|
||||
String normalizedExternalCode = externalDeptCode.trim();
|
||||
String normalizedExternalName = StrUtil.blankToDefault(StrUtil.trimToNull(externalDeptName), null);
|
||||
|
||||
disableActiveMappingIfConflict(deptId, normalizedSystemCode, normalizedExternalCode);
|
||||
|
||||
// 如果存在则更新,否则创建
|
||||
DeptExternalCodeDO exists = espMapper.selectBySystemCodeAndDeptId(normalizedSystemCode, deptId);
|
||||
if (exists != null) {
|
||||
DeptExternalCodeSaveReqVO updateReqVO = new DeptExternalCodeSaveReqVO();
|
||||
updateReqVO.setId(exists.getId());
|
||||
updateReqVO.setDeptId(deptId);
|
||||
updateReqVO.setSystemCode(normalizedSystemCode);
|
||||
updateReqVO.setExternalDeptCode(normalizedExternalCode);
|
||||
updateReqVO.setExternalDeptName(normalizedExternalName);
|
||||
updateReqVO.setStatus(status == null ? exists.getStatus() : status);
|
||||
|
||||
//TODO
|
||||
//getDeptPushMsgDetails(updateReqVO);
|
||||
return exists.getId();
|
||||
}
|
||||
|
||||
DeptExternalCodeSaveReqVO createReqVO = new DeptExternalCodeSaveReqVO();
|
||||
createReqVO.setDeptId(deptId);
|
||||
createReqVO.setSystemCode(normalizedSystemCode);
|
||||
createReqVO.setExternalDeptCode(normalizedExternalCode);
|
||||
createReqVO.setExternalDeptName(normalizedExternalName);
|
||||
createReqVO.setStatus(status == null ? CommonStatusEnum.ENABLE.getStatus() : status);
|
||||
return getDeptPushMsgDetails(createReqVO);
|
||||
}*/
|
||||
|
||||
/* @Override
|
||||
public void deleteDeptExternalCodesByDeptId(Long deptId) {
|
||||
if (deptId == null) {
|
||||
return;
|
||||
}
|
||||
espMapper.deleteByDeptId(deptId);
|
||||
evictCacheSafely(deptId);
|
||||
}*/
|
||||
|
||||
private DeptPushMsgDO validateExists(Long id) {
|
||||
if (id == null) {
|
||||
throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS);
|
||||
}
|
||||
DeptPushMsgDO entity = espMapper.selectById(id);
|
||||
if (entity == null) {
|
||||
throw exception(DEPT_EXTERNAL_RELATION_NOT_EXISTS);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void validateForCreateOrUpdate(Long id, Long deptId, String systemCode, String externalDeptCode) {
|
||||
|
||||
// 校验部门存在
|
||||
DeptDO dept = deptMapper.selectById(deptId);
|
||||
if (dept == null) {
|
||||
throw exception(DEPT_NOT_FOUND);
|
||||
}
|
||||
String normalizedSystemCode = StrUtil.blankToDefault(systemCode, null);
|
||||
String normalizedExternalCode = StrUtil.blankToDefault(externalDeptCode, null);
|
||||
|
||||
// 校验同一系统下部门唯一
|
||||
if (StrUtil.isNotBlank(normalizedSystemCode)) {
|
||||
DeptPushMsgDO sameDept = espMapper
|
||||
.selectBySystemCodeAndDeptId(normalizedSystemCode, deptId);
|
||||
if (sameDept != null && (id == null || !sameDept.getId().equals(id))) {
|
||||
throw exception(DEPT_EXTERNAL_RELATION_EXISTS, normalizedSystemCode);
|
||||
}
|
||||
}
|
||||
// 校验同一系统下外部编码唯一
|
||||
if (StrUtil.isNotBlank(normalizedSystemCode) && StrUtil.isNotBlank(normalizedExternalCode)) {
|
||||
DeptPushMsgDO sameExternal = espMapper
|
||||
.selectBySystemCodeAndExternalCode(normalizedSystemCode, normalizedExternalCode);
|
||||
if (sameExternal != null && (id == null || !sameExternal.getId().equals(id))) {
|
||||
boolean sameDept = Objects.equals(deptId, sameExternal.getDeptId());
|
||||
boolean activeConflict = !sameDept && CommonStatusEnum.isEnable(sameExternal.getStatus());
|
||||
if (activeConflict) {
|
||||
throw exception(DEPT_EXTERNAL_CODE_DUPLICATE, normalizedSystemCode, normalizedExternalCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeRequest(EspSaveRespVo reqVO) {
|
||||
if (reqVO == null) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isNotBlank(reqVO.getSystemCode())) {
|
||||
reqVO.setSystemCode(reqVO.getSystemCode().trim());
|
||||
}
|
||||
if (StrUtil.isNotBlank(reqVO.getExternalDeptCode())) {
|
||||
reqVO.setExternalDeptCode(reqVO.getExternalDeptCode().trim());
|
||||
}
|
||||
if (reqVO.getStatus() == null) {
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void disableActiveMappingIfConflict(Long targetDeptId, String systemCode, String externalDeptCode) {
|
||||
String normalizedSystem = StrUtil.trimToNull(systemCode);
|
||||
String normalizedExternal = StrUtil.trimToNull(externalDeptCode);
|
||||
if (StrUtil.hasEmpty(normalizedSystem, normalizedExternal) || targetDeptId == null) {
|
||||
return;
|
||||
}
|
||||
DeptPushMsgDO existing = espMapper.selectBySystemCodeAndExternalCode(normalizedSystem, normalizedExternal);
|
||||
if (existing == null) {
|
||||
return;
|
||||
}
|
||||
if (Objects.equals(existing.getDeptId(), targetDeptId)) {
|
||||
return;
|
||||
}
|
||||
if (CommonStatusEnum.isEnable(existing.getStatus())) {
|
||||
DeptPushMsgDO update = new DeptPushMsgDO();
|
||||
update.setId(existing.getId());
|
||||
update.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
espMapper.updateById(update);
|
||||
evictCacheSafely(existing.getDeptId());
|
||||
}
|
||||
}
|
||||
|
||||
private void evictCacheSafely(Long deptId) {
|
||||
|
||||
if (deptId == null || cacheManager == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (cacheManager.getCache(RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST) != null) {
|
||||
cacheManager.getCache(RedisKeyConstants.DEPT_EXTERNAL_CODE_LIST).evict(deptId);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 缓存失效失败不影响主流程
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.zt.plat.module.system.service.dept;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.depexternalcode.EspSaveRespVo;
|
||||
import com.zt.plat.module.system.dal.dataobject.dept.DeptPushMsgDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门推送消息 Service 接口
|
||||
*/
|
||||
public interface IEspService {
|
||||
|
||||
/**
|
||||
* 创建映射关系
|
||||
* @param createReqVO 创建请求
|
||||
* @return 新增记录编号
|
||||
*/
|
||||
Long createDeptPushMsg(EspSaveRespVo createReqVO);
|
||||
|
||||
/**
|
||||
* 更新映射关系
|
||||
* @param updateReqVO 更新请求
|
||||
*/
|
||||
void updateDeptPushMsg(EspSaveRespVo updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除映射关系
|
||||
*
|
||||
* @param id 记录编号
|
||||
*/
|
||||
void deleteDeptPushMsg(Long id);
|
||||
|
||||
/**
|
||||
* 获取映射详情
|
||||
*/
|
||||
DeptPushMsgDO getDeptPushMsgDetails(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询映射
|
||||
*/
|
||||
PageResult<DeptPushMsgDO> getDeptExternalCodePage(EspPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 根据部门推送消息
|
||||
*/
|
||||
List<DeptPushMsgDO> getPushMsgByDeptId(Long deptId);
|
||||
|
||||
/**
|
||||
* 根据部门与外部系统保存/更新映射(存在则更新,不存在则创建)
|
||||
* @param deptId 本系统部门 ID
|
||||
* @param systemCode 外部系统标识
|
||||
* @param externalDeptCode 外部系统组织编码
|
||||
* @param externalDeptName 外部系统组织名称(可选)
|
||||
* @param status 状态,默认启用
|
||||
* @return 映射记录 ID
|
||||
*/
|
||||
/* Long getDeptPushMsgDetails(Long deptId, String systemCode, String externalDeptCode, String externalDeptName,
|
||||
Integer status);*/
|
||||
|
||||
/**
|
||||
* 根据部门删除全部外部编码映射
|
||||
*
|
||||
* @param deptId 部门编号
|
||||
*/
|
||||
//void deleteDeptExternalCodesByDeptId(Long deptId);
|
||||
|
||||
/**
|
||||
* 根据外部系统与外部组织编码查询映射
|
||||
*/
|
||||
//DeptExternalCodeDO getBySystemCodeAndExternalCode(String systemCode, String externalDeptCode);
|
||||
|
||||
/**
|
||||
* 根据外部系统与部门编号查询映射
|
||||
*/
|
||||
//DeptExternalCodeDO getBySystemCodeAndDeptId(String systemCode, Long deptId);
|
||||
|
||||
}
|
||||
@@ -89,4 +89,8 @@ public interface PostService {
|
||||
*/
|
||||
Long getOrCreatePostByName(String postName);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
void syncPost(PostSaveReqVO syncReqVO);
|
||||
|
||||
}
|
||||
|
||||
@@ -191,4 +191,25 @@ public class PostServiceImpl implements PostService {
|
||||
|
||||
return createPost(createReqVO);
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
public void syncPost(PostSaveReqVO syncReqVO) {
|
||||
if (syncReqVO.getId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PostDO existingPost = postMapper.selectById(syncReqVO.getId());
|
||||
PostDO post = BeanUtils.toBean(syncReqVO, PostDO.class);
|
||||
|
||||
if (existingPost != null) {
|
||||
postMapper.updateById(post);
|
||||
} else {
|
||||
if (post.getStatus() == null) {
|
||||
post.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
postMapper.insert(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,42 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrJo
|
||||
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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Abstraction for applying iWork entities into local persistence.
|
||||
*/
|
||||
public interface IWorkSyncProcessor {
|
||||
|
||||
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options);
|
||||
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||
SyncOptions options);
|
||||
|
||||
BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context);
|
||||
|
||||
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options);
|
||||
|
||||
BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context);
|
||||
|
||||
BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options);
|
||||
|
||||
BatchResult syncUsers(List<IWorkHrUserPageRespVO.User> data, SyncOptions options);
|
||||
|
||||
/**
|
||||
* 对当次同步累计的待处理/占位部门做最终补偿(跨页父子依赖)。
|
||||
*/
|
||||
default BatchResult flushDeptPending(DeptSyncContext context, SyncOptions options) {
|
||||
return BatchResult.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution options shared by batch and single sync flows.
|
||||
*/
|
||||
@@ -53,6 +72,32 @@ public interface IWorkSyncProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门/分部跨页同步上下文,用于累计待处理记录与已就绪父级。
|
||||
*/
|
||||
final class DeptSyncContext {
|
||||
private final Set<Long> readyParentIds = new HashSet<>();
|
||||
private final List<IWorkHrSubcompanyPageRespVO.Subcompany> pendingSubcompanies = new ArrayList<>();
|
||||
private final List<IWorkHrDepartmentPageRespVO.Department> pendingDepartments = new ArrayList<>();
|
||||
private final Set<Long> placeholderDeptIds = new HashSet<>();
|
||||
|
||||
public Set<Long> getReadyParentIds() {
|
||||
return readyParentIds;
|
||||
}
|
||||
|
||||
public List<IWorkHrSubcompanyPageRespVO.Subcompany> getPendingSubcompanies() {
|
||||
return pendingSubcompanies;
|
||||
}
|
||||
|
||||
public List<IWorkHrDepartmentPageRespVO.Department> getPendingDepartments() {
|
||||
return pendingDepartments;
|
||||
}
|
||||
|
||||
public Set<Long> getPlaceholderDeptIds() {
|
||||
return placeholderDeptIds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregated result for a sync batch.
|
||||
*/
|
||||
@@ -170,11 +215,11 @@ public interface IWorkSyncProcessor {
|
||||
}
|
||||
|
||||
default BatchResult syncSubcompany(IWorkHrSubcompanyPageRespVO.Subcompany data, SyncOptions options) {
|
||||
return syncSubcompanies(Collections.singletonList(data), options);
|
||||
return syncSubcompanies(Collections.singletonList(data), options, null);
|
||||
}
|
||||
|
||||
default BatchResult syncDepartment(IWorkHrDepartmentPageRespVO.Department data, SyncOptions options) {
|
||||
return syncDepartments(Collections.singletonList(data), options);
|
||||
return syncDepartments(Collections.singletonList(data), options, null);
|
||||
}
|
||||
|
||||
default BatchResult syncJobTitle(IWorkHrJobTitlePageRespVO.JobTitle data, SyncOptions options) {
|
||||
|
||||
@@ -50,14 +50,34 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
|
||||
@Override
|
||||
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data, SyncOptions options) {
|
||||
return syncSubcompanies(data, options, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchResult syncSubcompanies(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context) {
|
||||
return syncSubcompaniesInternal(data, options, context, false);
|
||||
}
|
||||
|
||||
private BatchResult syncSubcompaniesInternal(List<IWorkHrSubcompanyPageRespVO.Subcompany> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context,
|
||||
boolean allowPlaceholderOnRemaining) {
|
||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> records = CollUtil.emptyIfNull(data);
|
||||
BatchResult result = BatchResult.empty();
|
||||
if (records.isEmpty()) {
|
||||
if (records.isEmpty()
|
||||
&& (context == null || CollUtil.isEmpty(context.getPendingSubcompanies()))) {
|
||||
return result;
|
||||
}
|
||||
result.increasePulled(records.size());
|
||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> queue = new ArrayList<>(records);
|
||||
Set<Long> readyParentIds = new HashSet<>();
|
||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> queue = new ArrayList<>();
|
||||
if (context != null && CollUtil.isNotEmpty(context.getPendingSubcompanies())) {
|
||||
queue.addAll(context.getPendingSubcompanies());
|
||||
context.getPendingSubcompanies().clear();
|
||||
}
|
||||
queue.addAll(records);
|
||||
Set<Long> readyParentIds = context != null ? context.getReadyParentIds() : new HashSet<>();
|
||||
int guard = 0;
|
||||
int maxPasses = Math.max(1, queue.size() * 2);
|
||||
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||
@@ -106,6 +126,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!queue.isEmpty()) {
|
||||
if (context != null && !allowPlaceholderOnRemaining) {
|
||||
context.getPendingSubcompanies().addAll(queue);
|
||||
queue.clear();
|
||||
}
|
||||
}
|
||||
if (!queue.isEmpty()) {
|
||||
for (IWorkHrSubcompanyPageRespVO.Subcompany remaining : queue) {
|
||||
log.warn("[iWork] 分部父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getSubcompanyname());
|
||||
@@ -117,6 +143,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
try {
|
||||
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
||||
applyDeptOutcome(result, outcome, "分部", remaining.getSubcompanyname());
|
||||
if (context != null && outcome.deptId() != null) {
|
||||
context.getPlaceholderDeptIds().add(outcome.deptId());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("[iWork] 分部占位插入失败: id={} name={}", remaining.getId(), remaining.getSubcompanyname(), ex);
|
||||
result.increaseFailed();
|
||||
@@ -128,14 +157,34 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
|
||||
@Override
|
||||
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data, SyncOptions options) {
|
||||
return syncDepartments(data, options, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchResult syncDepartments(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context) {
|
||||
return syncDepartmentsInternal(data, options, context, false);
|
||||
}
|
||||
|
||||
private BatchResult syncDepartmentsInternal(List<IWorkHrDepartmentPageRespVO.Department> data,
|
||||
SyncOptions options,
|
||||
DeptSyncContext context,
|
||||
boolean allowPlaceholderOnRemaining) {
|
||||
List<IWorkHrDepartmentPageRespVO.Department> records = CollUtil.emptyIfNull(data);
|
||||
BatchResult result = BatchResult.empty();
|
||||
if (records.isEmpty()) {
|
||||
if (records.isEmpty()
|
||||
&& (context == null || CollUtil.isEmpty(context.getPendingDepartments()))) {
|
||||
return result;
|
||||
}
|
||||
result.increasePulled(records.size());
|
||||
List<IWorkHrDepartmentPageRespVO.Department> queue = new ArrayList<>(records);
|
||||
Set<Long> readyParentIds = new HashSet<>();
|
||||
List<IWorkHrDepartmentPageRespVO.Department> queue = new ArrayList<>();
|
||||
if (context != null && CollUtil.isNotEmpty(context.getPendingDepartments())) {
|
||||
queue.addAll(context.getPendingDepartments());
|
||||
context.getPendingDepartments().clear();
|
||||
}
|
||||
queue.addAll(records);
|
||||
Set<Long> readyParentIds = context != null ? context.getReadyParentIds() : new HashSet<>();
|
||||
int guard = 0;
|
||||
int maxPasses = Math.max(1, queue.size() * 2);
|
||||
while (!queue.isEmpty() && guard++ < maxPasses) {
|
||||
@@ -184,6 +233,12 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!queue.isEmpty()) {
|
||||
if (context != null && !allowPlaceholderOnRemaining) {
|
||||
context.getPendingDepartments().addAll(queue);
|
||||
queue.clear();
|
||||
}
|
||||
}
|
||||
if (!queue.isEmpty()) {
|
||||
for (IWorkHrDepartmentPageRespVO.Department remaining : queue) {
|
||||
log.warn("[iWork] 部门父级缺失,延迟生成编码插入占位: id={} name={}", remaining.getId(), remaining.getDepartmentname());
|
||||
@@ -195,6 +250,9 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
try {
|
||||
DeptSyncOutcome outcome = upsertDept(saveReq.getId(), saveReq, isCanceledFlag(remaining.getCanceled()), options);
|
||||
applyDeptOutcome(result, outcome, "部门", remaining.getDepartmentname());
|
||||
if (context != null && outcome.deptId() != null) {
|
||||
context.getPlaceholderDeptIds().add(outcome.deptId());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("[iWork] 部门占位插入失败: id={} name={}", remaining.getId(), remaining.getDepartmentname(), ex);
|
||||
result.increaseFailed();
|
||||
@@ -204,6 +262,17 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchResult flushDeptPending(DeptSyncContext context, SyncOptions options) {
|
||||
BatchResult result = BatchResult.empty();
|
||||
if (context == null) {
|
||||
return result;
|
||||
}
|
||||
result.merge(syncSubcompaniesInternal(Collections.emptyList(), options, context, true));
|
||||
result.merge(syncDepartmentsInternal(Collections.emptyList(), options, context, true));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchResult syncJobTitles(List<IWorkHrJobTitlePageRespVO.JobTitle> data, SyncOptions options) {
|
||||
List<IWorkHrJobTitlePageRespVO.JobTitle> records = CollUtil.emptyIfNull(data);
|
||||
@@ -444,7 +513,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
req.setIsGroup(Boolean.FALSE);
|
||||
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
||||
req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getSubcompanycode()), String.valueOf(data.getId())));
|
||||
req.setExternalDeptCode(trimToNull(data.getSubcompanycode()));
|
||||
req.setExternalDeptName(data.getSubcompanyname());
|
||||
return req;
|
||||
}
|
||||
@@ -464,7 +533,7 @@ public class IWorkSyncProcessorImpl implements IWorkSyncProcessor {
|
||||
req.setIsGroup(Boolean.FALSE);
|
||||
req.setDeptSource(DeptSourceEnum.IWORK.getSource());
|
||||
req.setExternalSystemCode(ExternalPlatformEnum.IWORK.getCode());
|
||||
req.setExternalDeptCode(StrUtil.blankToDefault(trimToNull(data.getDepartmentcode()), String.valueOf(data.getId())));
|
||||
req.setExternalDeptCode(trimToNull(data.getDepartmentcode()));
|
||||
req.setExternalDeptName(data.getDepartmentname());
|
||||
return req;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
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;
|
||||
@@ -31,6 +32,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
|
||||
private final IWorkOrgRestService orgRestService;
|
||||
private final IWorkSyncProcessor syncProcessor;
|
||||
private final DeptService deptService;
|
||||
|
||||
@Override
|
||||
public IWorkFullSyncRespVO fullSyncDepartments(IWorkFullSyncReqVO reqVO) {
|
||||
@@ -64,11 +66,14 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
boolean syncJobTitle = scopes.contains(IWorkSyncEntityTypeEnum.JOB_TITLE);
|
||||
int processedPages = 0;
|
||||
IWorkSyncProcessor.SyncOptions options = buildFullSyncOptions(reqVO);
|
||||
IWorkSyncProcessor.DeptSyncContext deptSyncContext = (syncDepartments || syncSubcompanies)
|
||||
? new IWorkSyncProcessor.DeptSyncContext()
|
||||
: null;
|
||||
if (syncSubcompanies) {
|
||||
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats);
|
||||
processedPages += executeSubcompanyFullSync(reqVO, options, respVO.getSubcompanyStat(), batchStats, deptSyncContext);
|
||||
}
|
||||
if (syncDepartments) {
|
||||
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats);
|
||||
processedPages += executeDepartmentFullSync(reqVO, options, respVO.getDepartmentStat(), batchStats, deptSyncContext);
|
||||
}
|
||||
if (syncJobTitle) {
|
||||
processedPages += executeJobTitleFullSync(reqVO, options, respVO.getJobTitleStat(), batchStats);
|
||||
@@ -76,6 +81,13 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
if (syncUsers) {
|
||||
processedPages += executeUserFullSync(reqVO, options, respVO.getUserStat(), batchStats);
|
||||
}
|
||||
if (deptSyncContext != null) {
|
||||
IWorkSyncProcessor.BatchResult flushResult = syncProcessor.flushDeptPending(deptSyncContext, options);
|
||||
updateStat(respVO.getDepartmentStat(), flushResult, 0);
|
||||
if (CollUtil.isNotEmpty(deptSyncContext.getPlaceholderDeptIds())) {
|
||||
deptService.backfillMissingCodesWithoutEvent(deptSyncContext.getPlaceholderDeptIds());
|
||||
}
|
||||
}
|
||||
respVO.setProcessedPages(processedPages);
|
||||
return respVO;
|
||||
}
|
||||
@@ -83,7 +95,8 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
private int executeSubcompanyFullSync(IWorkFullSyncReqVO reqVO,
|
||||
IWorkSyncProcessor.SyncOptions options,
|
||||
IWorkSyncEntityStatVO stat,
|
||||
List<IWorkSyncBatchStatVO> batches) {
|
||||
List<IWorkSyncBatchStatVO> batches,
|
||||
IWorkSyncProcessor.DeptSyncContext context) {
|
||||
return executePaged(reqVO, IWorkSyncEntityTypeEnum.SUBCOMPANY, batches, (page, pageSize) -> {
|
||||
IWorkSubcompanyQueryReqVO query = new IWorkSubcompanyQueryReqVO();
|
||||
query.setCurpage(page);
|
||||
@@ -92,7 +105,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
IWorkHrSubcompanyPageRespVO pageResp = orgRestService.listSubcompanies(query);
|
||||
ensureIWorkSuccess("拉取分部", pageResp.isSuccess(), pageResp.getMessage());
|
||||
List<IWorkHrSubcompanyPageRespVO.Subcompany> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options);
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncSubcompanies(dataList, options, context);
|
||||
updateStat(stat, result, dataList.size());
|
||||
return new BatchExecution(result, dataList.size());
|
||||
});
|
||||
@@ -101,7 +114,8 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
private int executeDepartmentFullSync(IWorkFullSyncReqVO reqVO,
|
||||
IWorkSyncProcessor.SyncOptions options,
|
||||
IWorkSyncEntityStatVO stat,
|
||||
List<IWorkSyncBatchStatVO> batches) {
|
||||
List<IWorkSyncBatchStatVO> batches,
|
||||
IWorkSyncProcessor.DeptSyncContext context) {
|
||||
return executePaged(reqVO, IWorkSyncEntityTypeEnum.DEPARTMENT, batches, (page, pageSize) -> {
|
||||
IWorkDepartmentQueryReqVO query = new IWorkDepartmentQueryReqVO();
|
||||
query.setCurpage(page);
|
||||
@@ -110,7 +124,7 @@ public class IWorkSyncServiceImpl implements IWorkSyncService {
|
||||
IWorkHrDepartmentPageRespVO pageResp = orgRestService.listDepartments(query);
|
||||
ensureIWorkSuccess("拉取部门", pageResp.isSuccess(), pageResp.getMessage());
|
||||
List<IWorkHrDepartmentPageRespVO.Department> dataList = CollUtil.emptyIfNull(pageResp.getDataList());
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options);
|
||||
IWorkSyncProcessor.BatchResult result = syncProcessor.syncDepartments(dataList, options, context);
|
||||
updateStat(stat, result, dataList.size());
|
||||
return new BatchExecution(result, dataList.size());
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.zt.plat.module.system.service.permission;
|
||||
|
||||
import com.zt.plat.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
|
||||
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
@@ -143,4 +144,12 @@ public interface PermissionService {
|
||||
*/
|
||||
DeptDataPermissionRespDTO getDeptDataPermission(Long userId);
|
||||
|
||||
/**
|
||||
* 获得用户的数据权限级别
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 数据权限范围枚举
|
||||
*/
|
||||
DataScopeEnum getUserDataPermissionLevel(Long userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.zt.plat.module.system.enums.permission.RoleTypeEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||
import com.zt.plat.module.system.service.userdept.UserDeptService;
|
||||
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -57,6 +58,15 @@ import static com.zt.plat.module.system.enums.ErrorCodeConstants.ROLE_CAN_NOT_UP
|
||||
@Slf4j
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
private static final List<DataScopeEnum> DATA_SCOPE_PRIORITY = Arrays.asList(
|
||||
DataScopeEnum.ALL,
|
||||
DataScopeEnum.COMPANY_AND_DEPT,
|
||||
DataScopeEnum.DEPT_AND_CHILD,
|
||||
DataScopeEnum.DEPT_ONLY,
|
||||
DataScopeEnum.DEPT_CUSTOM,
|
||||
DataScopeEnum.SELF
|
||||
);
|
||||
|
||||
@Resource
|
||||
private RoleMenuMapper roleMenuMapper;
|
||||
@Resource
|
||||
@@ -404,6 +414,40 @@ public class PermissionServiceImpl implements PermissionService {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataPermission(enable = false)
|
||||
@TenantIgnore
|
||||
public DataScopeEnum getUserDataPermissionLevel(Long userId) {
|
||||
List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
|
||||
if (CollUtil.isEmpty(roles)) {
|
||||
return DataScopeEnum.SELF;
|
||||
}
|
||||
|
||||
DataScopeEnum best = null;
|
||||
for (RoleDO role : roles) {
|
||||
DataScopeEnum scopeEnum = DataScopeEnum.findByScope(role.getDataScope());
|
||||
if (scopeEnum == null) {
|
||||
continue;
|
||||
}
|
||||
if (best == null || compareScope(scopeEnum, best) < 0) {
|
||||
best = scopeEnum;
|
||||
if (DataScopeEnum.ALL.equals(best)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best != null ? best : DataScopeEnum.SELF;
|
||||
}
|
||||
|
||||
private int compareScope(DataScopeEnum left, DataScopeEnum right) {
|
||||
return getScopePriority(left) - getScopePriority(right);
|
||||
}
|
||||
|
||||
private int getScopePriority(DataScopeEnum scope) {
|
||||
int idx = DATA_SCOPE_PRIORITY.indexOf(scope);
|
||||
return idx >= 0 ? idx : Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
|
||||
@@ -10,10 +10,10 @@ import com.zt.plat.module.system.dal.dataobject.sms.SmsCodeDO;
|
||||
import com.zt.plat.module.system.dal.mysql.sms.SmsCodeMapper;
|
||||
import com.zt.plat.module.system.enums.sms.SmsSceneEnum;
|
||||
import com.zt.plat.module.system.framework.sms.config.SmsCodeProperties;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomInt;
|
||||
@@ -56,11 +56,11 @@ public class SmsCodeServiceImpl implements SmsCodeService {
|
||||
if (lastSmsCode != null) {
|
||||
if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis()
|
||||
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
|
||||
throw exception(SMS_CODE_SEND_TOO_FAST);
|
||||
throw exception(SMS_CODE_SEND_TOO_FAST, smsCodeProperties.getSendFrequency().toMinutes());
|
||||
}
|
||||
if (isToday(lastSmsCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限
|
||||
lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
|
||||
throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
|
||||
throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY, smsCodeProperties.getSendMaximumQuantityPerDay());
|
||||
}
|
||||
// TODO ZT:提升,每个 IP 每天可发送数量
|
||||
// TODO ZT:提升,每个 IP 每小时可发送数量
|
||||
|
||||
@@ -212,4 +212,8 @@ public interface AdminUserService {
|
||||
*/
|
||||
boolean isPasswordMatch(AdminUserDO user, String rawPassword);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
void syncUser(UserSaveReqVO syncReqVO);
|
||||
|
||||
}
|
||||
|
||||
@@ -725,4 +725,48 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
return StrUtil.isNotBlank(value) && value.startsWith("$2");
|
||||
}
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncUser(UserSaveReqVO syncReqVO) {
|
||||
if (syncReqVO.getId() == null) {
|
||||
log.warn("[syncUser] 同步用户失败,ID 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
AdminUserDO existingUser = userMapper.selectById(syncReqVO.getId());
|
||||
|
||||
// 转换为 DO(只同步用户主表,不处理关联关系)
|
||||
AdminUserDO user = BeanUtils.toBean(syncReqVO, AdminUserDO.class);
|
||||
user.setDeptIds(null); // 部门关联单独同步
|
||||
user.setPostIds(null); // 岗位关联单独同步
|
||||
|
||||
if (existingUser != null) {
|
||||
// 用户存在,执行更新(不更新密码)
|
||||
user.setPassword(null);
|
||||
userMapper.updateById(user);
|
||||
log.info("[syncUser] 用户同步-更新成功, userId={}, username={}", user.getId(), user.getUsername());
|
||||
} else {
|
||||
// 用户不存在,执行插入
|
||||
// 设置默认状态
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
}
|
||||
// 设置默认用户来源
|
||||
if (user.getUserSource() == null) {
|
||||
user.setUserSource(UserSourceEnum.EXTERNAL.getSource());
|
||||
}
|
||||
// 如果没有密码,设置默认密码
|
||||
if (StrUtil.isBlank(user.getPassword())) {
|
||||
user.setPassword(passwordEncoder.encode("123456"));
|
||||
}
|
||||
userMapper.insert(user);
|
||||
log.info("[syncUser] 用户同步-创建成功, userId={}, username={}", user.getId(), user.getUsername());
|
||||
}
|
||||
|
||||
// 注意:不发布变更事件,避免循环同步
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,5 +76,8 @@ public interface UserDeptService {
|
||||
*/
|
||||
void batchCreateUserDept(List<UserDeptDO> createReqVOList);
|
||||
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
void syncUserDept(UserDeptDO syncReqVO);
|
||||
|
||||
}
|
||||
@@ -155,4 +155,22 @@ public class UserDeptServiceImpl implements UserDeptService {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// ========== 数据同步专用接口 ==========
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncUserDept(UserDeptDO syncReqVO) {
|
||||
if (syncReqVO.getId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
UserDeptDO existing = userDeptMapper.selectById(syncReqVO.getId());
|
||||
|
||||
if (existing != null) {
|
||||
userDeptMapper.updateById(syncReqVO);
|
||||
} else {
|
||||
userDeptMapper.insert(syncReqVO);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -240,9 +240,9 @@ zt:
|
||||
sms-code: # 短信验证码相关的配置项
|
||||
expire-times: 10m
|
||||
send-frequency: 1m
|
||||
send-maximum-quantity-per-day: 10
|
||||
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
|
||||
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
|
||||
send-maximum-quantity-per-day: 20
|
||||
begin-code: 100000
|
||||
end-code: 999999
|
||||
|
||||
|
||||
# E办OAuth2配置文件
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||
|
||||
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.zt.plat.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrSubcompanyPageRespVO;
|
||||
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.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 org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests for cross-page pending handling and placeholder backfill in IWorkSyncProcessorImpl.
|
||||
*/
|
||||
class IWorkSyncProcessorImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private IWorkSyncProcessorImpl processor;
|
||||
|
||||
@Mock
|
||||
private DeptService deptService;
|
||||
@Mock
|
||||
private PostService postService;
|
||||
@Mock
|
||||
private PostMapper postMapper;
|
||||
@Mock
|
||||
private AdminUserService adminUserService;
|
||||
@Mock
|
||||
private AdminUserMapper adminUserMapper;
|
||||
|
||||
@Test
|
||||
void shouldProcessPendingChildWhenParentArrivesInLaterPage() {
|
||||
IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext();
|
||||
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||
|
||||
IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department();
|
||||
child.setId(200);
|
||||
child.setDepartmentname("child");
|
||||
child.setSupdepid(100); // parent comes later
|
||||
|
||||
IWorkHrDepartmentPageRespVO.Department parent = new IWorkHrDepartmentPageRespVO.Department();
|
||||
parent.setId(100);
|
||||
parent.setDepartmentname("parent");
|
||||
parent.setSupdepid(0); // root
|
||||
|
||||
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(100L, 200L);
|
||||
|
||||
processor.syncDepartments(List.of(child), options, context);
|
||||
|
||||
verify(deptService, never()).createDept(any());
|
||||
assertEquals(1, context.getPendingDepartments().size());
|
||||
|
||||
processor.syncDepartments(List.of(parent), options, context);
|
||||
|
||||
verify(deptService, times(2)).createDept(any());
|
||||
assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after parent processed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInsertPlaceholderWhenParentMissingAfterFlush() {
|
||||
IWorkSyncProcessor.DeptSyncContext context = new IWorkSyncProcessor.DeptSyncContext();
|
||||
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||
|
||||
IWorkHrDepartmentPageRespVO.Department child = new IWorkHrDepartmentPageRespVO.Department();
|
||||
child.setId(300);
|
||||
child.setDepartmentname("orphan");
|
||||
child.setSupdepid(9999); // never provided
|
||||
|
||||
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(300L);
|
||||
|
||||
processor.syncDepartments(List.of(child), options, context);
|
||||
assertEquals(1, context.getPendingDepartments().size());
|
||||
|
||||
IWorkSyncProcessor.BatchResult flushResult = processor.flushDeptPending(context, options);
|
||||
assertNotNull(flushResult);
|
||||
|
||||
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||
verify(deptService, times(1)).createDept(captor.capture());
|
||||
DeptSaveReqVO placeholderReq = captor.getValue();
|
||||
assertTrue(Boolean.TRUE.equals(placeholderReq.getDelayCodeGeneration()));
|
||||
assertNull(placeholderReq.getCode());
|
||||
|
||||
assertTrue(context.getPendingDepartments().isEmpty(), "pending should be cleared after placeholder insert");
|
||||
assertTrue(context.getPlaceholderDeptIds().contains(300L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepExternalCodeNullWhenDepartmentCodeBlank() {
|
||||
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||
|
||||
IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department();
|
||||
dept.setId(500);
|
||||
dept.setDepartmentname("blank-code-dept");
|
||||
dept.setDepartmentcode(" ");
|
||||
dept.setSupdepid(0);
|
||||
|
||||
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(500L);
|
||||
|
||||
processor.syncDepartments(List.of(dept), options, null);
|
||||
|
||||
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||
verify(deptService, times(1)).createDept(captor.capture());
|
||||
DeptSaveReqVO req = captor.getValue();
|
||||
assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is blank");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepExternalCodeNullWhenSubcompanyCodeBlank() {
|
||||
IWorkSyncProcessor.SyncOptions options = IWorkSyncProcessor.SyncOptions.custom(true, true, true);
|
||||
|
||||
IWorkHrSubcompanyPageRespVO.Subcompany subcompany = new IWorkHrSubcompanyPageRespVO.Subcompany();
|
||||
subcompany.setId(600);
|
||||
subcompany.setSubcompanyname("blank-code-sub");
|
||||
subcompany.setSubcompanycode(null);
|
||||
subcompany.setSupsubcomid(0);
|
||||
|
||||
when(deptService.getDept(anyLong())).thenReturn(null);
|
||||
when(deptService.createDept(any(DeptSaveReqVO.class))).thenReturn(600L);
|
||||
|
||||
processor.syncSubcompanies(List.of(subcompany), options, null);
|
||||
|
||||
ArgumentCaptor<DeptSaveReqVO> captor = ArgumentCaptor.forClass(DeptSaveReqVO.class);
|
||||
verify(deptService, times(1)).createDept(captor.capture());
|
||||
DeptSaveReqVO req = captor.getValue();
|
||||
assertNull(req.getExternalDeptCode(), "externalDeptCode should remain null when source code is null or blank");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||
|
||||
import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFullSyncReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkHrDepartmentPageRespVO;
|
||||
import com.zt.plat.module.system.enums.integration.IWorkSyncEntityTypeEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
|
||||
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncProcessor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class IWorkSyncServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private IWorkSyncServiceImpl syncService;
|
||||
|
||||
@Mock
|
||||
private IWorkOrgRestService orgRestService;
|
||||
@Mock
|
||||
private IWorkSyncProcessor syncProcessor;
|
||||
@Mock
|
||||
private DeptService deptService;
|
||||
|
||||
@Test
|
||||
void shouldBackfillCodesWhenPlaceholdersExistAfterFullSync() {
|
||||
IWorkFullSyncReqVO reqVO = new IWorkFullSyncReqVO();
|
||||
reqVO.setPageSize(1);
|
||||
reqVO.setMaxPages(1);
|
||||
|
||||
IWorkHrDepartmentPageRespVO pageResp = new IWorkHrDepartmentPageRespVO();
|
||||
pageResp.setSuccess(true);
|
||||
IWorkHrDepartmentPageRespVO.Department dept = new IWorkHrDepartmentPageRespVO.Department();
|
||||
dept.setId(1);
|
||||
pageResp.setDataList(List.of(dept));
|
||||
when(orgRestService.listDepartments(any())).thenReturn(pageResp);
|
||||
|
||||
// 在部门同步时标记占位 ID
|
||||
doAnswer((Answer<IWorkSyncProcessor.BatchResult>) invocation -> {
|
||||
IWorkSyncProcessor.DeptSyncContext context = invocation.getArgument(2);
|
||||
if (context != null) {
|
||||
context.getPlaceholderDeptIds().add(500L);
|
||||
}
|
||||
return IWorkSyncProcessor.BatchResult.empty();
|
||||
}).when(syncProcessor).syncDepartments(any(), any(), any(IWorkSyncProcessor.DeptSyncContext.class));
|
||||
|
||||
when(syncProcessor.flushDeptPending(any(), any())).thenReturn(IWorkSyncProcessor.BatchResult.empty());
|
||||
|
||||
syncService.fullSyncDepartments(reqVO);
|
||||
|
||||
verify(deptService, times(1)).backfillMissingCodesWithoutEvent(argThat(set -> set.contains(500L)));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.zt.plat.module.system.service.permission;
|
||||
|
||||
import com.zt.plat.framework.common.exception.ServiceException;
|
||||
import com.zt.plat.framework.common.enums.CommonStatusEnum;
|
||||
import com.zt.plat.framework.test.core.ut.BaseDbUnitTest;
|
||||
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.RoleMenuDO;
|
||||
@@ -11,6 +13,7 @@ import com.zt.plat.module.system.dal.mysql.permission.RoleMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.permission.RoleMenuMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.permission.UserRoleMapper;
|
||||
import com.zt.plat.module.system.dal.mysql.rolemenuexclusion.RoleMenuExclusionMapper;
|
||||
import com.zt.plat.module.system.enums.permission.DataScopeEnum;
|
||||
import com.zt.plat.module.system.enums.permission.RoleTypeEnum;
|
||||
import com.zt.plat.module.system.service.dept.DeptService;
|
||||
import com.zt.plat.module.system.service.user.AdminUserService;
|
||||
@@ -408,4 +411,54 @@ public class PermissionServiceTest extends BaseDbUnitTest {
|
||||
assertEquals(1, exclusionDOS.size());
|
||||
assertEquals(101L, exclusionDOS.get(0).getMenuId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserDataPermissionLevel_noRolesReturnSelf() {
|
||||
Long userId = 1000L;
|
||||
|
||||
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||
|
||||
assertEquals(DataScopeEnum.SELF, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserDataPermissionLevel_pickHighestPriority() {
|
||||
Long userId = 2000L;
|
||||
RoleDO roleCustom = randomPojo(RoleDO.class, o -> o
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
|
||||
.setId(110L)
|
||||
.setTenantId(0L));
|
||||
roleMapper.insert(roleCustom);
|
||||
RoleDO roleCompany = randomPojo(RoleDO.class, o -> o
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setDataScope(DataScopeEnum.COMPANY_AND_DEPT.getScope())
|
||||
.setId(120L)
|
||||
.setTenantId(0L));
|
||||
roleMapper.insert(roleCompany);
|
||||
|
||||
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleCustom.getId())));
|
||||
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleCompany.getId())));
|
||||
|
||||
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||
|
||||
assertEquals(DataScopeEnum.COMPANY_AND_DEPT, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserDataPermissionLevel_serializeAsNumber() {
|
||||
Long userId = 3000L;
|
||||
RoleDO roleAll = randomPojo(RoleDO.class, o -> o
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setDataScope(DataScopeEnum.ALL.getScope())
|
||||
.setId(210L)
|
||||
.setTenantId(0L));
|
||||
roleMapper.insert(roleAll);
|
||||
userRoleMapper.insert(randomPojo(UserRoleDO.class, o -> o.setUserId(userId).setRoleId(roleAll.getId())));
|
||||
|
||||
DataScopeEnum result = permissionService.getUserDataPermissionLevel(userId);
|
||||
|
||||
assertEquals(DataScopeEnum.ALL, result);
|
||||
assertEquals("1", JsonUtils.toJsonString(result));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user