Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2025-11-07 14:04:29 +08:00
10 changed files with 743 additions and 8 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
package com.jeelowcode.service.system.controller;
import com.jeelowcode.service.system.controller.vo.user.sync.MasterRespResult;
import com.jeelowcode.service.system.controller.vo.user.sync.MasterSystemReqVO;
import com.jeelowcode.service.system.controller.vo.user.sync.MasterUserResultVO;
import com.jeelowcode.service.system.service.IUserSyncService;
import com.jeelowcode.tool.framework.tenant.core.util.TenantUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
/**
* 从主数据用户同步接口
*
* @author yangchenjj
*/
@Validated
@RestController
@Tag(name = "从主数据用户同步接口")
@RequestMapping("/system/user-sync")
public class UserSyncController {
/**
* 用户同步服务
*/
@Resource
private IUserSyncService userSyncService;
/**
* 从主数据同步用户
*
* @param reqVO 请求参数
* @return 响应结果
*/
@PostMapping
@Operation(tags = "从主数据用户同步接口", summary = "同步用户")
public MasterRespResult syncUser(@Validated MasterSystemReqVO reqVO) {
try {
// 调用服务,返回数据消费成功结果
Callable<MasterRespResult> callable = () -> userSyncService.syncUser(reqVO);
// 在租户环境下执行
return TenantUtils.execute(1L, callable);
} catch (Exception e) {
List<MasterUserResultVO> mdMappings = reqVO.getMasterData()
.stream().map(md -> MasterUserResultVO.buildFail(md.getMdmCode(), reqVO.getMdType(), null))
.collect(Collectors.toList());
// 调用服务失败,返回数据消费失败结果
return MasterRespResult.buildFail(e.getMessage(), mdMappings);
}
}
}

View File

@@ -0,0 +1,87 @@
package com.jeelowcode.service.system.controller.vo.user.sync;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class MasterRespResult {
// 默认成功消息
private static final String DEFAULT_SUCCESS_MESSAGE = "消费系统消费这批数据成功!";
// 默认失败消息
private static final String DEFAULT_FAIL_MESSAGE = "消费系统消费这批数据失败!";
/**
* 是否成功
*/
private Boolean success;
/**
* 消息
*/
private String message;
/**
* 映射列表
*/
private List<MasterUserResultVO> mdMappings;
/**
* 构建成功响应结果
*
* @param mdMappings 映射列表
* @return 响应结果
*/
public static MasterRespResult buildSuccess(List<MasterUserResultVO> mdMappings) {
return buildSuccess(DEFAULT_SUCCESS_MESSAGE, mdMappings);
}
/**
* 构建成功响应结果
*
* @param message 消息
* @param mdMappings 映射列表
* @return 响应结果
*/
public static MasterRespResult buildSuccess(String message, List<MasterUserResultVO> mdMappings) {
return buildMasterResult(true, message, mdMappings);
}
/**
* 构建失败响应结果
*
* @param mdMappings 映射列表
* @return 响应结果
*/
public static MasterRespResult buildFail(List<MasterUserResultVO> mdMappings) {
return buildFail(DEFAULT_FAIL_MESSAGE, mdMappings);
}
/**
* 构建失败响应结果
*
* @param message 消息
* @param mdMappings 映射列表
* @return 响应结果
*/
public static MasterRespResult buildFail(String message, List<MasterUserResultVO> mdMappings) {
return buildMasterResult(false, message, mdMappings);
}
/**
* 构建结果
*
* @param success 是否成功
* @param message 消息
* @param mdMappings 映射列表
* @return 结果
*/
public static MasterRespResult buildMasterResult(Boolean success, String message, List<MasterUserResultVO> mdMappings) {
return new MasterRespResult()
.setSuccess(success)
.setMessage(message)
.setMdMappings(mdMappings);
}
}

View File

@@ -0,0 +1,61 @@
package com.jeelowcode.service.system.controller.vo.user.sync;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
/**
* 从主数据同步用户请求参数
*
* @author yangchenjj
*/
@Data
@Schema(description = "从主数据同步用户请求参数")
public class MasterSystemReqVO {
/**
* 数据分发所触发的类型
*/
@Schema(description = "数据分发所触发的类型", requiredMode = REQUIRED)
private String action;
/**
* 数据审批状态
*/
@Schema(description = "数据审批状态")
private Integer code;
/**
* 审批拒绝时的详情信息
*/
@Schema(description = "审批拒绝时的详情信息")
private String msg;
/**
* 主数据模型的编码
*/
@Schema(description = "主数据模型的编码", requiredMode = REQUIRED)
private String mdType;
/**
* 集成系统的编码
*/
@Schema(description = "集成系统的编码", requiredMode = REQUIRED)
private String systemCode;
/**
* 消费系统中配置的分发令牌
*/
@Schema(description = "消费系统中配置的分发令牌", requiredMode = REQUIRED)
private String distributeToken;
/**
* 主数据同步用户列表
*/
@Schema(description = "主数据同步用户列表")
private List<MasterUserReqVO> masterData;
}

View File

@@ -0,0 +1,172 @@
package com.jeelowcode.service.system.controller.vo.user.sync;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
/**
* 主数据同步用户请求参数
*
* @author yangchenjj
*/
@Data
@Schema(description = "主数据同步用户请求参数")
public class MasterUserReqVO {
/**
* 数据在此集成系统的唯一值
*/
@Schema(description = "数据在此集成系统的唯一值")
private String id;
/**
* 数据在主数据模型唯一编码
*/
@JsonProperty("mdm_code")
@NotEmpty(message = "数据在主数据模型唯一编码不能为空")
@Schema(description = "数据在主数据模型的唯一编码", requiredMode = REQUIRED)
private String mdmCode;
/**
* 人员编码
*/
@JsonProperty("emp_code")
@Schema(description = "人员编码")
private String empCode;
/**
* 人员名称
*/
@JsonProperty("emp_name")
@Schema(description = "人员名称")
private String empName;
/**
* 显示名称
*/
@JsonProperty("display_name")
@Schema(description = "显示名称")
private String displayName;
/**
* 性别
*/
@JsonProperty("sex")
@Schema(description = "性别")
private String sex;
/**
* 证件类型
*/
@Schema(description = "证件类型")
@JsonProperty("certificate_type")
private String certificateType;
/**
* 证件号码
*/
@JsonProperty("emp_idcard")
@Schema(description = "证件号码")
private String empIdCard;
/**
* 姓
*/
@Schema(description = "")
@JsonProperty("first_name")
private String firstName;
/**
* 名
*/
@Schema(description = "")
@JsonProperty("last_name")
private String lastName;
/**
* 出生日期
*/
@JsonProperty("date_of_birth")
@Schema(description = "出生日期")
private String dateOfBirth;
/**
* 人员类型
*/
@JsonProperty("cunc_person_type")
@Schema(description = "人员类型")
private String cuncPersonType;
/**
* 手机号码
*/
@Schema(description = "手机号码")
private String mobile;
/**
* 座机号码
*/
@Schema(description = "座机号码")
@JsonProperty("phone_number")
private String phoneNumber;
/**
* 国家或地区
*/
@Schema(description = "国家或地区")
private String region;
/**
* 办公地址
*/
@Schema(description = "办公地址")
@JsonProperty("location_address")
private String locationAddress;
/**
* 邮箱
*/
@Schema(description = "邮箱")
private String email;
/**
* 显示顺序
*/
@JsonProperty("display_sort")
@Schema(description = "显示顺序")
private String displaySort;
/**
* 备注
*/
@Schema(description = "备注")
private String description;
/**
* 启用状态0停用、1正常、2注销
*/
@JsonProperty("usable_status")
@Schema(description = "启用状态0停用、1正常、2注销")
private String usableStatus;
/**
* 头像
*/
@JsonProperty("emp_photo")
@Schema(description = "头像")
private String empPhoto;
/**
* 子用户数据
*/
@JsonProperty("sub_userinfo")
@Schema(description = "子用户数据")
private List<SubUserReqVO> subUserInfo;
}

View File

