大华口门对接

(cherry picked from commit 65ad2b538e)
dahua_koumen
zhangxiongfeng 2026-07-28 10:33:46 +08:00
parent 92cc6154c3
commit 59748186c6
12 changed files with 182 additions and 495 deletions

View File

@ -0,0 +1,20 @@
package com.zcloud.basic.info.config;
import com.jjb.saas.framework.auth.filter.AuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class LocalDevFilterConfig {
@Bean
public FilterRegistrationBean<AuthenticationFilter> disableAuthenticationFilter(AuthenticationFilter authenticationFilter) {
log.info("========== 本地开发环境:禁用 AuthenticationFilter 过滤器 ==========");
FilterRegistrationBean<AuthenticationFilter> registration = new FilterRegistrationBean<>(authenticationFilter);
registration.setEnabled(false);
return registration;
}
}

View File

@ -0,0 +1,11 @@
common:
mysql:
host: nlb-kd2xz70qhllfet2koj.cn-beijing.nlb.aliyuncsslb.com
port: 33068
username: root
password: 5tS3owZ7w8Uk1egv
dahua:
config:
dockFlag: 1
primeportUrl: https://gbs-gateway.qhdsafety.com
primeportGateway: primeport

View File

@ -3,8 +3,8 @@ spring:
import: import:
# - classpath:nacos.yml # - classpath:nacos.yml
# - classpath:sdk.yml # - classpath:sdk.yml
# - classpath:nacos-prod.yml - classpath:nacos-prod.yml
# - classpath:sdk-prod.yml - classpath:sdk-prod.yml
- classpath:nacos-prod2.yml # - classpath:nacos-prod2.yml
- classpath:sdk-prod2.yml # - classpath:sdk-prod2.yml
- classpath:swagger.yml - classpath:swagger.yml

View File

@ -1,9 +1,9 @@
common: common:
mysql: mysql:
host: 192.168.2.166 host: nlb-kd2xz70qhllfet2koj.cn-beijing.nlb.aliyuncsslb.com
port: 3306 port: 33068
username: root username: root
password: root password: 5tS3owZ7w8Uk1egv
redis: redis:
host: 10.43.253.4 host: 10.43.253.4
password: jjb123456 password: jjb123456

View File

