feat(smartCam): 添加车辆出入记录功能

- 新增 SmartCamCarData 模块,用于记录车辆出入信息
- 在 SmartCamCarLicenseSnapshotData 中添加方向字段
- 实现车辆出入记录的创建、更新、删除和查询功能
- 添加字典翻译支持,用于摄像头方向描述
This commit is contained in:
2025-03-14 22:47:56 +08:00
parent a3ba459172
commit c70d5f53b8
13 changed files with 393 additions and 18 deletions

View File

@@ -1,6 +1,7 @@
package cn.lihongjie.coal.smartCam.dto;
import cn.lihongjie.coal.base.dto.OrgCommonDto;
import cn.lihongjie.coal.pojoProcessor.DictTranslate;
import cn.lihongjie.coal.smartCamSupplier.dto.SmartCamSupplierDto;
import jakarta.persistence.ManyToOne;
@@ -19,4 +20,7 @@ public class SmartCamDto extends OrgCommonDto {
@Comment("摄像头方向")
private String direction;
@DictTranslate(dictKey = "smartCam.dir")
private String directionName;
}

View File

@@ -35,4 +35,5 @@ public class SmartCamEntity extends OrgCommonEntity {
}

View File

@@ -0,0 +1,54 @@
package cn.lihongjie.coal.smartCamCarData.controller;
import cn.lihongjie.coal.annotation.OrgScope;
import cn.lihongjie.coal.annotation.SysLog;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.smartCamCarData.dto.CreateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.SmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.UpdateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.service.SmartCamCarDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/smartCamCarData")
@SysLog(module = "智能摄像头车辆出入记录")
@Slf4j
@OrgScope
public class SmartCamCarDataController {
@Autowired private SmartCamCarDataService service;
@PostMapping("/create")
public SmartCamCarDataDto create(@RequestBody CreateSmartCamCarDataDto request) {
return this.service.create(request);
}
@PostMapping("/update")
public SmartCamCarDataDto update(@RequestBody UpdateSmartCamCarDataDto request) {
return this.service.update(request);
}
@PostMapping("/delete")
public Object delete(@RequestBody IdRequest request) {
this.service.delete(request);
return true;
}
@PostMapping("/getById")
public SmartCamCarDataDto getById(@RequestBody IdRequest request) {
return this.service.getById(request.getId());
}
@PostMapping("/list")
public Page<SmartCamCarDataDto> list(@RequestBody CommonQuery request) {
return this.service.list(request);
}
}

View File

@@ -0,0 +1,8 @@
package cn.lihongjie.coal.smartCamCarData.dto;
import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
@Data
public class CreateSmartCamCarDataDto extends OrgCommonDto {}

View File

@@ -0,0 +1,23 @@
package cn.lihongjie.coal.smartCamCarData.dto;
import cn.lihongjie.coal.base.dto.OrgCommonDto;
import cn.lihongjie.coal.smartCamCarLicenseSnapshotData.dto.SmartCamCarLicenseSnapshotDataDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class SmartCamCarDataDto extends OrgCommonDto {
private String number;
private LocalDateTime entryTime;
private LocalDateTime exitTime;
private SmartCamCarLicenseSnapshotDataDto entry;
private SmartCamCarLicenseSnapshotDataDto exit;
}

View File

@@ -0,0 +1,8 @@
package cn.lihongjie.coal.smartCamCarData.dto;
import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
@Data
public class UpdateSmartCamCarDataDto extends OrgCommonDto {}

View File

@@ -0,0 +1,39 @@
package cn.lihongjie.coal.smartCamCarData.entity;
import cn.lihongjie.coal.base.entity.OrgCommonEntity;
import cn.lihongjie.coal.smartCamCarLicenseSnapshotData.entity.SmartCamCarLicenseSnapshotDataEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Entity
@Table(
indexes =
@jakarta.persistence.Index(
name = "idx_smartCamCarData_org_id",
columnList = "organization_id"))
public class SmartCamCarDataEntity extends OrgCommonEntity {
private String number;
private LocalDateTime entryTime;
private LocalDateTime exitTime;
@ManyToOne
private SmartCamCarLicenseSnapshotDataEntity entry;
@ManyToOne
private SmartCamCarLicenseSnapshotDataEntity exit;
}