@@ -0,0 +1,117 @@
package com.jeelowcode.service.system.controller.vo.user.sync;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 主数据同步用户返回结果
*
* @author yangchenjj
*/
@Data
@Accessors(chain = true)
public class MasterUserResultVO {
// 主数据在消费系统中的业务id
private static final String JSON_PROPERTY_BUSINESS_DATA_ID = "busidataid";
// 默认成功消息
private static final String DEFAULT_SUCCESS_MESSAGE = "该条主数据消费成功!";
// 默认失败消息
private static final String DEFAULT_ERROR_MESSAGE = "该条主数据消费失败!";
/**
* 主数据的编码
*/
private String mdmCode;
/**
* 主数据模型的编码
*/
private String entityCode;
/**
* 主数据在消费系统中的业务id
*/
@JsonProperty(JSON_PROPERTY_BUSINESS_DATA_ID)
private String businessDataId;
/**
* 消息
*/
private String message;
/**
* 是否成功
*/
private Boolean success;
public static MasterUserResultVO buildSuccess(String mdmCode,
String entityCode,
String businessDataId) {
return buildSuccess(mdmCode, entityCode, businessDataId, DEFAULT_SUCCESS_MESSAGE);
}
/**
* 构建成功响应结果
*
* @param mdmCode 主数据的编码
* @param entityCode 主数据模型的编码
* @param businessDataId 主数据在消费系统中的业务id
* @param message 消息
* @return MasterUserResult 响应结果
*/
public static MasterUserResultVO buildSuccess(String mdmCode,
String entityCode,
String businessDataId,
String message) {
return buildMasterResult(mdmCode, entityCode, businessDataId, message, true);
}
/**
* 构建失败响应结果
*
* @param mdmCode 主数据的编码
* @param entityCode 主数据模型的编码
* @param businessDataId 主数据在消费系统中的业务id
* @return MasterUserResult 响应结果
*/
public static MasterUserResultVO buildFail(String mdmCode,
String entityCode,
String businessDataId) {
return buildFail(mdmCode, entityCode, businessDataId, DEFAULT_ERROR_MESSAGE);
}
/**
* 构建失败响应结果
*
* @param mdmCode 主数据的编码
* @param entityCode 主数据模型的编码
* @param businessDataId 主数据在消费系统中的业务id
* @param message 消息
* @return MasterUserResult 响应结果
*/
public static MasterUserResultVO buildFail(String mdmCode,
String entityCode,
String businessDataId,
String message) {
return buildMasterResult(mdmCode, entityCode, businessDataId, message, false);
}
/**
* 构建响应结果
*
* @param mdmCode 主数据的编码
* @param entityCode 主数据模型的编码
* @param businessDataId 主数据在消费系统中的业务id
* @param message 消息
* @param success 是否成功
* @return MasterUserResult 响应结果
*/
public static MasterUserResultVO buildMasterResult(String mdmCode,
String entityCode,
String businessDataId,
String message, Boolean success) {
return new MasterUserResultVO()
.setMdmCode(mdmCode).setEntityCode(entityCode)
.setBusinessDataId(businessDataId).setMessage(message)
.setSuccess(success);
}
}

View File

@@ -0,0 +1,73 @@
package com.jeelowcode.service.system.controller.vo.user.sync;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 从主数据同步用户请求参数
*
* @author yangchenjj
*/
@Data
@Schema(description = "从主数据同步用户请求参数")
public class SubUserReqVO {
/**
* 数据在此集成系统的唯一值
*/
@Schema(description = "数据在此集成系统的唯一值")
private String id;
/**
* 人员编码
*/
@JsonProperty("emp_code")
@Schema(description = "人员编码")
private String empCode;
/**
* 账号
*/
@JsonProperty("user_id")
@Schema(description = "账号")
private String userId;
/**
* 账号类型
*/
@JsonProperty("account_type")
@Schema(description = "账号类型")
private String accountType;
/**
* 账号状态
*/
@JsonProperty("account_status")
@Schema(description = "账号状态")
private String accountStatus;
/**
* 人员排序
*/
@JsonProperty("emp_sort")
@Schema(description = "人员排序")
private String empSort;
/**
* 启用状态0停用、1正常、2注销
*/
@JsonProperty("usable_status")
@Schema(description = "启用状态0停用、1正常、2注销")
private String usableStatus;
/**
* 账号是否为空
*
* @return true/false
*/
public boolean userIdIsNotEmpty() {
return userId != null && !userId.isEmpty();
}
}

View File

@@ -20,8 +20,8 @@ public class UserSaveReqVO {
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "jeelowcode")
@NotBlank(message = "用户账号不能为空")
@Pattern(regexp = "^[a-zA-Z0-9_]{4,30}$", message = "用户账号由 数字、字母、下划线 组成")
@Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符")
@Pattern(regexp = "^[a-zA-Z0-9_]{1,30}$", message = "用户账号由 数字、字母、下划线 组成")
@Size(min = 1, max = 30, message = "用户账号长度为 1-30 个字符")
private String username;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")

View File

@@ -0,0 +1,21 @@
package com.jeelowcode.service.system.service;
import com.jeelowcode.service.system.controller.vo.user.sync.MasterRespResult;
import com.jeelowcode.service.system.controller.vo.user.sync.MasterSystemReqVO;
/**
* 从主数据用户同步接口
*
* @author yangchenjj
*/
public interface IUserSyncService {
/**
* 从主数据用户同步接口
*
* @param reqVO 请求参数
* @return 响应结果
*/
MasterRespResult syncUser(MasterSystemReqVO reqVO);
}

View File

