From 62883433784e874fdd4bf56f6c76a93259a84c1f Mon Sep 17 00:00:00 2001 From: lishiwei <3230787218@qq.com> Date: Wed, 26 Aug 2026 13:37:12 +0800 Subject: [PATCH] =?UTF-8?q?feat():=20=E4=BC=98=E5=8C=96=E8=BD=A6=E8=BE=86?= =?UTF-8?q?=E5=92=8C=E4=BA=BA=E5=91=98=E9=97=A8=E7=A6=81=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../close/ClosedAreaCarApplyController.java | 14 +- .../ClosedAreaPersonApplyController.java | 14 +- .../command/ClosedAreaPersonApplyAddExe.java | 30 ++- .../domain/model/DockSyncNoticeHolder.java | 30 +++ .../mapper/ClosedAreaCarApplyMapper.java | 7 +- .../mapper/ClosedAreaPersonApplyMapper.java | 7 +- .../plan/mjDevice/OneLevelCarUtil.java | 16 +- .../primeport/plan/mjDevice/TwoCarUtil.java | 81 ++++-- .../plan/mjDevice/TwoPersonUtil.java | 232 ++++++++++++++++-- .../mapper/ClosedAreaCarApplyMapper.xml | 87 +++---- .../mapper/ClosedAreaPersonApplyMapper.xml | 38 +-- 11 files changed, 410 insertions(+), 146 deletions(-) create mode 100644 web-domain/src/main/java/com/zcloud/primeport/domain/model/DockSyncNoticeHolder.java diff --git a/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaCarApplyController.java b/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaCarApplyController.java index 09f1cd4..f02092a 100644 --- a/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaCarApplyController.java +++ b/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaCarApplyController.java @@ -11,6 +11,7 @@ import com.zcloud.primeport.dto.ClosedAreaCarApplyAuditCmd; import com.zcloud.primeport.dto.ClosedAreaCarApplyPageQry; import com.zcloud.primeport.dto.ClosedAreaCarApplyScreenPageQry; import com.zcloud.primeport.dto.ClosedAreaCarApplyUpdateCmd; +import com.zcloud.primeport.domain.model.DockSyncNoticeHolder; import com.zcloud.primeport.dto.clientobject.ClosedAreaCarApplyCO; import com.zcloud.primeport.dto.clientobject.ClosedAreaCarApplyScreenCO; import io.swagger.annotations.Api; @@ -64,8 +65,17 @@ public class ClosedAreaCarApplyController { if (!(auditCmd.getAuditFlag().equals(2) || auditCmd.getAuditFlag().equals(3))) { throw new BizException("只能是通过或者不通过"); } - closedAreaCarApplyService.auditCarApply(auditCmd); - return Response.buildSuccess(); + try { + closedAreaCarApplyService.auditCarApply(auditCmd); + String notice = DockSyncNoticeHolder.getAndClear(); + if (notice != null) { + return SingleResponse.of(notice); + } + return Response.buildSuccess(); + } catch (RuntimeException e) { + DockSyncNoticeHolder.clear(); + throw e; + } } @ApiOperation("可视化大屏-车申请记录") diff --git a/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaPersonApplyController.java b/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaPersonApplyController.java index f9ea3f9..274825c 100644 --- a/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaPersonApplyController.java +++ b/web-adapter/src/main/java/com/zcloud/primeport/web/close/ClosedAreaPersonApplyController.java @@ -13,6 +13,7 @@ import com.zcloud.primeport.dto.clientobject.ClosedAreaPersonApplyCO; import com.zcloud.primeport.dto.clientobject.ClosedAreaPersonApplyDeptCO; import com.zcloud.primeport.dto.clientobject.ClosedAreaPersonApplyScreenCO; import com.zcloud.primeport.dto.clientobject.UserCo; +import com.zcloud.primeport.domain.model.DockSyncNoticeHolder; import com.zcloud.primeport.persistence.dataobject.UserDto; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -54,8 +55,17 @@ public class ClosedAreaPersonApplyController { if (!(auditCmd.getAuditFlag().equals(2) || auditCmd.getAuditFlag().equals(3))) { throw new BizException("只能是通过或者不通过"); } - closedAreaPersonApplyService.auditPersonApply(auditCmd); - return Response.buildSuccess(); + try { + closedAreaPersonApplyService.auditPersonApply(auditCmd); + String notice = DockSyncNoticeHolder.getAndClear(); + if (notice != null) { + return SingleResponse.of(notice); + } + return Response.buildSuccess(); + } catch (RuntimeException e) { + DockSyncNoticeHolder.clear(); + throw e; + } } @ApiOperation("获取本单位人员信息数据,用于随行人员,企业人员权限列表") diff --git a/web-app/src/main/java/com/zcloud/primeport/command/ClosedAreaPersonApplyAddExe.java b/web-app/src/main/java/com/zcloud/primeport/command/ClosedAreaPersonApplyAddExe.java index 7e88f7b..00e84c4 100644 --- a/web-app/src/main/java/com/zcloud/primeport/command/ClosedAreaPersonApplyAddExe.java +++ b/web-app/src/main/java/com/zcloud/primeport/command/ClosedAreaPersonApplyAddExe.java @@ -5,6 +5,7 @@ import com.jjb.saas.framework.auth.model.SSOUser; import com.jjb.saas.framework.auth.utils.AuthContext; import com.zcloud.gbscommon.todolistmq.TodoListEventPusherUtil; import com.zcloud.gbscommon.todolistmq.event.TodoListAddEvent; +import com.zcloud.gbscommon.utils.Tools; import com.zcloud.gbscommon.zclouduser.facade.ZcloudUserFacade; import com.zcloud.gbscommon.zclouduser.response.ZcloudUserCo; import com.zcloud.primeport.domain.config.MessageTemplateConfig; @@ -46,19 +47,21 @@ public class ClosedAreaPersonApplyAddExe { closedAreaPersonApplyE.setAuditFlag(1); if (!cmd.getPersonBelongType().equals(4)) { SingleResponse infoByUserId = zcloudUserFacade.getInfoByUserId(AuthContext.getUserId()); - ZcloudUserCo userData = infoByUserId.getData(); + ZcloudUserCo userData = infoByUserId == null ? null : infoByUserId.getData(); SSOUser currentUser = AuthContext.getCurrentUser(); closedAreaPersonApplyE.setApplyPersonCorpId(currentUser.getTenantId()); closedAreaPersonApplyE.setApplyPersonCorpName(currentUser.getTenantName()); closedAreaPersonApplyE.setApplyPersonDepartmentId(currentUser.getOrgId()); closedAreaPersonApplyE.setApplyPersonDepartmentName(currentUser.getOrgName()); closedAreaPersonApplyE.setApplyPersonUserId(currentUser.getUserId()); - closedAreaPersonApplyE.setApplyPersonUserName(userData.getName()); - closedAreaPersonApplyE.setUserPhone(userData.getPhone()); - closedAreaPersonApplyE.setUserCard(userData.getUserIdCard()); - closedAreaPersonApplyE.setUserFaceUrl(userData.getUserAvatarUrl()); + closedAreaPersonApplyE.setApplyPersonUserName(firstNotEmpty(userData == null ? null : userData.getName(), cmd.getApplyPersonUserName())); + closedAreaPersonApplyE.setUserPhone(firstNotEmpty(userData == null ? null : userData.getPhone(), cmd.getUserPhone())); + closedAreaPersonApplyE.setUserCard(normalizeUserCard(firstNotEmpty(userData == null ? null : userData.getUserIdCard(), cmd.getUserCard()))); + closedAreaPersonApplyE.setUserFaceUrl(firstNotEmpty(userData == null ? null : userData.getUserAvatarUrl(), cmd.getUserFaceUrl())); }else { -// closedAreaPersonApplyE.setUserCard(Base64.encodeToString(cmd.getUserCard())); + closedAreaPersonApplyE.setUserCard(normalizeUserCard(cmd.getUserCard())); + closedAreaPersonApplyE.setUserPhone(cmd.getUserPhone()); + closedAreaPersonApplyE.setUserFaceUrl(cmd.getUserFaceUrl()); } boolean res = closedAreaPersonApplyGateway.add(closedAreaPersonApplyE); if (!res) { @@ -78,6 +81,21 @@ public class ClosedAreaPersonApplyAddExe { return closedAreaPersonApplyE.getId(); } + private String firstNotEmpty(String primary, String fallback) { + return Tools.notEmpty(primary) ? primary : fallback; + } + + private String normalizeUserCard(String userCard) { + if (Tools.isEmpty(userCard)) { + return userCard; + } + String trimmed = userCard.trim(); + if (trimmed.matches("\\d{17}[\\dXx]")) { + return Base64.encodeToString(trimmed); + } + return trimmed; + } + public static void main(String[] args) { System.out.println(Base64.encodeToString("130323199504189999")); System.out.println(Base64.decodeToString("MTMwMzIzMTk5NTA0MTg5OTk5")); diff --git a/web-domain/src/main/java/com/zcloud/primeport/domain/model/DockSyncNoticeHolder.java b/web-domain/src/main/java/com/zcloud/primeport/domain/model/DockSyncNoticeHolder.java new file mode 100644 index 0000000..402da3d --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/primeport/domain/model/DockSyncNoticeHolder.java @@ -0,0 +1,30 @@ +package com.zcloud.primeport.domain.model; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class DockSyncNoticeHolder { + private static final ThreadLocal> NOTICES = ThreadLocal.withInitial(LinkedHashSet::new); + + private DockSyncNoticeHolder() { + } + + public static void add(String notice) { + if (notice != null && !notice.trim().isEmpty()) { + NOTICES.get().add(notice); + } + } + + public static String getAndClear() { + try { + Set notices = NOTICES.get(); + return notices.isEmpty() ? null : String.join(";", notices); + } finally { + NOTICES.remove(); + } + } + + public static void clear() { + NOTICES.remove(); + } +} diff --git a/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaCarApplyMapper.java b/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaCarApplyMapper.java index 114ca27..a4f044a 100644 --- a/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaCarApplyMapper.java +++ b/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaCarApplyMapper.java @@ -1,5 +1,6 @@ package com.zcloud.primeport.persistence.mapper; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.jjb.saas.framework.datascope.annotation.DataScope; @@ -31,8 +32,10 @@ public interface ClosedAreaCarApplyMapper extends BaseMapper screenList(Page> page, @Param("params") Map params); - List getFgsCarToDay(String licenceNo); + @InterceptorIgnore(tenantLine = "true") + List getFgsCarToDay(@Param("licenceNo") String licenceNo); - List dockTmpCarLevelTwo(String licenceNo); + @InterceptorIgnore(tenantLine = "true") + List dockTmpCarLevelTwo(@Param("licenceNo") String licenceNo); } diff --git a/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaPersonApplyMapper.java b/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaPersonApplyMapper.java index a28a5b8..c7ec66c 100644 --- a/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaPersonApplyMapper.java +++ b/web-infrastructure/src/main/java/com/zcloud/primeport/persistence/mapper/ClosedAreaPersonApplyMapper.java @@ -1,5 +1,6 @@ package com.zcloud.primeport.persistence.mapper; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.jjb.saas.framework.datascope.annotation.DataScope; @@ -35,10 +36,12 @@ public interface ClosedAreaPersonApplyMapper extends BaseMapper params); - List getDockAuthInfoById(Long applyId); + @InterceptorIgnore(tenantLine = "true") + List getDockAuthInfoById(@Param("applyId") Long applyId); List deptList(); - List getDockAuthTmpInfoById(Long applyId); + @InterceptorIgnore(tenantLine = "true") + List getDockAuthTmpInfoById(@Param("applyId") Long applyId); } diff --git a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/OneLevelCarUtil.java b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/OneLevelCarUtil.java index 7daed94..090270a 100644 --- a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/OneLevelCarUtil.java +++ b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/OneLevelCarUtil.java @@ -7,6 +7,7 @@ import com.zcloud.primeport.persistence.repository.VehicleDockLogRepository; import com.zcloud.primeport.plan.mjDevice.dto.OneLevelCarBlackDto; import com.zcloud.primeport.plan.mjDevice.dto.OneLevelCarRemoveDto; import com.zcloud.primeport.plan.mjDevice.dto.OneLevelCarSaveDto; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -16,9 +17,10 @@ import javax.annotation.Resource; * 一级车辆门禁对接 前缀 * */ +@Slf4j @Component public class OneLevelCarUtil { - @Value("${kmmj.dockflag:0}") + @Value("${kmmj.dockFlag:0}") private Integer dockFlag; @Value("${oneLevelCar.prefix:}") private String prefix; @@ -32,8 +34,14 @@ public class OneLevelCarUtil { * */ public void internalVehicleSave(OneLevelCarSaveDto oneLevelCarSaveDto) { + if (!dockFlag.equals(1)) { + log.info("一级车行-内部车辆新增跳过, dockFlag:{}, 车牌号:{}", dockFlag, oneLevelCarSaveDto.getTruckNo()); + return; + } if (dockFlag.equals(1)) { + log.info("一级车行-内部车辆新增请求, url:{}, body:{}", prefix + "HD_QHD_IGATE_1.0/webresources/login/CTruck/qaSaveInternal", new JSONObject(oneLevelCarSaveDto)); String body = HttpRequest.post(prefix + "HD_QHD_IGATE_1.0/webresources/login/CTruck/qaSaveInternal").body(new JSONObject(oneLevelCarSaveDto).toString()).execute().body(); + log.info("一级车行-内部车辆新增响应, 车牌号:{}, body:{}", oneLevelCarSaveDto.getTruckNo(), body); JSONObject resJson = new JSONObject(body); VehicleDockLogDO vehicleDockLogDO = new VehicleDockLogDO(); vehicleDockLogDO.setLevel("1"); @@ -90,9 +98,15 @@ public class OneLevelCarUtil { * */ public void tmpVehicleSave(OneLevelCarSaveDto oneLevelCarSaveDto) { + if (!dockFlag.equals(1)) { + log.info("一级车行-临时车辆新增跳过, dockFlag:{}, 车牌号:{}", dockFlag, oneLevelCarSaveDto.getTruckNo()); + return; + } if (dockFlag.equals(1)) { oneLevelCarSaveDto.setIsNeedCheck("0"); + log.info("一级车行-临时车辆新增请求, url:{}, body:{}", prefix + "HD_QHD_IGATE_1.0/webresources/login/CTruck/qaSaveTemp", new JSONObject(oneLevelCarSaveDto)); String body = HttpRequest.post(prefix + "HD_QHD_IGATE_1.0/webresources/login/CTruck/qaSaveTemp").body(new JSONObject(oneLevelCarSaveDto).toString()).execute().body(); + log.info("一级车行-临时车辆新增响应, 车牌号:{}, body:{}", oneLevelCarSaveDto.getTruckNo(), body); JSONObject resJson = new JSONObject(body); VehicleDockLogDO vehicleDockLogDO = new VehicleDockLogDO(); vehicleDockLogDO.setLevel("1"); diff --git a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoCarUtil.java b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoCarUtil.java index 8a194ca..f4057c6 100644 --- a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoCarUtil.java +++ b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoCarUtil.java @@ -3,34 +3,26 @@ package com.zcloud.primeport.plan.mjDevice; import cn.hutool.http.HttpRequest; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; +import com.alibaba.cola.exception.BizException; import com.zcloud.gbscommon.dahuaDevice.DaHuaDeviceCommon; import com.zcloud.gbscommon.dahuaDevice.DhuaConfig; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.zcloud.gbscommon.hkDevice.HKDeviceUtil; import com.zcloud.gbscommon.utils.DateUtil; -import com.zcloud.gbscommon.utils.Tools; -import com.zcloud.gbscommon.zclouduser.facade.ZcloudUserFacade; -import com.zcloud.gbscommon.zclouduser.request.ZcloudUserImgQry; -import com.zcloud.gbscommon.zclouduser.response.ZcloudUserImgBase64Co; import com.zcloud.primeport.persistence.dataobject.ClosedAreaCarApplyDO; -import com.zcloud.primeport.persistence.dataobject.ClosedAreaPersonApplyDO; import com.zcloud.primeport.persistence.dataobject.VehicleApplyDO; import com.zcloud.primeport.persistence.repository.ClosedAreaCarApplyRepository; -import com.zcloud.primeport.persistence.repository.ClosedAreaPersonApplyRepository; import com.zcloud.primeport.persistence.repository.VehicleApplyRepository; +import com.zcloud.primeport.domain.model.DockSyncNoticeHolder; import com.zcloud.primeport.plan.mjDevice.dto.CarEnum; import com.zcloud.primeport.plan.mjDevice.dto.OneLevelCarSaveDto; import com.zcloud.primeport.plan.mjDevice.dto.TwoLevelCar; -import com.zcloud.primeport.plan.mjDevice.dto.TwoLevelUser; -import jodd.util.Base64; import lombok.extern.slf4j.Slf4j; -import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.*; -import java.util.stream.Collectors; /** * 一级人员门禁对接 前缀 @@ -47,8 +39,10 @@ public class TwoCarUtil { private OneLevelCarUtil oneLevelCarUtil; - private static Integer dockFlag = 0; - private static String prefix; + @Value("${kmmj.dockFlag:0}") + private Integer dockFlag; + @Value("${oneLevelCar.prefix:}") + private String prefix; /* * 定时任务对接二级口门车辆 @@ -63,11 +57,12 @@ public class TwoCarUtil { int fgsSuccess = 0, fgsFail = 0; for (TwoLevelCar twoLevelCar : fgsCarList) { try { - HKDeviceUtil.saveOrUpdCar(twoLevelCar.getLicenceNo(), 0); - HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, + addVehicleExistsNotice(twoLevelCar.getLicenceNo()); + JSONObject topUpResult = HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + assertHkSuccess(topUpResult, "海康车辆授权失败,车牌号:" + twoLevelCar.getLicenceNo()); fgsSuccess++; } catch (Exception e) { fgsFail++; @@ -82,11 +77,12 @@ public class TwoCarUtil { int tmpSuccess = 0, tmpFail = 0; for (TwoLevelCar twoLevelCar : tmpCarList) { try { - HKDeviceUtil.saveOrUpdCar(twoLevelCar.getLicenceNo(), 0); - HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, + addVehicleExistsNotice(twoLevelCar.getLicenceNo()); + JSONObject topUpResult = HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + assertHkSuccess(topUpResult, "海康车辆授权失败,车牌号:" + twoLevelCar.getLicenceNo()); tmpSuccess++; } catch (Exception e) { tmpFail++; @@ -217,11 +213,12 @@ public class TwoCarUtil { List carList = closedAreaCarApplyRepository.getFgsCarToDay(licence_no); log.info("车辆同步-分公司车辆, 车牌号:{}, 查询到记录数:{}", licence_no, carList.size()); for (TwoLevelCar twoLevelCar : carList) { - log.info("车辆同步-保存/更新分公司车辆到海康, 车牌号:{}, hkIndexCode:{}, parkSysCode:{}", + log.info("车辆同步-分公司车辆停车场授权, 车牌号:{}, hkIndexCode:{}, parkSysCode:{}", twoLevelCar.getLicenceNo(), twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode()); // todo 车牌号转一下类型 - HKDeviceUtil.saveOrUpdCar(twoLevelCar.getLicenceNo(), 0); - HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + addVehicleExistsNotice(twoLevelCar.getLicenceNo()); + JSONObject topUpResult = HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + assertHkSuccess(topUpResult, "海康车辆授权失败,车牌号:" + twoLevelCar.getLicenceNo()); } } @@ -234,14 +231,54 @@ public class TwoCarUtil { List carList = closedAreaCarApplyRepository.dockTmpCarLevelTwo(licence_no); log.info("车辆同步-临时车辆, 车牌号:{}, 查询到记录数:{}", licence_no, carList.size()); for (TwoLevelCar twoLevelCar : carList) { - log.info("车辆同步-保存/更新临时车辆到海康, 车牌号:{}, hkIndexCode:{}, parkSysCode:{}", + log.info("车辆同步-临时车辆停车场授权, 车牌号:{}, hkIndexCode:{}, parkSysCode:{}", twoLevelCar.getLicenceNo(), twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode()); // todo 车牌号转一下类型 - HKDeviceUtil.saveOrUpdCar(twoLevelCar.getLicenceNo(), 0); - HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + addVehicleExistsNotice(twoLevelCar.getLicenceNo()); + JSONObject topUpResult = HKDeviceUtil.vehicleTopUp(twoLevelCar.getLicenceNo(), 0, twoLevelCar.getHkIndexCode(), twoLevelCar.getKhParkSysCode(), DateUtil.date2Str(new Date(), "yyyy-MM-dd"), DateUtil.date2Str(new Date(), "yyyy-MM-dd")); + assertHkSuccess(topUpResult, "海康车辆授权失败,车牌号:" + twoLevelCar.getLicenceNo()); } } + private void assertHkSuccess(JSONObject result, String message) { + if (result == null) { + throw new BizException(message + ",海康返回为空"); + } + String code = result.getStr("code"); + if (!"0".equals(code)) { + throw new BizException(message + ",code:" + code + ",msg:" + result.getStr("msg")); + } + } + + private void addVehicleExistsNotice(String licenceNo) { + if (vehicleExists(licenceNo)) { + log.info("当前车辆车牌号已存在系统中,访问期限已更新, 车牌号:{}", licenceNo); + DockSyncNoticeHolder.add("当前车辆车牌号已存在系统中,访问期限已更新"); + } else { + log.info("车辆同步-海康未查到车辆基础信息,仅调用车辆充值授权避免默认区域重复建车, 车牌号:{}", licenceNo); + } + } + + private boolean vehicleExists(String licenceNo) { + JSONObject result = HKDeviceUtil.getVehiclesByLicenceNoList(licenceNo); + assertHkSuccess(result, "海康查询车辆失败,车牌号:" + licenceNo); + JSONObject data = result.getJSONObject("data"); + if (data == null) { + return false; + } + JSONArray list = data.getJSONArray("list"); + if (list == null || list.isEmpty()) { + return false; + } + for (int i = 0; i < list.size(); i++) { + JSONObject vehicle = list.getJSONObject(i); + if (licenceNo.equals(vehicle.getStr("plateNo"))) { + return true; + } + } + return false; + } + private static List> partition(List list, int size) { List> partitions = new ArrayList<>(); diff --git a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoPersonUtil.java b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoPersonUtil.java index 66ea1f4..050d727 100644 --- a/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoPersonUtil.java +++ b/web-infrastructure/src/main/java/com/zcloud/primeport/plan/mjDevice/TwoPersonUtil.java @@ -2,29 +2,39 @@ package com.zcloud.primeport.plan.mjDevice; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; +import com.alibaba.cola.exception.BizException; import com.zcloud.gbscommon.dahuaDevice.DaHuaDeviceCommon; import com.zcloud.gbscommon.dahuaDevice.DhuaConfig; import com.zcloud.gbscommon.hkDevice.HKDeviceUtil; import com.zcloud.gbscommon.utils.DateUtil; import com.zcloud.gbscommon.utils.Tools; -import com.zcloud.gbscommon.zclouduser.facade.ZcloudUserFacade; -import com.zcloud.gbscommon.zclouduser.request.ZcloudUserImgQry; -import com.zcloud.gbscommon.zclouduser.response.ZcloudUserImgBase64Co; import com.zcloud.primeport.persistence.dataobject.ClosedAreaPersonApplyDO; import com.zcloud.primeport.persistence.dataobject.XgfApplyPersonDO; import com.zcloud.primeport.persistence.repository.ClosedAreaPersonApplyRepository; import com.zcloud.primeport.persistence.repository.XgfApplyPersonRepository; +import com.zcloud.primeport.domain.model.DockSyncNoticeHolder; import com.zcloud.primeport.plan.mjDevice.dto.TwoLevelUser; import jodd.util.Base64; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; import javax.annotation.Resource; -import java.io.File; -import java.io.FileInputStream; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; import java.util.*; import java.util.stream.Collectors; @@ -39,14 +49,15 @@ public class TwoPersonUtil { private ClosedAreaPersonApplyRepository closedAreaPersonApplyRepository; @Resource private OnePersonUtil onePersonUtil; - @DubboReference - private ZcloudUserFacade zcloudUserFacade; - // @Value("${kmmj.dockflag}") -// private Integer dockFlag; + @Value("${kmmj.dockFlag:0}") + private Integer dockFlag; + @Value("${file.upload.url:}") + private String fileUrl; + @Value("${hk.person.face.max-bytes:15360}") + private Integer faceMaxBytes; // @Value("${oneLevelPerson.prefix}") // private String prefix; - private static Integer dockFlag = 0; private static String prefix = "https://skqhdg.porthebei.com:9008/yjkmRy/"; private static String username = "shuangkong"; private static String password = "qinan@202604"; @@ -254,24 +265,29 @@ public class TwoPersonUtil { userList.forEach(item -> { List u = new ArrayList<>(); List faceUrl = new ArrayList<>(); - ZcloudUserImgQry zcloudUserImgQry = new ZcloudUserImgQry(); - item.setUserCard(Base64.decodeToString(item.getUserCard())); - faceUrl.add(item.getUserFaceImages()); if (Tools.notEmpty(item.getEntourage())) { List entourageList = getEntourageList(faceUrl, new JSONArray(item.getEntourage()), item.getHkIndexCode()); if (!entourageList.isEmpty()) { u.addAll(entourageList); } } - zcloudUserImgQry.setUrlList(faceUrl); - // todo 提醒下悦哥 上线口门需要刷数据 - List data = zcloudUserFacade.listBase64ByUrl(zcloudUserImgQry).getData(); - Map collect = data.stream().collect(Collectors.toMap(ZcloudUserImgBase64Co::getUserAvatarUrl, ZcloudUserImgBase64Co::getUserAvatarBase64, (existing, replacement) -> replacement)); - u.add(item); + if (Tools.notEmpty(item.getUserCard())) { + item.setUserCard(decodeUserCard(item.getUserCard())); + u.add(item); + if (Tools.notEmpty(item.getUserFaceImages())) { + faceUrl.add(item.getUserFaceImages()); + } + } else { + log.warn("人员同步-主申请人身份证为空,仅同步有效随行人员, userName:{}, userPhone:{}", item.getUserName(), item.getUserPhone()); + } + if (u.isEmpty()) { + log.warn("人员同步-无可同步人员,跳过授权, userName:{}, hkIndexCode:{}", item.getUserName(), item.getHkIndexCode()); + return; + } // 过滤掉没有人脸base64数据的用户 List validUsers = new ArrayList<>(); for (TwoLevelUser itemUser : u) { - String base64Data = collect.get(itemUser.getUserFaceImages()); + String base64Data = resolveFaceBase64(itemUser); if (Tools.notEmpty(base64Data)) { itemUser.setUserFaceImagesBase64(base64Data); validUsers.add(itemUser); @@ -314,17 +330,161 @@ public class TwoPersonUtil { // "trainingState": "0", // "endTrainingTime": "2026-04-11 00:00:00", // "startTrainingTime": "2026-04-06 00:00:00"}, + String userCard = firstNotEmpty(jsonObject.getStr("userCard"), jsonObject.getStr("userIdCard")); + if (Tools.isEmpty(userCard)) { + log.warn("人员同步-跳过身份证为空的随行人员, name:{}, phone:{}", firstNotEmpty(jsonObject.getStr("name"), jsonObject.getStr("userName")), jsonObject.getStr("phone")); + continue; + } u.setUserPhone(jsonObject.getStr("phone")); - u.setUserCard(Base64.decodeToString(jsonObject.getStr("userCard"))); + u.setUserCard(decodeUserCard(userCard)); u.setUserFaceImages(jsonObject.getStr("userFaceUrl")); - u.setUserName(jsonObject.getStr("userName")); + u.setUserName(firstNotEmpty(jsonObject.getStr("name"), jsonObject.getStr("userName"))); u.setHkIndexCode(hkindecodes); objects.add(u); - faceUrl.add(jsonObject.getStr("userFaceUrl")); + if (Tools.notEmpty(jsonObject.getStr("userFaceUrl"))) { + faceUrl.add(jsonObject.getStr("userFaceUrl")); + } } return objects; } + private String firstNotEmpty(String primary, String fallback) { + return Tools.notEmpty(primary) ? primary : fallback; + } + + private String decodeUserCard(String userCard) { + if (Tools.isEmpty(userCard)) { + return userCard; + } + String trimmed = userCard.trim(); + if (trimmed.matches("\\d{17}[\\dXx]")) { + return trimmed; + } + return Base64.decodeToString(trimmed); + } + + private String resolveFaceBase64(TwoLevelUser user) { + if (Tools.isEmpty(user.getUserFaceImages())) { + log.warn("人员同步-跳过头像为空用户, 姓名:{}, 手机号:{}, 身份证:{}", user.getUserName(), user.getUserPhone(), user.getUserCard()); + return null; + } + String imageUrl = buildImageUrl(user.getUserFaceImages()); + try { + byte[] imageBytes = downloadImageBytes(imageUrl); + byte[] usableBytes = compressImageToLimit(imageBytes, faceMaxBytes == null ? 15360 : faceMaxBytes); + String base64 = java.util.Base64.getEncoder().encodeToString(usableBytes); + log.info("人员同步-人脸图片转换完成, 姓名:{}, 图片URL:{}, 原始大小:{}字节, 处理后大小:{}字节, base64长度:{}", + user.getUserName(), user.getUserFaceImages(), imageBytes.length, usableBytes.length, base64.length()); + return base64; + } catch (BizException e) { + throw e; + } catch (Exception e) { + log.error("人员同步-人脸图片转换失败, 姓名:{}, 图片URL:{}", user.getUserName(), imageUrl, e); + throw new BizException("人脸图片获取或转换失败"); + } + } + + private String buildImageUrl(String faceUrl) { + String trimmed = faceUrl.trim(); + if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) { + return trimmed; + } + if (Tools.isEmpty(fileUrl)) { + throw new BizException("文件访问地址未配置"); + } + String baseUrl = fileUrl.trim(); + String relativePath = trimmed.startsWith("/") ? trimmed.substring(1) : trimmed; + return baseUrl.endsWith("/") ? baseUrl + relativePath : baseUrl + "/" + relativePath; + } + + private byte[] downloadImageBytes(String imageUrl) throws IOException { + URLConnection connection = new URL(imageUrl).openConnection(); + connection.setConnectTimeout(10000); + connection.setReadTimeout(15000); + try (java.io.InputStream inputStream = connection.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + byte[] buffer = new byte[4096]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + return outputStream.toByteArray(); + } + } + + private byte[] compressImageToLimit(byte[] imageBytes, int maxBytes) throws IOException { + if (imageBytes.length <= maxBytes) { + return imageBytes; + } + BufferedImage source = ImageIO.read(new ByteArrayInputStream(imageBytes)); + if (source == null) { + throw new BizException("人脸图片格式不正确"); + } + int[] maxWidths = new int[]{480, 360, 300, 240, 180, 120}; + float[] qualities = new float[]{0.85f, 0.75f, 0.65f, 0.55f, 0.45f, 0.35f}; + byte[] best = imageBytes; + for (int maxWidth : maxWidths) { + BufferedImage scaled = scaleImage(source, maxWidth); + for (float quality : qualities) { + byte[] compressed = writeJpeg(scaled, quality); + if (compressed.length < best.length) { + best = compressed; + } + if (compressed.length <= maxBytes) { + return compressed; + } + } + } + log.warn("人员同步-人脸图片压缩后仍超限, 原始大小:{}字节, 最小大小:{}字节, 限制:{}字节", imageBytes.length, best.length, maxBytes); + throw new BizException("人脸图片BASE64数值过大,请控制在15kb以内"); + } + + private BufferedImage scaleImage(BufferedImage source, int maxWidth) { + int width = source.getWidth(); + int height = source.getHeight(); + if (width <= maxWidth) { + return toRgbImage(source, width, height); + } + int targetHeight = Math.max(1, height * maxWidth / width); + Image scaledInstance = source.getScaledInstance(maxWidth, targetHeight, Image.SCALE_SMOOTH); + BufferedImage target = new BufferedImage(maxWidth, targetHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = target.createGraphics(); + graphics.setColor(Color.WHITE); + graphics.fillRect(0, 0, maxWidth, targetHeight); + graphics.drawImage(scaledInstance, 0, 0, null); + graphics.dispose(); + return target; + } + + private BufferedImage toRgbImage(BufferedImage source, int width, int height) { + BufferedImage target = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = target.createGraphics(); + graphics.setColor(Color.WHITE); + graphics.fillRect(0, 0, width, height); + graphics.drawImage(source, 0, 0, null); + graphics.dispose(); + return target; + } + + private byte[] writeJpeg(BufferedImage image, float quality) throws IOException { + Iterator writers = ImageIO.getImageWritersByFormatName("jpg"); + if (!writers.hasNext()) { + throw new BizException("当前环境不支持图片压缩"); + } + ImageWriter writer = writers.next(); + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream)) { + ImageWriteParam param = writer.getDefaultWriteParam(); + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionQuality(quality); + writer.setOutput(imageOutputStream); + writer.write(null, new IIOImage(image, null, null), param); + return outputStream.toByteArray(); + } finally { + writer.dispose(); + } + } + /** * 对接二级口门人:用户不存在则新增,已存在跳过 @@ -339,6 +499,8 @@ public class TwoPersonUtil { if (!phoneResult.getJSONObject("data").getJSONArray("list").isEmpty()) { // 手机号查到,用户已存在,无需新增 log.info("人员同步-手机号查到,用户已存在,跳过新增, 姓名:{}, 手机号:{}", towUser.getUserName(), towUser.getUserPhone()); + log.info("当前人员手机号已存在系统中,访问期限已更新, 姓名:{}, 手机号:{}", towUser.getUserName(), towUser.getUserPhone()); + DockSyncNoticeHolder.add("当前人员手机号已存在系统中,访问期限已更新"); return; } // 手机号查不到,尝试用身份证号(手机号可能变更了,但身份证号不变) @@ -346,13 +508,16 @@ public class TwoPersonUtil { if (!idCardResult.getJSONObject("data").getJSONArray("list").isEmpty()) { // 身份证查到 -> 用户存在,只是手机号变了,无需新增 log.info("人员同步-身份证查到,用户已存在(手机号可能变更),跳过新增, 姓名:{}, 身份证:{}", towUser.getUserName(), towUser.getUserCard()); + log.info("当前人员证件号已存在系统中,访问期限已更新, 姓名:{}, 身份证:{}", towUser.getUserName(), towUser.getUserCard()); + DockSyncNoticeHolder.add("当前人员证件号已存在系统中,访问期限已更新"); return; } // 手机号和身份证都查不到 -> 海康无此用户,新增 log.info("人员同步-海康无此用户,开始新增, 姓名:{}, 手机号:{}, 身份证:{}, 人脸长度:{}", towUser.getUserName(), towUser.getUserPhone(), towUser.getUserCard(), towUser.getUserFaceImagesBase64() != null ? towUser.getUserFaceImagesBase64().length() : 0); - HKDeviceUtil.saveUser(towUser.getUserName(), towUser.getUserCard(), towUser.getUserFaceImagesBase64(), towUser.getUserPhone()); + JSONObject saveResult = HKDeviceUtil.saveUser(towUser.getUserName(), towUser.getUserCard(), towUser.getUserFaceImagesBase64(), towUser.getUserPhone()); + assertHkSuccess(saveResult, "新增海康人员失败,姓名:" + towUser.getUserName() + ",身份证:" + towUser.getUserCard()); } private static List> partition(List list, int size) { @@ -414,13 +579,30 @@ public class TwoPersonUtil { userIds.add(jsonObject.getStr("personId")); } List> partitionUserIds = partition(userIds, 1000); + if (userIds.isEmpty()) { + throw new BizException("海康门禁授权失败:未查询到可授权的海康人员ID"); + } log.info("门禁授权-分批调用海康授权接口, 批次数:{}", partitionUserIds.size()); partitionUserIds.forEach(itemUserPhones -> { - HKDeviceUtil.authPersonArea(itemUserPhones, objects, time + "T00:00:00+08:00", time + "T23:59:59+08:00"); + JSONObject authResult = HKDeviceUtil.authPersonArea(itemUserPhones, objects, time + "T00:00:00+08:00", time + "T23:59:59+08:00"); + assertHkSuccess(authResult, "海康门禁授权失败"); }); log.info("门禁授权-完成, 授权personId数:{}, 门禁点数:{}", userIds.size(), resourceInfos.size()); } + private void assertHkSuccess(JSONObject result, String message) { + if (result == null) { + throw new BizException(message + ",海康返回为空"); + } + String code = result.getStr("code"); + if (!"0".equals(code)) { + if ("0x09a05203".equals(code) || "Picture Size err".equalsIgnoreCase(result.getStr("msg"))) { + throw new BizException("人脸图片BASE64数值过大,请控制在15kb以内"); + } + throw new BizException(message + ",code:" + code + ",msg:" + result.getStr("msg")); + } + } + protected DaHuaDeviceCommon getDaHuaDeviceCommon() { return new DaHuaDeviceCommon(new DhuaConfig(dockFlag, prefix, username, password, clientId, clientSecret)); } diff --git a/web-infrastructure/src/main/resources/mapper/ClosedAreaCarApplyMapper.xml b/web-infrastructure/src/main/resources/mapper/ClosedAreaCarApplyMapper.xml index 455600b..dfa8409 100644 --- a/web-infrastructure/src/main/resources/mapper/ClosedAreaCarApplyMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/ClosedAreaCarApplyMapper.xml @@ -80,90 +80,63 @@ diff --git a/web-infrastructure/src/main/resources/mapper/ClosedAreaPersonApplyMapper.xml b/web-infrastructure/src/main/resources/mapper/ClosedAreaPersonApplyMapper.xml index 8f18f7c..1e860e8 100644 --- a/web-infrastructure/src/main/resources/mapper/ClosedAreaPersonApplyMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/ClosedAreaPersonApplyMapper.xml @@ -49,7 +49,7 @@ AND u.main_corp_flag = 0 AND u.corpinfo_id = #{params.corpinfoId} AND u.rz_flag = 1 - + AND u.user_avatar_url != '' AND u.user_avatar_url IS NOT NULL @@ -155,7 +155,7 @@ SELECT capa.id, capa.closed_area_id, - GROUP_CONCAT( chvi.hk_index_code ) hk_index_code, + GROUP_CONCAT( DISTINCT mg.hk_index_code ) hk_index_code, capa.visit_start_time, capa.visit_end_time, capa.user_face_url user_face_images, @@ -165,37 +165,21 @@ capa.entourage FROM closed_area_person_apply capa - LEFT JOIN ( - SELECT - m.closed_area_id, - GROUP_CONCAT( DISTINCT tmp.hk_index_code ) hk_index_code - FROM - mkmj m - LEFT JOIN ( - SELECT - mg.mkmj_id, - mg.hk_index_code - FROM - mkmj_gate mg - WHERE - mg.gate_type = 'HGKM_MKMJ_GATE_TYPE_PERSON' - AND mg.gate_status = 2 - AND mg.delete_enum = 'FALSE' - ) tmp ON m.id = tmp.mkmj_id - WHERE - m.delete_enum = 'FALSE' - AND m.mkmj_status = 2 - AND m.mkmj_level = 2 - GROUP BY - m.closed_area_id - ) chvi ON chvi.closed_area_id = capa.closed_area_id + INNER JOIN mkmj m ON capa.closed_area_id = m.closed_area_id + INNER JOIN mkmj_gate mg ON m.id = mg.mkmj_id WHERE capa.delete_enum = 'FALSE' AND capa.audit_flag = 2 AND capa.person_belong_type IN ( 1, 2, 3 ) AND capa.visit_end_time >= DATE_FORMAT( now(), '%Y-%m-%d' ) AND capa.visit_start_time <= DATE_FORMAT( now(), '%Y-%m-%d' ) - AND chvi.hk_index_code IS NOT NULL + AND m.delete_enum = 'FALSE' + AND m.mkmj_status = 2 + AND m.mkmj_level = 2 + AND mg.gate_type = 'HGKM_MKMJ_GATE_TYPE_PERSON' + AND mg.gate_status = 2 + AND mg.delete_enum = 'FALSE' + AND mg.hk_index_code IS NOT NULL and capa.id = #{applyId}