大华口门对接

dahua_koumen
zhangxiongfeng 2026-07-29 17:15:00 +08:00
parent ac94e9fe88
commit 604d96b3fa
6 changed files with 194 additions and 18 deletions

View File

@ -63,6 +63,8 @@ dahua:
password: qinan@202604
clientId: mkmj_management
clientSecret: 5ef82f7c-9926-46a3-8d07-136fd054849c
primeportUrl: https://gbs-gateway.qhdsafety.com
primeportGateway: primeport
nine:
config:
companyId: 2008712261453131776,2046785341004177408
companyId: 2008712261453131776,2046785341004177408

View File

@ -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<String, Object> 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失败用默认值1nation={}", 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<Object, String> extractThumbFromSingle = (singleObj) -> {
if (singleObj == null) {
return null;
}
if (singleObj instanceof String) {
return singleObj.toString();
}
if (singleObj instanceof Map) {
Map<String, Object> faceFileMap = (Map<String, Object>) 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
/**

View File

@ -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;
@ -751,6 +768,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<String, String> 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<String, Object> 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());
}
}

View File

@ -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")

View File

@ -81,6 +81,9 @@ public class UserXgfAddCmd extends Command {
@ApiModelProperty(value = "人脸头像url", name = "userAvatarUrl", required = true)
private String userAvatarUrl;
@ApiModelProperty(value = "用户头像数组(取第一个元素的thumbUrl字段作为base64传大华优先级高于userAvatarUrl)")
private Object userImg;
@ApiModelProperty(value = "现住址", name = "currentAddress", required = true)
private String currentAddress;
@ -159,5 +162,4 @@ public class UserXgfAddCmd extends Command {
private String payNumber;
@ApiModelProperty(value = "是否缴纳其他人身伤害保险")
private Integer isBf;
}
}

View File

@ -594,7 +594,7 @@
<where>
and ui.delete_enum = 'FALSE'
<if test="params.urlList != null and params.urlList.size() > 0">
ui.user_avatar_url IN
and ui.user_avatar_url IN
<foreach collection="params.urlList" item="url" open="(" close=")" separator=",">
#{url}
</foreach>