feat(asset): integrate inspection records with prepare and delivery order

- Add inspection_record_id column to asset_vehicle_prepare and asset_delivery_order tables
- Add inspectionRecordId field to VehiclePrepareDO, DeliveryOrderDO, and their RespVOs
- Auto-create inspection record from matched template when creating a prepare record
- Auto-complete inspection record when completing a prepare record
- Clone prepare inspection record to delivery order on delivery order creation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-03-13 10:06:55 +08:00
parent 1dca703caa
commit 46485289a2
8 changed files with 604 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
-- 交车单表增加验车记录关联字段
ALTER TABLE asset_delivery_order
ADD COLUMN inspection_record_id bigint DEFAULT NULL COMMENT '关联验车记录';

View File

@@ -0,0 +1,3 @@
-- 备车表增加验车记录关联字段
ALTER TABLE asset_vehicle_prepare
ADD COLUMN inspection_record_id bigint DEFAULT NULL COMMENT '关联验车记录' AFTER defect_photos;

View File

@@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 交车单 Response VO")
@Data
public class DeliveryOrderRespVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "交车单编码")
private String orderCode;
@Schema(description = "交车任务ID")
private Long taskId;
@Schema(description = "任务编码")
private String taskCode;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "交车日期")
private LocalDateTime deliveryDate;
@Schema(description = "交车人")
private String deliveryPerson;
@Schema(description = "交车地点")
private String deliveryLocation;
@Schema(description = "被授权人姓名")
private String authorizedPersonName;
@Schema(description = "被授权人电话")
private String authorizedPersonPhone;
@Schema(description = "E签宝状态")
private Integer esignStatus;
@Schema(description = "交车照片")
private String deliveryPhotos;
@Schema(description = "司机姓名")
private String driverName;
@Schema(description = "司机身份证")
private String driverIdCard;
@Schema(description = "司机手机号")
private String driverPhone;
@Schema(description = "交检清单JSON")
private String inspectionData;
@Schema(description = "费用信息JSON")
private String costList;
@Schema(description = "关联验车记录ID")
private Long inspectionRecordId;
@Schema(description = "状态")
private Integer status;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "车辆列表")
private List<DeliveryOrderVehicleVO> vehicles;
}

View File

@@ -0,0 +1,106 @@
package cn.iocoder.yudao.module.asset.controller.admin.prepare.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 备车记录 Response VO")
@Data
public class VehiclePrepareRespVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码")
private String vin;
@Schema(description = "车型ID")
private Long vehicleModelId;
@Schema(description = "品牌")
private String brand;
@Schema(description = "型号")
private String model;
@Schema(description = "车辆类型")
private String vehicleType;
@Schema(description = "停车场")
private String parkingLot;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "整备类型")
private String preparationType;
@Schema(description = "里程(km)")
private Integer mileage;
@Schema(description = "剩余氢量")
private BigDecimal hydrogenRemaining;
@Schema(description = "氢量单位")
private String hydrogenUnit;
@Schema(description = "剩余电量(%)")
private BigDecimal batteryRemaining;
@Schema(description = "是否有车身广告")
private Boolean hasBodyAd;
@Schema(description = "广告照片")
private String bodyAdPhotos;
@Schema(description = "放大字照片")
private String enlargedTextPhoto;
@Schema(description = "是否有尾板")
private Boolean hasTailLift;
@Schema(description = "备胎胎纹深度(mm)")
private BigDecimal spareTireDepth;
@Schema(description = "备胎照片")
private String spareTirePhoto;
@Schema(description = "挂车牌号")
private String trailerPlateNo;
@Schema(description = "瑕疵照片")
private String defectPhotos;
@Schema(description = "关联验车记录ID")
private Long inspectionRecordId;
@Schema(description = "检查清单JSON")
private String checkList;
@Schema(description = "备注")
private String remark;
@Schema(description = "状态")
private Integer status;
@Schema(description = "完成时间")
private LocalDateTime completeTime;
@Schema(description = "创建者")
private String creator;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.delivery;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
* 交车单 DO
*/
@TableName("asset_delivery_order")
@KeySequence("asset_delivery_order_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryOrderDO extends BaseDO {
@TableId
private Long id;
private String orderCode;
private Long taskId;
private String taskCode;
private Long contractId;
private String contractCode;
private String projectName;
private Long customerId;
private String customerName;
private LocalDateTime deliveryDate;
private String deliveryPerson;
private String deliveryLocation;
private Long authorizedPersonId;
private String authorizedPersonName;
private String authorizedPersonPhone;
private String authorizedPersonIdCard;
private String esignFlowId;
private Integer esignStatus;
private String deliveryPhotos;
private String driverName;
private String driverIdCard;
private String driverPhone;
private String inspectionData;
private String costList;
/**
* 关联验车记录ID
*/
private Long inspectionRecordId;
/**
* 状态0=待完成 1=已完成)
*/
private Integer status;
}