@ -1,10 +1,15 @@
package com.zcloud.basic.info.facade; package com.zcloud.basic.info.facade;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.json.JSONUtil;
import com.alibaba.cola.dto.MultiResponse; import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse; import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.zcloud.basic.info.api.UserServiceI; import com.zcloud.basic.info.api.UserServiceI;
import com.zcloud.basic.info.command.convertor.UserCoConvertor; import com.zcloud.basic.info.command.convertor.UserCoConvertor;
import com.zcloud.basic.info.dto.UserAddCmd;
import com.zcloud.basic.info.dto.UserUpdateCmd;
import com.zcloud.basic.info.dto.clientobject.UserCO; import com.zcloud.basic.info.dto.clientobject.UserCO;
import com.zcloud.basic.info.persistence.dataobject.UserDO; import com.zcloud.basic.info.persistence.dataobject.UserDO;
import com.zcloud.basic.info.persistence.dataobject.UserImgDO; import com.zcloud.basic.info.persistence.dataobject.UserImgDO;
@ -14,7 +19,9 @@ import com.zcloud.gbscommon.zclouduser.facade.ZcloudUserFacade;
import com.zcloud.gbscommon.zclouduser.request.*; import com.zcloud.gbscommon.zclouduser.request.*;
import com.zcloud.gbscommon.zclouduser.response.ZcloudUserCo; import com.zcloud.gbscommon.zclouduser.response.ZcloudUserCo;
import com.zcloud.gbscommon.zclouduser.response.ZcloudUserImgBase64Co; import com.zcloud.gbscommon.zclouduser.response.ZcloudUserImgBase64Co;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
@ -25,6 +32,7 @@ import java.util.Map;
/** /**
* @author lin * @author lin
*/ */
@Slf4j
@DubboService @DubboService
public class ZcloudUserFacadeImpl implements ZcloudUserFacade { public class ZcloudUserFacadeImpl implements ZcloudUserFacade {
@Resource @Resource
@ -97,18 +105,97 @@ public class ZcloudUserFacadeImpl implements ZcloudUserFacade {
} }
@Override @Override
public SingleResponse addHumanUser(ZcloudUserAddCmd zcloudUserAddCmd) { public SingleResponse<Long> addHumanUser(ZcloudUserAddCmd cmd) {
return null; UserAddCmd userAddCmd = new UserAddCmd();
BeanUtil.copyProperties(cmd, userAddCmd);
return userServiceI.addHumanUser(userAddCmd);
} }
/**
* Dubbo
*
*
* humanUserUpdateExe
*/
@Override @Override
public SingleResponse<Long> updateHumanUser(ZcloudUserUpdateCmd zcloudUserUpdateCmd) { public SingleResponse<Long> updateHumanUser(ZcloudUserUpdateCmd zcloudUserUpdateCmd) {
return null; if (zcloudUserUpdateCmd == null) {
throw new BizException("用户更新参数不能为空");
}
log.info("接收人资用户变更请求参数: {}", JSONUtil.toJsonStr(zcloudUserUpdateCmd));
UserUpdateCmd userUpdateCmd = new UserUpdateCmd();
UserDO userDO = null;
if (zcloudUserUpdateCmd.getId() != null) {
userDO = userRepository.getInfoById(zcloudUserUpdateCmd.getId());
if (userDO != null) {
userUpdateCmd = BeanUtil.toBean(userDO, UserUpdateCmd.class);
}
}
BeanUtil.copyProperties(zcloudUserUpdateCmd, userUpdateCmd, CopyOptions.create().ignoreNullValue());
fillHumanUserUpdateCmd(userUpdateCmd, userDO, zcloudUserUpdateCmd);
return userServiceI.updateHumanUser(userUpdateCmd);
} }
@Override @Override
public MultiResponse<ZcloudUserCo> listUserByInfo(ZcloudUserInfoQry zcloudUserInfoQry) { public MultiResponse<ZcloudUserCo> listUserByInfo(ZcloudUserInfoQry zcloudUserInfoQry) {
return null; Map<String, Object> parmas = PageQueryHelper.toHashMap(zcloudUserInfoQry);
List<UserDO> userList = userRepository.listUserByInfo(parmas);
List<ZcloudUserCo> zcloudUserCos = userCoConvertor.converDOsToDubboCOs(userList);
return MultiResponse.of(zcloudUserCos);
} }
/**
*
* userId
* id
* id after
*/
private void fillHumanUserUpdateCmd(UserUpdateCmd userUpdateCmd, UserDO userDO, ZcloudUserUpdateCmd zcloudUserUpdateCmd) {
if (userDO != null && !StringUtils.hasText(userUpdateCmd.getUserId())) {
userUpdateCmd.setUserId(userDO.getUserId());
}
if (userDO != null && !StringUtils.hasText(userUpdateCmd.getName())) {
userUpdateCmd.setName(userDO.getName());
}
if (userDO != null && !StringUtils.hasText(userUpdateCmd.getPhone())) {
userUpdateCmd.setPhone(userDO.getPhone());
}
if (!StringUtils.hasText(userUpdateCmd.getUsername())) {
if (StringUtils.hasText(zcloudUserUpdateCmd.getPhone())
&& (userDO == null || !StringUtils.hasText(userDO.getUsername()) || userDO.getUsername().equals(userDO.getPhone()))) {
userUpdateCmd.setUsername(zcloudUserUpdateCmd.getPhone());
} else if (userDO != null) {
userUpdateCmd.setUsername(userDO.getUsername());
}
}
if (userDO != null
&& userUpdateCmd.getCorpinfoId() != null
&& !userUpdateCmd.getCorpinfoId().equals(userDO.getCorpinfoId())) {
userUpdateCmd.setCorpinfoName(null);
}
if (userDO != null
&& userUpdateCmd.getDepartmentId() != null
&& !userUpdateCmd.getDepartmentId().equals(userDO.getDepartmentId())) {
userUpdateCmd.setDepartmentName(null);
}
if (userDO != null && !StringUtils.hasText(userUpdateCmd.getDepartmentName())) {
userUpdateCmd.setDepartmentName(userDO.getDepartmentName());
}
if (userDO != null
&& userUpdateCmd.getPostId() != null
&& !userUpdateCmd.getPostId().equals(userDO.getPostId())) {
userUpdateCmd.setPostName(null);
}
if (userDO != null && !StringUtils.hasText(userUpdateCmd.getPostName())) {
userUpdateCmd.setPostName(userDO.getPostName());
}
}
//人员
} }

