diff --git a/web-app/src/main/java/com/zcloud/basic/info/command/UserAddExe.java b/web-app/src/main/java/com/zcloud/basic/info/command/UserAddExe.java index 8e940261..f63d5f1f 100644 --- a/web-app/src/main/java/com/zcloud/basic/info/command/UserAddExe.java +++ b/web-app/src/main/java/com/zcloud/basic/info/command/UserAddExe.java @@ -21,6 +21,8 @@ import com.zcloud.basic.info.command.convertor.UserCoConvertor; import com.zcloud.basic.info.command.query.CorpInfoQueryExe; import com.zcloud.basic.info.constant.RedisConstant; import com.zcloud.basic.info.domain.config.CodeConfig; +import com.zcloud.basic.info.domain.config.DaHuaConfig; +import com.zcloud.basic.info.domain.enums.CommonFlagEnum; import com.zcloud.basic.info.domain.enums.CorpTypeEnum; import com.zcloud.basic.info.domain.enums.UserEmploymentFlagEnum; import com.zcloud.basic.info.domain.enums.UserFlowFlagEnum; @@ -31,6 +33,7 @@ import com.zcloud.basic.info.persistence.dataobject.*; import com.zcloud.basic.info.persistence.repository.*; import com.zcloud.gbscommon.utils.*; import lombok.AllArgsConstructor; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.BeanUtils; @@ -38,9 +41,18 @@ import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; import java.util.*; import java.util.stream.Collectors; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.Period; +import java.time.format.DateTimeFormatter; +import java.util.Base64; /** @@ -53,6 +65,11 @@ import java.util.stream.Collectors; @AllArgsConstructor @Slf4j public class UserAddExe { + @Data + public static class DaHuaSyncResult { + private Integer dahuaId; + private String dahuaCode; + } private final UserGateway userGateway; private final UserExpandInfoGateway userExpandInfoGateway; private UserCoConvertor userCoConvertor; @@ -80,17 +97,56 @@ public class UserAddExe { private final CodeConfig codeConfig; private final UserExpandInfoRepository userExpandInfoRepository; private final ImgFilesRepository imgFilesRepository; + private final DaHuaConfig daHuaConfig; @Transactional(rollbackFor = Exception.class) public boolean execute(UserAddCmd cmd) { - Boolean b = corpInfoQueryExe.verifyCorpInfo(); - if (!b) { - throw new BizException("请先完善企业信息"); + Boolean b = corpInfoQueryExe.verifyCorpInfo(); + if (!b) { + throw new BizException("请先完善企业信息"); + } + log.info("开始新增用户,name: {}, corpinfoId: {}, phone: {}", cmd.getName(), cmd.getCorpinfoId(), cmd.getPhone()); + try { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attrs != null) { + HttpServletRequest req = attrs.getRequest(); + String orgidHeader = req.getHeader("orgid"); + if (orgidHeader != null && cmd.getCorpinfoId() == null) { + try { + cmd.setCorpinfoId(Long.parseLong(orgidHeader.trim())); + } catch (Exception e) { + log.warn("orgid 转 Long 失败: {}", orgidHeader); + } + } + } + } catch (Exception e) { + log.warn("获取请求头异常: {}", e.getMessage()); } + Long tenantId = null; SSOUser ssoUser = AuthContext.getCurrentUser(); - Long tenantId = ssoUser.getTenantId(); + if (ssoUser != null) { + tenantId = ssoUser.getTenantId(); + } UserE userE = new UserE(); BeanUtils.copyProperties(cmd, userE); + // tenantId 优先级:cmd 传值 -> AuthContext 上下文 -> ssoUser + if (!ObjectUtils.isEmpty(userE.getTenantId())) { + tenantId = userE.getTenantId(); + } else if (!ObjectUtils.isEmpty(userE.getCorpinfoId())) { + tenantId = userE.getCorpinfoId(); + } else { + try { + Long ctxTenantId = AuthContext.getTenantId(); + if (ctxTenantId != null) { + tenantId = ctxTenantId; + } + } catch (Exception e) { + log.warn("从 AuthContext 获取 tenantId 失败: {}", e.getMessage()); + } + } + if (tenantId == null && ObjectUtils.isEmpty(userE.getCorpinfoId())) { + throw new BizException("无法获取租户信息,请登录后重试或在请求中指定 corpinfoId"); + } userE.initAdd(tenantId, userE); //校验身份证是否存在 List userDOList = userRepository.getByIdCard(userE.getUserIdCard(), null); @@ -106,7 +162,13 @@ public class UserAddExe { userE.checkPhone(userEList); } + if (userE.getCorpinfoId() == null) { + throw new BizException("企业ID不能为空"); + } CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId()); + if (corpInfoDO == null) { + throw new BizException("企业信息不存在,corpinfoId=" + userE.getCorpinfoId()); + } String corpName = null; UserEmploymentLogE userEmploymentLogE = new UserEmploymentLogE(); BeanUtils.copyProperties(userE, userEmploymentLogE); @@ -122,21 +184,332 @@ public class UserAddExe { userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType())); userE.resetPassword(corpInfoDO.getType()); + // ① 先本地落库 + 底座GBS新增。GBS失败抛 RuntimeException,整个事务回滚 res = userGateway.add(userE); if (corpInfoDO != null && !ObjectUtils.isEmpty(corpInfoDO.getCorpName())) { corpName = corpInfoDO.getCorpName(); } userEmploymentLogE.initAdd(userEmploymentLogE, corpName, userE.getId()); - userEmploymentLogGateway.add(userEmploymentLogE); + try { + userEmploymentLogGateway.add(userEmploymentLogE); + } catch (Exception e) { + log.error("用户就业日志保存失败(不影响用户主体), userId: {}, error: {}", userE.getId(), e.getMessage(), e); + } + try { + addUserChangeRecordForSave(userE, corpInfoDO); + } catch (Exception e) { + log.error("用户变更记录保存失败(不影响用户主体), userId: {}, error: {}", userE.getId(), e.getMessage(), e); + } + // ② 本地和底座都成功后,最后才同步大华。失败→抛异常→回滚本地+GBS(避免留下"大华人成功但本地没数据") + DaHuaSyncResult syncResult = syncDaHuaPerson(userE); + if (syncResult.dahuaId != null || (syncResult.dahuaCode != null && !syncResult.dahuaCode.trim().isEmpty())) { + try { + UserE updateDahua = new UserE(); + updateDahua.setId(userE.getId()); + updateDahua.setDahuaId(syncResult.dahuaId); + updateDahua.setDahuaCode(syncResult.dahuaCode); + userRepository.updateDahuaInfo(updateDahua.getId(), updateDahua.getDahuaId(), updateDahua.getDahuaCode()); + log.info("写回dahuaId/dahuaCode成功, userId={}, dahuaId={}, dahuaCode={}", + userE.getId(), syncResult.dahuaId, syncResult.dahuaCode); + } catch (Exception e) { + log.error("写回dahuaId/dahuaCode失败, userId={}, dahuaId={}, dahuaCode={}", + userE.getId(), syncResult.dahuaId, syncResult.dahuaCode, e); + throw new BizException("写回大华信息到本地失败:" + e.getMessage()); + } + } + } catch (BizException e) { + log.error("新增用户业务异常: {}", e.getMessage(), e); + throw e; } catch (Exception e) { + log.error("新增用户系统异常: {}", e.getMessage(), e); throw new RuntimeException(e); } if (!res) { throw new BizException("保存失败"); } + cmd.setId(userE.getId()); + cmd.setUsername(userE.getUsername()); + log.info("新增用户完成, userId: {}, username: {}", userE.getId(), userE.getUsername()); return true; } + private DaHuaSyncResult syncDaHuaPerson(UserE userE) { + return syncDaHuaPerson(userE, userE.getFaceFile(), true); + } + + private DaHuaSyncResult syncDaHuaPerson(UserE userE, Object avatarSource, boolean throwOnFail) { + Map personMap = new HashMap<>(); + personMap.put("name", userE.getName()); + String idCard = userE.getUserIdCard(); + String plainIdCard = idCard; + if (idCard != null && !idCard.trim().isEmpty()) { + try { + byte[] decodedBytes = Base64.getDecoder().decode(idCard.trim()); + plainIdCard = new String(decodedBytes, StandardCharsets.UTF_8); + } catch (Exception e) { + log.warn("身份证号非Base64格式,按原值处理: {}", idCard); + } + } + personMap.put("code", plainIdCard); + personMap.put("paperType", 111); + personMap.put("paperNumber", plainIdCard); + personMap.put("phone", userE.getPhone()); + Long realCorpId = userE.getCorpinfoId() != null ? userE.getCorpinfoId() : 1L; + personMap.put("departmentId", realCorpId); + personMap.put("departmentType", 2); + personMap.put("type", 3); + if (userE.getNation() != null && !userE.getNation().trim().isEmpty()) { + try { + personMap.put("nation", Integer.valueOf(userE.getNation().trim())); + } catch (Exception e) { + log.warn("用户民族编码转Integer失败,用默认值1,nation={}", userE.getNation()); + personMap.put("nation", 1); + } + } else { + personMap.put("nation", 1); + } + personMap.put("nationName", (userE.getNationName() != null && !userE.getNationName().trim().isEmpty()) ? userE.getNationName().trim() : "汉族"); + personMap.put("service", "evo-thirdParty"); + + Object faceFile = avatarSource != null ? avatarSource : userE.getFaceFile(); + + java.util.function.Function extractThumbFromSingle = (singleObj) -> { + if (singleObj == null) { + return null; + } + if (singleObj instanceof String) { + return singleObj.toString(); + } + if (singleObj instanceof Map) { + Map faceFileMap = (Map) singleObj; + Object thumbUrlObj = faceFileMap.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } else if (singleObj instanceof cn.hutool.json.JSONObject) { + cn.hutool.json.JSONObject faceJson = (cn.hutool.json.JSONObject) singleObj; + Object thumbUrlObj = faceJson.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } else { + try { + cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj); + Object thumbUrlObj = faceJson.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } catch (Exception e) { + log.warn("解析单个图片对象为JSON失败: {}", e.getMessage()); + } + } + return null; + }; + + String thumbUrl = null; + if (faceFile != null) { + boolean isArray = false; + Object firstElement = null; + if (faceFile instanceof java.util.Collection) { + isArray = true; + java.util.Collection coll = (java.util.Collection) faceFile; + if (!coll.isEmpty()) { + firstElement = coll.iterator().next(); + } + } else if (faceFile.getClass().isArray()) { + isArray = true; + Object[] arr = (Object[]) faceFile; + if (arr.length > 0) { + firstElement = arr[0]; + } + } else if (faceFile instanceof cn.hutool.json.JSONArray) { + isArray = true; + cn.hutool.json.JSONArray jsonArr = (cn.hutool.json.JSONArray) faceFile; + if (!jsonArr.isEmpty()) { + firstElement = jsonArr.get(0); + } + } + + if (isArray) { + if (firstElement != null) { + thumbUrl = extractThumbFromSingle.apply(firstElement); + } + } else { + thumbUrl = extractThumbFromSingle.apply(faceFile); + } + } + + + + if (thumbUrl != null && !thumbUrl.trim().isEmpty()) { + String trimmedThumb = thumbUrl.trim(); + boolean startsWithHttp = trimmedThumb.startsWith("http://") || trimmedThumb.startsWith("https://"); + boolean containsComma = trimmedThumb.contains(","); + boolean startsWithDataImage = trimmedThumb.startsWith("data:image"); + + String cleanBase64; + if (containsComma && startsWithDataImage) { + String prefixPart = trimmedThumb.substring(0, trimmedThumb.indexOf(",") + 1); + String bodyPart = trimmedThumb.substring(trimmedThumb.indexOf(",") + 1); + String cleanBody = bodyPart.replaceAll("\\s+", ""); + cleanBase64 = prefixPart + cleanBody; + } else { + String pureBody = trimmedThumb.replaceAll("\\s+", ""); + cleanBase64 = "data:image/jpeg;base64," + pureBody; + } + int commaIdx = cleanBase64.indexOf(","); + String base64BodyOnly = commaIdx >= 0 ? cleanBase64.substring(commaIdx + 1) : cleanBase64; + boolean isLikelyBase64 = base64BodyOnly.matches("^[A-Za-z0-9+/=]+$"); + int bodyLen = base64BodyOnly.length(); + boolean finalHasPrefix = cleanBase64.startsWith("data:image"); + + if (startsWithHttp) { + log.error("同步大华人员失败:人脸图片传的是HTTP/HTTPS URL,要求必须传带data:image前缀的base64图片"); + } else if (!finalHasPrefix) { + log.error("同步大华人员失败:人脸图片最终无data:image前缀,代码补齐逻辑失效"); + } else if (!isLikelyBase64) { + log.error("同步大华人员失败:人脸图片base64主体含非base64非法字符"); + } else if (bodyLen < 1000) { + log.error("同步大华人员失败:人脸图片base64主体长度过短({}),缩略图可能损坏", bodyLen); + } + + List> personBiosignatures = new ArrayList<>(); + Map biosignature = new java.util.LinkedHashMap<>(); + biosignature.put("type", 3); + biosignature.put("index", 1); + biosignature.put("base64Data", cleanBase64); + personBiosignatures.add(biosignature); + personMap.put("personBiosignatures", personBiosignatures); + } else { + log.warn("未获取到人脸图片thumbUrl,personBiosignatures将不传递"); + } + + Integer age = null; + Integer sex = null; + if (plainIdCard != null && plainIdCard.length() == 18) { + try { + String birthStr = plainIdCard.substring(6, 14); + LocalDate birthDate = LocalDate.parse(birthStr, DateTimeFormatter.ofPattern("yyyyMMdd")); + age = Period.between(birthDate, LocalDate.now()).getYears(); + char sexChar = plainIdCard.charAt(16); + int sexNum = Integer.parseInt(String.valueOf(sexChar)); + sex = (sexNum % 2 == 1) ? 1 : 2; + } catch (Exception e) { + log.warn("解析身份证号获取年龄性别失败: {}, error={}", plainIdCard, e.getMessage()); + } + } + if (age == null) { + if (userE.getBirthday() != null && !userE.getBirthday().trim().isEmpty()) { + try { + String birthdayStr = userE.getBirthday().trim(); + LocalDate birthDate = null; + if (birthdayStr.length() == 8 && birthdayStr.matches("\\d{8}")) { + birthDate = LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd")); + } else if (birthdayStr.contains("-")) { + birthDate = LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } else if (birthdayStr.contains("/")) { + birthDate = LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyy/MM/dd")); + } + if (birthDate != null) { + age = Period.between(birthDate, LocalDate.now()).getYears(); + } + } catch (Exception ignored) {} + } + } + if (age == null) { + age = 23; + } + if (sex == null) { + if ("1".equals(userE.getSex()) || "男".equals(userE.getSex())) { + sex = 1; + } else if ("2".equals(userE.getSex()) || "女".equals(userE.getSex())) { + sex = 2; + } else { + sex = 1; + } + } + personMap.put("age", age); + personMap.put("sex", sex); + Map extraHeaders = new HashMap<>(); + try { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attrs != null) { + HttpServletRequest req = attrs.getRequest(); + String token = req.getHeader("token"); + String orgid = req.getHeader("orgid"); + String tenantId = req.getHeader("tenantId"); + if (token != null && !token.trim().isEmpty()) { + extraHeaders.put("token", token); + } + if (orgid != null && !orgid.trim().isEmpty()) { + extraHeaders.put("orgid", orgid); + } + if (tenantId != null && !tenantId.trim().isEmpty()) { + extraHeaders.put("tenantId", tenantId); + } + } + } catch (Exception e) { + log.warn("获取当前请求头异常: {}", e.getMessage()); + } + Map syncResult = daHuaConfig.syncPerson(personMap, extraHeaders); + Boolean success = (Boolean) syncResult.get("success"); + Boolean skipped = (Boolean) syncResult.get("skipped"); + DaHuaSyncResult result = new DaHuaSyncResult(); + result.setDahuaCode(plainIdCard); + if (skipped != null && skipped) { + return result; + } + String maskedIdCard = plainIdCard; + if (plainIdCard != null && plainIdCard.length() >= 14) { + maskedIdCard = plainIdCard.substring(0, 6) + "********" + plainIdCard.substring(plainIdCard.length() - 4); + } + result.setDahuaCode(plainIdCard); + userE.setDahuaCode(plainIdCard); + if (success == null || !success) { + String errCode = (String) syncResult.getOrDefault("errCode", ""); + String errMessage = (String) syncResult.getOrDefault("errMessage", "大华人员同步失败"); + if (throwOnFail) { + throw new BizException("同步大华平台失败:" + errMessage); + } else { + log.warn("同步大华平台未成功(重复信息或其他原因,不影响本地数据保存): errCode={}, errMessage={}, userId={}, idCardMask={}", + errCode, errMessage, userE.getId(), maskedIdCard); + return result; + } + } + Object data = syncResult.get("data"); + if (data != null) { + if (data instanceof Map) { + Map dataMap = (Map) data; + Object idObj = dataMap.get("id"); + if (idObj != null) { + try { + Integer dahuaId = Integer.valueOf(idObj.toString()); + result.setDahuaId(dahuaId); + userE.setDahuaId(dahuaId); + log.info("设置大华dahuaId成功, dahuaId={}", dahuaId); + } catch (Exception e) { + log.warn("转换大华返回id为Integer失败, idObj={}", idObj); + } + } + } else if (data instanceof cn.hutool.json.JSONObject) { + cn.hutool.json.JSONObject dataJson = (cn.hutool.json.JSONObject) data; + Object idObj = dataJson.get("id"); + if (idObj != null) { + try { + Integer dahuaId = Integer.valueOf(idObj.toString()); + result.setDahuaId(dahuaId); + userE.setDahuaId(dahuaId); + log.info("设置大华dahuaId成功, dahuaId={}", dahuaId); + } catch (Exception e) { + log.warn("转换大华返回id为Integer失败, idObj={}", idObj); + } + } + } + } + userE.setDahuaCode(plainIdCard); + return result; + } + @Transactional(rollbackFor = Exception.class) public boolean executeRegister(AppUserRegisterCmd cmd) { @@ -494,19 +867,63 @@ public class UserAddExe { sendParams.put("code", phoneCode); messageSendCmd.setParams(sendParams); SingleResponse d = messageFacade.send(messageSendCmd); - System.out.println( d.toString()); + log.info("短信发送结果: {}, phone: {}", d, phone); return d == null ? false : d.getData(); } @Transactional(rollbackFor = Exception.class) public boolean executeXgf(UserXgfAddCmd cmd) { + log.info("开始新增相关方用户,name: {}, corpinfoId: {}, phone: {}", cmd.getName(), cmd.getCorpinfoId(), cmd.getPhone()); + try { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attrs != null) { + HttpServletRequest req = attrs.getRequest(); + String orgidHeader = req.getHeader("orgid"); + if (orgidHeader != null && cmd.getCorpinfoId() == null) { + try { + cmd.setCorpinfoId(Long.parseLong(orgidHeader.trim())); + } catch (Exception e) { + log.warn("orgid 转 Long 失败: {}", orgidHeader); + } + } + } + } catch (Exception e) { + log.warn("获取请求头异常: {}", e.getMessage()); + } + Long tenantId = null; SSOUser ssoUser = AuthContext.getCurrentUser(); - Long tenantId = ssoUser.getTenantId(); + if (ssoUser != null) { + tenantId = ssoUser.getTenantId(); + } UserE userE = new UserE(); BeanUtils.copyProperties(cmd, userE); + // tenantId 优先级:cmd 传值 -> AuthContext 上下文 -> ssoUser + if (!ObjectUtils.isEmpty(userE.getTenantId())) { + tenantId = userE.getTenantId(); + } else if (!ObjectUtils.isEmpty(userE.getCorpinfoId())) { + tenantId = userE.getCorpinfoId(); + } else { + try { + Long ctxTenantId = AuthContext.getTenantId(); + if (ctxTenantId != null) { + tenantId = ctxTenantId; + } + } catch (Exception e) { + log.warn("从 AuthContext 获取 tenantId 失败: {}", e.getMessage()); + } + } + if (tenantId == null && ObjectUtils.isEmpty(userE.getCorpinfoId())) { + throw new BizException("无法获取租户信息,请登录后重试或在请求中指定 corpinfoId"); + } + if (ObjectUtils.isEmpty(userE.getCorpinfoId())) { + userE.setCorpinfoId(tenantId); + } userE.initAdd(tenantId, userE); CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId()); + if (corpInfoDO == null) { + throw new BizException("企业信息不存在,corpinfoId=" + userE.getCorpinfoId()); + } userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType())); Long roleId = userRepository.getDefaultRoleId(); if (roleId == null) { @@ -579,6 +996,54 @@ public class UserAddExe { userExpandInfoRepository.updateByPhone(userE.getPhone(),userE.getFlowFlag()); } + // ========== 相关方用户同步大华(统一用 /person/sync;重复信息返回false直接跳过不影响本地)========== + Long xgfUserId = addFlag ? userE.getId() : userDOUpdate.getId(); + if (xgfUserId == null) { + log.warn("相关方用户保存后未获取到userId,跳过大华同步及dahua信息写回, addFlag={}", addFlag); + } else { + Object xgfAvatarSource = cmd.getUserImg(); + if (xgfAvatarSource == null) { + xgfAvatarSource = cmd.getUserAvatarUrl(); + } + DaHuaSyncResult xgfSyncResult = null; + try { + xgfSyncResult = syncDaHuaPerson(userE, xgfAvatarSource, false); + } catch (Exception e) { + log.error("相关方用户同步大华异常(不影响本地保存,继续执行写回身份证号为dahuaCode), userId={}, addFlag={}, error={}", + xgfUserId, addFlag, e.getMessage(), e); + } + // 无论大华同步成功/失败/返回false/抛异常,都尝试写回dahuaId/dahuaCode: + // dahuaId = 大华返回的id(成功时才有,失败/异常/重复时为null) + // dahuaCode = 前端传的身份证号(经过Base64解码的明文,syncDaHuaPerson里即使失败也已经set到result了) + Integer xgfDahuaId = xgfSyncResult != null ? xgfSyncResult.dahuaId : null; + String xgfDahuaCode = xgfSyncResult != null ? xgfSyncResult.dahuaCode : null; + // 如果xgfSyncResult异常或dahuaCode为空兜底:直接把userE.getUserIdCard()做一次Base64解码写入dahuaCode,保证100%写回 + if ((xgfDahuaCode == null || xgfDahuaCode.trim().isEmpty()) && userE.getUserIdCard() != null && !userE.getUserIdCard().trim().isEmpty()) { + String idCardRaw = userE.getUserIdCard().trim(); + try { + xgfDahuaCode = new String(java.util.Base64.getDecoder().decode(idCardRaw), java.nio.charset.StandardCharsets.UTF_8); + } catch (Exception e) { + xgfDahuaCode = idCardRaw; + } + } + if (xgfDahuaId != null || (xgfDahuaCode != null && !xgfDahuaCode.trim().isEmpty())) { + try { + UserE updateDahua = new UserE(); + updateDahua.setId(xgfUserId); + updateDahua.setDahuaId(xgfDahuaId); + updateDahua.setDahuaCode(xgfDahuaCode); + userRepository.updateDahuaInfo(updateDahua.getId(), updateDahua.getDahuaId(), updateDahua.getDahuaCode()); + log.info("相关方用户写回dahuaId/dahuaCode成功, userId={}, addFlag={}, dahuaId={}, dahuaCode={}", + xgfUserId, addFlag, xgfDahuaId, xgfDahuaCode); + } catch (Exception e) { + log.error("相关方用户写回dahuaId/dahuaCode失败, userId={}, addFlag={}, dahuaId={}, dahuaCode={}", + xgfUserId, addFlag, xgfDahuaId, xgfDahuaCode, e); + } + } else { + log.warn("相关方用户既无dahuaId也无dahuaCode可写回,跳过update, userId={}, addFlag={}", xgfUserId, addFlag); + } + } + // ========================================================================= //页面相关保存user_corp /** @@ -635,5 +1100,86 @@ public class UserAddExe { imgFilesRepository.saveBatch(newImgFiles); } } -} + @Transactional(rollbackFor = Exception.class) + public Long executeHumanUser(UserAddCmd cmd) { + UserE userE = new UserE(); + BeanUtils.copyProperties(cmd, userE); + userE.setTenantId(cmd.getCorpinfoId()); + //校验手机号 + List employmentFlagList = Arrays.asList(UserEmploymentFlagEnum.ON.getCode(), UserEmploymentFlagEnum.ENTRY_AUDIT.getCode(), UserEmploymentFlagEnum.RESIGNATION_AUDIT.getCode()); + List userList = userRepository.getListByPhone(userE.getPhone(), employmentFlagList); + if (CollUtil.isNotEmpty(userList)) { + List userEList = userCoConvertor.convertDOsToEs(userList); +// userE.checkPhone(userEList); + if (CollUtil.isNotEmpty(userEList)) { + //判断是否有当前企业 + boolean existInCurrentCorp = userEList.stream() + .anyMatch(userE1 -> userE1.getCorpinfoId().equals(cmd.getCorpinfoId())); + + //判断是否在其他企业存在且为在职状态(employment_flag=1) + boolean existInOtherCorpAndOnJob = userEList.stream() + .filter(userE1 -> !userE1.getCorpinfoId().equals(cmd.getCorpinfoId())) + .anyMatch(userE1 -> UserEmploymentFlagEnum.ON.getCode().equals(userE1.getEmploymentFlag())); + + if (existInCurrentCorp) { + throw new BizException("当前手机号在当前企业已存在,请联系管理员"); + } else if (existInOtherCorpAndOnJob) { + throw new BizException("当前手机号已在其他企业存在且为在职状态,请联系管理员"); + } + } + } + + CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId()); + String corpName = null; + UserEmploymentLogE userEmploymentLogE = new UserEmploymentLogE(); + BeanUtils.copyProperties(userE, userEmploymentLogE); + boolean res = false; + try { + userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType())); + userE.resetPassword(corpInfoDO.getType()); + userE.setDepartmentLeaderFlag(0); + userE.setUserId(Tools.get32UUID()); + userE.setRzFlag(CommonFlagEnum.YES.getCode()); + res = userGateway.add(userE); + if (corpInfoDO != null && !ObjectUtils.isEmpty(corpInfoDO.getCorpName())) { + corpName = corpInfoDO.getCorpName(); + } + userEmploymentLogE.initAdd(userEmploymentLogE, corpName, userE.getId()); + userEmploymentLogGateway.add(userEmploymentLogE); +// addUserChangeRecordForSave(userE, corpInfoDO); + } catch (Exception e) { + throw new RuntimeException(e); + } + if (!res) { + throw new BizException("保存失败"); + } + return userE.getId(); + } + + /** + * 普通保存企业用户时补充一条用户变更记录。 + * 该场景只记录变更后的企业、部门、岗位和姓名信息,变更前信息保持为空。 + */ + private void addUserChangeRecordForSave(UserE userE, CorpInfoDO corpInfoDO) { + String corpName = corpInfoDO == null ? null : corpInfoDO.getCorpName(); + String departmentName = null; + String postName = userE.getPostName(); + if (userE.getDepartmentId() != null) { + DepartmentDO departmentDO = departmentRepository.getById(userE.getDepartmentId()); + if (departmentDO != null) { + departmentName = departmentDO.getName(); + } + } + if (userE.getPostId() != null && StringUtils.isEmpty(postName)) { + PostDO postDO = postRepository.getById(userE.getPostId()); + if (postDO != null) { + postName = postDO.getPostName(); + } + } + userE.setPostName(postName); + UserChangeRecordE userChangeRecordE = new UserChangeRecordE(); + userChangeRecordE.initUserAdd(userE, corpName, departmentName); + userChangeRecordGateway.add(userChangeRecordE); + } +} \ No newline at end of file diff --git a/web-app/src/main/java/com/zcloud/basic/info/command/UserUpdateExe.java b/web-app/src/main/java/com/zcloud/basic/info/command/UserUpdateExe.java index fc612380..5c8997ce 100644 --- a/web-app/src/main/java/com/zcloud/basic/info/command/UserUpdateExe.java +++ b/web-app/src/main/java/com/zcloud/basic/info/command/UserUpdateExe.java @@ -14,6 +14,7 @@ import com.jjb.saas.system.client.user.request.UserUpdateQuitCmd; import com.jjb.saas.system.client.user.response.UserDetailCO; import com.sun.xml.bind.v2.TODO; import com.zcloud.basic.info.constant.RedisConstant; +import com.zcloud.basic.info.domain.config.DaHuaConfig; import com.zcloud.basic.info.domain.enums.*; import com.zcloud.basic.info.domain.gateway.*; import com.zcloud.basic.info.domain.model.*; @@ -34,13 +35,19 @@ import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.Period; +import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.Base64; import java.util.stream.Collectors; -import static dm.jdbc.util.DriverUtil.log; - /** * web-app @@ -70,21 +77,32 @@ public class UserUpdateExe { private ZcloudHiddenFacade zcloudHiddenFacade; @DubboReference private ZcloudRiskFacade zcloudRiskFacade; + private final DaHuaConfig daHuaConfig; @Transactional(rollbackFor = Exception.class) public void execute(UserUpdateCmd userUpdateCmd) { - //pc端不允许修改固定和流动状态 + log.info("【用户编辑execute入口】开始执行, id={}, name={}, phone={}", + userUpdateCmd.getId(), userUpdateCmd.getName(), userUpdateCmd.getPhone()); userUpdateCmd.setFlowFlag(null); UserE userE = new UserE(); BeanUtils.copyProperties(userUpdateCmd, userE); - // 对比用户老数据与要修改数据,查看是否涉及调岗及入职 UserDO userDO = userRepository.getInfoById(userUpdateCmd.getId()); + if (userDO == null) { + throw new BizException("用户不存在,用户id:" + userUpdateCmd.getId()); + } + userE.setDahuaId(userDO.getDahuaId()); + userE.setDahuaCode(userDO.getDahuaCode()); + log.info("【getInfoById查询结果】dahuaId={}, dahuaCode={}, name={}, userIdCard={}", + userDO.getDahuaId(), userDO.getDahuaCode(), userDO.getName(), userDO.getUserIdCard()); + syncDaHuaPersonUpdate(userUpdateCmd, userDO); + UserE oldUserE = new UserE(); BeanUtils.copyProperties(userDO, oldUserE); // boolean transferPositionFlag = userE.verifyTransferPosition(oldUserE, userE); boolean transferFlowFlag = userE.verifyTransferFlow(oldUserE, userE); + boolean nameChanged = userUpdateCmd.getName() != null && !Objects.equals(userDO.getName(), userUpdateCmd.getName()); if (transferFlowFlag) { List userCorpRecordDOList = userCorpRecordRepository.getInfoListByUserId(userDO.getId()); if (CollUtil.isNotEmpty(userCorpRecordDOList)) { @@ -179,6 +197,9 @@ public class UserUpdateExe { }else { throw new BizException("未找到该用户所属企业"); } + if (nameChanged) { + addUserChangeRecordForNameUpdate(userDO); + } @@ -207,6 +228,270 @@ public class UserUpdateExe { } + /** + * 普通编辑场景下姓名发生变化时,补充一条用户变更记录。 + * 该记录只在姓名实际变更后新增,避免影响其他普通修改逻辑。 + */ + private void addUserChangeRecordForNameUpdate(UserDO oldUserDO) { + UserDO newUserDO = userRepository.getInfoById(oldUserDO.getId()); + if (newUserDO == null) { + log.warn("用户编辑后未查询到最新用户信息,跳过姓名变更记录,用户id:{}", oldUserDO.getId()); + return; + } + + UserE oldUserE = new UserE(); + BeanUtils.copyProperties(oldUserDO, oldUserE); + UserE newUserE = new UserE(); + BeanUtils.copyProperties(newUserDO, newUserE); + + UserChangeRecordE userChangeRecordE = new UserChangeRecordE(); + userChangeRecordE.initUserUpdate(newUserE, oldUserE, newUserDO.getCorpinfoName(), newUserDO.getDepartmentName(),UserChangeRecordStatusEnum.APPROVED.getCode()); + userChangeRecordGateway.add(userChangeRecordE); + } + + private void syncDaHuaPersonUpdate(UserUpdateCmd userUpdateCmd, UserDO userDO) { + log.info("========== 大华人员(更新)检查开始 =========="); + log.info("用户主键userDO.id={}, name={}, userIdCard={}, dahuaId={}, dahuaCode={}", + userDO.getId(), userDO.getName(), userDO.getUserIdCard(), userDO.getDahuaId(), userDO.getDahuaCode()); + Integer dahuaId = userDO.getDahuaId(); + String dahuaCode = userDO.getDahuaCode(); + if (dahuaId == null && (dahuaCode == null || dahuaCode.trim().isEmpty())) { + log.info("【跳过同步大华】该用户user表中dahuaId和dahuaCode都为空,说明从未同步过大华,本次跳过不调更新接口"); + return; + } + + String newName = userUpdateCmd.getName() != null ? userUpdateCmd.getName() : userDO.getName(); + String newIdCard = userUpdateCmd.getUserIdCard() != null ? userUpdateCmd.getUserIdCard() : userDO.getUserIdCard(); + String newPhone = userUpdateCmd.getPhone() != null ? userUpdateCmd.getPhone() : userDO.getPhone(); + String newNation = userUpdateCmd.getNation() != null ? userUpdateCmd.getNation() : userDO.getNation(); + String newNationName = userUpdateCmd.getNationName() != null ? userUpdateCmd.getNationName() : userDO.getNationName(); + Object newFaceFile = userUpdateCmd.getUserImg() != null ? userUpdateCmd.getUserImg() : userUpdateCmd.getFaceFile(); + Long newCorpId = userUpdateCmd.getCorpinfoId() != null ? userUpdateCmd.getCorpinfoId() : userDO.getCorpinfoId(); + log.info("【本次请求入参】name是否传={}, phone是否传={}, userIdCard是否传={}, nation是否传={}, nationName是否传={}, userImg是否传={}, faceFile是否传={}, corpinfoId是否传={}", + userUpdateCmd.getName() != null, userUpdateCmd.getPhone() != null, userUpdateCmd.getUserIdCard() != null, + userUpdateCmd.getNation() != null, userUpdateCmd.getNationName() != null, + userUpdateCmd.getUserImg() != null, userUpdateCmd.getFaceFile() != null, + userUpdateCmd.getCorpinfoId() != null); + + boolean nameChanged = !Objects.equals(newName, userDO.getName()); + boolean idCardChanged = !Objects.equals(newIdCard, userDO.getUserIdCard()); + boolean phoneChanged = !Objects.equals(newPhone, userDO.getPhone()); + boolean nationChanged = !Objects.equals(newNation, userDO.getNation()) || !Objects.equals(newNationName, userDO.getNationName()); + boolean faceChanged = newFaceFile != null; + boolean corpChanged = !Objects.equals(newCorpId, userDO.getCorpinfoId()); + log.info("【变化检测】nameChanged={}({}→{}), idCardChanged={}, phoneChanged={}, nationChanged={}({}/{}→{}/{}), faceChanged={}, corpChanged={}({}→{})", + nameChanged, userDO.getName(), newName, + idCardChanged, phoneChanged, + nationChanged, userDO.getNation(), userDO.getNationName(), newNation, newNationName, + faceChanged, corpChanged, userDO.getCorpinfoId(), newCorpId); + if (!nameChanged && !idCardChanged && !phoneChanged && !nationChanged && !faceChanged && !corpChanged) { + log.info("【跳过同步大华】姓名/身份证/手机号/民族/人脸图/企业 6项都没变化,按优化逻辑不调更新接口"); + return; + } + + String plainIdCard = newIdCard; + if (newIdCard != null && !newIdCard.trim().isEmpty()) { + try { + byte[] decodedBytes = Base64.getDecoder().decode(newIdCard.trim()); + plainIdCard = new String(decodedBytes, StandardCharsets.UTF_8); + log.info("身份证号Base64解密成功, 密文前20={}, 明文前10={}", + newIdCard.substring(0, Math.min(20, newIdCard.length())) + (newIdCard.length() > 20 ? "..." : ""), + plainIdCard.substring(0, Math.min(10, plainIdCard.length())) + (plainIdCard.length() > 10 ? "..." : "")); + } catch (Exception e) { + log.warn("身份证号非Base64格式,按原值处理: {}", newIdCard); + } + } + + Map personMap = new HashMap<>(); + if (dahuaId != null) { + personMap.put("id", dahuaId); + } + personMap.put("name", newName); + personMap.put("code", dahuaCode != null ? dahuaCode : plainIdCard); + personMap.put("paperType", 111); + personMap.put("paperNumber", plainIdCard); + personMap.put("phone", newPhone); + Long realCorpId = newCorpId != null ? newCorpId : 1L; + personMap.put("departmentId", realCorpId); + personMap.put("nationName", newNationName != null && !newNationName.trim().isEmpty() ? newNationName : "汉族"); + try { + if (newNation != null && !newNation.trim().isEmpty()) { + personMap.put("nation", Integer.parseInt(newNation.trim())); + } else { + personMap.put("nation", 1); + } + } catch (Exception e) { + log.warn("民族编码转Integer失败: {}, 用默认值1(汉族)", newNation); + personMap.put("nation", 1); + } + personMap.put("service", "evo-thirdParty"); + + Integer age = null; + Integer sex = null; + if (plainIdCard != null && plainIdCard.length() == 18) { + try { + String birthStr = plainIdCard.substring(6, 14); + LocalDate birthDate = LocalDate.parse(birthStr, DateTimeFormatter.ofPattern("yyyyMMdd")); + age = Period.between(birthDate, LocalDate.now()).getYears(); + String sexChar = plainIdCard.substring(16, 17); + int sexNum = Integer.parseInt(sexChar); + sex = sexNum % 2 == 0 ? 2 : 1; + } catch (Exception e) { + log.warn("从身份证号解析年龄/性别失败, idCard={}", plainIdCard); + } + } + personMap.put("age", age); + personMap.put("sex", sex); + + Object faceFile = newFaceFile; + java.util.function.Function extractThumbFromSingle = (singleObj) -> { + if (singleObj == null) { + return null; + } + if (singleObj instanceof Map) { + Map faceFileMap = (Map) singleObj; + Object thumbUrlObj = faceFileMap.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } else if (singleObj instanceof cn.hutool.json.JSONObject) { + cn.hutool.json.JSONObject faceJson = (cn.hutool.json.JSONObject) singleObj; + Object thumbUrlObj = faceJson.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } else { + try { + cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj); + Object thumbUrlObj = faceJson.get("thumbUrl"); + if (thumbUrlObj != null) { + return thumbUrlObj.toString(); + } + } catch (Exception e) { + log.warn("解析单个图片对象为JSON失败: {}", e.getMessage()); + } + } + return null; + }; + + String thumbUrl = null; + if (faceFile != null) { + boolean isArray = false; + Object firstElement = null; + if (faceFile instanceof java.util.Collection) { + isArray = true; + java.util.Collection coll = (java.util.Collection) faceFile; + if (!coll.isEmpty()) { + firstElement = coll.iterator().next(); + } + } else if (faceFile.getClass().isArray()) { + isArray = true; + Object[] arr = (Object[]) faceFile; + if (arr.length > 0) { + firstElement = arr[0]; + } + } else if (faceFile instanceof cn.hutool.json.JSONArray) { + isArray = true; + cn.hutool.json.JSONArray jsonArr = (cn.hutool.json.JSONArray) faceFile; + if (!jsonArr.isEmpty()) { + firstElement = jsonArr.get(0); + } + } + if (isArray) { + if (firstElement != null) { + thumbUrl = extractThumbFromSingle.apply(firstElement); + } + } else { + thumbUrl = extractThumbFromSingle.apply(faceFile); + } + } + + if (thumbUrl != null && !thumbUrl.trim().isEmpty()) { + String trimmedThumb = thumbUrl.trim(); + boolean startsWithHttp = trimmedThumb.startsWith("http://") || trimmedThumb.startsWith("https://"); + boolean containsComma = trimmedThumb.contains(","); + boolean startsWithDataImage = trimmedThumb.startsWith("data:image"); + + String cleanBase64; + if (containsComma && startsWithDataImage) { + String prefixPart = trimmedThumb.substring(0, trimmedThumb.indexOf(",") + 1); + String bodyPart = trimmedThumb.substring(trimmedThumb.indexOf(",") + 1); + String cleanBody = bodyPart.replaceAll("\\s+", ""); + cleanBase64 = prefixPart + cleanBody; + } else { + String pureBody = trimmedThumb.replaceAll("\\s+", ""); + cleanBase64 = "data:image/jpeg;base64," + pureBody; + } + int commaIdx = cleanBase64.indexOf(","); + String base64BodyOnly = commaIdx >= 0 ? cleanBase64.substring(commaIdx + 1) : cleanBase64; + boolean isLikelyBase64 = base64BodyOnly.matches("^[A-Za-z0-9+/=]+$"); + int bodyLen = base64BodyOnly.length(); + boolean finalHasPrefix = cleanBase64.startsWith("data:image"); + + if (startsWithHttp) { + log.error("同步大华人员(更新)失败:人脸图片传的是HTTP/HTTPS URL,要求必须传带data:image前缀的base64图片"); + throw new BizException("同步大华平台失败:人脸图片不支持URL格式,请上传图片文件"); + } else if (!finalHasPrefix) { + log.error("同步大华人员(更新)失败:人脸图片最终无data:image前缀,代码补齐逻辑失效"); + throw new BizException("同步大华平台失败:人脸图片格式异常"); + } else if (!isLikelyBase64) { + log.error("同步大华人员(更新)失败:人脸图片base64主体含非base64非法字符"); + throw new BizException("同步大华平台失败:人脸图片格式错误"); + } else if (bodyLen < 1000) { + log.error("同步大华人员(更新)失败:人脸图片base64主体长度过短({}),缩略图可能损坏", bodyLen); + throw new BizException("同步大华平台失败:人脸图片不完整"); + } + + List> personBiosignatures = new ArrayList<>(); + Map biosignature = new java.util.LinkedHashMap<>(); + biosignature.put("type", 3); + biosignature.put("index", 1); + biosignature.put("base64Data", cleanBase64); + personBiosignatures.add(biosignature); + personMap.put("personBiosignatures", personBiosignatures); + } + + Map extraHeaders = new HashMap<>(); + try { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attrs != null) { + HttpServletRequest req = attrs.getRequest(); + String token = req.getHeader("token"); + String orgid = req.getHeader("orgid"); + String tenantId = req.getHeader("tenantId"); + if (token != null && !token.trim().isEmpty()) { + extraHeaders.put("token", token); + } + if (orgid != null && !orgid.trim().isEmpty()) { + extraHeaders.put("orgid", orgid); + } + if (tenantId != null && !tenantId.trim().isEmpty()) { + extraHeaders.put("tenantId", tenantId); + } + } + } catch (Exception e) { + log.warn("获取当前请求头异常: {}", e.getMessage()); + } + + Map syncResult = daHuaConfig.updatePerson(personMap, extraHeaders); + Boolean success = (Boolean) syncResult.get("success"); + Boolean skipped = (Boolean) syncResult.get("skipped"); + if (skipped != null && skipped) { + return; + } + String maskedIdCard = plainIdCard; + if (plainIdCard != null && plainIdCard.length() >= 14) { + maskedIdCard = plainIdCard.substring(0, 6) + "********" + plainIdCard.substring(plainIdCard.length() - 4); + } + if (success == null || !success) { + String errCode = (String) syncResult.getOrDefault("errCode", ""); + String errMessage = (String) syncResult.getOrDefault("errMessage", "大华人员更新失败"); + log.error("同步大华人员(更新)失败, errCode={}, errMessage={}, name={}, idCard={}", errCode, errMessage, newName, maskedIdCard); + throw new BizException("同步大华平台失败:" + errMessage); + } + log.info("同步大华人员(更新)成功, name={}, idCard={}", newName, maskedIdCard); + } + @Transactional(rollbackFor = Exception.class) public void editData(UserQryCmd userUpdateCmd) { List userDOList = new ArrayList<>(); @@ -395,16 +680,9 @@ public class UserUpdateExe { @Transactional(rollbackFor = Exception.class) public boolean updatePasswordFromApp(AppUserUpdatePassWordCmd cmd) { - //新密码和旧密码不能相同 - if (cmd.getPassword().equals(cmd.getNewPassword())) { - throw new BizException("新密码不能与旧密码相同"); - } - UserE userE = new UserE(); - userE.checkPassword(cmd.getNewPassword(), cmd.getConfirmPassword()); - - BeanUtils.copyProperties(cmd, userE); - userE.encryptionPassword(); - userGateway.updatePassword(userE); + UserUpdatePassWordCmd userUpdatePassWordCmd = new UserUpdatePassWordCmd(); + BeanUtils.copyProperties(cmd, userUpdatePassWordCmd); + executeUpdatePassword(userUpdatePassWordCmd); return true; } @@ -422,6 +700,10 @@ public class UserUpdateExe { @Transactional(rollbackFor = Exception.class) public Response executeUpdatePassword(UserUpdatePassWordCmd userUpdatePassWordCmd) { UserE userE = new UserE(); + if (userUpdatePassWordCmd.getPassword().equals(userUpdatePassWordCmd.getNewPassword())) { + throw new BizException("新密码不能与旧密码相同"); + } + userE.checkPassword(userUpdatePassWordCmd.getNewPassword(), userUpdatePassWordCmd.getConfirmPassword()); BeanUtils.copyProperties(userUpdatePassWordCmd, userE); userE.encryptionPassword(); return userGateway.updatePassword(userE); @@ -490,6 +772,43 @@ public class UserUpdateExe { throw new IllegalArgumentException("GBS离职处理失败,用户id:"+userDO.getId()+",错误信息:"+quit.getErrMessage()); } log.info("GBS离职处理结束,用户id:{},结果:{}", userDO.getId(), JSONUtil.toJsonStr(quit)); + + Integer dahuaId = userDO.getDahuaId(); + if (dahuaId != null) { + log.info("【离职-同步删除大华人员】userId={}, name={}, dahuaId={}", userDO.getId(), userDO.getName(), dahuaId); + Map extraHeaders = new HashMap<>(); + try { + ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attrs != null) { + HttpServletRequest req = attrs.getRequest(); + String token = req.getHeader("token"); + String orgid = req.getHeader("orgid"); + String tenantId = req.getHeader("tenantId"); + if (token != null && !token.trim().isEmpty()) extraHeaders.put("token", token); + if (orgid != null && !orgid.trim().isEmpty()) extraHeaders.put("orgid", orgid); + if (tenantId != null && !tenantId.trim().isEmpty()) extraHeaders.put("tenantId", tenantId); + } + } catch (Exception e) { + log.warn("【离职-获取大华透传请求头异常】: {}", e.getMessage()); + } + Map deleteResult = daHuaConfig.deletePerson(Collections.singletonList(dahuaId), extraHeaders); + Boolean skipped = (Boolean) deleteResult.get("skipped"); + if (skipped == null || !skipped) { + Boolean success = (Boolean) deleteResult.get("success"); + String errCode = (String) deleteResult.getOrDefault("errCode", ""); + String errMessage = (String) deleteResult.getOrDefault("errMessage", ""); + if (success == null || !success) { + log.error("【离职-大华删除失败→触发本地回滚】userId={}, dahuaId={}, errCode={}, errMessage={}", + userDO.getId(), dahuaId, errCode, errMessage); + throw new BizException("同步大华平台删除离职人员失败:" + errMessage); + } + log.info("【离职-大华删除成功】userId={}, dahuaId={}", userDO.getId(), dahuaId); + } else { + log.info("【离职-大华删除跳过】dockFlag未开启, userId={}", userDO.getId()); + } + } else { + log.info("【离职-跳过同步大华】用户dahuaId为空,未同步过大华平台, userId={}, name={}", userDO.getId(), userDO.getName()); + } } @@ -805,4 +1124,4 @@ public class UserUpdateExe { } -} +} \ No newline at end of file