package com.ruoyi.wrzs.access.service;
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.ruoyi.wrzs.access.mapper.AccessGateMapper;
|
import com.ruoyi.wrzs.access.mapper.AccessPersonMapper;
|
import com.ruoyi.wrzs.access.mapper.AccessRecordMapper;
|
import com.ruoyi.wrzs.access.mapper.AccessVehicleMapper;
|
import com.ruoyi.wrzs.access.pojo.AccessGate;
|
import com.ruoyi.wrzs.access.pojo.AccessPerson;
|
import com.ruoyi.wrzs.access.pojo.AccessRecord;
|
import com.ruoyi.wrzs.access.pojo.AccessVehicle;
|
import com.ruoyi.wrzs.weighbridge.mapper.WeighbridgeRecordMapper;
|
import com.ruoyi.wrzs.weighbridge.pojo.WeighbridgeRecord;
|
import lombok.AllArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.StringUtils;
|
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 门禁管理业务
|
* <p>
|
* 车辆:车牌识别 + RFID 标签;人员:人脸识别 + 工卡刷卡;
|
* 车辆放行与地磅联动:未完成过磅的车辆不允许出场,同时记录进出时间、车牌号与承运商
|
*/
|
@Slf4j
|
@AllArgsConstructor
|
@Service
|
public class AccessService {
|
|
private final AccessGateMapper gateMapper;
|
private final AccessVehicleMapper vehicleMapper;
|
private final AccessPersonMapper personMapper;
|
private final AccessRecordMapper recordMapper;
|
private final WeighbridgeRecordMapper weighbridgeRecordMapper;
|
|
// ==================== 门禁点 ====================
|
|
public IPage<AccessGate> gatePage(Page<AccessGate> page, AccessGate query) {
|
return gateMapper.selectPage(page, Wrappers.<AccessGate>lambdaQuery()
|
.like(StringUtils.hasText(query.getGateName()), AccessGate::getGateName, query.getGateName())
|
.eq(StringUtils.hasText(query.getGateType()), AccessGate::getGateType, query.getGateType())
|
.eq(StringUtils.hasText(query.getAreaName()), AccessGate::getAreaName, query.getAreaName())
|
.eq(StringUtils.hasText(query.getStatus()), AccessGate::getStatus, query.getStatus())
|
.orderByDesc(AccessGate::getId));
|
}
|
|
public List<AccessGate> gateList() {
|
// 门禁点下拉:只排除"停用"的(本系统 status='1' 表示正常/启用,'0' 表示停用,
|
// 与 RuoYi 惯例相反。原先写死 status='0' 导致下拉框查不到任何门禁点)
|
return gateMapper.selectList(Wrappers.<AccessGate>lambdaQuery()
|
.ne(AccessGate::getStatus, "0")
|
.orderByAsc(AccessGate::getGateCode));
|
}
|
|
public boolean gateSave(AccessGate gate) {
|
if (!StringUtils.hasText(gate.getStatus())) {
|
gate.setStatus("0");
|
}
|
if (!StringUtils.hasText(gate.getLinkageEnabled())) {
|
gate.setLinkageEnabled("1");
|
}
|
return gateMapper.insert(gate) > 0;
|
}
|
|
public boolean gateUpdate(AccessGate gate) {
|
return gateMapper.updateById(gate) > 0;
|
}
|
|
public boolean gateRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return gateMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 车辆授权 ====================
|
|
public IPage<AccessVehicle> vehiclePage(Page<AccessVehicle> page, AccessVehicle query) {
|
return vehicleMapper.selectPage(page, Wrappers.<AccessVehicle>lambdaQuery()
|
.like(StringUtils.hasText(query.getPlateNumber()), AccessVehicle::getPlateNumber, query.getPlateNumber())
|
.like(StringUtils.hasText(query.getCarrierName()), AccessVehicle::getCarrierName, query.getCarrierName())
|
.like(StringUtils.hasText(query.getRfidTag()), AccessVehicle::getRfidTag, query.getRfidTag())
|
.eq(StringUtils.hasText(query.getStatus()), AccessVehicle::getStatus, query.getStatus())
|
.orderByDesc(AccessVehicle::getId));
|
}
|
|
public boolean vehicleSave(AccessVehicle vehicle) {
|
if (!StringUtils.hasText(vehicle.getStatus())) {
|
vehicle.setStatus("0");
|
}
|
return vehicleMapper.insert(vehicle) > 0;
|
}
|
|
public boolean vehicleUpdate(AccessVehicle vehicle) {
|
return vehicleMapper.updateById(vehicle) > 0;
|
}
|
|
public boolean vehicleRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return vehicleMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 人员授权 ====================
|
|
public IPage<AccessPerson> personPage(Page<AccessPerson> page, AccessPerson query) {
|
return personMapper.selectPage(page, Wrappers.<AccessPerson>lambdaQuery()
|
.like(StringUtils.hasText(query.getPersonName()), AccessPerson::getPersonName, query.getPersonName())
|
.like(StringUtils.hasText(query.getCardNo()), AccessPerson::getCardNo, query.getCardNo())
|
.eq(StringUtils.hasText(query.getDeptName()), AccessPerson::getDeptName, query.getDeptName())
|
.eq(StringUtils.hasText(query.getPersonType()), AccessPerson::getPersonType, query.getPersonType())
|
.eq(StringUtils.hasText(query.getStatus()), AccessPerson::getStatus, query.getStatus())
|
.orderByDesc(AccessPerson::getId));
|
}
|
|
public boolean personSave(AccessPerson person) {
|
if (!StringUtils.hasText(person.getStatus())) {
|
person.setStatus("0");
|
}
|
return personMapper.insert(person) > 0;
|
}
|
|
public boolean personUpdate(AccessPerson person) {
|
return personMapper.updateById(person) > 0;
|
}
|
|
public boolean personRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return personMapper.deleteBatchIds(ids) > 0;
|
}
|
|
// ==================== 识别放行 ====================
|
|
/**
|
* 车辆识别放行(车牌识别 + RFID 标签 + 地磅联动)
|
*
|
* @param gateId 门禁点ID
|
* @param plateNumber 识别车牌
|
* @param rfidTag RFID 标签(可空,车牌识别失败时用标签匹配)
|
* @param direction 0进场 1出场
|
* @param photoUrl 抓拍图片
|
*/
|
public Map<String, Object> vehicleRecognize(Long gateId, String plateNumber, String rfidTag,
|
String direction, String photoUrl) {
|
AccessGate gate = gateMapper.selectById(gateId);
|
if (gate == null) {
|
throw new IllegalArgumentException("门禁点不存在");
|
}
|
if (!"0".equals(gate.getStatus())) {
|
throw new IllegalArgumentException("门禁点已停用");
|
}
|
if (!StringUtils.hasText(plateNumber) && !StringUtils.hasText(rfidTag)) {
|
throw new IllegalArgumentException("请至少提供车牌号或RFID标签");
|
}
|
|
AccessRecord record = new AccessRecord();
|
record.setGateId(gate.getId());
|
record.setGateName(gate.getGateName());
|
record.setRecordType("0");
|
record.setPlateNumber(plateNumber);
|
record.setDirection(StringUtils.hasText(direction) ? direction : "0");
|
record.setPassTime(LocalDateTime.now());
|
record.setPhotoUrl(photoUrl);
|
record.setWeighbridgeCode(gate.getWeighbridgeCode());
|
|
StringBuilder reasons = new StringBuilder();
|
AccessVehicle vehicle = matchVehicle(plateNumber, rfidTag);
|
String identifyMode = StringUtils.hasText(plateNumber) ? "车牌识别" : "RFID标签";
|
if (vehicle != null) {
|
// 车牌识别成功后由 RFID 复核,两种方式同时可用
|
if (StringUtils.hasText(plateNumber) && StringUtils.hasText(rfidTag)
|
&& StringUtils.hasText(vehicle.getRfidTag())) {
|
identifyMode = vehicle.getRfidTag().equals(rfidTag) ? "车牌识别+RFID标签" : "车牌识别";
|
if (!vehicle.getRfidTag().equals(rfidTag)) {
|
reasons.append("RFID标签与车牌不匹配;");
|
}
|
}
|
record.setCarrierName(vehicle.getCarrierName());
|
} else {
|
reasons.append("车辆未登记授权;");
|
}
|
record.setIdentifyMode(identifyMode);
|
|
// 授权有效性校验
|
if (vehicle != null) {
|
if (!"0".equals(vehicle.getStatus())) {
|
reasons.append("车辆授权已禁用;");
|
}
|
LocalDate today = LocalDate.now();
|
if (vehicle.getValidEnd() != null && vehicle.getValidEnd().isBefore(today)) {
|
reasons.append("车辆授权已过期(").append(vehicle.getValidEnd()).append(");");
|
}
|
if (vehicle.getValidStart() != null && vehicle.getValidStart().isAfter(today)) {
|
reasons.append("车辆授权未生效(").append(vehicle.getValidStart()).append(");");
|
}
|
}
|
|
// 地磅联动:出场必须完成过磅
|
if ("1".equals(gate.getLinkageEnabled()) && StringUtils.hasText(gate.getWeighbridgeCode())
|
&& StringUtils.hasText(plateNumber)) {
|
Map<String, Object> linkage = checkWeighbridgeLinkage(plateNumber, gate.getWeighbridgeCode(), record.getDirection());
|
record.setLinkageStatus(String.valueOf(linkage.get("message")));
|
if (Boolean.FALSE.equals(linkage.get("pass"))) {
|
reasons.append(linkage.get("message")).append(";");
|
}
|
if (linkage.get("recordNo") != null) {
|
record.setWeightRecordNo(String.valueOf(linkage.get("recordNo")));
|
}
|
} else if ("1".equals(gate.getLinkageEnabled())) {
|
record.setLinkageStatus("门禁未配置联动地磅,跳过地磅校验");
|
}
|
|
boolean allow = reasons.length() == 0;
|
record.setAllowFlag(allow ? "0" : "1");
|
record.setRejectReason(allow ? null : trimTail(reasons.toString()));
|
recordMapper.insert(record);
|
|
Map<String, Object> result = new HashMap<>();
|
result.put("allow", allow);
|
result.put("record", record);
|
result.put("carrierName", record.getCarrierName());
|
result.put("message", allow ? "身份核验通过,道闸已放行" : "核验未通过:" + record.getRejectReason());
|
if (vehicle != null) {
|
result.put("driverName", vehicle.getDriverName());
|
result.put("driverPhone", vehicle.getDriverPhone());
|
}
|
return result;
|
}
|
|
/** 人员识别放行(人脸识别 + 工卡刷卡) */
|
public Map<String, Object> personRecognize(Long gateId, String cardNo, String personName,
|
String direction, String photoUrl) {
|
AccessGate gate = gateMapper.selectById(gateId);
|
if (gate == null) {
|
throw new IllegalArgumentException("门禁点不存在");
|
}
|
if (!"0".equals(gate.getStatus())) {
|
throw new IllegalArgumentException("门禁点已停用");
|
}
|
if (!StringUtils.hasText(cardNo) && !StringUtils.hasText(personName)) {
|
throw new IllegalArgumentException("请至少提供工卡号或人员姓名(人脸识别)");
|
}
|
AccessRecord record = new AccessRecord();
|
record.setGateId(gate.getId());
|
record.setGateName(gate.getGateName());
|
record.setRecordType("1");
|
record.setDirection(StringUtils.hasText(direction) ? direction : "0");
|
record.setPassTime(LocalDateTime.now());
|
record.setPhotoUrl(photoUrl);
|
record.setIdentifyMode(StringUtils.hasText(cardNo) ? "工卡刷卡" : "人脸识别");
|
|
AccessPerson person = null;
|
if (StringUtils.hasText(cardNo)) {
|
person = personMapper.selectOne(Wrappers.<AccessPerson>lambdaQuery()
|
.eq(AccessPerson::getCardNo, cardNo).last("limit 1"));
|
}
|
if (person == null && StringUtils.hasText(personName)) {
|
person = personMapper.selectOne(Wrappers.<AccessPerson>lambdaQuery()
|
.eq(AccessPerson::getPersonName, personName).last("limit 1"));
|
}
|
StringBuilder reasons = new StringBuilder();
|
if (person == null) {
|
reasons.append("人员未登记授权;");
|
record.setPersonName(personName);
|
} else {
|
record.setPersonName(person.getPersonName());
|
if (StringUtils.hasText(cardNo) && StringUtils.hasText(personName)) {
|
record.setIdentifyMode("人脸识别+工卡刷卡");
|
if (!person.getPersonName().equals(personName)) {
|
reasons.append("人脸识别结果与工卡持有人不一致;");
|
}
|
}
|
if (!"0".equals(person.getStatus())) {
|
reasons.append("人员授权已禁用;");
|
}
|
LocalDate today = LocalDate.now();
|
if (person.getValidEnd() != null && person.getValidEnd().isBefore(today)) {
|
reasons.append("人员授权已过期(").append(person.getValidEnd()).append(");");
|
}
|
if (person.getValidStart() != null && person.getValidStart().isAfter(today)) {
|
reasons.append("人员授权未生效(").append(person.getValidStart()).append(");");
|
}
|
}
|
boolean allow = reasons.length() == 0;
|
record.setAllowFlag(allow ? "0" : "1");
|
record.setRejectReason(allow ? null : trimTail(reasons.toString()));
|
recordMapper.insert(record);
|
|
Map<String, Object> result = new HashMap<>();
|
result.put("allow", allow);
|
result.put("record", record);
|
result.put("message", allow ? "身份核验通过,闸机已放行" : "核验未通过:" + record.getRejectReason());
|
return result;
|
}
|
|
/** 车牌/RFID 匹配车辆授权 */
|
private AccessVehicle matchVehicle(String plateNumber, String rfidTag) {
|
if (StringUtils.hasText(plateNumber)) {
|
AccessVehicle byPlate = vehicleMapper.selectOne(Wrappers.<AccessVehicle>lambdaQuery()
|
.eq(AccessVehicle::getPlateNumber, plateNumber).last("limit 1"));
|
if (byPlate != null) {
|
return byPlate;
|
}
|
}
|
if (StringUtils.hasText(rfidTag)) {
|
return vehicleMapper.selectOne(Wrappers.<AccessVehicle>lambdaQuery()
|
.eq(AccessVehicle::getRfidTag, rfidTag).last("limit 1"));
|
}
|
return null;
|
}
|
|
/**
|
* 地磅联动校验
|
* 进场:校验是否有有效订单或在途过磅任务,不做强制拦截
|
* 出场:必须存在已完成过磅记录,否则不允许放行(防止绕磅逃单)
|
*/
|
private Map<String, Object> checkWeighbridgeLinkage(String plateNumber, String weighbridgeCode, String direction) {
|
Map<String, Object> result = new HashMap<>();
|
List<WeighbridgeRecord> records = weighbridgeRecordMapper.selectList(Wrappers.<WeighbridgeRecord>lambdaQuery()
|
.eq(WeighbridgeRecord::getPlateNumber, plateNumber)
|
.eq(StringUtils.hasText(weighbridgeCode), WeighbridgeRecord::getWeighbridgeCode, weighbridgeCode)
|
.orderByDesc(WeighbridgeRecord::getId));
|
if (CollectionUtils.isEmpty(records)) {
|
if ("1".equals(direction)) {
|
result.put("pass", false);
|
result.put("message", "地磅联动校验:未查询到过磅记录,禁止出场");
|
} else {
|
result.put("pass", true);
|
result.put("message", "地磅联动校验:进场登记成功,等待过磅");
|
}
|
return result;
|
}
|
WeighbridgeRecord latest = records.get(0);
|
result.put("recordNo", latest.getRecordNo());
|
if ("1".equals(direction)) {
|
if ("2".equals(latest.getWeighStatus())) {
|
result.put("pass", true);
|
result.put("message", "地磅联动校验:过磅完成,允许出场");
|
} else {
|
result.put("pass", false);
|
result.put("message", "地磅联动校验:车辆尚未完成称重,禁止出场");
|
}
|
} else {
|
result.put("pass", true);
|
result.put("message", "地磅联动校验:进场登记成功");
|
}
|
return result;
|
}
|
|
private String trimTail(String text) {
|
String value = text.trim();
|
while (value.endsWith(";") || value.endsWith(";")) {
|
value = value.substring(0, value.length() - 1);
|
}
|
return value;
|
}
|
|
// ==================== 通行记录 ====================
|
|
public IPage<AccessRecord> recordPage(Page<AccessRecord> page, AccessRecord query) {
|
return recordMapper.selectPage(page, Wrappers.<AccessRecord>lambdaQuery()
|
.like(StringUtils.hasText(query.getPlateNumber()), AccessRecord::getPlateNumber, query.getPlateNumber())
|
.like(StringUtils.hasText(query.getPersonName()), AccessRecord::getPersonName, query.getPersonName())
|
.like(StringUtils.hasText(query.getCarrierName()), AccessRecord::getCarrierName, query.getCarrierName())
|
.eq(StringUtils.hasText(query.getGateName()), AccessRecord::getGateName, query.getGateName())
|
.eq(StringUtils.hasText(query.getRecordType()), AccessRecord::getRecordType, query.getRecordType())
|
.eq(StringUtils.hasText(query.getDirection()), AccessRecord::getDirection, query.getDirection())
|
.eq(StringUtils.hasText(query.getAllowFlag()), AccessRecord::getAllowFlag, query.getAllowFlag())
|
.ge(query.getPassTime() != null, AccessRecord::getPassTime, query.getPassTime())
|
.orderByDesc(AccessRecord::getId));
|
}
|
|
public boolean recordRemove(List<Long> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return false;
|
}
|
return recordMapper.deleteBatchIds(ids) > 0;
|
}
|
|
/** 今日通行统计 */
|
public Map<String, Object> recordOverview() {
|
LocalDateTime start = LocalDate.now().atStartOfDay();
|
Map<String, Object> result = new HashMap<>();
|
result.put("vehicleIn", recordMapper.selectCount(Wrappers.<AccessRecord>lambdaQuery()
|
.eq(AccessRecord::getRecordType, "0").eq(AccessRecord::getDirection, "0")
|
.ge(AccessRecord::getPassTime, start)));
|
result.put("vehicleOut", recordMapper.selectCount(Wrappers.<AccessRecord>lambdaQuery()
|
.eq(AccessRecord::getRecordType, "0").eq(AccessRecord::getDirection, "1")
|
.ge(AccessRecord::getPassTime, start)));
|
result.put("personIn", recordMapper.selectCount(Wrappers.<AccessRecord>lambdaQuery()
|
.eq(AccessRecord::getRecordType, "1").eq(AccessRecord::getDirection, "0")
|
.ge(AccessRecord::getPassTime, start)));
|
result.put("personOut", recordMapper.selectCount(Wrappers.<AccessRecord>lambdaQuery()
|
.eq(AccessRecord::getRecordType, "1").eq(AccessRecord::getDirection, "1")
|
.ge(AccessRecord::getPassTime, start)));
|
result.put("reject", recordMapper.selectCount(Wrappers.<AccessRecord>lambdaQuery()
|
.eq(AccessRecord::getAllowFlag, "1").ge(AccessRecord::getPassTime, start)));
|
return result;
|
}
|
}
|