View File

@@ -0,0 +1,23 @@
package cn.lihongjie.coal.smartCamCarData.mapper;
import cn.lihongjie.coal.base.mapper.BaseMapper;
import cn.lihongjie.coal.base.mapper.CommonEntityMapper;
import cn.lihongjie.coal.base.mapper.CommonMapper;
import cn.lihongjie.coal.smartCamCarData.dto.CreateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.SmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.UpdateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.entity.SmartCamCarDataEntity;
import org.mapstruct.Mapper;
import org.mapstruct.control.DeepClone;
@Mapper(
componentModel = org.mapstruct.MappingConstants.ComponentModel.SPRING,
uses = {CommonMapper.class, CommonEntityMapper.class},
mappingControl = DeepClone.class)
public interface SmartCamCarDataMapper
extends BaseMapper<
SmartCamCarDataEntity,
SmartCamCarDataDto,
CreateSmartCamCarDataDto,
UpdateSmartCamCarDataDto> {}

View File

@@ -0,0 +1,18 @@
package cn.lihongjie.coal.smartCamCarData.repository;
import cn.lihongjie.coal.base.dao.BaseRepository;
import cn.lihongjie.coal.smartCamCarData.entity.SmartCamCarDataEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface SmartCamCarDataRepository extends BaseRepository<SmartCamCarDataEntity> {
@Query("select false")
boolean isLinked(List<String> ids);
@Query("select d from SmartCamCarDataEntity d where d.number = :number and d.organizationId = :organizationId order by d.createTime desc limit 1")
SmartCamCarDataEntity findByNumber(String organizationId, String number);
}

View File

@@ -0,0 +1,81 @@
package cn.lihongjie.coal.smartCamCarData.service;
import cn.lihongjie.coal.base.dto.CommonQuery;
import cn.lihongjie.coal.base.dto.IdRequest;
import cn.lihongjie.coal.base.service.BaseService;
import cn.lihongjie.coal.dbFunctions.DbFunctionService;
import cn.lihongjie.coal.exception.BizException;
import cn.lihongjie.coal.smartCamCarData.dto.CreateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.SmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.dto.UpdateSmartCamCarDataDto;
import cn.lihongjie.coal.smartCamCarData.entity.SmartCamCarDataEntity;
import cn.lihongjie.coal.smartCamCarData.mapper.SmartCamCarDataMapper;
import cn.lihongjie.coal.smartCamCarData.repository.SmartCamCarDataRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Slf4j
@Transactional
public class SmartCamCarDataService
extends BaseService<SmartCamCarDataEntity, SmartCamCarDataRepository> {
@Autowired private SmartCamCarDataRepository repository;
@Autowired private SmartCamCarDataMapper mapper;
@Autowired private ConversionService conversionService;
@Autowired private DbFunctionService dbFunctionService;
public SmartCamCarDataDto create(CreateSmartCamCarDataDto request) {
SmartCamCarDataEntity entity = mapper.toEntity(request);
this.repository.save(entity);
return getById(entity.getId());
}
public SmartCamCarDataDto update(UpdateSmartCamCarDataDto request) {
SmartCamCarDataEntity entity = this.repository.get(request.getId());
this.mapper.updateEntity(entity, request);
this.repository.save(entity);
return getById(entity.getId());
}
public void delete(IdRequest request) {
boolean linked = this.repository.isLinked(request.getIds());
if (linked) {
throw new BizException("数据已被关联,无法删除");
}
this.repository.deleteAllById(request.getIds());
}
public SmartCamCarDataDto getById(String id) {
SmartCamCarDataEntity entity = repository.get(id);
return mapper.toDto(entity);
}
public Page<SmartCamCarDataDto> list(CommonQuery query) {
Page<SmartCamCarDataEntity> page =
repository.findAll(
query.specification(conversionService),
PageRequest.of(
query.getPageNo(),
query.getPageSize(),
Sort.by(query.getOrders())));
return page.map(this.mapper::toDto);
}
}

