From 042a44aebe9afc95afc8df6e3b4b361323cc4171 Mon Sep 17 00:00:00 2001 From: zhangxiongfeng Date: Wed, 29 Jul 2026 17:15:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E5=8D=8E=E5=8F=A3=E9=97=A8=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 604d96b3faea05d5ecb2522a4072b40527564235) --- .../zcloud/basic/info/command/UserAddExe.java | 126 +++++++++++++++++- .../basic/info/command/UserUpdateExe.java | 72 ++++++++-- .../zcloud/basic/info/dto/UserUpdateCmd.java | 2 + .../src/main/resources/mapper/UserMapper.xml | 2 +- 4 files changed, 187 insertions(+), 15 deletions(-) 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 221ffd3f..cb377ce9 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 @@ -234,6 +234,10 @@ public class UserAddExe { } 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(); @@ -253,16 +257,28 @@ public class UserAddExe { personMap.put("departmentId", 1L); personMap.put("departmentType", 2); personMap.put("type", 3); - personMap.put("nation", 1); - personMap.put("nationName", "汉族"); + 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 = userE.getFaceFile(); + 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"); @@ -446,10 +462,18 @@ public class UserAddExe { 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", "大华人员同步失败"); - throw new BizException("同步大华平台失败:" + 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) { @@ -849,12 +873,56 @@ public class UserAddExe { @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) { @@ -927,6 +995,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 /** 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 133fe6f1..1482aa36 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 @@ -92,6 +92,8 @@ public class UserUpdateExe { 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); @@ -261,21 +263,26 @@ public class UserUpdateExe { 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(); - Object newFaceFile = userUpdateCmd.getFaceFile(); - log.info("【本次请求入参】name是否传={}, phone是否传={}, userIdCard是否传={}, faceFile是否传={}", - userUpdateCmd.getName() != null, userUpdateCmd.getPhone() != null, userUpdateCmd.getUserIdCard() != null, newFaceFile != null); + 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(); + log.info("【本次请求入参】name是否传={}, phone是否传={}, userIdCard是否传={}, nation是否传={}, nationName是否传={}, userImg是否传={}, faceFile是否传={}", + userUpdateCmd.getName() != null, userUpdateCmd.getPhone() != null, userUpdateCmd.getUserIdCard() != null, + userUpdateCmd.getNation() != null, userUpdateCmd.getNationName() != null, + userUpdateCmd.getUserImg() != null, userUpdateCmd.getFaceFile() != 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; - log.info("【变化检测】nameChanged={}({}→{}), idCardChanged={}({}→{}), phoneChanged={}({}→{}), faceChanged={}", + log.info("【变化检测】nameChanged={}({}→{}), idCardChanged={}, phoneChanged={}, nationChanged={}({}/{}→{}/{}), faceChanged={}", nameChanged, userDO.getName(), newName, - idCardChanged, userDO.getUserIdCard(), newIdCard, - phoneChanged, userDO.getPhone(), newPhone, + idCardChanged, phoneChanged, + nationChanged, userDO.getNation(), userDO.getNationName(), newNation, newNationName, faceChanged); - if (!nameChanged && !idCardChanged && !phoneChanged && !faceChanged) { - log.info("【跳过同步大华】姓名/身份证/手机号/人脸图 4项都没变化,按优化逻辑不调更新接口"); + if (!nameChanged && !idCardChanged && !phoneChanged && !nationChanged && !faceChanged) { + log.info("【跳过同步大华】姓名/身份证/手机号/民族/人脸图 5项都没变化,按优化逻辑不调更新接口"); return; } @@ -302,7 +309,17 @@ public class UserUpdateExe { personMap.put("paperNumber", plainIdCard); personMap.put("phone", newPhone); personMap.put("departmentId", 1); - personMap.put("nationName", "汉族"); + 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; @@ -754,6 +771,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()); + } } diff --git a/web-client/src/main/java/com/zcloud/basic/info/dto/UserUpdateCmd.java b/web-client/src/main/java/com/zcloud/basic/info/dto/UserUpdateCmd.java index 5ffdea03..f4b927b9 100644 --- a/web-client/src/main/java/com/zcloud/basic/info/dto/UserUpdateCmd.java +++ b/web-client/src/main/java/com/zcloud/basic/info/dto/UserUpdateCmd.java @@ -65,6 +65,8 @@ public class UserUpdateCmd extends Command { private String userAvatarUrl; @ApiModelProperty(value = "人脸图片文件(取thumbUrl字段作为base64传大华)") private Object faceFile; + @ApiModelProperty(value = "用户头像数组(相关方人员头像,取第一个元素的thumbUrl字段作为base64传大华,优先级高于faceFile)") + private Object userImg; @ApiModelProperty(value = "现住址", name = "currentAddress") private String currentAddress; @ApiModelProperty(value = "户口所在地", name = "locationAddress") diff --git a/web-infrastructure/src/main/resources/mapper/UserMapper.xml b/web-infrastructure/src/main/resources/mapper/UserMapper.xml index 198223bb..92b30c18 100644 --- a/web-infrastructure/src/main/resources/mapper/UserMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/UserMapper.xml @@ -594,7 +594,7 @@ and ui.delete_enum = 'FALSE' - ui.user_avatar_url IN + and ui.user_avatar_url IN #{url}