@@ -0,0 +1,142 @@
package com.jeelowcode.service.system.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.service.system.controller.vo.dept.dept.DeptListReqVO;
import com.jeelowcode.service.system.controller.vo.permission.role.RolePageReqVO;
import com.jeelowcode.service.system.controller.vo.user.sync.*;
import com.jeelowcode.service.system.controller.vo.user.user.UserSaveReqVO;
import com.jeelowcode.service.system.entity.AdminUserDO;
import com.jeelowcode.service.system.entity.DeptDO;
import com.jeelowcode.service.system.entity.RoleDO;
import com.jeelowcode.service.system.service.IAdminUserService;
import com.jeelowcode.service.system.service.IDeptService;
import com.jeelowcode.service.system.service.IRoleService;
import com.jeelowcode.service.system.service.IUserSyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 主数据同步用户信息接口实现类
*
* @author yangchenjj
*/
@Slf4j
@Service
public class UserSyncServiceImpl implements IUserSyncService {
/**
* 用户服务,懒加载避免循环依赖
*/
@Lazy
@Resource
private IAdminUserService userService;
/**
* 角色服务,懒加载避免循环依赖
*/
@Lazy
@Resource
private IRoleService roleService;
/**
* 部门服务,懒加载避免循环依赖
*/
@Lazy
@Resource
private IDeptService deptService;
/**
* 从主数据同步用户
*
* @param reqVO 请求参数
* @return 响应结果
*/
@Override
@Transactional
public MasterRespResult syncUser(MasterSystemReqVO reqVO) {
// 0.1.先检查是否有数据
if (CollUtil.isEmpty(reqVO.getMasterData())) return MasterRespResult.buildSuccess(Collections.emptyList());
// 0.2.准备好新用户需要创建的部门信息和角色信息
List<DeptDO> deptList = deptService.getDeptList(new DeptListReqVO().setName("临时部门"));
List<RoleDO> roleList = roleService.getRolePage(new RolePageReqVO().setCode("common")).getList();
Set<Long> roleIds = roleList.stream().map(RoleDO::getId).collect(Collectors.toSet());
List<UserSaveReqVO.DeptInfo> deptInfoList = deptList.stream().map(dept -> new UserSaveReqVO.DeptInfo()
.setDeptId(dept.getId())
.setRoleIds(roleIds))
.collect(Collectors.toList());
// 1.将数据解析出来,这里采用并行处理,这样可以提升性能
List<MasterUserResultVO> mdMappings = reqVO.getMasterData().parallelStream().map(md -> {
String username = getUsername(md);
// 2.如果用户名为空,则返回数据消费失败结果
if (StrUtil.isEmpty(username))
return MasterUserResultVO.buildFail(md.getMdmCode(), reqVO.getMdType(), null);
AdminUserDO adminUser = userService.getUserByUsername(username);
if (Objects.isNull(adminUser)) {
// 这里需要做插入操作
try {
UserSaveReqVO userSaveReqVO = new UserSaveReqVO()
.setUsername(username).setNickname(md.getEmpName())
.setRemark(md.getDescription()).setEmail(md.getEmail())
.setMobile(md.getMobile()).setPassword(username)
.setSex(Convert.toInt(md.getSex(), 0))
.setTenantId(1L).setDeptInfoList(deptInfoList);
Long userId = userService.createUser(userSaveReqVO);
return MasterUserResultVO.buildSuccess(md.getMdmCode(), reqVO.getMdType(), String.valueOf(userId));
} catch (Exception e) {
log.error("用户创建失败", e);
return MasterUserResultVO.buildFail(md.getMdmCode(), reqVO.getMdType(), null);
}
} else {
// 这里需要做更新操作
try {
UserSaveReqVO userSaveReqVO = new UserSaveReqVO()
.setId(adminUser.getId()).setUsername(username)
.setNickname(md.getEmpName()).setRemark(md.getDescription())
.setEmail(md.getEmail()).setMobile(md.getMobile())
.setSex(Convert.toInt(md.getSex(), 0))
.setTenantId(1L);
userService.updateUser(userSaveReqVO);
return MasterUserResultVO.buildSuccess(md.getMdmCode(), reqVO.getMdType(), String.valueOf(adminUser.getId()));
} catch (Exception e) {
log.error("用户更新失败", e);
return MasterUserResultVO.buildFail(md.getMdmCode(), reqVO.getMdType(), String.valueOf(adminUser.getId()));
}
}
}).collect(Collectors.toList());
return MasterRespResult.buildSuccess(mdMappings);
}
/**
* 获取用户名
*
* @param masterUser 主数据用户信息
* @return 用户名
*/
private String getUsername(MasterUserReqVO masterUser) {
// 首先获取邮箱,如果邮箱数据存在,则从邮箱中取
String email = masterUser.getEmail();
if (StrUtil.isNotEmpty(email) && Validator.isEmail(email)) return email.split("@")[0];
// 如果邮箱中没有,则从用户信息中取得
if (CollUtil.isNotEmpty(masterUser.getSubUserInfo())) {
String username = masterUser.getSubUserInfo().stream()
.filter(SubUserReqVO::userIdIsNotEmpty).findFirst()
.map(SubUserReqVO::getUserId).orElse(null);
if (StrUtil.isNotEmpty(username)) return username;
}
// 最后实在找不到则无法解析到用户名信息返回null
return null;
}
}