View File

@ -41,18 +41,9 @@ import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils; 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.*;
import java.util.stream.Collectors; 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;
/** /**
@ -65,11 +56,6 @@ import java.util.Base64;
@AllArgsConstructor @AllArgsConstructor
@Slf4j @Slf4j
public class UserAddExe { public class UserAddExe {
@Data
public static class DaHuaSyncResult {
private Integer dahuaId;
private String dahuaCode;
}
private final UserGateway userGateway; private final UserGateway userGateway;
private final UserExpandInfoGateway userExpandInfoGateway; private final UserExpandInfoGateway userExpandInfoGateway;
private UserCoConvertor userCoConvertor; private UserCoConvertor userCoConvertor;
@ -97,7 +83,6 @@ public class UserAddExe {
private final CodeConfig codeConfig; private final CodeConfig codeConfig;
private final UserExpandInfoRepository userExpandInfoRepository; private final UserExpandInfoRepository userExpandInfoRepository;
private final ImgFilesRepository imgFilesRepository; private final ImgFilesRepository imgFilesRepository;
private final DaHuaConfig daHuaConfig;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean execute(UserAddCmd cmd) { public boolean execute(UserAddCmd cmd) {
@ -105,48 +90,10 @@ public class UserAddExe {
if (!b) { if (!b) {
throw new BizException("请先完善企业信息"); 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(); SSOUser ssoUser = AuthContext.getCurrentUser();
if (ssoUser != null) { Long tenantId = ssoUser.getTenantId();
tenantId = ssoUser.getTenantId();
}
UserE userE = new UserE(); UserE userE = new UserE();
BeanUtils.copyProperties(cmd, 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); userE.initAdd(tenantId, userE);
//校验身份证是否存在 //校验身份证是否存在
List<UserDO> userDOList = userRepository.getByIdCard(userE.getUserIdCard(), null); List<UserDO> userDOList = userRepository.getByIdCard(userE.getUserIdCard(), null);
@ -162,13 +109,7 @@ public class UserAddExe {
userE.checkPhone(userEList); userE.checkPhone(userEList);
} }
if (userE.getCorpinfoId() == null) {
throw new BizException("企业ID不能为空");
}
CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId()); CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId());
if (corpInfoDO == null) {
throw new BizException("企业信息不存在corpinfoId=" + userE.getCorpinfoId());
}
String corpName = null; String corpName = null;
UserEmploymentLogE userEmploymentLogE = new UserEmploymentLogE(); UserEmploymentLogE userEmploymentLogE = new UserEmploymentLogE();
BeanUtils.copyProperties(userE, userEmploymentLogE); BeanUtils.copyProperties(userE, userEmploymentLogE);
@ -184,331 +125,22 @@ public class UserAddExe {
userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType())); userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType()));
userE.resetPassword(corpInfoDO.getType()); userE.resetPassword(corpInfoDO.getType());
// ① 先本地落库 + 底座GBS新增。GBS失败抛 RuntimeException整个事务回滚
res = userGateway.add(userE); res = userGateway.add(userE);
if (corpInfoDO != null && !ObjectUtils.isEmpty(corpInfoDO.getCorpName())) { if (corpInfoDO != null && !ObjectUtils.isEmpty(corpInfoDO.getCorpName())) {
corpName = corpInfoDO.getCorpName(); corpName = corpInfoDO.getCorpName();
} }
userEmploymentLogE.initAdd(userEmploymentLogE, corpName, userE.getId()); userEmploymentLogE.initAdd(userEmploymentLogE, corpName, userE.getId());
try {
userEmploymentLogGateway.add(userEmploymentLogE); userEmploymentLogGateway.add(userEmploymentLogE);
} catch (Exception e) {
log.error("用户就业日志保存失败(不影响用户主体), userId: {}, error: {}", userE.getId(), e.getMessage(), e);
}
try {
addUserChangeRecordForSave(userE, corpInfoDO); addUserChangeRecordForSave(userE, corpInfoDO);
} catch (Exception e) { } 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); throw new RuntimeException(e);
} }
if (!res) { if (!res) {
throw new BizException("保存失败"); throw new BizException("保存失败");
} }
cmd.setId(userE.getId());
cmd.setUsername(userE.getUsername());
log.info("新增用户完成, userId: {}, username: {}", userE.getId(), userE.getUsername());
return true; return true;
} }
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();
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());
personMap.put("departmentId", 1L);
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失败用默认值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 = 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");
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<Map<String, Object>> personBiosignatures = new ArrayList<>();
Map<String, Object> 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("未获取到人脸图片thumbUrlpersonBiosignatures将不传递");
}
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<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> 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<String, Object> dataMap = (Map<String, Object>) 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) @Transactional(rollbackFor = Exception.class)
public boolean executeRegister(AppUserRegisterCmd cmd) { public boolean executeRegister(AppUserRegisterCmd cmd) {
@ -866,63 +498,19 @@ public class UserAddExe {
sendParams.put("code", phoneCode); sendParams.put("code", phoneCode);
messageSendCmd.setParams(sendParams); messageSendCmd.setParams(sendParams);
SingleResponse<Boolean> d = messageFacade.send(messageSendCmd); SingleResponse<Boolean> d = messageFacade.send(messageSendCmd);
log.info("短信发送结果: {}, phone: {}", d, phone); System.out.println( d.toString());
return d == null ? false : d.getData(); return d == null ? false : d.getData();
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean executeXgf(UserXgfAddCmd cmd) { 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(); SSOUser ssoUser = AuthContext.getCurrentUser();
if (ssoUser != null) { Long tenantId = ssoUser.getTenantId();
tenantId = ssoUser.getTenantId();
}
UserE userE = new UserE(); UserE userE = new UserE();
BeanUtils.copyProperties(cmd, 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); userE.initAdd(tenantId, userE);
CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId()); CorpInfoDO corpInfoDO = corpInfoRepository.getById(userE.getCorpinfoId());
if (corpInfoDO == null) {
throw new BizException("企业信息不存在corpinfoId=" + userE.getCorpinfoId());
}
userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType())); userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType()));
Long roleId = userRepository.getDefaultRoleId(); Long roleId = userRepository.getDefaultRoleId();
if (roleId == null) { if (roleId == null) {
@ -995,54 +583,6 @@ public class UserAddExe {
userExpandInfoRepository.updateByPhone(userE.getPhone(),userE.getFlowFlag()); 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 //页面相关保存user_corp
/** /**
@ -1182,3 +722,4 @@ public class UserAddExe {
userChangeRecordGateway.add(userChangeRecordE); userChangeRecordGateway.add(userChangeRecordE);
} }
} }

View File

@ -415,9 +415,9 @@ public class UserImportExe {
user.setPostId(postId); user.setPostId(postId);
user.setCorpinfoId(corpinfoId); user.setCorpinfoId(corpinfoId);
user.setName(importEntity.getName()); user.setName(importEntity.getName());
user.setUserIdCard(encodeBase64IdCard(importEntity.getUserIdCard())); user.setUserIdCard(null);
user.setPersonnelType(importEntity.getPersonnelType()); user.setPersonnelType(importEntity.getPersonnelType());
user.setPersonnelTypeName(importEntity.getPersonnelTypeName()); user.setPersonnelTypeName(importEntity.getPersonnelType());
user.resetPassword(corpInfoDO.getType()); user.resetPassword(corpInfoDO.getType());
userGateway.add(user); userGateway.add(user);
String corpName = null; String corpName = null;
@ -445,4 +445,3 @@ public class UserImportExe {
} }
} }
} }

View File

@ -80,6 +80,9 @@ public class UserAddCmd extends Command {
@ApiModelProperty(value = "人脸头像url", name = "userAvatarUrl") @ApiModelProperty(value = "人脸头像url", name = "userAvatarUrl")
private String userAvatarUrl; private String userAvatarUrl;
@ApiModelProperty(value = "人脸图片文件(取thumbUrl字段作为base64传大华)")
private Object faceFile;
@ApiModelProperty(value = "现住址", name = "currentAddress") @ApiModelProperty(value = "现住址", name = "currentAddress")
private String currentAddress; private String currentAddress;
@ -159,5 +162,10 @@ public class UserAddCmd extends Command {
@ApiModelProperty(value = "是否缴纳其他人身伤害保险") @ApiModelProperty(value = "是否缴纳其他人身伤害保险")
private Integer isBf; private Integer isBf;
} private Integer rzFlag;
@ApiModelProperty(value = "该人员在大华口门对应的id")
private Integer dahuaId;
@ApiModelProperty(value = "该人员在大华口门对应的code")
private String dahuaCode;

View File

@ -194,6 +194,10 @@ public class UserCO extends ClientObject {
@ApiModelProperty(value = "微信openid") @ApiModelProperty(value = "微信openid")
private String openId; private String openId;
@ApiModelProperty(value = "该人员在大华口门对应的id")
private Integer dahuaId;
@ApiModelProperty(value = "该人员在大华口门对应的code")
private String dahuaCode;
@ApiModelProperty(value = "是否特殊工种") @ApiModelProperty(value = "是否特殊工种")
private Integer isSpecialJob; private Integer isSpecialJob;
@ -222,4 +226,3 @@ public class UserCO extends ClientObject {
@ApiModelProperty(value = "是否九公司人员") @ApiModelProperty(value = "是否九公司人员")
private Boolean nineCompanyFlag; private Boolean nineCompanyFlag;
} }

View File

@ -21,6 +21,7 @@ import com.zcloud.gbscommon.utils.Sm2Util;
import com.zcloud.gbscommon.utils.Tools; import com.zcloud.gbscommon.utils.Tools;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -42,6 +43,7 @@ import java.util.stream.Collectors;
* @Date 2025-11-04 14:07:34 * @Date 2025-11-04 14:07:34
*/ */
@Data @Data
@Slf4j
public class UserE extends BaseE { public class UserE extends BaseE {
//GBS用户id //GBS用户id
private Long id; private Long id;
@ -155,6 +157,12 @@ public class UserE extends BaseE {
// 入职状态 // 入职状态
private Integer flowFlag; private Integer flowFlag;
private Integer rzFlag; private Integer rzFlag;
// 人脸图片文件
private Object faceFile;
// 该人员在大华口门对应的id
private Integer dahuaId;
// 该人员在大华口门对应的code
private String dahuaCode;
// 父级租户id // 父级租户id
private final Long parentTenantId = 1989259383784415232L; private final Long parentTenantId = 1989259383784415232L;
// 默认密码 // 默认密码
@ -358,10 +366,19 @@ public class UserE extends BaseE {
if (CollUtil.isEmpty(userEList)) { if (CollUtil.isEmpty(userEList)) {
return; return;
} }
//判断AuthContext.gettenantId在userList中是否存在 //优先用当前对象自身的 corpinfoId否则回退到 AuthContext
Long currentCorpId = this.getCorpinfoId();
if (currentCorpId == null) {
try {
currentCorpId = AuthContext.getTenantId();
} catch (Exception e) {
log.warn("checkPhone: 从 AuthContext 获取 tenantId 失败,无法按企业维度校验手机号");
}
}
if (CollUtil.isNotEmpty(userEList)) { if (CollUtil.isNotEmpty(userEList)) {
//判断是否有当前企业 //判断是否有当前企业
boolean flag = userEList.stream().anyMatch(userE -> userE.getCorpinfoId().equals(AuthContext.getTenantId())); final Long finalCorpId = currentCorpId;
boolean flag = finalCorpId != null && userEList.stream().anyMatch(userE -> finalCorpId.equals(userE.getCorpinfoId()));
if (flag) { if (flag) {
//需要修改,不是提示 //需要修改,不是提示
throw new BizException("当前手机号当前企业已存在,请联系管理员"); throw new BizException("当前手机号当前企业已存在,请联系管理员");
@ -417,4 +434,3 @@ public class UserE extends BaseE {
} }

View File

@ -164,7 +164,10 @@ public class UserDO extends BaseDO {
@ApiModelProperty(value = "微信openid") @ApiModelProperty(value = "微信openid")
private String openId; private String openId;
@ApiModelProperty(value = "该人员在大华口门对应的id")
private Integer dahuaId;
@ApiModelProperty(value = "该人员在大华口门对应的code")
private String dahuaCode;
@ -207,4 +210,3 @@ public class UserDO extends BaseDO {
} }
} }