View File

@@ -11,6 +11,8 @@ import cn.lihongjie.coal.file.entity.FileEntity;
import cn.lihongjie.coal.file.service.FileService;
import cn.lihongjie.coal.smartCam.entity.SmartCamEntity;
import cn.lihongjie.coal.smartCam.service.SmartCamService;
import cn.lihongjie.coal.smartCamCarData.entity.SmartCamCarDataEntity;
import cn.lihongjie.coal.smartCamCarData.repository.SmartCamCarDataRepository;
import cn.lihongjie.coal.smartCamCarLicenseSnapshotData.dto.CreateSmartCamCarLicenseSnapshotDataDto;
import cn.lihongjie.coal.smartCamCarLicenseSnapshotData.dto.SmartCamCarLicenseSnapshotDataDto;
import cn.lihongjie.coal.smartCamCarLicenseSnapshotData.dto.UpdateSmartCamCarLicenseSnapshotDataDto;
@@ -24,6 +26,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -36,6 +42,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -58,6 +65,8 @@ public class SmartCamCarLicenseSnapshotDataService
@Autowired private ConversionService conversionService;
@Autowired private DbFunctionService dbFunctionService;
@Autowired private SmartCamCarDataRepository smartCamCarDataRepository;
public SmartCamCarLicenseSnapshotDataDto create(
CreateSmartCamCarLicenseSnapshotDataDto request) {
SmartCamCarLicenseSnapshotDataEntity entity = mapper.toEntity(request);
@@ -93,9 +102,29 @@ public class SmartCamCarLicenseSnapshotDataService
}
public Page<SmartCamCarLicenseSnapshotDataDto> list(CommonQuery query) {
Specification specification = query.specification(conversionService);
if (Boolean.parseBoolean(query.get("ignoreEmptyNumber", "true"))) {
specification =
specification.and(
new Specification() {
@Override
public Predicate toPredicate(
Root root,
CriteriaQuery query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.and(
criteriaBuilder.notEqual(root.get("number"), ""),
criteriaBuilder.isNotNull(root.get("number")));
}
});
}
Page<SmartCamCarLicenseSnapshotDataEntity> page =
repository.findAll(
query.specification(conversionService),
specification,
PageRequest.of(
query.getPageNo(),
query.getPageSize(),
@@ -107,18 +136,14 @@ public class SmartCamCarLicenseSnapshotDataService
@SneakyThrows
public ObjectNode saveEvent(String json) {
// log.info("saveEvent {}", json);
// log.info("saveEvent {}", json);
JsonNode rootNode = objectMapper.readTree(json);
SmartCamCarLicenseSnapshotDataEntity entity = new SmartCamCarLicenseSnapshotDataEntity();
// Operator
entity.setOperator(rootNode.path("operator").asText());
if (StringUtils.equalsIgnoreCase(entity.getOperator(), "heartbeat")){
if (StringUtils.equalsIgnoreCase(entity.getOperator(), "heartbeat")) {
return getHeartbeatAck(rootNode);
}
@@ -226,7 +251,8 @@ public class SmartCamCarLicenseSnapshotDataService
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ "_"
+ "plate",
"car_license_snapshot", smartCam.getOrganizationId());
"car_license_snapshot",
smartCam.getOrganizationId());
entity.setCaptureImage(em.getReference(FileEntity.class, captureDto.getId()));
}
@@ -247,7 +273,8 @@ public class SmartCamCarLicenseSnapshotDataService
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ "_"
+ "bg",
"car_license_snapshot", smartCam.getOrganizationId());
"car_license_snapshot",
smartCam.getOrganizationId());
entity.setBackgroundImage(em.getReference(FileEntity.class, backgroundDto.getId()));
}
@@ -270,9 +297,65 @@ public class SmartCamCarLicenseSnapshotDataService
ObjectNode objectNode = getAck(entity.getInfoEventId());
saveCarData(entity);
return objectNode;
}
private void saveCarData(SmartCamCarLicenseSnapshotDataEntity entity) {
try {
if (StringUtils.isEmpty(entity.getNumber())) {
log.info("车牌号为空");
return;
}
SmartCamCarDataEntity byNumber =
smartCamCarDataRepository.findByNumber(
entity.getOrganizationId(), entity.getNumber());
boolean entry = StringUtils.equals(entity.getSmartCam().getDirection(), "0");
if (byNumber == null) {
if (entry) {
SmartCamCarDataEntity smartCamCarDataEntity = new SmartCamCarDataEntity();
smartCamCarDataEntity.setNumber(entity.getNumber());
smartCamCarDataEntity.setEntry(entity);
smartCamCarDataEntity.setEntryTime(entity.getInfoTimeObj());
smartCamCarDataRepository.save(smartCamCarDataEntity);
} else {
SmartCamCarDataEntity smartCamCarDataEntity = new SmartCamCarDataEntity();
smartCamCarDataEntity.setNumber(entity.getNumber());
smartCamCarDataEntity.setExit(entity);
smartCamCarDataEntity.setExitTime(entity.getInfoTimeObj());
smartCamCarDataRepository.save(smartCamCarDataEntity);
}
} else {
if (entry) {
log.info("重复的车牌号数据 {} {}", byNumber.getId(), byNumber.getNumber());
} else {
byNumber.setExit(entity);
byNumber.setExitTime(entity.getInfoTimeObj());
smartCamCarDataRepository.save(byNumber);
}
}
} catch (Exception e) {
log.error("保存数据异常", e);
}
}
@SneakyThrows
private ObjectNode getHeartbeatAck(JsonNode rootNode) {
@@ -287,8 +370,10 @@ public class SmartCamCarLicenseSnapshotDataService
* true, "isCarDetectEnable": true, "isMotionDetectEnable": true } }, "result": { "errorNo":
* 0, "description": "ok" } }
*/
ObjectNode jsonNode = (ObjectNode) objectMapper.readTree(
"""
ObjectNode jsonNode =
(ObjectNode)
objectMapper.readTree(
"""
{
"operator": "heartbeat-Ack",
"info": {
@@ -325,14 +410,9 @@ public class SmartCamCarLicenseSnapshotDataService
((ObjectNode) jsonNode.get("info"))
.put("eventId", rootNode.get("info").get("eventId").asInt());
((ObjectNode) jsonNode.get("info"))
.put("time", rootNode.get("info").get("time").asInt());
((ObjectNode) jsonNode.get("info"))
.put("heartbeatInterval", 300);
((ObjectNode) jsonNode.get("info")).put("time", rootNode.get("info").get("time").asInt());
((ObjectNode) jsonNode.get("info")).put("heartbeatInterval", 300);
jsonNode.put("operator", "heartbeat-Ack");

View File

@@ -2864,5 +2864,22 @@
"name": "其他"
}
]
},
{
"code": "smartCam.dir",
"name": "智能摄像头方向",
"item": [
{
"code": "0",
"name": "入口"
},
{
"code": "1",
"name": "出口"
}
]
}
]

View File

@@ -0,0 +1,19 @@
package scripts.dict
import cn.lihongjie.coal.base.dto.CommonQuery
import cn.lihongjie.coal.smartCamCarData.controller.SmartCamCarDataController
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.context.ApplicationContext
ApplicationContext ioc = ioc
def controller = ioc.getBean(SmartCamCarDataController.class)
def objectMapper = ioc.getBean(ObjectMapper.class) as ObjectMapper
return controller.list(params!=null ? objectMapper.convertValue(params, CommonQuery.class ) : new CommonQuery())