Merge branch 'dahua_koumen' into main_koumen
# Conflicts: # web-app/src/main/java/com/zcloud/basic/info/command/UserAddExe.javamain_koumen
commit
68b225d42a
|
|
@ -200,5 +200,4 @@ public class UserController {
|
||||||
return userService.getUserIdByPhone(getUserIdByPhoneCmd);
|
return userService.getUserIdByPhone(getUserIdByPhoneCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,7 +206,75 @@ public class UserAddExe {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("用户变更记录保存失败(不影响用户主体), userId: {}, error: {}", userE.getId(), e.getMessage(), e);
|
log.error("用户变更记录保存失败(不影响用户主体), userId: {}, error: {}", userE.getId(), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
runAfterCommitAsync(() -> syncDaHuaPersonAndWriteBack(userE));
|
log.info("========== 新增用户-开始查询大华是否已存在该人员 ==========");
|
||||||
|
Integer queryDahuaId = null;
|
||||||
|
String queryDahuaCode = null;
|
||||||
|
String plainIdCard = userE.getUserIdCard();
|
||||||
|
if (plainIdCard != null && !plainIdCard.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(plainIdCard.trim());
|
||||||
|
plainIdCard = new String(decodedBytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("身份证号非Base64格式,按原值处理: {}", plainIdCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("查询大华入参paperNumber(身份证号明文): {}", plainIdCard);
|
||||||
|
SingleResponse<Map<String, Object>> queryResp = null;
|
||||||
|
try {
|
||||||
|
queryResp = zcloudDaHuaFacade.findPersonByPaperNumber(plainIdCard);
|
||||||
|
log.info("查询大华人员返回完整结果: {}", JSONUtil.toJsonStr(queryResp));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("查询大华人员异常(不影响本地新增), paperNumber={}", plainIdCard, e);
|
||||||
|
}
|
||||||
|
if (queryResp != null && queryResp.isSuccess() && queryResp.getData() != null) {
|
||||||
|
Map<String, Object> dataMap = queryResp.getData();
|
||||||
|
Object idObj = dataMap.get("id");
|
||||||
|
Object codeObj = dataMap.get("code");
|
||||||
|
if (idObj == null && codeObj == null) {
|
||||||
|
Object innerDataObj = dataMap.get("data");
|
||||||
|
if (innerDataObj instanceof Map) {
|
||||||
|
Map<String, Object> innerData = (Map<String, Object>) innerDataObj;
|
||||||
|
idObj = innerData.get("id");
|
||||||
|
codeObj = innerData.get("code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idObj != null) {
|
||||||
|
queryDahuaId = Integer.valueOf(idObj.toString());
|
||||||
|
}
|
||||||
|
if (codeObj != null) {
|
||||||
|
queryDahuaCode = codeObj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (queryDahuaId != null) {
|
||||||
|
userE.setDahuaId(queryDahuaId);
|
||||||
|
userE.setDahuaCode(queryDahuaCode);
|
||||||
|
try {
|
||||||
|
userRepository.updateDahuaInfo(userE.getId(), queryDahuaId, queryDahuaCode);
|
||||||
|
log.info("大华已存在该人员,跳过大华新增,直接写回 dahuaId={}, dahuaCode={}, userId={}", queryDahuaId, queryDahuaCode, userE.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("写回dahuaId/dahuaCode失败(不影响本地数据), userId={}, dahuaId={}, dahuaCode={}",
|
||||||
|
userE.getId(), queryDahuaId, queryDahuaCode, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("大华未找到该人员,开始执行 syncDaHuaPerson 新增大华人员");
|
||||||
|
DaHuaSyncResult syncResult = syncDaHuaPerson(userE);
|
||||||
|
if (syncResult.dahuaId != null) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("大华同步未返回dahuaId,跳过写回操作, userId={}, dahuaCode={}", userE.getId(), syncResult.dahuaCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (BizException e) {
|
} catch (BizException e) {
|
||||||
log.error("新增用户业务异常: {}", e.getMessage(), e);
|
log.error("新增用户业务异常: {}", e.getMessage(), e);
|
||||||
throw e;
|
throw e;
|
||||||
|
|
@ -223,43 +291,6 @@ public class UserAddExe {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAfterCommitAsync(Runnable runnable) {
|
|
||||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
CompletableFuture.runAsync(runnable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CompletableFuture.runAsync(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncDaHuaPersonAndWriteBack(UserE userE) {
|
|
||||||
try {
|
|
||||||
DaHuaSyncResult syncResult = syncDaHuaPerson(userE);
|
|
||||||
if (syncResult.dahuaId != null) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.info("大华同步未返回dahuaId,跳过写回操作, userId={}, dahuaCode={}", userE.getId(), syncResult.dahuaCode);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("异步同步大华异常(不影响新增用户主流程), userId={}", userE.getId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DaHuaSyncResult syncDaHuaPerson(UserE userE) {
|
private DaHuaSyncResult syncDaHuaPerson(UserE userE) {
|
||||||
return syncDaHuaPerson(userE, userE.getFaceFile(), true);
|
return syncDaHuaPerson(userE, userE.getFaceFile(), true);
|
||||||
}
|
}
|
||||||
|
|
@ -460,11 +491,6 @@ public class UserAddExe {
|
||||||
|
|
||||||
DaHuaSyncResult result = new DaHuaSyncResult();
|
DaHuaSyncResult result = new DaHuaSyncResult();
|
||||||
result.setDahuaCode(plainIdCard);
|
result.setDahuaCode(plainIdCard);
|
||||||
if (daHuaConfig.getDockFlag() == null || daHuaConfig.getDockFlag() != 1) {
|
|
||||||
log.info("大华对接未开启(dockFlag={}), 跳过同步人员到大华", daHuaConfig.getDockFlag());
|
|
||||||
userE.setDahuaCode(plainIdCard);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DaHuaPersonSyncCmd cmd = cn.hutool.core.bean.BeanUtil.toBean(personMap, DaHuaPersonSyncCmd.class);
|
DaHuaPersonSyncCmd cmd = cn.hutool.core.bean.BeanUtil.toBean(personMap, DaHuaPersonSyncCmd.class);
|
||||||
Object biosObj = personMap.get("personBiosignatures");
|
Object biosObj = personMap.get("personBiosignatures");
|
||||||
|
|
@ -1237,4 +1263,4 @@ public class UserAddExe {
|
||||||
userChangeRecordE.initUserAdd(userE, corpName, departmentName);
|
userChangeRecordE.initUserAdd(userE, corpName, departmentName);
|
||||||
userChangeRecordGateway.add(userChangeRecordE);
|
userChangeRecordGateway.add(userChangeRecordE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +40,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.Period;
|
import java.time.Period;
|
||||||
|
|
@ -98,7 +103,74 @@ public class UserUpdateExe {
|
||||||
userE.setDahuaCode(userDO.getDahuaCode());
|
userE.setDahuaCode(userDO.getDahuaCode());
|
||||||
log.info("【getInfoById查询结果】dahuaId={}, dahuaCode={}, name={}, userIdCard={}",
|
log.info("【getInfoById查询结果】dahuaId={}, dahuaCode={}, name={}, userIdCard={}",
|
||||||
userDO.getDahuaId(), userDO.getDahuaCode(), userDO.getName(), userDO.getUserIdCard());
|
userDO.getDahuaId(), userDO.getDahuaCode(), userDO.getName(), userDO.getUserIdCard());
|
||||||
syncDaHuaPersonUpdate(userUpdateCmd, userDO);
|
|
||||||
|
String newIdCardForQuery = userUpdateCmd.getUserIdCard() != null ? userUpdateCmd.getUserIdCard() : userDO.getUserIdCard();
|
||||||
|
String plainIdCardForQuery = newIdCardForQuery;
|
||||||
|
if (newIdCardForQuery != null && !newIdCardForQuery.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(newIdCardForQuery.trim());
|
||||||
|
plainIdCardForQuery = new String(decodedBytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("身份证号非Base64格式,按原值处理: {}", newIdCardForQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean dahuaPersonFound = false;
|
||||||
|
log.info("修改前查询大华人员入参paperNumber(身份证号明文): {}", plainIdCardForQuery);
|
||||||
|
Integer queryDahuaId = null;
|
||||||
|
String queryDahuaCode = null;
|
||||||
|
SingleResponse<Map<String, Object>> queryResp = null;
|
||||||
|
try {
|
||||||
|
log.info("调用大华Dubbo接口: zcloudDaHuaFacade.findPersonByPaperNumber, 参数paperNumber={}", plainIdCardForQuery);
|
||||||
|
queryResp = zcloudDaHuaFacade.findPersonByPaperNumber(plainIdCardForQuery);
|
||||||
|
log.info("修改前查询大华人员返回完整结果: {}", JSONUtil.toJsonStr(queryResp));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("修改前查询大华人员异常(不影响本地修改), paperNumber={}", plainIdCardForQuery, e);
|
||||||
|
}
|
||||||
|
if (queryResp != null && queryResp.isSuccess() && queryResp.getData() != null) {
|
||||||
|
Map<String, Object> dataMap = queryResp.getData();
|
||||||
|
log.info("修改前查询大华返回data: {}", dataMap);
|
||||||
|
Object idObj = dataMap.get("id");
|
||||||
|
Object codeObj = dataMap.get("code");
|
||||||
|
if (idObj == null && codeObj == null) {
|
||||||
|
Object innerDataObj = dataMap.get("data");
|
||||||
|
if (innerDataObj instanceof Map) {
|
||||||
|
Map<String, Object> innerData = (Map<String, Object>) innerDataObj;
|
||||||
|
idObj = innerData.get("id");
|
||||||
|
codeObj = innerData.get("code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idObj != null) {
|
||||||
|
queryDahuaId = Integer.valueOf(idObj.toString());
|
||||||
|
}
|
||||||
|
if (codeObj != null) {
|
||||||
|
queryDahuaCode = codeObj.toString();
|
||||||
|
}
|
||||||
|
if (queryDahuaId != null) {
|
||||||
|
dahuaPersonFound = true;
|
||||||
|
userDO.setDahuaId(queryDahuaId);
|
||||||
|
userDO.setDahuaCode(queryDahuaCode);
|
||||||
|
userE.setDahuaId(queryDahuaId);
|
||||||
|
userE.setDahuaCode(queryDahuaCode);
|
||||||
|
log.info("修改前查询大华已存在该人员, dahuaId={}, dahuaCode={}, 走更新逻辑", queryDahuaId, queryDahuaCode);
|
||||||
|
} else {
|
||||||
|
log.info("修改前查询大华返回中无id字段,视为不存在, dataMap={}", dataMap);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("修改前查询大华结果为null或失败,视为不存在, queryResp={}", queryResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dahuaPersonFound) {
|
||||||
|
log.info("修改前查询大华未找到该人员, 走新增逻辑, paperNumber={}", plainIdCardForQuery);
|
||||||
|
Integer addDahuaId = syncDaHuaPersonAdd(userUpdateCmd, userDO, userE);
|
||||||
|
if (addDahuaId != null) {
|
||||||
|
log.info("修改前新增大华人员成功, dahuaId={}, dahuaCode={}", userE.getDahuaId(), userE.getDahuaCode());
|
||||||
|
} else {
|
||||||
|
log.info("修改前新增大华人员未获取到dahuaId/dahuaCode,仍继续本地修改");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
syncDaHuaPersonUpdate(userUpdateCmd, userDO);
|
||||||
|
}
|
||||||
|
|
||||||
UserE oldUserE = new UserE();
|
UserE oldUserE = new UserE();
|
||||||
BeanUtils.copyProperties(userDO, oldUserE);
|
BeanUtils.copyProperties(userDO, oldUserE);
|
||||||
|
|
@ -353,22 +425,34 @@ public class UserUpdateExe {
|
||||||
if (singleObj instanceof Map) {
|
if (singleObj instanceof Map) {
|
||||||
Map<String, Object> faceFileMap = (Map<String, Object>) singleObj;
|
Map<String, Object> faceFileMap = (Map<String, Object>) singleObj;
|
||||||
Object thumbUrlObj = faceFileMap.get("thumbUrl");
|
Object thumbUrlObj = faceFileMap.get("thumbUrl");
|
||||||
if (thumbUrlObj != null) {
|
if (thumbUrlObj != null && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
return thumbUrlObj.toString();
|
return thumbUrlObj.toString();
|
||||||
}
|
}
|
||||||
|
Object urlObj = faceFileMap.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
} else if (singleObj instanceof cn.hutool.json.JSONObject) {
|
} else if (singleObj instanceof cn.hutool.json.JSONObject) {
|
||||||
cn.hutool.json.JSONObject faceJson = (cn.hutool.json.JSONObject) singleObj;
|
cn.hutool.json.JSONObject faceJson = (cn.hutool.json.JSONObject) singleObj;
|
||||||
Object thumbUrlObj = faceJson.get("thumbUrl");
|
Object thumbUrlObj = faceJson.get("thumbUrl");
|
||||||
if (thumbUrlObj != null) {
|
if (thumbUrlObj != null && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
return thumbUrlObj.toString();
|
return thumbUrlObj.toString();
|
||||||
}
|
}
|
||||||
|
Object urlObj = faceJson.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj);
|
cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj);
|
||||||
Object thumbUrlObj = faceJson.get("thumbUrl");
|
Object thumbUrlObj = faceJson.get("thumbUrl");
|
||||||
if (thumbUrlObj != null) {
|
if (thumbUrlObj != null && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
return thumbUrlObj.toString();
|
return thumbUrlObj.toString();
|
||||||
}
|
}
|
||||||
|
Object urlObj = faceJson.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("解析单个图片对象为JSON失败: {}", e.getMessage());
|
log.warn("解析单个图片对象为JSON失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -411,29 +495,33 @@ public class UserUpdateExe {
|
||||||
if (thumbUrl != null && !thumbUrl.trim().isEmpty()) {
|
if (thumbUrl != null && !thumbUrl.trim().isEmpty()) {
|
||||||
String trimmedThumb = thumbUrl.trim();
|
String trimmedThumb = thumbUrl.trim();
|
||||||
boolean startsWithHttp = trimmedThumb.startsWith("http://") || trimmedThumb.startsWith("https://");
|
boolean startsWithHttp = trimmedThumb.startsWith("http://") || trimmedThumb.startsWith("https://");
|
||||||
boolean containsComma = trimmedThumb.contains(",");
|
String cleanBase64 = null;
|
||||||
boolean startsWithDataImage = trimmedThumb.startsWith("data:image");
|
|
||||||
|
|
||||||
String cleanBase64;
|
if (startsWithHttp) {
|
||||||
if (containsComma && startsWithDataImage) {
|
log.info("同步大华人员(更新):人脸为HTTP URL,开始下载转base64, url={}", trimmedThumb);
|
||||||
String prefixPart = trimmedThumb.substring(0, trimmedThumb.indexOf(",") + 1);
|
cleanBase64 = downloadImageToBase64(trimmedThumb);
|
||||||
String bodyPart = trimmedThumb.substring(trimmedThumb.indexOf(",") + 1);
|
log.info("同步大华人员(更新):URL下载转base64完成, base64长度={}", cleanBase64 == null ? 0 : cleanBase64.length());
|
||||||
String cleanBody = bodyPart.replaceAll("\\s+", "");
|
|
||||||
cleanBase64 = prefixPart + cleanBody;
|
|
||||||
} else {
|
} else {
|
||||||
String pureBody = trimmedThumb.replaceAll("\\s+", "");
|
boolean containsComma = trimmedThumb.contains(",");
|
||||||
cleanBase64 = "data:image/jpeg;base64," + pureBody;
|
boolean startsWithDataImage = trimmedThumb.startsWith("data:image");
|
||||||
|
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(",");
|
int commaIdx = cleanBase64.indexOf(",");
|
||||||
String base64BodyOnly = commaIdx >= 0 ? cleanBase64.substring(commaIdx + 1) : cleanBase64;
|
String base64BodyOnly = commaIdx >= 0 ? cleanBase64.substring(commaIdx + 1) : cleanBase64;
|
||||||
boolean isLikelyBase64 = base64BodyOnly.matches("^[A-Za-z0-9+/=]+$");
|
boolean isLikelyBase64 = base64BodyOnly.matches("^[A-Za-z0-9+/=]+$");
|
||||||
int bodyLen = base64BodyOnly.length();
|
int bodyLen = base64BodyOnly.length();
|
||||||
boolean finalHasPrefix = cleanBase64.startsWith("data:image");
|
boolean finalHasPrefix = cleanBase64.startsWith("data:image");
|
||||||
|
|
||||||
if (startsWithHttp) {
|
if (!finalHasPrefix) {
|
||||||
log.error("同步大华人员(更新)失败:人脸图片传的是HTTP/HTTPS URL,要求必须传带data:image前缀的base64图片");
|
|
||||||
throw new BizException("同步大华平台失败:人脸图片不支持URL格式,请上传图片文件");
|
|
||||||
} else if (!finalHasPrefix) {
|
|
||||||
log.error("同步大华人员(更新)失败:人脸图片最终无data:image前缀,代码补齐逻辑失效");
|
log.error("同步大华人员(更新)失败:人脸图片最终无data:image前缀,代码补齐逻辑失效");
|
||||||
throw new BizException("同步大华平台失败:人脸图片格式异常");
|
throw new BizException("同步大华平台失败:人脸图片格式异常");
|
||||||
} else if (!isLikelyBase64) {
|
} else if (!isLikelyBase64) {
|
||||||
|
|
@ -453,11 +541,6 @@ public class UserUpdateExe {
|
||||||
personMap.put("personBiosignatures", personBiosignatures);
|
personMap.put("personBiosignatures", personBiosignatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (daHuaConfig.getDockFlag() == null || daHuaConfig.getDockFlag() != 1) {
|
|
||||||
log.info("大华对接未开启(dockFlag={}), 跳过更新大华人员", daHuaConfig.getDockFlag());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DaHuaPersonSyncCmd cmd = cn.hutool.core.bean.BeanUtil.toBean(personMap, DaHuaPersonSyncCmd.class);
|
DaHuaPersonSyncCmd cmd = cn.hutool.core.bean.BeanUtil.toBean(personMap, DaHuaPersonSyncCmd.class);
|
||||||
Object biosObj = personMap.get("personBiosignatures");
|
Object biosObj = personMap.get("personBiosignatures");
|
||||||
if (biosObj instanceof List) {
|
if (biosObj instanceof List) {
|
||||||
|
|
@ -489,6 +572,255 @@ public class UserUpdateExe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer syncDaHuaPersonAdd(UserUpdateCmd userUpdateCmd, UserDO userDO, UserE userE) {
|
||||||
|
log.info("========== 大华人员(新增)检查开始 ==========");
|
||||||
|
log.info("用户主键userDO.id={}, name={}, userIdCard={}",
|
||||||
|
userDO.getId(), userDO.getName(), userDO.getUserIdCard());
|
||||||
|
|
||||||
|
String newName = userUpdateCmd.getName() != null ? userUpdateCmd.getName() : userDO.getName();
|
||||||
|
String newIdCard = userUpdateCmd.getUserIdCard() != null ? userUpdateCmd.getUserIdCard() : userDO.getUserIdCard();
|
||||||
|
String newPhone = userUpdateCmd.getPhone() != null ? userUpdateCmd.getPhone() : userDO.getPhone();
|
||||||
|
String newNation = userUpdateCmd.getNation() != null ? userUpdateCmd.getNation() : userDO.getNation();
|
||||||
|
String newNationName = userUpdateCmd.getNationName() != null ? userUpdateCmd.getNationName() : userDO.getNationName();
|
||||||
|
Object newFaceFile = userUpdateCmd.getUserImg() != null ? userUpdateCmd.getUserImg() : userUpdateCmd.getFaceFile();
|
||||||
|
Long newCorpId = userUpdateCmd.getCorpinfoId() != null ? userUpdateCmd.getCorpinfoId() : userDO.getCorpinfoId();
|
||||||
|
|
||||||
|
String plainIdCard = newIdCard;
|
||||||
|
if (newIdCard != null && !newIdCard.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(newIdCard.trim());
|
||||||
|
plainIdCard = new String(decodedBytes, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("身份证号非Base64格式,按原值处理: {}", newIdCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> personMap = new HashMap<>();
|
||||||
|
personMap.put("name", newName);
|
||||||
|
personMap.put("code", plainIdCard);
|
||||||
|
personMap.put("paperType", 111);
|
||||||
|
personMap.put("paperNumber", plainIdCard);
|
||||||
|
personMap.put("phone", newPhone);
|
||||||
|
Long realCorpId = newCorpId != null ? newCorpId : 1L;
|
||||||
|
personMap.put("departmentId", realCorpId);
|
||||||
|
personMap.put("departmentType", 2);
|
||||||
|
personMap.put("type", 3);
|
||||||
|
personMap.put("nationName", newNationName != null && !newNationName.trim().isEmpty() ? newNationName : "汉族");
|
||||||
|
try {
|
||||||
|
if (newNation != null && !newNation.trim().isEmpty()) {
|
||||||
|
personMap.put("nation", Integer.parseInt(newNation.trim()));
|
||||||
|
} else {
|
||||||
|
personMap.put("nation", 1);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("民族编码转Integer失败: {}, 用默认值1(汉族)", newNation);
|
||||||
|
personMap.put("nation", 1);
|
||||||
|
}
|
||||||
|
personMap.put("service", "evo-thirdParty");
|
||||||
|
|
||||||
|
Integer age = null;
|
||||||
|
Integer sex = null;
|
||||||
|
if (plainIdCard != null && plainIdCard.length() == 18) {
|
||||||
|
try {
|
||||||
|
String birthStr = plainIdCard.substring(6, 14);
|
||||||
|
LocalDate birthDate = LocalDate.parse(birthStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||||
|
age = Period.between(birthDate, LocalDate.now()).getYears();
|
||||||
|
String sexChar = plainIdCard.substring(16, 17);
|
||||||
|
int sexNum = Integer.parseInt(sexChar);
|
||||||
|
sex = sexNum % 2 == 0 ? 2 : 1;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("从身份证号解析年龄/性别失败, idCard={}", plainIdCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
personMap.put("age", age);
|
||||||
|
personMap.put("sex", sex);
|
||||||
|
|
||||||
|
Object faceFile = newFaceFile;
|
||||||
|
java.util.function.Function<Object, String> extractThumbFromSingle = (singleObj) -> {
|
||||||
|
if (singleObj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (singleObj instanceof Map) {
|
||||||
|
Map<String, Object> faceFileMap = (Map<String, Object>) singleObj;
|
||||||
|
Object thumbUrlObj = faceFileMap.get("thumbUrl");
|
||||||
|
if (thumbUrlObj != null && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
|
return thumbUrlObj.toString();
|
||||||
|
}
|
||||||
|
Object urlObj = faceFileMap.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
|
} 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 && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
|
return thumbUrlObj.toString();
|
||||||
|
}
|
||||||
|
Object urlObj = faceJson.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
cn.hutool.json.JSONObject faceJson = cn.hutool.json.JSONUtil.parseObj(singleObj);
|
||||||
|
Object thumbUrlObj = faceJson.get("thumbUrl");
|
||||||
|
if (thumbUrlObj != null && !thumbUrlObj.toString().trim().isEmpty()) {
|
||||||
|
return thumbUrlObj.toString();
|
||||||
|
}
|
||||||
|
Object urlObj = faceJson.get("url");
|
||||||
|
if (urlObj != null && !urlObj.toString().trim().isEmpty()) {
|
||||||
|
return urlObj.toString().trim().replaceAll("^`+|`+$", "");
|
||||||
|
}
|
||||||
|
} 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://");
|
||||||
|
String cleanBase64 = null;
|
||||||
|
|
||||||
|
if (startsWithHttp) {
|
||||||
|
log.info("同步大华人员(新增):人脸为HTTP URL,开始下载转base64, url={}", trimmedThumb);
|
||||||
|
cleanBase64 = downloadImageToBase64(trimmedThumb);
|
||||||
|
log.info("同步大华人员(新增):URL下载转base64完成, base64长度={}", cleanBase64 == null ? 0 : cleanBase64.length());
|
||||||
|
} else {
|
||||||
|
boolean startsWithDataImage = trimmedThumb.startsWith("data:image");
|
||||||
|
if (trimmedThumb.contains(",") && 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 (finalHasPrefix && isLikelyBase64 && bodyLen >= 1000) {
|
||||||
|
List<DaHuaPersonSyncCmd.PersonBiosignature> personBiosignatures = new ArrayList<>();
|
||||||
|
DaHuaPersonSyncCmd.PersonBiosignature biosignature = new DaHuaPersonSyncCmd.PersonBiosignature();
|
||||||
|
biosignature.setType(3);
|
||||||
|
biosignature.setIndex(1);
|
||||||
|
biosignature.setBase64Data(cleanBase64);
|
||||||
|
personBiosignatures.add(biosignature);
|
||||||
|
personMap.put("personBiosignatures", personBiosignatures);
|
||||||
|
} else {
|
||||||
|
log.warn("同步大华人员(新增):人脸图片校验不通过,放弃传人脸, finalHasPrefix={}, isLikelyBase64={}, bodyLen={}",
|
||||||
|
finalHasPrefix, isLikelyBase64, bodyLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DaHuaPersonSyncCmd cmd = cn.hutool.core.bean.BeanUtil.toBean(personMap, DaHuaPersonSyncCmd.class);
|
||||||
|
Object biosObj = personMap.get("personBiosignatures");
|
||||||
|
if (biosObj instanceof List) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<DaHuaPersonSyncCmd.PersonBiosignature> bioList = (List<DaHuaPersonSyncCmd.PersonBiosignature>) biosObj;
|
||||||
|
cmd.setPersonBiosignatures(bioList);
|
||||||
|
}
|
||||||
|
String maskedIdCard = plainIdCard;
|
||||||
|
if (plainIdCard != null && plainIdCard.length() >= 14) {
|
||||||
|
maskedIdCard = plainIdCard.substring(0, 6) + "********" + plainIdCard.substring(plainIdCard.length() - 4);
|
||||||
|
}
|
||||||
|
log.info("Dubbo调用primeport.zcloudDaHuaFacade.syncPerson(新增), name={}, departmentId(corpId)={}, idCardMask={}",
|
||||||
|
cmd.getName(), cmd.getDepartmentId(), maskedIdCard);
|
||||||
|
if (cmd.getPersonBiosignatures() != null) {
|
||||||
|
log.info("Dubbo调用新增大华人员携带人脸生物特征, size={}", cmd.getPersonBiosignatures().size());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SingleResponse<Object> syncResp = zcloudDaHuaFacade.syncPerson(cmd);
|
||||||
|
if (syncResp == null || !syncResp.isSuccess()) {
|
||||||
|
String errCode = syncResp == null ? "" : syncResp.getErrCode();
|
||||||
|
String errMessage = syncResp == null ? "大华Dubbo返回为空" : syncResp.getErrMessage();
|
||||||
|
if (errMessage == null || errMessage.trim().isEmpty()) errMessage = "大华人员新增失败";
|
||||||
|
log.warn("新增大华人员失败(不影响本地修改), errCode={}, errMessage={}, name={}, idCard={}", errCode, errMessage, newName, maskedIdCard);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
log.info("新增大华人员成功, name={}, idCard={}", newName, maskedIdCard);
|
||||||
|
Object data = syncResp.getData();
|
||||||
|
if (data != null) {
|
||||||
|
Integer dahuaId = null;
|
||||||
|
if (data instanceof Map) {
|
||||||
|
Map<String, Object> dataMap = (Map<String, Object>) data;
|
||||||
|
Object idObj = dataMap.get("personId");
|
||||||
|
if (idObj == null) {
|
||||||
|
idObj = dataMap.get("id");
|
||||||
|
}
|
||||||
|
if (idObj != null) {
|
||||||
|
dahuaId = Integer.valueOf(idObj.toString());
|
||||||
|
}
|
||||||
|
} else if (data instanceof cn.hutool.json.JSONObject) {
|
||||||
|
cn.hutool.json.JSONObject dataJson = (cn.hutool.json.JSONObject) data;
|
||||||
|
Object idObj = dataJson.get("personId");
|
||||||
|
if (idObj == null) {
|
||||||
|
idObj = dataJson.get("id");
|
||||||
|
}
|
||||||
|
if (idObj != null) {
|
||||||
|
dahuaId = Integer.valueOf(idObj.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dahuaId != null) {
|
||||||
|
String dahuaCode = plainIdCard;
|
||||||
|
userRepository.updateDahuaInfo(userDO.getId(), dahuaId, dahuaCode);
|
||||||
|
userDO.setDahuaId(dahuaId);
|
||||||
|
userDO.setDahuaCode(dahuaCode);
|
||||||
|
userE.setDahuaId(dahuaId);
|
||||||
|
userE.setDahuaCode(dahuaCode);
|
||||||
|
log.info("新增大华人员后写回dahuaId/dahuaCode成功, userId={}, dahuaId={}, dahuaCode={}",
|
||||||
|
userDO.getId(), dahuaId, dahuaCode);
|
||||||
|
return dahuaId;
|
||||||
|
} else {
|
||||||
|
log.warn("新增大华人员成功但未获取到dahuaId, data={}", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("新增大华人员异常(不影响本地修改), name={}, idCard={}", newName, maskedIdCard, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void editData(UserQryCmd userUpdateCmd) {
|
public void editData(UserQryCmd userUpdateCmd) {
|
||||||
List<UserDO> userDOList = new ArrayList<>();
|
List<UserDO> userDOList = new ArrayList<>();
|
||||||
|
|
@ -1115,4 +1447,69 @@ public class UserUpdateExe {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String downloadImageToBase64(String urlStr) {
|
||||||
|
if (urlStr == null || urlStr.trim().isEmpty()) {
|
||||||
|
throw new BizException("同步大华平台失败:人脸图片URL为空");
|
||||||
|
}
|
||||||
|
String cleanUrl = urlStr.trim().replaceAll("^`+|`+$", "");
|
||||||
|
HttpURLConnection conn = null;
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
URL url = new URL(cleanUrl);
|
||||||
|
conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setConnectTimeout(15000);
|
||||||
|
conn.setReadTimeout(30000);
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setRequestProperty("User-Agent", "Mozilla/5.0 ZCloudGBS");
|
||||||
|
int respCode = conn.getResponseCode();
|
||||||
|
if (respCode < 200 || respCode >= 300) {
|
||||||
|
log.error("下载人脸图片失败, HTTP状态码={}, url={}", respCode, cleanUrl);
|
||||||
|
throw new BizException("同步大华平台失败:人脸图片下载失败(HTTP " + respCode + ")");
|
||||||
|
}
|
||||||
|
String contentType = conn.getContentType();
|
||||||
|
in = conn.getInputStream();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) != -1) {
|
||||||
|
baos.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
byte[] imgBytes = baos.toByteArray();
|
||||||
|
if (imgBytes == null || imgBytes.length < 1000) {
|
||||||
|
log.error("下载人脸图片失败, 文件过小({}字节), url={}", imgBytes == null ? 0 : imgBytes.length, cleanUrl);
|
||||||
|
throw new BizException("同步大华平台失败:人脸图片下载不完整");
|
||||||
|
}
|
||||||
|
String imgType = "jpeg";
|
||||||
|
if (contentType != null) {
|
||||||
|
String ct = contentType.toLowerCase();
|
||||||
|
if (ct.contains("image/png")) {
|
||||||
|
imgType = "png";
|
||||||
|
} else if (ct.contains("image/jpg") || ct.contains("image/jpeg")) {
|
||||||
|
imgType = "jpeg";
|
||||||
|
} else if (ct.contains("image/gif")) {
|
||||||
|
imgType = "gif";
|
||||||
|
} else if (ct.contains("image/webp")) {
|
||||||
|
imgType = "webp";
|
||||||
|
} else if (ct.contains("image/bmp")) {
|
||||||
|
imgType = "bmp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String base64Body = Base64.getEncoder().encodeToString(imgBytes);
|
||||||
|
return "data:image/" + imgType + ";base64," + base64Body;
|
||||||
|
} catch (BizException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("下载人脸图片异常, url={}", cleanUrl, e);
|
||||||
|
throw new BizException("同步大华平台失败:人脸图片下载异常(" + e.getMessage() + ")");
|
||||||
|
} finally {
|
||||||
|
if (in != null) {
|
||||||
|
try { in.close(); } catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
try { conn.disconnect(); } catch (Exception ignore) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import com.zcloud.basic.info.dto.clientobject.*;
|
||||||
import com.zcloud.basic.info.persistence.dataobject.UserImgDO;
|
import com.zcloud.basic.info.persistence.dataobject.UserImgDO;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
@ -41,6 +42,7 @@ public class UserServiceImpl implements UserServiceI {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public SingleResponse<UserCO> add(UserAddCmd cmd) {
|
public SingleResponse<UserCO> add(UserAddCmd cmd) {
|
||||||
|
|
||||||
userAddExe.execute(cmd);
|
userAddExe.execute(cmd);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue