大华口门对接

komen
zhangxiongfeng 2026-07-28 10:33:46 +08:00
parent 2cc429996f
commit 65ad2b538e
13 changed files with 574 additions and 34 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

@ -1,10 +1,10 @@
spring:
config:
import:
# - classpath:nacos.yml
# - classpath:sdk.yml
- classpath:nacos-prod.yml
- classpath:sdk-prod.yml
- classpath:nacos.yml
- classpath:sdk.yml
# - classpath:nacos-prod.yml
# - classpath:sdk-prod.yml
# - classpath:nacos-prod2.yml
# - classpath:sdk-prod2.yml
- classpath:swagger.yml
- classpath:swagger.yml

View File

@ -35,4 +35,4 @@ spring:
- config-flyway.yml
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
namespace: ${spring.cloud.nacos.config.namespace}

View File

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

View File

@ -175,8 +175,13 @@ public class ZcloudUserFacadeImpl implements ZcloudUserFacade {
&& !userUpdateCmd.getCorpinfoId().equals(userDO.getCorpinfoId())) {
userUpdateCmd.setCorpinfoName(null);
}
if (StringUtils.hasText(zcloudUserUpdateCmd.getDepartmentName())) {
userUpdateCmd.setDepartmentName(zcloudUserUpdateCmd.getDepartmentName());
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
@ -193,4 +198,4 @@ public class ZcloudUserFacadeImpl implements ZcloudUserFacade {
}
}

View File