View File

@@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.prepare;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 备车记录 DO
*/
@TableName("asset_vehicle_prepare")
@KeySequence("asset_vehicle_prepare_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VehiclePrepareDO extends BaseDO {
@TableId
private Long id;
private Long vehicleId;
private String plateNo;
private String vin;
private Long vehicleModelId;
private String brand;
private String model;
private String vehicleType;
private String parkingLot;
private Long contractId;
private String contractCode;
private String preparationType;
private Integer mileage;
private BigDecimal hydrogenRemaining;
private String hydrogenUnit;
private BigDecimal batteryRemaining;
private Boolean hasBodyAd;
private String bodyAdPhotos;
private String enlargedTextPhoto;
private Boolean hasTailLift;
private BigDecimal spareTireDepth;
private String spareTirePhoto;
private String trailerPlateNo;
private String defectPhotos;
/**
* 关联验车记录ID
*/
private Long inspectionRecordId;
private String checkList;
private String remark;
/**
* 状态0=待提交 1=已完成)
*/
private Integer status;
private LocalDateTime completeTime;
}

View File

@@ -0,0 +1,182 @@
package cn.iocoder.yudao.module.asset.service.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.inspection.InspectionRecordDO;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.*;
import cn.iocoder.yudao.module.asset.enums.inspection.InspectionSourceTypeEnum;
import cn.iocoder.yudao.module.asset.service.inspection.InspectionRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*;
@Service
@Validated
public class DeliveryOrderServiceImpl implements DeliveryOrderService {
@Resource
private DeliveryOrderMapper deliveryOrderMapper;
@Resource
private DeliveryOrderVehicleMapper deliveryOrderVehicleMapper;
@Resource
private DeliveryTaskMapper deliveryTaskMapper;
@Resource
private DeliveryTaskVehicleMapper deliveryTaskVehicleMapper;
@Resource
private InspectionRecordService inspectionRecordService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createDeliveryOrder(DeliveryOrderSaveReqVO createReqVO) {
// Get task info
DeliveryTaskDO task = deliveryTaskMapper.selectById(createReqVO.getTaskId());
if (task == null) {
throw exception(DELIVERY_TASK_NOT_EXISTS);
}
if (task.getTaskStatus().equals(1)) {
throw exception(DELIVERY_TASK_SUSPENDED);
}
// Generate order code
String orderCode = task.getTaskCode() + "O0001";
DeliveryOrderDO order = BeanUtils.toBean(createReqVO, DeliveryOrderDO.class);
order.setOrderCode(orderCode);
order.setTaskCode(task.getTaskCode());
order.setContractId(task.getContractId());
order.setContractCode(task.getContractCode());
order.setProjectName(task.getProjectName());
order.setCustomerId(task.getCustomerId());
order.setCustomerName(task.getCustomerName());
order.setStatus(0); // pending
order.setEsignStatus(0);
deliveryOrderMapper.insert(order);
// Insert vehicles
if (createReqVO.getVehicles() != null) {
for (DeliveryOrderVehicleVO vehicleVO : createReqVO.getVehicles()) {
DeliveryOrderVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryOrderVehicleDO.class);
vehicle.setOrderId(order.getId());
deliveryOrderVehicleMapper.insert(vehicle);
}
// 从第一辆车的备车验车记录克隆交车验车记录
DeliveryOrderVehicleVO firstVehicle = createReqVO.getVehicles().get(0);
if (firstVehicle.getVehicleId() != null) {
InspectionRecordDO prepareRecord = inspectionRecordService.getLatestRecord(
firstVehicle.getVehicleId(), InspectionSourceTypeEnum.PREPARE.getType());
if (prepareRecord != null) {
Long inspectionRecordId = inspectionRecordService.cloneRecord(
prepareRecord.getId(), InspectionSourceTypeEnum.DELIVERY.getType(), order.getId());
deliveryOrderMapper.updateById(DeliveryOrderDO.builder()
.id(order.getId()).inspectionRecordId(inspectionRecordId).build());
}
}
}
return order.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateDeliveryOrder(DeliveryOrderSaveReqVO updateReqVO) {
DeliveryOrderDO order = validateDeliveryOrderExists(updateReqVO.getId());
if (order.getStatus().equals(1)) {
throw exception(DELIVERY_ORDER_ALREADY_COMPLETED);
}
DeliveryOrderDO updateObj = BeanUtils.toBean(updateReqVO, DeliveryOrderDO.class);
deliveryOrderMapper.updateById(updateObj);
// Update vehicles
if (updateReqVO.getVehicles() != null) {
deliveryOrderVehicleMapper.deleteByOrderId(updateReqVO.getId());
for (DeliveryOrderVehicleVO vehicleVO : updateReqVO.getVehicles()) {
DeliveryOrderVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryOrderVehicleDO.class);
vehicle.setOrderId(updateReqVO.getId());
deliveryOrderVehicleMapper.insert(vehicle);
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteDeliveryOrder(Long id) {
validateDeliveryOrderExists(id);
deliveryOrderMapper.deleteById(id);
deliveryOrderVehicleMapper.deleteByOrderId(id);
}
@Override
public DeliveryOrderDO getDeliveryOrder(Long id) {
return deliveryOrderMapper.selectById(id);
}
@Override
public DeliveryOrderRespVO getDeliveryOrderDetail(Long id) {
DeliveryOrderDO order = validateDeliveryOrderExists(id);
DeliveryOrderRespVO respVO = BeanUtils.toBean(order, DeliveryOrderRespVO.class);
List<DeliveryOrderVehicleDO> vehicles = deliveryOrderVehicleMapper.selectListByOrderId(id);
respVO.setVehicles(BeanUtils.toBean(vehicles, DeliveryOrderVehicleVO.class));
return respVO;
}
@Override
public PageResult<DeliveryOrderDO> getDeliveryOrderPage(DeliveryOrderPageReqVO pageReqVO) {
return deliveryOrderMapper.selectPage(pageReqVO);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void completeDeliveryOrder(Long id) {
DeliveryOrderDO order = validateDeliveryOrderExists(id);
if (order.getStatus().equals(1)) {
throw exception(DELIVERY_ORDER_ALREADY_COMPLETED);
}
// Update order status
deliveryOrderMapper.updateById(DeliveryOrderDO.builder().id(id).status(1).build());
// Mark task vehicles as delivered
List<DeliveryOrderVehicleDO> orderVehicles = deliveryOrderVehicleMapper.selectListByOrderId(id);
for (DeliveryOrderVehicleDO ov : orderVehicles) {
deliveryTaskVehicleMapper.updateById(
DeliveryTaskVehicleDO.builder()
.id(ov.getTaskVehicleId())
.isDelivered(true)
.build()
);
}
// Check if all task vehicles are delivered, update task delivery status
DeliveryTaskDO task = deliveryTaskMapper.selectById(order.getTaskId());
if (task != null) {
List<DeliveryTaskVehicleDO> taskVehicles = deliveryTaskVehicleMapper.selectListByTaskId(task.getId());
boolean allDelivered = taskVehicles.stream().allMatch(v -> Boolean.TRUE.equals(v.getIsDelivered()));
if (allDelivered) {
deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(task.getId()).deliveryStatus(1).build());
}
}
}
private DeliveryOrderDO validateDeliveryOrderExists(Long id) {
DeliveryOrderDO order = deliveryOrderMapper.selectById(id);
if (order == null) {
throw exception(DELIVERY_ORDER_NOT_EXISTS);
}
return order;
}
}

View File

@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.asset.service.prepare;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.asset.controller.admin.prepare.vo.VehiclePreparePageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.prepare.vo.VehiclePrepareSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.inspection.InspectionTemplateDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.prepare.VehiclePrepareDO;
import cn.iocoder.yudao.module.asset.dal.mysql.prepare.VehiclePrepareMapper;
import cn.iocoder.yudao.module.asset.enums.inspection.InspectionSourceTypeEnum;
import cn.iocoder.yudao.module.asset.service.inspection.InspectionRecordService;
import cn.iocoder.yudao.module.asset.service.inspection.InspectionTemplateService;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*;
@Service
@Validated
public class VehiclePrepareServiceImpl implements VehiclePrepareService {
@Resource
private VehiclePrepareMapper vehiclePrepareMapper;
@Resource
private InspectionTemplateService inspectionTemplateService;
@Resource
private InspectionRecordService inspectionRecordService;
@Override
public Long createVehiclePrepare(VehiclePrepareSaveReqVO createReqVO) {
VehiclePrepareDO prepare = BeanUtils.toBean(createReqVO, VehiclePrepareDO.class);
vehiclePrepareMapper.insert(prepare);
// 自动匹配验车模板并创建验车记录
InspectionTemplateDO template = inspectionTemplateService.matchTemplate(
InspectionSourceTypeEnum.PREPARE.getType(), prepare.getVehicleType());
if (template != null) {
Long inspectionRecordId = inspectionRecordService.createRecord(
template.getId(), InspectionSourceTypeEnum.PREPARE.getType(),
prepare.getId(), prepare.getVehicleId());
// 回写验车记录ID
vehiclePrepareMapper.updateById(VehiclePrepareDO.builder()
.id(prepare.getId()).inspectionRecordId(inspectionRecordId).build());
}
return prepare.getId();
}
@Override
public void updateVehiclePrepare(VehiclePrepareSaveReqVO updateReqVO) {
validateVehiclePrepareExists(updateReqVO.getId());
VehiclePrepareDO updateObj = BeanUtils.toBean(updateReqVO, VehiclePrepareDO.class);
vehiclePrepareMapper.updateById(updateObj);
}
@Override
public void deleteVehiclePrepare(Long id) {
validateVehiclePrepareExists(id);
vehiclePrepareMapper.deleteById(id);
}
@Override
public VehiclePrepareDO getVehiclePrepare(Long id) {
return vehiclePrepareMapper.selectById(id);
}
@Override
public PageResult<VehiclePrepareDO> getVehiclePreparePage(VehiclePreparePageReqVO pageReqVO) {
return vehiclePrepareMapper.selectPage(pageReqVO);
}
@Override
public void completeVehiclePrepare(Long id) {
VehiclePrepareDO prepare = validateVehiclePrepareExists(id);
if (prepare.getStatus().equals(1)) {
throw exception(VEHICLE_PREPARE_ALREADY_COMPLETED);
}
vehiclePrepareMapper.updateById(VehiclePrepareDO.builder()
.id(id).status(1).completeTime(LocalDateTime.now()).build());
// 同时完成关联的验车记录
if (prepare.getInspectionRecordId() != null) {
inspectionRecordService.completeRecord(prepare.getInspectionRecordId(), null);
}
}
private VehiclePrepareDO validateVehiclePrepareExists(Long id) {
VehiclePrepareDO prepare = vehiclePrepareMapper.selectById(id);
if (prepare == null) {
throw exception(VEHICLE_PREPARE_NOT_EXISTS);
}
return prepare;
}
}