@ -21,6 +21,7 @@ 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;
@ -39,9 +40,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;
/**
@ -81,17 +91,66 @@ 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: {}, 请求体 JSON: {}", cmd.getName(), cmd.getCorpinfoId(), cmd.getPhone(), JSONUtil.toJsonStr(cmd));
try {
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
HttpServletRequest req = attrs.getRequest();
Enumeration<String> headerNames = req.getHeaderNames();
Map<String, String> headerMap = new HashMap<>();
while (headerNames.hasMoreElements()) {
String h = headerNames.nextElement();
headerMap.put(h, req.getHeader(h));
}
log.info("========== 请求头信息: {}", JSONUtil.toJsonStr(headerMap));
String orgidHeader = req.getHeader("orgid");
log.info("========== 从请求头获取 orgid: {}", orgidHeader);
if (orgidHeader != null && cmd.getCorpinfoId() == null) {
try {
cmd.setCorpinfoId(Long.parseLong(orgidHeader.trim()));
log.info("========== 用请求头 orgid 设置 cmd.corpinfoId: {}", cmd.getCorpinfoId());
} 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<UserDO> userDOList = userRepository.getByIdCard(userE.getUserIdCard(), null);
@ -107,7 +166,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);
@ -120,25 +185,362 @@ public class UserAddExe {
userE.setRoleId(roleId);
}
try {
log.info("========== 企业类型 type: {}", corpInfoDO.getType());
userE.setUserType(CorpTypeEnum.getUserTypeByCode(corpInfoDO.getType()));
userE.resetPassword(corpInfoDO.getType());
//先同步大华人脸平台,成功后再保存本地
log.info("========== 开始调用大华同步接口");
syncDaHuaPerson(userE);
log.info("========== 大华同步成功,开始保存本地用户");
res = userGateway.add(userE);
log.info("========== 本地保存结果: {}", res);
if (corpInfoDO != null && !ObjectUtils.isEmpty(corpInfoDO.getCorpName())) {
corpName = corpInfoDO.getCorpName();
}
userEmploymentLogE.initAdd(userEmploymentLogE, corpName, userE.getId());
userEmploymentLogGateway.add(userEmploymentLogE);
addUserChangeRecordForSave(userE, corpInfoDO);
log.info("========== 用户就业日志保存成功");
} 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("保存失败");
}
log.info("========== 新增用户完成");
return true;
}
private void syncDaHuaPerson(UserE userE) {
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);
personMap.put("nation", 1);
personMap.put("nationName", "汉族");
personMap.put("service", "evo-thirdParty");
Object faceFile = userE.getFaceFile();
log.info("========== 大华图片调试开始 ==========");
log.info("faceFile原始类型: {}, faceFile原始值(截取前500字符): {}",
faceFile == null ? "null" : faceFile.getClass().getName(),
faceFile == null ? "null" : (faceFile.toString().length() > 500 ? faceFile.toString().substring(0, 500) + "..." : faceFile.toString()));
log.info("userE.getUserAvatarUrl()值(截取前500字符): {}",
userE.getUserAvatarUrl() == null ? "null" : (userE.getUserAvatarUrl().length() > 500 ? userE.getUserAvatarUrl().substring(0, 500) + "..." : userE.getUserAvatarUrl()));
java.util.function.Function<Object, String> extractThumbFromSingle = (singleObj) -> {
if (singleObj == null) {
log.debug("单个图片对象为null");
return null;
}
log.info("从单个对象提取thumbUrl对象类型: {}", singleObj.getClass().getName());
if (singleObj instanceof Map) {
Map<String, Object> faceFileMap = (Map<String, Object>) singleObj;
log.info("单个对象是Map类型所有key: {}", faceFileMap.keySet());
for (Map.Entry<String, Object> entry : faceFileMap.entrySet()) {
Object v = entry.getValue();
String vStr = v == null ? "null" : (v.toString().length() > 200 ? v.toString().substring(0, 200) + "..." : v.toString());
log.info("单个对象 key={}, value类型={}, value={}", entry.getKey(), v == null ? "null" : v.getClass().getName(), vStr);
}
Object thumbUrlObj = faceFileMap.get("thumbUrl");
if (thumbUrlObj != null) {
String res = thumbUrlObj.toString();
log.info("从Map提取到thumbUrl长度={}", res.length());
return res;
}
} else if (singleObj instanceof cn.hutool.json.JSONObject) {
cn.hutool.json.JSONObject faceJson = (cn.hutool.json.JSONObject) singleObj;
log.info("单个对象是JSONObject类型所有key: {}", faceJson.keySet());
for (String key : faceJson.keySet()) {
Object v = faceJson.get(key);
String vStr = v == null ? "null" : (v.toString().length() > 200 ? v.toString().substring(0, 200) + "..." : v.toString());
log.info("单个对象 key={}, value类型={}, value={}", key, v == null ? "null" : v.getClass().getName(), vStr);
}
Object thumbUrlObj = faceJson.get("thumbUrl");
if (thumbUrlObj != null) {
String res = thumbUrlObj.toString();
log.info("从JSONObject提取到thumbUrl长度={}", res.length());
return res;
}
} else {
try {
cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj);
log.info("单个对象解析为JSONObject后所有key: {}", faceJson.keySet());
for (String key : faceJson.keySet()) {
Object v = faceJson.get(key);
String vStr = v == null ? "null" : (v.toString().length() > 200 ? v.toString().substring(0, 200) + "..." : v.toString());
log.info("单个对象 key={}, value类型={}, value={}", key, v == null ? "null" : v.getClass().getName(), vStr);
}
Object thumbUrlObj = faceJson.get("thumbUrl");
if (thumbUrlObj != null) {
String res = thumbUrlObj.toString();
log.info("从解析后的JSONObject提取到thumbUrl长度={}", res.length());
return res;
}
} catch (Exception e) {
log.warn("解析单个图片对象为JSON失败: {}, 错误: {}", singleObj, e.getMessage());
}
}
log.warn("单个对象中未找到thumbUrl字段");
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;
log.info("faceFile是Collection/List类型size={}", coll.size());
if (!coll.isEmpty()) {
firstElement = coll.iterator().next();
log.info("取Collection第一个元素类型: {}", firstElement == null ? "null" : firstElement.getClass().getName());
} else {
log.warn("faceFile是空数组无元素可取");
}
} else if (faceFile.getClass().isArray()) {
isArray = true;
Object[] arr = (Object[]) faceFile;
log.info("faceFile是原生数组类型length={}", arr.length);
if (arr.length > 0) {
firstElement = arr[0];
log.info("取数组第0个元素类型: {}", firstElement == null ? "null" : firstElement.getClass().getName());
} else {
log.warn("faceFile是空原生数组无元素可取");
}
} else if (faceFile instanceof cn.hutool.json.JSONArray) {
isArray = true;
cn.hutool.json.JSONArray jsonArr = (cn.hutool.json.JSONArray) faceFile;
log.info("faceFile是JSONArray类型size={}", jsonArr.size());
if (!jsonArr.isEmpty()) {
firstElement = jsonArr.get(0);
log.info("取JSONArray第0个元素类型: {}", firstElement == null ? "null" : firstElement.getClass().getName());
} else {
log.warn("faceFile是空JSONArray无元素可取");
}
}
if (isArray) {
log.info("faceFile是数组/集合类型从第一个元素中提取thumbUrl");
if (firstElement != null) {
thumbUrl = extractThumbFromSingle.apply(firstElement);
} else {
log.warn("数组第一个元素是null无法提取thumbUrl");
}
} else {
log.info("faceFile是单个对象类型直接提取thumbUrl");
thumbUrl = extractThumbFromSingle.apply(faceFile);
}
}
log.info("从faceFile提取thumbUrl结果(截取前500字符): {}",
thumbUrl == null ? "null" : (thumbUrl.length() > 500 ? thumbUrl.substring(0, 500) + "..." : thumbUrl));
if (thumbUrl != null && !thumbUrl.trim().isEmpty()) {
String trimmedThumb = thumbUrl.trim();
boolean startsWithDataImage = trimmedThumb.startsWith("data:image");
boolean startsWithHttp = trimmedThumb.startsWith("http://") || trimmedThumb.startsWith("https://");
boolean containsComma = trimmedThumb.contains(",");
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;
log.info("已保留data:image前缀(严格对齐大华文档示例), 前缀={}, 仅清洗主体中的空白字符", prefixPart.length() > 80 ? prefixPart.substring(0, 80) + "..." : prefixPart);
} else {
String pureBody = trimmedThumb.replaceAll("\\s+", "");
cleanBase64 = "data:image/jpeg;base64," + pureBody;
log.info("原始thumbUrl无标准data:image前缀已自动补齐(data:image/jpeg;base64,)以对齐大华文档示例");
}
int commaIdx = cleanBase64.indexOf(",");
String base64BodyOnly = commaIdx >= 0 ? cleanBase64.substring(commaIdx + 1) : cleanBase64;
boolean isLikelyBase64 = base64BodyOnly.matches("^[A-Za-z0-9+/=]+$");
int totalLen = cleanBase64.length();
int bodyLen = base64BodyOnly.length();
boolean finalHasPrefix = cleanBase64.startsWith("data:image");
log.info("========== 图片格式判断结果(严格对齐大华文档示例) ==========");
log.info("原始thumbUrl总长度: {}", trimmedThumb.length());
log.info("最终结果是否带data:image前缀(文档示例:true): {}", finalHasPrefix);
log.info("是否是http/https URL(文档要求:false): {}", startsWithHttp);
log.info("最终整体长度(含前缀): {}", totalLen);
log.info("base64主体长度(去掉前缀逗号后): {}", bodyLen);
log.info("base64主体是否匹配纯base64字符集(A-Za-z0-9+/=): {}", isLikelyBase64);
if (startsWithHttp) {
log.error("===== 严重错误传的是HTTP/HTTPS URL(或文件路径)大华文档personBiosignatures要求必须传【带data:image前缀的base64图片】不是URL =====");
System.out.println("===== 大华图片错误HTTP URL无效, 前200: " + (trimmedThumb.length() > 200 ? trimmedThumb.substring(0, 200) : trimmedThumb));
} else if (!finalHasPrefix) {
log.error("===== 最终结果竟然没有data:image前缀(代码补齐逻辑失效) =====");
System.out.println("===== 大华图片错误无data:image前缀, 前100: " + (cleanBase64.length() > 100 ? cleanBase64.substring(0, 100) : cleanBase64));
} else if (!isLikelyBase64) {
log.error("===== base64主体部分含非base64非法字符前200: {}", base64BodyOnly.length() > 200 ? base64BodyOnly.substring(0, 200) : base64BodyOnly);
System.out.println("===== 大华图片错误主体非base64字符, 前200: " + (base64BodyOnly.length() > 200 ? base64BodyOnly.substring(0, 200) : base64BodyOnly));
} else if (bodyLen < 1000) {
log.error("===== base64主体长度过短({}),缩略图太小/损坏正常300KB JPEG→主体约40万字符 =====", bodyLen);
System.out.println("===== 大华图片错误:主体过短=" + bodyLen);
} else {
log.info("===== ✅ 完全符合大华文档personBiosignatures格式带data:image前缀 + 纯base64主体 + 长度正常 =====");
}
log.info("【最终传给大华base64Data(严格对齐文档)】前150字符: {}", cleanBase64.length() > 150 ? cleanBase64.substring(0, 150) : cleanBase64);
log.info("【最终传给大华base64Data】后150字符: {}", cleanBase64.length() > 150 ? cleanBase64.substring(cleanBase64.length() - 150) : cleanBase64);
System.out.println("===== 最终带前缀完整base64Data 前200: " + (cleanBase64.length() > 200 ? cleanBase64.substring(0, 200) : cleanBase64));
System.out.println("===== 最终带前缀完整base64Data 后200: " + (cleanBase64.length() > 200 ? cleanBase64.substring(cleanBase64.length() - 200) : cleanBase64));
System.out.println("===== 是否按文档带data:image前缀=" + finalHasPrefix + " / 最终总长度=" + totalLen + " / 主体长度=" + 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);
log.info("组装personBiosignatures成功(严格按文档顺序type→index→base64Data,LinkedHashMap有序)");
String biosigJson = JSONUtil.toJsonStr(personBiosignatures);
log.info("personBiosignatures JSON(截取前500字符): {}", biosigJson.length() > 500 ? biosigJson.substring(0, 500) + "..." : biosigJson);
System.out.println("===== 传给大华personBiosignatures完整JSON前500字符: " + (biosigJson.length() > 500 ? biosigJson.substring(0, 500) + "..." : biosigJson));
} else {
log.warn("未获取到人脸图片thumbUrlpersonBiosignatures将不传递");
System.out.println("===== 警告未从faceFile中获取到有效的thumbUrlpersonBiosignatures不会传给大华");
}
log.info("========== 大华图片调试结束 ==========");
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);
}
log.info("收集到需要透传的请求头: {}", extraHeaders);
}
} 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");
if (skipped != null && skipped) {
return;
}
if (success == null || !success) {
String errCode = (String) syncResult.getOrDefault("errCode", "");
String errMessage = (String) syncResult.getOrDefault("errMessage", "大华人员同步失败");
log.error("同步大华人员失败, errCode={}, errMessage={}, name={}, idCard={}", errCode, errMessage, userE.getName(), plainIdCard);
throw new BizException("同步大华平台失败:" + errMessage);
}
log.info("同步大华人员成功, name={}, idCard={}", userE.getName(), plainIdCard);
System.out.println("同步大华人员成功, name=" + userE.getName() + ", idCard=" + plainIdCard);
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());
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());
userE.setDahuaId(dahuaId);
log.info("设置大华dahuaId成功, dahuaId={}", dahuaId);
} catch (Exception e) {
log.warn("转换大华返回id为Integer失败, idObj={}", idObj);
}
}
}
}
userE.setDahuaCode(plainIdCard);
log.info("设置大华dahuaCode成功, dahuaCode={}", plainIdCard);
}
@Transactional(rollbackFor = Exception.class)
public boolean executeRegister(AppUserRegisterCmd cmd) {
@ -719,5 +1121,4 @@ public class UserAddExe {
userChangeRecordE.initUserAdd(userE, corpName, departmentName);
userChangeRecordGateway.add(userChangeRecordE);
}
}
}

View File

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

View File

@ -80,6 +80,9 @@ public class UserAddCmd extends Command {
@ApiModelProperty(value = "人脸头像url", name = "userAvatarUrl")
private String userAvatarUrl;
@ApiModelProperty(value = "人脸图片文件(取thumbUrl字段作为base64传大华)")
private Object faceFile;
@ApiModelProperty(value = "现住址", name = "currentAddress")
private String currentAddress;
@ -161,5 +164,9 @@ public class UserAddCmd extends Command {
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")
private String openId;
@ApiModelProperty(value = "该人员在大华口门对应的id")
private Integer dahuaId;
@ApiModelProperty(value = "该人员在大华口门对应的code")
private String dahuaCode;
@ApiModelProperty(value = "是否特殊工种")
private Integer isSpecialJob;
@ -221,5 +225,4 @@ public class UserCO extends ClientObject {
@ApiModelProperty(value = "是否九公司人员")
private Boolean nineCompanyFlag;
}
}

View File

@ -1,13 +1,18 @@
package com.zcloud.basic.info.domain.config;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zcloud.gbscommon.dahuaDevice.DaHuaDeviceCommon;
import com.zcloud.gbscommon.dahuaDevice.DhuaConfig;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhangyue
*
@ -17,6 +22,7 @@ import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "dahua.config")
@Data
@Slf4j
public class DaHuaConfig {
private Integer dockFlag;
private String prefix;
@ -24,6 +30,8 @@ public class DaHuaConfig {
private String password;
private String clientId;
private String clientSecret;
private String primeportUrl;
private String primeportGateway = "basicInfo";
protected DaHuaDeviceCommon getDaHuaDeviceCommon() {
return new DaHuaDeviceCommon(new DhuaConfig(dockFlag, prefix, username, password, clientId, clientSecret));
@ -35,4 +43,72 @@ public class DaHuaConfig {
public JSONObject uploadPersonAvatar(String base64ImgStr) throws Exception {
return getDaHuaDeviceCommon().uploadPersonAvatar(base64ImgStr);
}
}
public Map<String, Object> syncPerson(Map<String, Object> personMap) {
return syncPerson(personMap, null);
}
public Map<String, Object> syncPerson(Map<String, Object> personMap, Map<String, String> extraHeaders) {
Map<String, Object> resultMap = new HashMap<>();
if (dockFlag == null || dockFlag != 1) {
log.info("大华对接未开启(dockFlag={}), 跳过同步人员到大华", dockFlag);
resultMap.put("success", true);
resultMap.put("skipped", true);
return resultMap;
}
String baseUrl = primeportUrl;
if (baseUrl == null || baseUrl.trim().isEmpty()) {
throw new RuntimeException("大华对接网关地址未配置(dahua.config.primeportUrl)");
}
baseUrl = baseUrl.trim();
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
String gw = (primeportGateway == null || primeportGateway.trim().isEmpty()) ? "basicInfo" : primeportGateway.trim();
String url = baseUrl + "/" + gw + "/dahua/person/sync";
String reqJson = JSONUtil.toJsonStr(personMap);
log.info("调用大华人员同步接口, url={}, 请求参数={}", url, reqJson);
System.out.println("调用大华人员同步接口, url=" + url);
System.out.println("请求参数: " + reqJson);
try {
HttpRequest request = HttpRequest.post(url)
.header("Content-Type", "application/json;charset=UTF-8")
.timeout(30000)
.body(reqJson);
if (extraHeaders != null && !extraHeaders.isEmpty()) {
extraHeaders.forEach((k, v) -> {
if (k != null && v != null) {
request.header(k, v);
System.out.println("透传请求头: " + k + "=" + v);
}
});
log.info("透传请求头: {}", extraHeaders);
}
String respBody = request.execute().body();
log.info("大华人员同步接口返回: {}", respBody);
System.out.println("大华人员同步接口返回: " + respBody);
if (respBody == null || respBody.trim().isEmpty()) {
resultMap.put("success", false);
resultMap.put("errCode", "EMPTY_RESPONSE");
resultMap.put("errMessage", "大华人员同步接口返回为空");
return resultMap;
}
JSONObject respJson = JSONUtil.parseObj(respBody);
Boolean success = respJson.getBool("success", false);
String errCode = respJson.getStr("errCode", "");
String errMessage = respJson.getStr("errMessage", "");
Object data = respJson.get("data");
resultMap.put("success", success);
resultMap.put("errCode", errCode);
resultMap.put("errMessage", errMessage);
resultMap.put("data", data);
return resultMap;
} catch (Exception e) {
log.error("调用大华人员同步接口异常: {}", e.getMessage(), e);
resultMap.put("success", false);
resultMap.put("errCode", "HTTP_EXCEPTION");
resultMap.put("errMessage", "调用大华人员同步接口异常: " + e.getMessage());
return resultMap;
}
}
}

View File

@ -15,6 +15,7 @@ import com.zcloud.gbscommon.utils.MD5;
import com.zcloud.gbscommon.utils.Sm2Util;
import com.zcloud.gbscommon.utils.Tools;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.ObjectUtils;
@ -32,6 +33,7 @@ import java.util.stream.Collectors;
* @Date 2025-11-04 14:07:34
*/
@Data
@Slf4j
public class UserE extends BaseE {
//GBS用户id
private Long id;
@ -145,6 +147,12 @@ public class UserE extends BaseE {
// 入职状态
private Integer flowFlag;
private Integer rzFlag;
// 人脸图片文件
private Object faceFile;
// 该人员在大华口门对应的id
private Integer dahuaId;
// 该人员在大华口门对应的code
private String dahuaCode;
// 父级租户id
private final Long parentTenantId = 1989259383784415232L;
// 默认密码
@ -348,10 +356,19 @@ public class UserE extends BaseE {
if (CollUtil.isEmpty(userEList)) {
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)) {
//判断是否有当前企业
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) {
//需要修改,不是提示
throw new BizException("当前手机号当前企业已存在,请联系管理员");
@ -406,5 +423,4 @@ public class UserE extends BaseE {
}
}
}

View File

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