refactor(energy): 简化事件驱动系统(7个→3个)

- 删除旧事件:BillApprovedEvent, BillCreatedEvent, DeductionCompletedEvent, DetailAuditedEvent, DetailCreatedEvent, RecordMatchedEvent
- 新增事件:BillAuditPassedEvent, DetailAuditPassedEvent
- 保留事件:RecordImportedEvent
- 更新监听器:AccountEventListener, BillEventListener, DetailEventListener
- 清理代码中的旧事件引用和注释

优化原则:前端简单,后端健壮
事件流程:导入→匹配→生成明细→审核→扣款→生成账单→结算
This commit is contained in:
kkfluous
2026-03-16 12:53:14 +08:00
parent f5062cec22
commit 2f38a703f9
167 changed files with 9876 additions and 824 deletions

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.asset;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 资产管理模块 Application
@@ -9,6 +10,12 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author 芋道源码
*/
@SpringBootApplication
@EnableFeignClients(basePackages = {
"cn.iocoder.yudao.module.system.api", // System 模块 API
"cn.iocoder.yudao.module.ocr.api", // OCR 模块 API
"cn.iocoder.yudao.module.infra.api", // Infra 模块 API
"cn.iocoder.yudao.module.bpm.api" // BPM 模块 API
})
public class AssetServerApplication {
public static void main(String[] args) {

View File

@@ -0,0 +1,212 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.asset.convert.contract.ContractConvert;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.*;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.*;
import cn.iocoder.yudao.module.asset.service.contract.ContractService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* 车辆租赁合同 Controller
*
* @author 芋道源码
*/
@Tag(name = "管理后台 - 车辆租赁合同管理")
@RestController
@RequestMapping("/asset/contract")
@Validated
public class ContractController {
@Resource
private ContractService contractService;
@Resource
private ContractVehicleMapper contractVehicleMapper;
@Resource
private ContractVehicleServiceMapper contractVehicleServiceMapper;
@Resource
private ContractAuthorizedMapper contractAuthorizedMapper;
@Resource
private ContractChangeHistoryMapper contractChangeHistoryMapper;
@PostMapping("/create")
@Operation(summary = "创建合同")
@PreAuthorize("@ss.hasPermission('asset:contract:create')")
public CommonResult<Long> createContract(@Valid @RequestBody ContractSaveReqVO createReqVO) {
return success(contractService.createContract(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新合同")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Boolean> updateContract(@Valid @RequestBody ContractSaveReqVO updateReqVO) {
contractService.updateContract(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除合同")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:contract:delete')")
public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) {
contractService.deleteContract(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除合同")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('asset:contract:delete')")
public CommonResult<Boolean> deleteContractList(@RequestParam("ids") List<Long> ids) {
for (Long id : ids) {
contractService.deleteContract(id);
}
return success(true);
}
@GetMapping("/export-excel")
@Operation(summary = "导出合同 Excel")
@PreAuthorize("@ss.hasPermission('asset:contract:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportContractExcel(HttpServletResponse response, @Valid ContractPageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<ContractDO> list = contractService.getContractPage(exportReqVO).getList();
// 输出
ExcelUtils.write(response, "车辆租赁合同.xls", "合同数据", ContractExcelVO.class,
BeanUtils.toBean(list, ContractExcelVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得合同详情")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('asset:contract:query')")
public CommonResult<ContractDetailRespVO> getContract(@RequestParam("id") Long id) {
return success(contractService.getContractDetail(id));
}
@GetMapping("/page")
@Operation(summary = "获得合同分页")
@PreAuthorize("@ss.hasPermission('asset:contract:query')")
public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid ContractPageReqVO pageReqVO) {
PageResult<ContractDO> pageResult = contractService.getContractPage(pageReqVO);
PageResult<ContractRespVO> voPage = ContractConvert.INSTANCE.convertPage(pageResult);
// 填充车辆数和已交车辆数
for (ContractRespVO vo : voPage.getList()) {
List<ContractVehicleDO> vehicles = contractVehicleMapper.selectListByContractId(vo.getId());
vo.setVehicleCount(vehicles.size());
vo.setDeliveredCount((int) vehicles.stream()
.filter(v -> Integer.valueOf(1).equals(v.getVehicleStatus()))
.count());
}
return success(voPage);
}
@GetMapping("/change-history")
@Operation(summary = "获得合同变更历史")
@Parameter(name = "contractId", description = "合同ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('asset:contract:query')")
public CommonResult<List<ContractChangeHistoryDO>> getChangeHistory(@RequestParam("contractId") Long contractId) {
return success(contractChangeHistoryMapper.selectListByContractId(contractId));
}
@PostMapping("/submit-approval")
@Operation(summary = "提交合同审批")
@Parameter(name = "id", description = "合同ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<String> submitApproval(@RequestParam("id") Long id) {
return success(contractService.submitContractApproval(id));
}
@PostMapping("/withdraw-approval")
@Operation(summary = "撤回合同审批")
@Parameter(name = "id", description = "合同ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Boolean> withdrawApproval(@RequestParam("id") Long id) {
contractService.withdrawContractApproval(id);
return success(true);
}
@PostMapping("/terminate")
@Operation(summary = "终止合同")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Boolean> terminateContract(@RequestParam("id") Long id,
@RequestParam("reason") String reason) {
contractService.terminateContract(id, reason);
return success(true);
}
@GetMapping("/simple-list")
@Operation(summary = "获得合同精简列表", description = "用于下拉选择")
public CommonResult<List<ContractSimpleRespVO>> getContractSimpleList() {
List<ContractDO> list = contractService.getContractSimpleList();
return success(BeanUtils.toBean(list, ContractSimpleRespVO.class));
}
@PostMapping("/renew")
@Operation(summary = "续签合同")
@PreAuthorize("@ss.hasPermission('asset:contract:create')")
public CommonResult<Long> renewContract(@RequestParam("id") Long id,
@Valid @RequestBody ContractSaveReqVO newContractReqVO) {
return success(contractService.renewContract(id, newContractReqVO));
}
@PostMapping("/convert-to-third-party")
@Operation(summary = "变更为三方合同")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Long> convertToThirdParty(@RequestParam("id") Long id,
@Valid @RequestBody ContractSaveReqVO newContractReqVO) {
return success(contractService.convertToThirdParty(id, newContractReqVO));
}
@PostMapping("/convert-to-formal")
@Operation(summary = "试用合同转正式")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Long> convertToFormal(@RequestParam("id") Long id,
@Valid @RequestBody ContractSaveReqVO newContractReqVO) {
return success(contractService.convertToFormal(id, newContractReqVO));
}
@PostMapping("/add-vehicle")
@Operation(summary = "往现有合同追加车辆")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Boolean> addVehicle(@RequestParam("id") Long id,
@Valid @RequestBody List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles) {
contractService.addVehiclesToContract(id, vehicles);
return success(true);
}
@PostMapping("/upload-seal")
@Operation(summary = "上传盖章合同附件")
@PreAuthorize("@ss.hasPermission('asset:contract:update')")
public CommonResult<Boolean> uploadSeal(@RequestParam("id") Long id,
@RequestParam("fileUrl") String fileUrl,
@RequestParam("fileName") String fileName) {
contractService.uploadSealedContract(id, fileUrl, fileName);
return success(true);
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 合同附件 VO
*
* @author 芋道源码
*/
@Data
public class ContractAttachmentVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "附件类型1=合同原件 2=盖章合同)", example = "1")
private Integer attachmentType;
@Schema(description = "文件名称", example = "合同扫描件.pdf")
private String fileName;
@Schema(description = "文件URL", example = "https://xxx.com/contract.pdf")
private String fileUrl;
@Schema(description = "文件大小(字节)", example = "1024000")
private Long fileSize;
@Schema(description = "上传时间", example = "2026-01-01 00:00:00")
private LocalDateTime uploadTime;
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 合同被授权人 VO
*
* @author 芋道源码
*/
@Data
public class ContractAuthorizedVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
private String name;
@Schema(description = "电话", requiredMode = Schema.RequiredMode.REQUIRED, example = "13800138000")
private String phone;
@Schema(description = "身份证号", requiredMode = Schema.RequiredMode.REQUIRED, example = "310101199001011234")
private String idCard;
}

View File

@@ -0,0 +1,101 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 车辆租赁合同 Base VO
*
* @author 芋道源码
*/
@Data
public class ContractBaseVO {
@Schema(description = "合同编码(新增时自动生成)", example = "HT-2026-001")
private String contractCode;
@Schema(description = "合同类型1=试用 2=正式)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "合同类型不能为空")
private Integer contractType;
@Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张江高科技园区车辆租赁项目")
@NotBlank(message = "项目名称不能为空")
private String projectName;
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01")
@NotNull(message = "生效日期不能为空")
private LocalDate startDate;
@Schema(description = "结束日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-12-31")
@NotNull(message = "结束日期不能为空")
private LocalDate endDate;
@Schema(description = "付款方式", example = "月付")
private String paymentMethod;
@Schema(description = "付款周期", example = "每月1日")
private String paymentCycle;
@Schema(description = "签约公司(乙方)", example = "上海某某汽车租赁有限公司")
private String signingCompany;
@Schema(description = "交车省份", example = "上海市")
private String deliveryProvince;
@Schema(description = "交车城市", example = "浦东新区")
private String deliveryCity;
@Schema(description = "交车地点", example = "张江高科技园区")
private String deliveryLocation;
@Schema(description = "备注", example = "特殊要求说明")
private String remark;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "客户ID不能为空")
private Long customerId;
@Schema(description = "客户名称由后端根据客户ID自动填充", example = "上海某某科技有限公司")
private String customerName;
@Schema(description = "是否三方合同", example = "false")
private Boolean thirdPartyEnabled;
@Schema(description = "丙方客户ID", example = "2")
private Long thirdPartyCustomerId;
@Schema(description = "丙方名称", example = "北京某某公司")
private String thirdPartyName;
@Schema(description = "业务部门ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "业务部门ID不能为空")
private Long businessDeptId;
@Schema(description = "业务负责人ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "业务负责人ID不能为空")
private Long businessManagerId;
@Schema(description = "氢费承担方", example = "客户")
private String hydrogenBearer;
@Schema(description = "氢气付款方式", example = "预付")
private String hydrogenPaymentMethod;
@Schema(description = "氢气预付款", example = "5000.00")
private BigDecimal hydrogenPrepay;
@Schema(description = "退还车氢气单价", example = "35.00")
private BigDecimal hydrogenReturnPrice;
@Schema(description = "账单计算方式", example = "按自然月结算")
private String billingMethod;
@Schema(description = "主车型", example = "氢能重卡")
private String mainVehicleType;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
/**
* 车辆租赁合同详情 Response VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 车辆租赁合同详情 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContractDetailRespVO extends ContractRespVO {
@Schema(description = "车辆订单列表")
private List<ContractVehicleDetailVO> vehicles;
@Schema(description = "被授权人列表")
private List<ContractAuthorizedVO> authorizedPersons;
@Schema(description = "附件列表")
private List<ContractAttachmentVO> attachments;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public static class ContractVehicleDetailVO extends ContractVehicleVO {
@Schema(description = "服务项目列表")
private List<ContractVehicleServiceVO> services;
}
}

View File

@@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 车辆租赁合同 Excel 导出 VO
*
* @author 芋道源码
*/
@Data
@ExcelIgnoreUnannotated
public class ContractExcelVO {
@ExcelProperty("合同编码")
private String contractCode;
@ExcelProperty("合同类型")
private Integer contractType;
@ExcelProperty("项目名称")
private String projectName;
@ExcelProperty("客户名称")
private String customerName;
@ExcelProperty("签约公司")
private String signingCompany;
@ExcelProperty("生效日期")
private LocalDate startDate;
@ExcelProperty("结束日期")
private LocalDate endDate;
@ExcelProperty("付款方式")
private String paymentMethod;
@ExcelProperty("付款周期")
private String paymentCycle;
@ExcelProperty("交车省份")
private String deliveryProvince;
@ExcelProperty("交车城市")
private String deliveryCity;
@ExcelProperty("交车地点")
private String deliveryLocation;
@ExcelProperty("主车型")
private String mainVehicleType;
@ExcelProperty("审批状态")
private Integer approvalStatus;
@ExcelProperty("合同状态")
private Integer contractStatus;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@ExcelProperty("备注")
private String remark;
}

View File

@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* 车辆租赁合同分页查询 Request VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 车辆租赁合同分页查询 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContractPageReqVO extends PageParam {
@Schema(description = "合同编码", example = "HT-2026-001")
private String contractCode;
@Schema(description = "合同类型1=试用 2=正式)", example = "1")
private Integer contractType;
@Schema(description = "项目名称(模糊搜索)", example = "张江高科技园区")
private String projectName;
@Schema(description = "客户ID", example = "1")
private Long customerId;
@Schema(description = "客户名称(模糊搜索)", example = "上海某某科技")
private String customerName;
@Schema(description = "审批状态0=草稿 1=审批中 2=审批通过 3=审批拒绝 4=已撤回)", example = "0")
private Integer approvalStatus;
@Schema(description = "合同状态0=草稿 1=待生效 2=进行中 3=已到期 4=已终止 5=已续签)", example = "0")
private Integer contractStatus;
@Schema(description = "生效日期", example = "[2026-01-01, 2026-12-31]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate[] startDate;
@Schema(description = "结束日期", example = "[2026-01-01, 2026-12-31]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate[] endDate;
@Schema(description = "业务部门ID", example = "1")
private Long businessDeptId;
@Schema(description = "签约公司", example = "嘉兴羚牛")
private String signingCompany;
@Schema(description = "业务负责人ID", example = "1")
private Long businessManagerId;
@Schema(description = "创建人", example = "admin")
private String creator;
}

View File

@@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 车辆租赁合同 Response VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 车辆租赁合同 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContractRespVO extends ContractBaseVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "审批状态0=草稿 1=审批中 2=审批通过 3=审批拒绝 4=已撤回)", example = "0")
private Integer approvalStatus;
@Schema(description = "BPM流程实例ID", example = "123456")
private String bpmInstanceId;
@Schema(description = "合同状态0=草稿 1=待生效 2=进行中 3=已到期 4=已终止 5=已续签)", example = "0")
private Integer contractStatus;
@Schema(description = "实际生效时间", example = "2026-01-01 00:00:00")
private LocalDateTime effectiveTime;
@Schema(description = "终止时间", example = "2026-12-31 23:59:59")
private LocalDateTime terminateTime;
@Schema(description = "终止原因", example = "客户要求终止")
private String terminateReason;
@Schema(description = "续签后的新合同ID", example = "2")
private Long renewedContractId;
@Schema(description = "原合同ID如果是续签合同", example = "1")
private Long originalContractId;
@Schema(description = "创建时间", example = "2026-01-01 00:00:00")
private LocalDateTime createTime;
@Schema(description = "创建者", example = "admin")
private String creator;
@Schema(description = "租赁车辆数", example = "5")
private Integer vehicleCount;
@Schema(description = "已交车辆数", example = "3")
private Integer deliveredCount;
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
/**
* 车辆租赁合同创建/更新 Request VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 车辆租赁合同创建/更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ContractSaveReqVO extends ContractBaseVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "车辆订单列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "车辆订单列表不能为空")
@Valid
private List<ContractVehicleSaveVO> vehicles;
@Schema(description = "被授权人列表")
@Valid
private List<ContractAuthorizedVO> authorizedPersons;
@Data
public static class ContractVehicleSaveVO extends ContractVehicleVO {
@Schema(description = "服务项目列表")
@Valid
private List<ContractVehicleServiceVO> services;
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 合同精简 Response VO")
@Data
public class ContractSimpleRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "合同编码", example = "HT-2026-0001")
private String contractCode;
@Schema(description = "项目名称", example = "XX物流项目")
private String projectName;
@Schema(description = "客户名称", example = "XX公司")
private String customerName;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 合同车辆服务项目 VO
*
* @author 芋道源码
*/
@Data
public class ContractVehicleServiceVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "服务项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "保险")
private String serviceName;
@Schema(description = "服务费用(元)", requiredMode = Schema.RequiredMode.REQUIRED, example = "3000.00")
private BigDecimal serviceFee;
@Schema(description = "生效日期", example = "2026-01-01")
private LocalDate effectiveDate;
}

View File

@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.asset.controller.admin.contract.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 合同车辆 VO
*
* @author 芋道源码
*/
@Data
public class ContractVehicleVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "车辆ID", example = "1")
private Long vehicleId;
@Schema(description = "品牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "丰田")
private String brand;
@Schema(description = "型号", requiredMode = Schema.RequiredMode.REQUIRED, example = "凯美瑞")
private String model;
@Schema(description = "车牌号", example = "沪A12345")
private String plateNo;
@Schema(description = "VIN码", example = "LVSHCAMB1CE012345")
private String vin;
@Schema(description = "月租金(元)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5000.00")
private BigDecimal monthRent;
@Schema(description = "保证金(元)", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
private BigDecimal deposit;
@Schema(description = "车辆状态0=待交车 1=已交车 2=已退车)", example = "0")
private Integer vehicleStatus;
@Schema(description = "备注", example = "特殊要求")
private String remark;
}

View File

@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -75,4 +76,11 @@ public class CustomerController {
return success(CustomerConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/simple-list")
@Operation(summary = "获得客户精简列表", description = "用于下拉选择")
public CommonResult<List<CustomerRespVO>> getCustomerSimpleList() {
List<CustomerDO> list = customerService.getCustomerSimpleList();
return success(CustomerConvert.INSTANCE.convertList(list));
}
}

View File

@@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.DeliveryOrderDO;
import cn.iocoder.yudao.module.asset.service.delivery.DeliveryOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 交车单")
@RestController
@RequestMapping("/asset/delivery-order")
@Validated
public class DeliveryOrderController {
@Resource
private DeliveryOrderService deliveryOrderService;
@PostMapping("/create")
@Operation(summary = "创建交车单")
@PreAuthorize("@ss.hasPermission('asset:delivery-order:create')")
public CommonResult<Long> createDeliveryOrder(@Valid @RequestBody DeliveryOrderSaveReqVO createReqVO) {
return success(deliveryOrderService.createDeliveryOrder(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新交车单")
@PreAuthorize("@ss.hasPermission('asset:delivery-order:update')")
public CommonResult<Boolean> updateDeliveryOrder(@Valid @RequestBody DeliveryOrderSaveReqVO updateReqVO) {
deliveryOrderService.updateDeliveryOrder(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除交车单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-order:delete')")
public CommonResult<Boolean> deleteDeliveryOrder(@RequestParam("id") Long id) {
deliveryOrderService.deleteDeliveryOrder(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得交车单详情")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-order:query')")
public CommonResult<DeliveryOrderRespVO> getDeliveryOrder(@RequestParam("id") Long id) {
return success(deliveryOrderService.getDeliveryOrderDetail(id));
}
@GetMapping("/page")
@Operation(summary = "获得交车单分页")
@PreAuthorize("@ss.hasPermission('asset:delivery-order:query')")
public CommonResult<PageResult<DeliveryOrderRespVO>> getDeliveryOrderPage(@Valid DeliveryOrderPageReqVO pageReqVO) {
PageResult<DeliveryOrderDO> pageResult = deliveryOrderService.getDeliveryOrderPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DeliveryOrderRespVO.class));
}
@PutMapping("/complete")
@Operation(summary = "完成交车")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-order:update')")
public CommonResult<Boolean> completeDeliveryOrder(@RequestParam("id") Long id) {
deliveryOrderService.completeDeliveryOrder(id);
return success(true);
}
}

View File

@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.DeliveryTaskDO;
import cn.iocoder.yudao.module.asset.service.delivery.DeliveryTaskService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 交车任务")
@RestController
@RequestMapping("/asset/delivery-task")
@Validated
public class DeliveryTaskController {
@Resource
private DeliveryTaskService deliveryTaskService;
@PostMapping("/create")
@Operation(summary = "创建交车任务")
@PreAuthorize("@ss.hasPermission('asset:delivery-task:create')")
public CommonResult<Long> createDeliveryTask(@Valid @RequestBody DeliveryTaskSaveReqVO createReqVO) {
return success(deliveryTaskService.createDeliveryTask(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新交车任务")
@PreAuthorize("@ss.hasPermission('asset:delivery-task:update')")
public CommonResult<Boolean> updateDeliveryTask(@Valid @RequestBody DeliveryTaskSaveReqVO updateReqVO) {
deliveryTaskService.updateDeliveryTask(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除交车任务")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-task:delete')")
public CommonResult<Boolean> deleteDeliveryTask(@RequestParam("id") Long id) {
deliveryTaskService.deleteDeliveryTask(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得交车任务详情")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-task:query')")
public CommonResult<DeliveryTaskRespVO> getDeliveryTask(@RequestParam("id") Long id) {
return success(deliveryTaskService.getDeliveryTaskDetail(id));
}
@GetMapping("/page")
@Operation(summary = "获得交车任务分页")
@PreAuthorize("@ss.hasPermission('asset:delivery-task:query')")
public CommonResult<PageResult<DeliveryTaskRespVO>> getDeliveryTaskPage(@Valid DeliveryTaskPageReqVO pageReqVO) {
PageResult<DeliveryTaskDO> pageResult = deliveryTaskService.getDeliveryTaskPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DeliveryTaskRespVO.class));
}
@GetMapping("/contract-page")
@Operation(summary = "获得交车任务按合同分组分页")
@PreAuthorize("@ss.hasPermission('asset:delivery-task:query')")
public CommonResult<PageResult<DeliveryTaskContractGroupRespVO>> getDeliveryTaskContractPage(
@Valid DeliveryTaskPageReqVO pageReqVO) {
return success(deliveryTaskService.getDeliveryTaskContractPage(pageReqVO));
}
@GetMapping("/simple-list")
@Operation(summary = "获得交车任务精简列表", description = "用于下拉选择")
public CommonResult<java.util.List<DeliveryTaskSimpleRespVO>> getDeliveryTaskSimpleList() {
java.util.List<DeliveryTaskDO> list = deliveryTaskService.getDeliveryTaskSimpleList();
return success(BeanUtils.toBean(list, DeliveryTaskSimpleRespVO.class));
}
@PutMapping("/suspend")
@Operation(summary = "挂起交车任务")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:delivery-task:update')")
public CommonResult<Boolean> suspendDeliveryTask(@RequestParam("id") Long id) {
deliveryTaskService.suspendDeliveryTask(id);
return success(true);
}
@PutMapping("/activate")
@Operation(summary = "激活交车任务")
@PreAuthorize("@ss.hasPermission('asset:delivery-task:update')")
public CommonResult<Boolean> activateDeliveryTask(@Valid @RequestBody DeliveryTaskActivateReqVO reqVO) {
deliveryTaskService.activateDeliveryTask(reqVO);
return success(true);
}
}

View File

@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 交车单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DeliveryOrderPageReqVO extends PageParam {
@Schema(description = "交车单编码")
private String orderCode;
@Schema(description = "任务编码")
private String taskCode;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "交车人")
private String deliveryPerson;
@Schema(description = "交车地区")
private String deliveryRegion;
@Schema(description = "状态")
private Integer status;
@Schema(description = "交车日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] deliveryDate;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 交车单创建/更新 Request VO")
@Data
public class DeliveryOrderSaveReqVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "交车任务ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "交车任务ID不能为空")
private Long taskId;
@Schema(description = "交车日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "交车日期不能为空")
private LocalDateTime deliveryDate;
@Schema(description = "交车人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "交车人不能为空")
private String deliveryPerson;
@Schema(description = "交车地点")
private String deliveryLocation;
@Schema(description = "被授权人ID")
private Long authorizedPersonId;
@Schema(description = "被授权人姓名")
private String authorizedPersonName;
@Schema(description = "被授权人电话")
private String authorizedPersonPhone;
@Schema(description = "被授权人身份证")
private String authorizedPersonIdCard;
@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 = "车辆列表")
private List<DeliveryOrderVehicleVO> vehicles;
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "交车单车辆 VO")
@Data
public class DeliveryOrderVehicleVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "交车任务车辆ID")
private Long taskVehicleId;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码")
private String vin;
@Schema(description = "品牌")
private String brand;
@Schema(description = "型号")
private String model;
@Schema(description = "交车时里程")
private Integer mileage;
@Schema(description = "交车时氢气量kg")
private BigDecimal hydrogenLevel;
@Schema(description = "交车时电量(%")
private BigDecimal batteryLevel;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDate;
@Schema(description = "管理后台 - 交车任务激活 Request VO")
@Data
public class DeliveryTaskActivateReqVO {
@Schema(description = "任务ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "任务ID不能为空")
private Long id;
@Schema(description = "预计交车开始日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "预计交车开始日期不能为空")
private LocalDate expectedDeliveryDateStart;
@Schema(description = "预计交车结束日期")
private LocalDate expectedDeliveryDateEnd;
@Schema(description = "开始计费日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开始计费日期不能为空")
private LocalDate billingStartDate;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
@Schema(description = "管理后台 - 交车任务按合同分组 Response VO")
@Data
public class DeliveryTaskContractGroupRespVO {
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "业务部门名称")
private String businessDeptName;
@Schema(description = "业务负责人名称")
private String businessManagerName;
@Schema(description = "合同生效日期")
private LocalDate startDate;
@Schema(description = "合同结束日期")
private LocalDate endDate;
@Schema(description = "交车任务列表")
private List<DeliveryTaskRespVO> tasks;
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 交车任务分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DeliveryTaskPageReqVO extends PageParam {
@Schema(description = "任务编码")
private String taskCode;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "任务状态")
private Integer taskStatus;
@Schema(description = "交车状态")
private Integer deliveryStatus;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 交车任务 Response VO")
@Data
public class DeliveryTaskRespVO {
@Schema(description = "主键")
private Long id;
@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 LocalDate expectedDeliveryDateStart;
@Schema(description = "预计交车结束日期")
private LocalDate expectedDeliveryDateEnd;
@Schema(description = "开始计费日期")
private LocalDate billingStartDate;
@Schema(description = "交车省份")
private String deliveryProvince;
@Schema(description = "交车城市")
private String deliveryCity;
@Schema(description = "交车地点")
private String deliveryLocation;
@Schema(description = "交车数量")
private Integer vehicleCount;
@Schema(description = "任务状态")
private Integer taskStatus;
@Schema(description = "交车状态")
private Integer deliveryStatus;
@Schema(description = "是否需要还车")
private Boolean needReturn;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "车辆列表")
private List<DeliveryTaskVehicleVO> vehicles;
}

View File

@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
@Schema(description = "管理后台 - 交车任务创建/更新 Request VO")
@Data
public class DeliveryTaskSaveReqVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同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 LocalDate expectedDeliveryDateStart;
@Schema(description = "预计交车结束日期")
private LocalDate expectedDeliveryDateEnd;
@Schema(description = "开始计费日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开始计费日期不能为空")
private LocalDate billingStartDate;
@Schema(description = "交车省份")
private String deliveryProvince;
@Schema(description = "交车城市")
private String deliveryCity;
@Schema(description = "交车地点")
private String deliveryLocation;
@Schema(description = "是否需要还车")
private Boolean needReturn;
@Schema(description = "车辆列表")
private List<DeliveryTaskVehicleVO> vehicles;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 交车任务精简 Response VO")
@Data
public class DeliveryTaskSimpleRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "任务编码", example = "HT-2026-0001JC0001")
private String taskCode;
@Schema(description = "合同编码", example = "HT-2026-0001")
private String contractCode;
@Schema(description = "项目名称", example = "XX物流项目")
private String projectName;
@Schema(description = "客户名称", example = "XX公司")
private String customerName;
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.asset.controller.admin.delivery.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "交车任务车辆 VO")
@Data
public class DeliveryTaskVehicleVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "合同车辆ID")
private Long contractVehicleId;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码")
private String vin;
@Schema(description = "品牌")
private String brand;
@Schema(description = "型号")
private String model;
@Schema(description = "月租金")
private BigDecimal monthRent;
@Schema(description = "保证金")
private BigDecimal deposit;
@Schema(description = "是否已交车")
private Boolean isDelivered;
@Schema(description = "实际交车日期")
private java.time.LocalDateTime actualDeliveryDate;
@Schema(description = "交车人")
private String deliveryPerson;
}

View File

@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.asset.controller.admin.prepare;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.prepare.VehiclePrepareDO;
import cn.iocoder.yudao.module.asset.service.prepare.VehiclePrepareService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 备车管理")
@RestController
@RequestMapping("/asset/vehicle-prepare")
@Validated
public class VehiclePrepareController {
@Resource
private VehiclePrepareService vehiclePrepareService;
@PostMapping("/create")
@Operation(summary = "创建备车记录")
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:create')")
public CommonResult<Long> createVehiclePrepare(@Valid @RequestBody VehiclePrepareSaveReqVO createReqVO) {
return success(vehiclePrepareService.createVehiclePrepare(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新备车记录")
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:update')")
public CommonResult<Boolean> updateVehiclePrepare(@Valid @RequestBody VehiclePrepareSaveReqVO updateReqVO) {
vehiclePrepareService.updateVehiclePrepare(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除备车记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:delete')")
public CommonResult<Boolean> deleteVehiclePrepare(@RequestParam("id") Long id) {
vehiclePrepareService.deleteVehiclePrepare(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得备车记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:query')")
public CommonResult<VehiclePrepareRespVO> getVehiclePrepare(@RequestParam("id") Long id) {
VehiclePrepareDO prepare = vehiclePrepareService.getVehiclePrepare(id);
return success(BeanUtils.toBean(prepare, VehiclePrepareRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得备车记录分页")
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:query')")
public CommonResult<PageResult<VehiclePrepareRespVO>> getVehiclePreparePage(@Valid VehiclePreparePageReqVO pageReqVO) {
PageResult<VehiclePrepareDO> pageResult = vehiclePrepareService.getVehiclePreparePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, VehiclePrepareRespVO.class));
}
@PutMapping("/complete")
@Operation(summary = "完成备车")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:vehicle-prepare:update')")
public CommonResult<Boolean> completeVehiclePrepare(@RequestParam("id") Long id) {
vehiclePrepareService.completeVehiclePrepare(id);
return success(true);
}
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.asset.controller.admin.prepare.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 备车记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class VehiclePreparePageReqVO extends PageParam {
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码")
private String vin;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "车辆类型")
private String vehicleType;
@Schema(description = "品牌")
private String brand;
@Schema(description = "停车场")
private String parkingLot;
@Schema(description = "状态")
private Integer status;
@Schema(description = "备车日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] completeTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.asset.controller.admin.prepare.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 备车记录创建/更新 Request VO")
@Data
public class VehiclePrepareSaveReqVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "车辆ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "车辆ID不能为空")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "VIN码不能为空")
private String vin;
@Schema(description = "车型ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "车型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 = "检查清单JSON")
private String checkList;
@Schema(description = "备注")
private String remark;
@Schema(description = "状态")
private Integer status;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.asset.controller.admin.returnorder.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 还车单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ReturnOrderPageReqVO extends PageParam {
@Schema(description = "还车单编码")
private String orderCode;
@Schema(description = "合同编码")
private String contractCode;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "状态")
private Integer status;
@Schema(description = "还车原因")
private String returnReason;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.asset.controller.admin.returnorder.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 还车单 Response VO")
@Data
public class ReturnOrderRespVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "还车单编码")
private String orderCode;
@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 returnDate;
@Schema(description = "还车验收人")
private String returnPerson;
@Schema(description = "还车地点")
private String returnLocation;
@Schema(description = "还车原因")
private String returnReason;
@Schema(description = "还车原因说明")
private String returnReasonDesc;
@Schema(description = "退还总金额")
private BigDecimal totalRefundAmount;
@Schema(description = "退还保证金")
private BigDecimal depositRefund;
@Schema(description = "氢气退款")
private BigDecimal hydrogenRefund;
@Schema(description = "其他费用")
private BigDecimal otherCharges;
@Schema(description = "还车照片")
private String returnPhotos;
@Schema(description = "状态")
private Integer status;
@Schema(description = "审批状态")
private Integer approvalStatus;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "车辆列表")
private List<ReturnOrderVehicleVO> vehicles;
}

View File

@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.asset.controller.admin.returnorder.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 还车单创建/更新 Request VO")
@Data
public class ReturnOrderSaveReqVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同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 = "还车日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "还车日期不能为空")
private LocalDateTime returnDate;
@Schema(description = "还车验收人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "还车验收人不能为空")
private String returnPerson;
@Schema(description = "还车地点")
private String returnLocation;
@Schema(description = "还车原因")
private String returnReason;
@Schema(description = "还车原因说明")
private String returnReasonDesc;
@Schema(description = "退还保证金")
private BigDecimal depositRefund;
@Schema(description = "氢气退款")
private BigDecimal hydrogenRefund;
@Schema(description = "其他费用")
private BigDecimal otherCharges;
@Schema(description = "还车照片")
private String returnPhotos;
@Schema(description = "车辆列表")
private List<ReturnOrderVehicleVO> vehicles;
}

View File

@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.asset.controller.admin.returnorder.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "还车车辆 VO")
@Data
public class ReturnOrderVehicleVO {
@Schema(description = "主键")
private Long id;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNo;
@Schema(description = "VIN码")
private String vin;
@Schema(description = "品牌")
private String brand;
@Schema(description = "型号")
private String model;
@Schema(description = "还车时里程")
private Integer returnMileage;
@Schema(description = "还车时氢气量")
private BigDecimal returnHydrogenLevel;
@Schema(description = "交车时氢气量")
private BigDecimal deliveryHydrogenLevel;
@Schema(description = "氢气差值")
private BigDecimal hydrogenDiff;
@Schema(description = "氢气单价")
private BigDecimal hydrogenUnitPrice;
@Schema(description = "氢气退款金额")
private BigDecimal hydrogenRefundAmount;
@Schema(description = "检查清单JSON")
private String checkList;
@Schema(description = "瑕疵照片")
private String defectPhotos;
@Schema(description = "车损费")
private BigDecimal vehicleDamageFee;
@Schema(description = "工具损坏费")
private BigDecimal toolDamageFee;
@Schema(description = "未结算保养费")
private BigDecimal unpaidMaintenanceFee;
@Schema(description = "未结算维修费")
private BigDecimal unpaidRepairFee;
@Schema(description = "违章费用")
private BigDecimal violationFee;
@Schema(description = "其他费用")
private BigDecimal otherFee;
}

View File

@@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.asset.controller.admin.station;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.station.vo.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO;
import cn.iocoder.yudao.module.asset.service.station.HydrogenStationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* 加氢站管理 Controller
*
* @author 芋道源码
*/
@Tag(name = "管理后台 - 加氢站管理")
@RestController
@RequestMapping("/asset/hydrogen-station")
@Validated
public class HydrogenStationController {
@Resource
private HydrogenStationService hydrogenStationService;
@PostMapping("/create")
@Operation(summary = "创建加氢站")
@PreAuthorize("@ss.hasPermission('asset:hydrogen-station:create')")
public CommonResult<Long> createHydrogenStation(@Valid @RequestBody HydrogenStationSaveReqVO createReqVO) {
return success(hydrogenStationService.createHydrogenStation(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新加氢站")
@PreAuthorize("@ss.hasPermission('asset:hydrogen-station:update')")
public CommonResult<Boolean> updateHydrogenStation(@Valid @RequestBody HydrogenStationSaveReqVO updateReqVO) {
hydrogenStationService.updateHydrogenStation(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除加氢站")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('asset:hydrogen-station:delete')")
public CommonResult<Boolean> deleteHydrogenStation(@RequestParam("id") Long id) {
hydrogenStationService.deleteHydrogenStation(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得加氢站")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('asset:hydrogen-station:query')")
public CommonResult<HydrogenStationRespVO> getHydrogenStation(@RequestParam("id") Long id) {
HydrogenStationDO hydrogenStation = hydrogenStationService.getHydrogenStation(id);
return success(BeanUtils.toBean(hydrogenStation, HydrogenStationRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得加氢站分页")
@PreAuthorize("@ss.hasPermission('asset:hydrogen-station:query')")
public CommonResult<PageResult<HydrogenStationRespVO>> getHydrogenStationPage(@Valid HydrogenStationPageReqVO pageReqVO) {
PageResult<HydrogenStationDO> pageResult = hydrogenStationService.getHydrogenStationPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, HydrogenStationRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获得加氢站精简列表", description = "用于下拉选择")
public CommonResult<List<HydrogenStationSimpleRespVO>> getHydrogenStationSimpleList() {
List<HydrogenStationDO> list = hydrogenStationService.getHydrogenStationSimpleList();
return success(BeanUtils.toBean(list, HydrogenStationSimpleRespVO.class));
}
}

View File

@@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.asset.controller.admin.station.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDate;
import java.time.LocalTime;
/**
* 加氢站 Base VO
*
* @author 芋道源码
*/
@Data
public class HydrogenStationBaseVO {
@Schema(description = "站点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "嘉兴嘉燃经开站")
@NotBlank(message = "站点名称不能为空")
private String name;
@Schema(description = "简称", example = "经开站")
private String shortName;
@Schema(description = "站点编码", example = "JX001")
private String stationNo;
@Schema(description = "所属城市", example = "嘉兴")
private String city;
@Schema(description = "站点地址", example = "浙江省嘉兴市经济开发区岗山路")
private String address;
@Schema(description = "经度", example = "120.123456")
@Pattern(regexp = "^-?((0|[1-9]\\d?|1[0-7]\\d)(\\.\\d{1,6})?|180(\\.0{1,6})?)$", message = "经度格式不正确,范围:-180~180")
private String longitude;
@Schema(description = "纬度", example = "30.123456")
@Pattern(regexp = "^-?((0|[1-8]?\\d)(\\.\\d{1,6})?|90(\\.0{1,6})?)$", message = "纬度格式不正确,范围:-90~90")
private String latitude;
@Schema(description = "联系人", example = "张三")
private String contact;
@Schema(description = "联系电话", example = "13800138000")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@Schema(description = "站点类型(字典)", example = "1")
private Integer stationType;
@Schema(description = "合作类型0=合作 1=非合作)", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "合作类型不能为空")
private Integer cooperationType;
@Schema(description = "是否自动扣款1=是 0=否)", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否自动扣款不能为空")
private Boolean autoDeduct;
@Schema(description = "是否需要预约1=是 0=否)", example = "false")
private Boolean bookingRequired;
@Schema(description = "站点状态0=停用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "站点状态不能为空")
private Integer stationStatus;
@Schema(description = "开始营业时间", example = "08:00:00")
private LocalTime startBusiness;
@Schema(description = "结束营业时间", example = "18:00:00")
private LocalTime endBusiness;
@Schema(description = "结算方式(字典)", example = "1")
private Integer billingMethod;
@Schema(description = "合作期限", example = "2027-12-31")
private LocalDate cooperationTerm;
@Schema(description = "备注", example = "合作站点,自动扣款")
private String remark;
}

View File

@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.asset.controller.admin.station.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 加氢站 - 分页查询 Request VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 加氢站分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HydrogenStationPageReqVO extends PageParam {
@Schema(description = "站点名称", example = "嘉兴嘉燃经开站")
private String name;
@Schema(description = "站点编码", example = "JX001")
private String stationNo;
@Schema(description = "所属城市", example = "嘉兴")
private String city;
@Schema(description = "合作类型0=合作 1=非合作)", example = "0")
private Integer cooperationType;
@Schema(description = "站点状态0=停用 1=启用)", example = "1")
private Integer stationStatus;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.asset.controller.admin.station.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
/**
* 加氢站 - Response VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 加氢站 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HydrogenStationRespVO extends HydrogenStationBaseVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.asset.controller.admin.station.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 加氢站 - 创建/更新 Request VO
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 加氢站创建/更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HydrogenStationSaveReqVO extends HydrogenStationBaseVO {
@Schema(description = "主键", example = "1")
private Long id;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.asset.controller.admin.station.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 加氢站 - 精简信息 Response VO
* 用于下拉选择
*
* @author 芋道源码
*/
@Schema(description = "管理后台 - 加氢站精简信息 Response VO")
@Data
public class HydrogenStationSimpleRespVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "站点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "嘉兴嘉燃经开站")
private String name;
}

View File

@@ -21,6 +21,9 @@ import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -58,6 +61,23 @@ public class VehicleRegistrationController {
return success(result);
}
@PostMapping("/ocr")
@Operation(summary = "通过文件URL识别行驶证")
@PreAuthorize("@ss.hasPermission('asset:vehicle-registration:recognize')")
public CommonResult<VehicleLicenseRecognizeRespVO> ocrVehicleLicense(
@RequestBody Map<String, String> body) throws IOException {
String fileUrl = body.get("fileUrl");
if (fileUrl == null || fileUrl.isBlank()) {
throw new IllegalArgumentException("fileUrl 不能为空");
}
log.info("[ocrVehicleLicense][通过URL识别行驶证{}]", fileUrl);
try (InputStream is = URI.create(fileUrl).toURL().openStream()) {
byte[] imageData = is.readAllBytes();
VehicleLicenseRecognizeRespVO result = vehicleRegistrationService.recognizeVehicleLicense(imageData);
return success(result);
}
}
@PostMapping("/create")
@Operation(summary = "创建车辆上牌记录")
@PreAuthorize("@ss.hasPermission('asset:vehicle-registration:create')")
@@ -99,7 +119,7 @@ public class VehicleRegistrationController {
return success(VehicleRegistrationConvert.INSTANCE.convertPage(pageResult));
}
@PostMapping("/confirm")
@PutMapping("/confirm")
@Operation(summary = "确认上牌记录(更新车辆信息)")
@Parameter(name = "id", description = "上牌记录ID", required = true)
@PreAuthorize("@ss.hasPermission('asset:vehicle-registration:update')")
@@ -108,4 +128,13 @@ public class VehicleRegistrationController {
return success(true);
}
@PutMapping("/void")
@Operation(summary = "作废上牌记录")
@Parameter(name = "id", description = "上牌记录ID", required = true)
@PreAuthorize("@ss.hasPermission('asset:vehicle-registration:update')")
public CommonResult<Boolean> voidRegistration(@RequestParam("id") Long id) {
vehicleRegistrationService.voidRegistration(id);
return success(true);
}
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.asset.convert.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.*;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 车辆租赁合同 Convert
*
* @author 芋道源码
*/
@Mapper
public interface ContractConvert {
ContractConvert INSTANCE = Mappers.getMapper(ContractConvert.class);
ContractDO convert(ContractSaveReqVO bean);
ContractRespVO convert(ContractDO bean);
ContractDetailRespVO convertDetail(ContractDO bean);
PageResult<ContractRespVO> convertPage(PageResult<ContractDO> page);
List<ContractVehicleDO> convertVehicleList(List<ContractSaveReqVO.ContractVehicleSaveVO> list);
ContractVehicleDO convertVehicle(ContractSaveReqVO.ContractVehicleSaveVO bean);
List<ContractVehicleServiceDO> convertServiceList(List<ContractVehicleServiceVO> list);
ContractVehicleServiceDO convertService(ContractVehicleServiceVO bean);
List<ContractAuthorizedDO> convertAuthorizedList(List<ContractAuthorizedVO> list);
ContractAuthorizedDO convertAuthorized(ContractAuthorizedVO bean);
List<ContractDetailRespVO.ContractVehicleDetailVO> convertVehicleDetailList(List<ContractVehicleDO> list);
ContractDetailRespVO.ContractVehicleDetailVO convertVehicleDetail(ContractVehicleDO bean);
List<ContractVehicleServiceVO> convertServiceVOList(List<ContractVehicleServiceDO> list);
List<ContractAuthorizedVO> convertAuthorizedVOList(List<ContractAuthorizedDO> list);
List<ContractAttachmentVO> convertAttachmentVOList(List<ContractAttachmentDO> list);
ContractAttachmentDO convertAttachment(ContractAttachmentVO bean);
List<ContractAttachmentDO> convertAttachmentList(List<ContractAttachmentVO> list);
}

View File

@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 客户 Convert
*
@@ -23,4 +25,6 @@ public interface CustomerConvert {
PageResult<CustomerRespVO> convertPage(PageResult<CustomerDO> page);
List<CustomerRespVO> convertList(List<CustomerDO> list);
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.asset.convert.station;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationSimpleRespVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 加氢站 Convert
*
* @author 芋道源码
*/
@Mapper
public interface HydrogenStationConvert {
HydrogenStationConvert INSTANCE = Mappers.getMapper(HydrogenStationConvert.class);
/**
* 转换为响应 VO
*/
HydrogenStationRespVO convert(HydrogenStationDO bean);
/**
* 转换为精简响应 VO
*/
HydrogenStationSimpleRespVO convertSimple(HydrogenStationDO bean);
/**
* 批量转换为精简响应 VO
*/
List<HydrogenStationSimpleRespVO> convertSimpleList(List<HydrogenStationDO> list);
}

View File

@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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
*
* @author 芋道源码
*/
@TableName("asset_contract_attachment")
@KeySequence("asset_contract_attachment_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractAttachmentDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同ID
*/
private Long contractId;
/**
* 附件类型1=合同原件 2=盖章合同)
*/
private Integer attachmentType;
/**
* 文件ID关联 infra_file
*/
private Long fileId;
/**
* 文件名称
*/
private String fileName;
/**
* 文件URL
*/
private String fileUrl;
/**
* 文件大小(字节)
*/
private Long fileSize;
/**
* 上传时间
*/
private LocalDateTime uploadTime;
/**
* 上传人
*/
private String uploader;
}

View File

@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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.*;
/**
* 合同被授权人 DO
*
* @author 芋道源码
*/
@TableName("asset_contract_authorized")
@KeySequence("asset_contract_authorized_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractAuthorizedDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同ID
*/
private Long contractId;
/**
* 姓名
*/
private String name;
/**
* 电话
*/
private String phone;
/**
* 身份证号
*/
private String idCard;
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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
*
* @author 芋道源码
*/
@TableName("asset_contract_change_history")
@KeySequence("asset_contract_change_history_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractChangeHistoryDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同ID
*/
private Long contractId;
/**
* 变更类型
*/
private String changeType;
/**
* 变更内容
*/
private String changeContent;
/**
* 操作人
*/
private String operator;
/**
* 操作时间
*/
private LocalDateTime operateTime;
}

View File

@@ -0,0 +1,199 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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.LocalDate;
import java.time.LocalDateTime;
/**
* 车辆租赁合同 DO
*
* @author 芋道源码
*/
@TableName("asset_contract")
@KeySequence("asset_contract_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同编码
*/
private String contractCode;
/**
* 合同类型1=试用 2=正式)
*/
private Integer contractType;
/**
* 项目名称
*/
private String projectName;
/**
* 生效日期
*/
private LocalDate startDate;
/**
* 结束日期
*/
private LocalDate endDate;
/**
* 付款方式
*/
private String paymentMethod;
/**
* 付款周期
*/
private String paymentCycle;
/**
* 签约公司(乙方)
*/
private String signingCompany;
/**
* 交车省份
*/
private String deliveryProvince;
/**
* 交车城市
*/
private String deliveryCity;
/**
* 交车地点
*/
private String deliveryLocation;
/**
* 备注
*/
private String remark;
/**
* 客户ID
*/
private Long customerId;
/**
* 客户名称(冗余)
*/
private String customerName;
/**
* 是否三方合同
*/
private Boolean thirdPartyEnabled;
/**
* 丙方客户ID
*/
private Long thirdPartyCustomerId;
/**
* 丙方名称
*/
private String thirdPartyName;
/**
* 业务部门ID
*/
private Long businessDeptId;
/**
* 业务负责人ID
*/
private Long businessManagerId;
/**
* 审批状态0=草稿 1=审批中 2=审批通过 3=审批拒绝 4=已撤回)
*/
private Integer approvalStatus;
/**
* BPM流程实例ID
*/
private String bpmInstanceId;
/**
* 合同状态0=草稿 1=待生效 2=进行中 3=已到期 4=已终止 5=已续签)
*/
private Integer contractStatus;
/**
* 实际生效时间
*/
private LocalDateTime effectiveTime;
/**
* 终止时间
*/
private LocalDateTime terminateTime;
/**
* 终止原因
*/
private String terminateReason;
/**
* 续签后的新合同ID
*/
private Long renewedContractId;
/**
* 原合同ID如果是续签合同
*/
private Long originalContractId;
/**
* 氢费承担方
*/
private String hydrogenBearer;
/**
* 氢气付款方式
*/
private String hydrogenPaymentMethod;
/**
* 氢气预付款
*/
private BigDecimal hydrogenPrepay;
/**
* 退还车氢气单价
*/
private BigDecimal hydrogenReturnPrice;
/**
* 账单计算方式
*/
private String billingMethod;
/**
* 主车型
*/
private String mainVehicleType;
}

View File

@@ -0,0 +1,93 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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
*
* @author 芋道源码
*/
@TableName("asset_contract_vehicle")
@KeySequence("asset_contract_vehicle_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractVehicleDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同ID
*/
private Long contractId;
/**
* 车辆ID关联 asset_vehicle_base
*/
private Long vehicleId;
/**
* 品牌
*/
private String brand;
/**
* 型号
*/
private String model;
/**
* 车牌号
*/
private String plateNo;
/**
* VIN码
*/
private String vin;
/**
* 月租金(元)
*/
private BigDecimal monthRent;
/**
* 保证金(元)
*/
private BigDecimal deposit;
/**
* 车辆状态0=待交车 1=已交车 2=已退车)
*/
private Integer vehicleStatus;
/**
* 实际交车时间
*/
private LocalDateTime actualDeliveryTime;
/**
* 交车人
*/
private String deliveryPerson;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.contract;
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.LocalDate;
/**
* 合同车辆服务项目 DO
*
* @author 芋道源码
*/
@TableName("asset_contract_vehicle_service")
@KeySequence("asset_contract_vehicle_service_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContractVehicleServiceDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 合同车辆ID
*/
private Long contractVehicleId;
/**
* 服务项目名称
*/
private String serviceName;
/**
* 服务费用(元)
*/
private BigDecimal serviceFee;
/**
* 生效日期
*/
private LocalDate effectiveDate;
}

View File

@@ -0,0 +1,37 @@
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.math.BigDecimal;
/**
* 交车单车辆 DO
*/
@TableName("asset_delivery_order_vehicle")
@KeySequence("asset_delivery_order_vehicle_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryOrderVehicleDO extends BaseDO {
@TableId
private Long id;
private Long orderId;
private Long taskVehicleId;
private Long vehicleId;
private String plateNo;
private String vin;
private String brand;
private String model;
private Integer mileage;
private BigDecimal hydrogenLevel;
private BigDecimal batteryLevel;
}

View File

@@ -0,0 +1,49 @@
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.LocalDate;
/**
* 交车任务 DO
*/
@TableName("asset_delivery_task")
@KeySequence("asset_delivery_task_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryTaskDO extends BaseDO {
@TableId
private Long id;
private String taskCode;
private Long contractId;
private String contractCode;
private String projectName;
private Long customerId;
private String customerName;
private LocalDate expectedDeliveryDateStart;
private LocalDate expectedDeliveryDateEnd;
private LocalDate billingStartDate;
private String deliveryProvince;
private String deliveryCity;
private String deliveryLocation;
private Integer vehicleCount;
/**
* 任务状态0=激活 1=挂起)
*/
private Integer taskStatus;
/**
* 交车状态0=未交车 1=已交车)
*/
private Integer deliveryStatus;
private Boolean needReturn;
}

View File

@@ -0,0 +1,40 @@
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 交车任务车辆 DO
*/
@TableName("asset_delivery_task_vehicle")
@KeySequence("asset_delivery_task_vehicle_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryTaskVehicleDO extends BaseDO {
@TableId
private Long id;
private Long taskId;
private Long contractVehicleId;
private Long vehicleId;
private String plateNo;
private String vin;
private String brand;
private String model;
private BigDecimal monthRent;
private BigDecimal deposit;
private LocalDateTime actualDeliveryDate;
private String deliveryPerson;
private Boolean isDelivered;
}

View File

@@ -0,0 +1,128 @@
package cn.iocoder.yudao.module.asset.dal.dataobject.station;
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.LocalDate;
import java.time.LocalTime;
/**
* 加氢站 DO
*
* @author 芋道源码
*/
@TableName("asset_hydrogen_station")
@KeySequence("asset_hydrogen_station_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HydrogenStationDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 站点名称
*/
private String name;
/**
* 简称
*/
private String shortName;
/**
* 站点编码
*/
private String stationNo;
/**
* 所属城市
*/
private String city;
/**
* 站点地址
*/
private String address;
/**
* 经度
*/
private String longitude;
/**
* 纬度
*/
private String latitude;
/**
* 联系人
*/
private String contact;
/**
* 联系电话
*/
private String phone;
/**
* 站点类型(字典)
*/
private Integer stationType;
/**
* 合作类型0=合作 1=非合作)
*/
private Integer cooperationType;
/**
* 是否自动扣款1=是 0=否)
*/
private Boolean autoDeduct;
/**
* 是否需要预约1=是 0=否)
*/
private Boolean bookingRequired;
/**
* 站点状态0=停用 1=启用)
*/
private Integer stationStatus;
/**
* 开始营业时间
*/
private LocalTime startBusiness;
/**
* 结束营业时间
*/
private LocalTime endBusiness;
/**
* 结算方式(字典)
*/
private Integer billingMethod;
/**
* 合作期限
*/
private LocalDate cooperationTerm;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractAttachmentDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同附件 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractAttachmentMapper extends BaseMapperX<ContractAttachmentDO> {
default List<ContractAttachmentDO> selectListByContractId(Long contractId) {
return selectList(new LambdaQueryWrapperX<ContractAttachmentDO>()
.eq(ContractAttachmentDO::getContractId, contractId)
.orderByAsc(ContractAttachmentDO::getId));
}
default int deleteByContractId(Long contractId) {
return delete(new LambdaQueryWrapperX<ContractAttachmentDO>()
.eq(ContractAttachmentDO::getContractId, contractId));
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractAuthorizedDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同被授权人 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractAuthorizedMapper extends BaseMapperX<ContractAuthorizedDO> {
default List<ContractAuthorizedDO> selectListByContractId(Long contractId) {
return selectList(new LambdaQueryWrapperX<ContractAuthorizedDO>()
.eq(ContractAuthorizedDO::getContractId, contractId)
.orderByAsc(ContractAuthorizedDO::getId));
}
default int deleteByContractId(Long contractId) {
return delete(new LambdaQueryWrapperX<ContractAuthorizedDO>()
.eq(ContractAuthorizedDO::getContractId, contractId));
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractChangeHistoryDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同变更历史 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractChangeHistoryMapper extends BaseMapperX<ContractChangeHistoryDO> {
default List<ContractChangeHistoryDO> selectListByContractId(Long contractId) {
return selectList(new LambdaQueryWrapperX<ContractChangeHistoryDO>()
.eq(ContractChangeHistoryDO::getContractId, contractId)
.orderByDesc(ContractChangeHistoryDO::getOperateTime));
}
}

View File

@@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.ContractPageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDate;
/**
* 车辆租赁合同 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractMapper extends BaseMapperX<ContractDO> {
default PageResult<ContractDO> selectPage(ContractPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ContractDO>()
.likeIfPresent(ContractDO::getContractCode, reqVO.getContractCode())
.eqIfPresent(ContractDO::getContractType, reqVO.getContractType())
.likeIfPresent(ContractDO::getProjectName, reqVO.getProjectName())
.eqIfPresent(ContractDO::getCustomerId, reqVO.getCustomerId())
.likeIfPresent(ContractDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(ContractDO::getApprovalStatus, reqVO.getApprovalStatus())
.eqIfPresent(ContractDO::getContractStatus, reqVO.getContractStatus())
.betweenIfPresent(ContractDO::getStartDate, reqVO.getStartDate())
.betweenIfPresent(ContractDO::getEndDate, reqVO.getEndDate())
.eqIfPresent(ContractDO::getBusinessDeptId, reqVO.getBusinessDeptId())
.eqIfPresent(ContractDO::getSigningCompany, reqVO.getSigningCompany())
.eqIfPresent(ContractDO::getBusinessManagerId, reqVO.getBusinessManagerId())
.likeIfPresent(ContractDO::getCreator, reqVO.getCreator())
.orderByDesc(ContractDO::getId));
}
@Select("SELECT contract_code FROM asset_contract WHERE contract_code LIKE CONCAT(#{prefix}, '%') AND deleted = 0 ORDER BY contract_code DESC LIMIT 1")
String selectMaxContractCodeByPrefix(@Param("prefix") String prefix);
/**
* 检查车辆在指定时间段内是否有有效合同
*/
@Select("SELECT COUNT(*) FROM asset_contract c " +
"INNER JOIN asset_contract_vehicle cv ON c.id = cv.contract_id " +
"WHERE cv.vehicle_id = #{vehicleId} " +
"AND c.contract_status IN (1, 2) " +
"AND c.deleted = 0 AND cv.deleted = 0 " +
"AND NOT (c.end_date < #{startDate} OR c.start_date > #{endDate})")
int countActiveContractsByVehicleAndDateRange(@Param("vehicleId") Long vehicleId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractVehicleDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同车辆租赁订单 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractVehicleMapper extends BaseMapperX<ContractVehicleDO> {
default List<ContractVehicleDO> selectListByContractId(Long contractId) {
return selectList(new LambdaQueryWrapperX<ContractVehicleDO>()
.eq(ContractVehicleDO::getContractId, contractId)
.orderByAsc(ContractVehicleDO::getId));
}
default int deleteByContractId(Long contractId) {
return delete(new LambdaQueryWrapperX<ContractVehicleDO>()
.eq(ContractVehicleDO::getContractId, contractId));
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.asset.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractVehicleServiceDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同车辆服务项目 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface ContractVehicleServiceMapper extends BaseMapperX<ContractVehicleServiceDO> {
default List<ContractVehicleServiceDO> selectListByContractVehicleId(Long contractVehicleId) {
return selectList(new LambdaQueryWrapperX<ContractVehicleServiceDO>()
.eq(ContractVehicleServiceDO::getContractVehicleId, contractVehicleId)
.orderByAsc(ContractVehicleServiceDO::getId));
}
default int deleteByContractVehicleId(Long contractVehicleId) {
return delete(new LambdaQueryWrapperX<ContractVehicleServiceDO>()
.eq(ContractVehicleServiceDO::getContractVehicleId, contractVehicleId));
}
default int deleteByContractVehicleIds(List<Long> contractVehicleIds) {
return delete(new LambdaQueryWrapperX<ContractVehicleServiceDO>()
.in(ContractVehicleServiceDO::getContractVehicleId, contractVehicleIds));
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.asset.dal.mysql.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryOrderPageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryOrderDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeliveryOrderMapper extends BaseMapperX<DeliveryOrderDO> {
default PageResult<DeliveryOrderDO> selectPage(DeliveryOrderPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<DeliveryOrderDO>()
.likeIfPresent(DeliveryOrderDO::getOrderCode, reqVO.getOrderCode())
.likeIfPresent(DeliveryOrderDO::getTaskCode, reqVO.getTaskCode())
.likeIfPresent(DeliveryOrderDO::getContractCode, reqVO.getContractCode())
.likeIfPresent(DeliveryOrderDO::getProjectName, reqVO.getProjectName())
.likeIfPresent(DeliveryOrderDO::getCustomerName, reqVO.getCustomerName())
.likeIfPresent(DeliveryOrderDO::getDeliveryPerson, reqVO.getDeliveryPerson())
.eqIfPresent(DeliveryOrderDO::getStatus, reqVO.getStatus())
.betweenIfPresent(DeliveryOrderDO::getDeliveryDate, reqVO.getDeliveryDate())
.betweenIfPresent(DeliveryOrderDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeliveryOrderDO::getId));
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.asset.dal.mysql.delivery;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryOrderVehicleDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DeliveryOrderVehicleMapper extends BaseMapperX<DeliveryOrderVehicleDO> {
default List<DeliveryOrderVehicleDO> selectListByOrderId(Long orderId) {
return selectList(DeliveryOrderVehicleDO::getOrderId, orderId);
}
default void deleteByOrderId(Long orderId) {
delete(DeliveryOrderVehicleDO::getOrderId, orderId);
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.asset.dal.mysql.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskPageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DeliveryTaskMapper extends BaseMapperX<DeliveryTaskDO> {
default PageResult<DeliveryTaskDO> selectPage(DeliveryTaskPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<DeliveryTaskDO>()
.likeIfPresent(DeliveryTaskDO::getTaskCode, reqVO.getTaskCode())
.likeIfPresent(DeliveryTaskDO::getContractCode, reqVO.getContractCode())
.likeIfPresent(DeliveryTaskDO::getProjectName, reqVO.getProjectName())
.likeIfPresent(DeliveryTaskDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(DeliveryTaskDO::getTaskStatus, reqVO.getTaskStatus())
.eqIfPresent(DeliveryTaskDO::getDeliveryStatus, reqVO.getDeliveryStatus())
.betweenIfPresent(DeliveryTaskDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeliveryTaskDO::getId));
}
default long selectCountByContractId(Long contractId) {
return selectCount(DeliveryTaskDO::getContractId, contractId);
}
default List<DeliveryTaskDO> selectListByFilters(DeliveryTaskPageReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<DeliveryTaskDO>()
.likeIfPresent(DeliveryTaskDO::getTaskCode, reqVO.getTaskCode())
.likeIfPresent(DeliveryTaskDO::getContractCode, reqVO.getContractCode())
.likeIfPresent(DeliveryTaskDO::getProjectName, reqVO.getProjectName())
.likeIfPresent(DeliveryTaskDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(DeliveryTaskDO::getTaskStatus, reqVO.getTaskStatus())
.eqIfPresent(DeliveryTaskDO::getDeliveryStatus, reqVO.getDeliveryStatus())
.betweenIfPresent(DeliveryTaskDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeliveryTaskDO::getId));
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.asset.dal.mysql.delivery;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskVehicleDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DeliveryTaskVehicleMapper extends BaseMapperX<DeliveryTaskVehicleDO> {
default List<DeliveryTaskVehicleDO> selectListByTaskId(Long taskId) {
return selectList(DeliveryTaskVehicleDO::getTaskId, taskId);
}
default void deleteByTaskId(Long taskId) {
delete(DeliveryTaskVehicleDO::getTaskId, taskId);
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.asset.dal.mysql.prepare;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.prepare.vo.VehiclePreparePageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.prepare.VehiclePrepareDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface VehiclePrepareMapper extends BaseMapperX<VehiclePrepareDO> {
default PageResult<VehiclePrepareDO> selectPage(VehiclePreparePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<VehiclePrepareDO>()
.likeIfPresent(VehiclePrepareDO::getPlateNo, reqVO.getPlateNo())
.likeIfPresent(VehiclePrepareDO::getVin, reqVO.getVin())
.likeIfPresent(VehiclePrepareDO::getContractCode, reqVO.getContractCode())
.eqIfPresent(VehiclePrepareDO::getVehicleType, reqVO.getVehicleType())
.likeIfPresent(VehiclePrepareDO::getBrand, reqVO.getBrand())
.likeIfPresent(VehiclePrepareDO::getParkingLot, reqVO.getParkingLot())
.eqIfPresent(VehiclePrepareDO::getStatus, reqVO.getStatus())
.betweenIfPresent(VehiclePrepareDO::getCompleteTime, reqVO.getCompleteTime())
.betweenIfPresent(VehiclePrepareDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(VehiclePrepareDO::getId));
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.asset.dal.mysql.returnorder;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.returnorder.vo.ReturnOrderPageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.returnorder.ReturnOrderDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ReturnOrderMapper extends BaseMapperX<ReturnOrderDO> {
default PageResult<ReturnOrderDO> selectPage(ReturnOrderPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ReturnOrderDO>()
.likeIfPresent(ReturnOrderDO::getOrderCode, reqVO.getOrderCode())
.likeIfPresent(ReturnOrderDO::getContractCode, reqVO.getContractCode())
.likeIfPresent(ReturnOrderDO::getCustomerName, reqVO.getCustomerName())
.eqIfPresent(ReturnOrderDO::getStatus, reqVO.getStatus())
.eqIfPresent(ReturnOrderDO::getReturnReason, reqVO.getReturnReason())
.betweenIfPresent(ReturnOrderDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ReturnOrderDO::getId));
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.asset.dal.mysql.returnorder;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.asset.dal.dataobject.returnorder.ReturnOrderVehicleDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ReturnOrderVehicleMapper extends BaseMapperX<ReturnOrderVehicleDO> {
default List<ReturnOrderVehicleDO> selectListByReturnOrderId(Long returnOrderId) {
return selectList(ReturnOrderVehicleDO::getReturnOrderId, returnOrderId);
}
default void deleteByReturnOrderId(Long returnOrderId) {
delete(ReturnOrderVehicleDO::getReturnOrderId, returnOrderId);
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.asset.dal.mysql.station;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationPageReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 加氢站 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface HydrogenStationMapper extends BaseMapperX<HydrogenStationDO> {
default PageResult<HydrogenStationDO> selectPage(HydrogenStationPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<HydrogenStationDO>()
.likeIfPresent(HydrogenStationDO::getName, reqVO.getName())
.likeIfPresent(HydrogenStationDO::getStationNo, reqVO.getStationNo())
.likeIfPresent(HydrogenStationDO::getCity, reqVO.getCity())
.eqIfPresent(HydrogenStationDO::getCooperationType, reqVO.getCooperationType())
.eqIfPresent(HydrogenStationDO::getStationStatus, reqVO.getStationStatus())
.betweenIfPresent(HydrogenStationDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(HydrogenStationDO::getId));
}
default List<HydrogenStationDO> selectSimpleList() {
return selectList();
}
}

View File

@@ -20,4 +20,9 @@ public interface VehicleStatusMapper extends BaseMapperX<VehicleStatusDO> {
.in(VehicleStatusDO::getVehicleId, vehicleIds));
}
default VehicleStatusDO selectByVehicleId(Long vehicleId) {
return selectOne(new LambdaQueryWrapperX<VehicleStatusDO>()
.eq(VehicleStatusDO::getVehicleId, vehicleId));
}
}

View File

@@ -38,7 +38,8 @@ public interface ErrorCodeConstants {
ErrorCode CONTRACT_STATUS_NOT_ALLOW_ADD_VEHICLE = new ErrorCode(1_008_005_011, "当前合同状态不允许新增车辆");
ErrorCode CONTRACT_TYPE_NOT_TRIAL = new ErrorCode(1_008_005_012, "只有试用合同才能转正式");
// ========== 车辆登记 1-008-006-000 ==========
// ========== 车辆基础信息 1-008-006-000 ==========
ErrorCode VEHICLE_NOT_EXISTS = new ErrorCode(1_008_006_001, "车辆不存在");
ErrorCode VEHICLE_REGISTRATION_NOT_EXISTS = new ErrorCode(1_008_006_000, "车辆登记不存在");
// ========== 备车管理 1-008-007-000 ==========
@@ -78,4 +79,8 @@ public interface ErrorCodeConstants {
ErrorCode VEHICLE_REPLACEMENT_STATUS_NOT_ALLOW_WITHDRAW = new ErrorCode(1_008_012_004, "当前状态不允许撤回");
ErrorCode VEHICLE_REPLACEMENT_STATUS_NOT_ALLOW_CONFIRM = new ErrorCode(1_008_012_005, "当前状态不允许确认换回");
// ========== 加氢站管理 1-008-013-000 ==========
ErrorCode HYDROGEN_STATION_NOT_EXISTS = new ErrorCode(1_008_013_000, "加氢站不存在");
ErrorCode HYDROGEN_STATION_END_BUSINESS_BEFORE_START_BUSINESS = new ErrorCode(1_008_013_001, "结束营业时间不能早于开始营业时间");
}

View File

@@ -0,0 +1,106 @@
package cn.iocoder.yudao.module.asset.job;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper;
import cn.iocoder.yudao.module.asset.enums.contract.ContractStatusEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 合同状态自动流转定时任务
*
* 功能:
* 1. 将待生效的合同自动转为进行中(当前日期 >= 生效日期)
* 2. 将进行中的合同自动转为已到期(当前日期 > 结束日期)
*
* @author 芋道源码
*/
@Component
@Slf4j
public class ContractStatusJob {
@Resource
private ContractMapper contractMapper;
@XxlJob("contractStatusJob")
@TenantJob
public void execute() {
log.info("[contractStatusJob] 开始执行合同状态自动流转");
try {
// 1. 处理待生效 -> 进行中
int activatedCount = activatePendingContracts();
log.info("[contractStatusJob] 激活待生效合同数量: {}", activatedCount);
// 2. 处理进行中 -> 已到期
int expiredCount = expireActiveContracts();
log.info("[contractStatusJob] 到期合同数量: {}", expiredCount);
log.info("[contractStatusJob] 执行完成,激活: {}, 到期: {}", activatedCount, expiredCount);
} catch (Exception e) {
log.error("[contractStatusJob] 执行失败", e);
}
}
/**
* 激活待生效的合同
*/
private int activatePendingContracts() {
LocalDate today = LocalDate.now();
// 查询待生效且生效日期 <= 今天的合同
List<ContractDO> contracts = contractMapper.selectList(
new LambdaQueryWrapper<ContractDO>()
.eq(ContractDO::getContractStatus, ContractStatusEnum.PENDING.getStatus())
.le(ContractDO::getStartDate, today)
);
int count = 0;
for (ContractDO contract : contracts) {
ContractDO updateObj = new ContractDO();
updateObj.setId(contract.getId());
updateObj.setContractStatus(ContractStatusEnum.IN_PROGRESS.getStatus());
updateObj.setEffectiveTime(LocalDateTime.now());
contractMapper.updateById(updateObj);
count++;
log.info("[contractStatusJob] 合同 {} 已激活", contract.getContractCode());
}
return count;
}
/**
* 将进行中的合同标记为已到期
*/
private int expireActiveContracts() {
LocalDate today = LocalDate.now();
// 查询进行中且结束日期 < 今天的合同
List<ContractDO> contracts = contractMapper.selectList(
new LambdaQueryWrapper<ContractDO>()
.eq(ContractDO::getContractStatus, ContractStatusEnum.IN_PROGRESS.getStatus())
.lt(ContractDO::getEndDate, today)
);
int count = 0;
for (ContractDO contract : contracts) {
ContractDO updateObj = new ContractDO();
updateObj.setId(contract.getId());
updateObj.setContractStatus(ContractStatusEnum.EXPIRED.getStatus());
contractMapper.updateById(updateObj);
count++;
log.info("[contractStatusJob] 合同 {} 已到期", contract.getContractCode());
}
return count;
}
}

View File

@@ -0,0 +1,145 @@
package cn.iocoder.yudao.module.asset.service.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* 车辆租赁合同 Service 接口
*
* @author 芋道源码
*/
public interface ContractService {
/**
* 创建合同
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createContract(@Valid ContractSaveReqVO createReqVO);
/**
* 更新合同
*
* @param updateReqVO 更新信息
*/
void updateContract(@Valid ContractSaveReqVO updateReqVO);
/**
* 删除合同
*
* @param id 编号
*/
void deleteContract(Long id);
/**
* 获得合同
*
* @param id 编号
* @return 合同
*/
ContractDO getContract(Long id);
/**
* 获得合同分页
*
* @param pageReqVO 分页查询
* @return 合同分页
*/
PageResult<ContractDO> getContractPage(ContractPageReqVO pageReqVO);
/**
* 提交合同审批
*
* @param id 合同ID
* @return 流程实例ID
*/
String submitContractApproval(Long id);
/**
* 更新合同审批状态
*
* @param id 合同ID
* @param status 审批状态
*/
void updateContractApprovalStatus(Long id, Integer status);
/**
* 撤回合同审批
*
* @param id 合同ID
*/
void withdrawContractApproval(Long id);
/**
* 终止合同
*
* @param id 合同ID
* @param reason 终止原因
*/
void terminateContract(Long id, String reason);
/**
* 续签合同
*
* @param id 原合同ID
* @param newContractReqVO 新合同信息
* @return 新合同ID
*/
Long renewContract(Long id, ContractSaveReqVO newContractReqVO);
/**
* 获取合同详情(包含关联数据)
*
* @param id 合同ID
* @return 合同详情
*/
ContractDetailRespVO getContractDetail(Long id);
/**
* 获得合同精简列表(用于下拉选择)
*
* @return 合同列表
*/
List<ContractDO> getContractSimpleList();
/**
* 变更为三方合同
*
* @param id 原合同ID
* @param newContractReqVO 新合同信息
* @return 新合同ID
*/
Long convertToThirdParty(Long id, @Valid ContractSaveReqVO newContractReqVO);
/**
* 试用合同转正式
*
* @param id 原合同ID
* @param newContractReqVO 新合同信息
* @return 新合同ID
*/
Long convertToFormal(Long id, @Valid ContractSaveReqVO newContractReqVO);
/**
* 往现有合同追加车辆
*
* @param id 合同ID
* @param vehicles 车辆列表
*/
void addVehiclesToContract(Long id, List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles);
/**
* 上传盖章合同附件
*
* @param id 合同ID
* @param fileUrl 文件URL
* @param fileName 文件名
*/
void uploadSealedContract(Long id, String fileUrl, String fileName);
}

View File

@@ -0,0 +1,609 @@
package cn.iocoder.yudao.module.asset.service.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.asset.convert.contract.ContractConvert;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.*;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.*;
import cn.iocoder.yudao.module.asset.enums.contract.ContractApprovalStatusEnum;
import cn.iocoder.yudao.module.asset.enums.contract.ContractStatusEnum;
import cn.iocoder.yudao.module.asset.service.contract.listener.ContractBpmListener;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCancelReqDTO;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*;
/**
* 车辆租赁合同 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class ContractServiceImpl implements ContractService {
@Resource
private ContractMapper contractMapper;
@Resource
private ContractVehicleMapper contractVehicleMapper;
@Resource
private ContractVehicleServiceMapper contractVehicleServiceMapper;
@Resource
private ContractAuthorizedMapper contractAuthorizedMapper;
@Resource
private ContractAttachmentMapper contractAttachmentMapper;
@Resource
private ContractChangeHistoryMapper contractChangeHistoryMapper;
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createContract(ContractSaveReqVO createReqVO) {
// 校验合同日期
validateContractDate(createReqVO.getStartDate(), createReqVO.getEndDate());
// 转换并插入合同主表
ContractDO contract = ContractConvert.INSTANCE.convert(createReqVO);
// 生成合同编码
if (contract.getContractCode() == null || contract.getContractCode().isEmpty()) {
contract.setContractCode(generateContractCode());
}
// 设置初始状态
contract.setApprovalStatus(ContractApprovalStatusEnum.DRAFT.getStatus());
contract.setContractStatus(0); // 草稿
contractMapper.insert(contract);
// 保存车辆订单
saveContractVehicles(contract.getId(), createReqVO.getVehicles());
// 保存被授权人
saveContractAuthorized(contract.getId(), createReqVO.getAuthorizedPersons());
// 记录变更历史
saveChangeHistory(contract.getId(), "创建合同", "创建合同:" + contract.getContractCode());
return contract.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateContract(ContractSaveReqVO updateReqVO) {
// 校验存在
ContractDO existContract = validateContractExists(updateReqVO.getId());
// 校验状态:只有草稿、审批拒绝、已撤回状态才能修改
if (!canEdit(existContract.getApprovalStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_UPDATE);
}
// 校验合同日期
validateContractDate(updateReqVO.getStartDate(), updateReqVO.getEndDate());
// 转换并更新合同主表
ContractDO updateObj = ContractConvert.INSTANCE.convert(updateReqVO);
contractMapper.updateById(updateObj);
// 更新车辆订单:先删除旧的,再插入新的
List<ContractVehicleDO> oldVehicles = contractVehicleMapper.selectListByContractId(updateReqVO.getId());
List<Long> oldVehicleIds = oldVehicles.stream().map(ContractVehicleDO::getId).collect(Collectors.toList());
if (!oldVehicleIds.isEmpty()) {
contractVehicleServiceMapper.deleteByContractVehicleIds(oldVehicleIds);
}
contractVehicleMapper.deleteByContractId(updateReqVO.getId());
saveContractVehicles(updateReqVO.getId(), updateReqVO.getVehicles());
// 更新被授权人:先删除旧的,再插入新的
contractAuthorizedMapper.deleteByContractId(updateReqVO.getId());
saveContractAuthorized(updateReqVO.getId(), updateReqVO.getAuthorizedPersons());
// 记录变更历史
saveChangeHistory(updateReqVO.getId(), "更新合同", "更新合同信息");
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteContract(Long id) {
// 校验存在
ContractDO contract = validateContractExists(id);
// 校验状态:只有草稿、审批拒绝、已撤回状态才能删除
if (!canEdit(contract.getApprovalStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_DELETE);
}
// 删除合同主表
contractMapper.deleteById(id);
// 删除关联数据
List<ContractVehicleDO> vehicles = contractVehicleMapper.selectListByContractId(id);
List<Long> vehicleIds = vehicles.stream().map(ContractVehicleDO::getId).collect(Collectors.toList());
if (!vehicleIds.isEmpty()) {
contractVehicleServiceMapper.deleteByContractVehicleIds(vehicleIds);
}
contractVehicleMapper.deleteByContractId(id);
contractAuthorizedMapper.deleteByContractId(id);
contractAttachmentMapper.deleteByContractId(id);
// 记录变更历史
saveChangeHistory(id, "删除合同", "删除合同:" + contract.getContractCode());
}
@Override
public ContractDO getContract(Long id) {
return contractMapper.selectById(id);
}
@Override
public PageResult<ContractDO> getContractPage(ContractPageReqVO pageReqVO) {
return contractMapper.selectPage(pageReqVO);
}
// ==================== 私有方法 ====================
/**
* 校验合同是否存在
*/
private ContractDO validateContractExists(Long id) {
ContractDO contract = contractMapper.selectById(id);
if (contract == null) {
throw exception(CONTRACT_NOT_EXISTS);
}
return contract;
}
/**
* 校验合同日期
*/
private void validateContractDate(java.time.LocalDate startDate, java.time.LocalDate endDate) {
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
throw exception(CONTRACT_END_DATE_BEFORE_START_DATE);
}
}
/**
* 判断是否可以编辑
*/
private boolean canEdit(Integer approvalStatus) {
return ContractApprovalStatusEnum.DRAFT.getStatus().equals(approvalStatus)
|| ContractApprovalStatusEnum.REJECTED.getStatus().equals(approvalStatus)
|| ContractApprovalStatusEnum.WITHDRAWN.getStatus().equals(approvalStatus);
}
/**
* 生成合同编码
*/
private String generateContractCode() {
// 格式HT-YYYY-NNNN
String year = String.valueOf(java.time.LocalDate.now().getYear());
// 查询当年最大序号
String prefix = "HT-" + year + "-";
String maxCode = contractMapper.selectMaxContractCodeByPrefix(prefix);
int nextSeq = 1;
if (maxCode != null && maxCode.startsWith(prefix)) {
try {
String seqStr = maxCode.substring(prefix.length());
nextSeq = Integer.parseInt(seqStr) + 1;
} catch (Exception e) {
log.warn("解析合同编码失败: {}", maxCode, e);
}
}
return prefix + String.format("%04d", nextSeq);
}
/**
* 保存车辆订单
*/
private void saveContractVehicles(Long contractId, List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles) {
if (vehicles == null || vehicles.isEmpty()) {
return;
}
// 获取合同信息用于校验
ContractDO contract = contractMapper.selectById(contractId);
for (ContractSaveReqVO.ContractVehicleSaveVO vehicleVO : vehicles) {
// 校验车辆时间冲突
if (vehicleVO.getVehicleId() != null && contract != null) {
validateVehicleTimeConflict(vehicleVO.getVehicleId(), contract.getStartDate(), contract.getEndDate(), contractId);
}
// 保存车辆订单
ContractVehicleDO vehicle = ContractConvert.INSTANCE.convertVehicle(vehicleVO);
vehicle.setContractId(contractId);
contractVehicleMapper.insert(vehicle);
// 保存服务项目
if (vehicleVO.getServices() != null && !vehicleVO.getServices().isEmpty()) {
List<ContractVehicleServiceDO> services = ContractConvert.INSTANCE.convertServiceList(vehicleVO.getServices());
for (ContractVehicleServiceDO service : services) {
service.setContractVehicleId(vehicle.getId());
contractVehicleServiceMapper.insert(service);
}
}
}
}
/**
* 校验车辆时间冲突
*/
private void validateVehicleTimeConflict(Long vehicleId, java.time.LocalDate startDate, java.time.LocalDate endDate, Long excludeContractId) {
int count = contractMapper.countActiveContractsByVehicleAndDateRange(vehicleId, startDate, endDate);
if (count > 0) {
throw exception(CONTRACT_VEHICLE_TIME_CONFLICT);
}
}
/**
* 保存被授权人
*/
private void saveContractAuthorized(Long contractId, List<ContractAuthorizedVO> authorizedPersons) {
if (authorizedPersons == null || authorizedPersons.isEmpty()) {
return;
}
List<ContractAuthorizedDO> authorizedList = ContractConvert.INSTANCE.convertAuthorizedList(authorizedPersons);
for (ContractAuthorizedDO authorized : authorizedList) {
authorized.setContractId(contractId);
contractAuthorizedMapper.insert(authorized);
}
}
/**
* 保存变更历史
*/
private void saveChangeHistory(Long contractId, String changeType, String changeContent) {
ContractChangeHistoryDO history = new ContractChangeHistoryDO();
history.setContractId(contractId);
history.setChangeType(changeType);
history.setChangeContent(changeContent);
// 获取当前登录用户
Long userId = SecurityFrameworkUtils.getLoginUserId();
history.setOperator(userId != null ? String.valueOf(userId) : "system");
history.setOperateTime(LocalDateTime.now());
contractChangeHistoryMapper.insert(history);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String submitContractApproval(Long id) {
// 校验存在
ContractDO contract = validateContractExists(id);
// 校验状态:只有草稿、审批拒绝、已撤回状态才能提交审批
if (!canEdit(contract.getApprovalStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_SUBMIT);
}
// 创建流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("contractCode", contract.getContractCode());
variables.put("projectName", contract.getProjectName());
variables.put("customerName", contract.getCustomerName());
variables.put("startDate", contract.getStartDate());
variables.put("endDate", contract.getEndDate());
// 创建流程实例
BpmProcessInstanceCreateReqDTO reqDTO = new BpmProcessInstanceCreateReqDTO();
reqDTO.setProcessDefinitionKey(ContractBpmListener.PROCESS_KEY);
reqDTO.setBusinessKey(String.valueOf(id));
reqDTO.setVariables(variables);
Long userId = SecurityFrameworkUtils.getLoginUserId();
String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, reqDTO).getData();
// 更新合同状态
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVING.getStatus());
updateObj.setBpmInstanceId(processInstanceId);
contractMapper.updateById(updateObj);
// 记录变更历史
saveChangeHistory(id, "提交审批", "提交合同审批流程实例ID" + processInstanceId);
return processInstanceId;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateContractApprovalStatus(Long id, Integer status) {
// 校验存在
ContractDO contract = validateContractExists(id);
// 更新审批状态
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
// 根据 BPM 状态映射到合同审批状态
if (BpmProcessInstanceStatusEnum.RUNNING.getStatus().equals(status)) {
updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVING.getStatus());
} else if (BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(status)) {
updateObj.setApprovalStatus(ContractApprovalStatusEnum.APPROVED.getStatus());
updateObj.setContractStatus(ContractStatusEnum.PENDING.getStatus()); // 待生效
updateObj.setEffectiveTime(LocalDateTime.now());
} else if (BpmProcessInstanceStatusEnum.REJECT.getStatus().equals(status)) {
updateObj.setApprovalStatus(ContractApprovalStatusEnum.REJECTED.getStatus());
} else if (BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(status)) {
updateObj.setApprovalStatus(ContractApprovalStatusEnum.WITHDRAWN.getStatus());
}
contractMapper.updateById(updateObj);
// 记录变更历史
String changeContent = "审批状态变更:" + getApprovalStatusName(updateObj.getApprovalStatus());
saveChangeHistory(id, "审批状态变更", changeContent);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void withdrawContractApproval(Long id) {
// 校验存在
ContractDO contract = validateContractExists(id);
// 校验状态:只有审批中状态才能撤回
if (!ContractApprovalStatusEnum.APPROVING.getStatus().equals(contract.getApprovalStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_WITHDRAW);
}
// 调用 BPM API 取消流程实例
if (contract.getBpmInstanceId() != null) {
BpmProcessInstanceCancelReqDTO cancelReqDTO = new BpmProcessInstanceCancelReqDTO();
cancelReqDTO.setId(contract.getBpmInstanceId());
cancelReqDTO.setReason("合同审批撤回");
Long userId = SecurityFrameworkUtils.getLoginUserId();
bpmProcessInstanceApi.cancelProcessInstance(userId, cancelReqDTO);
}
// 更新合同状态
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setApprovalStatus(ContractApprovalStatusEnum.WITHDRAWN.getStatus());
contractMapper.updateById(updateObj);
// 记录变更历史
saveChangeHistory(id, "撤回审批", "撤回合同审批");
}
/**
* 获取审批状态名称
*/
private String getApprovalStatusName(Integer status) {
ContractApprovalStatusEnum statusEnum = ContractApprovalStatusEnum.valueOf(status);
return statusEnum != null ? statusEnum.getName() : "未知";
}
@Override
@Transactional(rollbackFor = Exception.class)
public void terminateContract(Long id, String reason) {
// 校验存在
ContractDO contract = validateContractExists(id);
// 校验状态:只有进行中的合同才能终止
if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(contract.getContractStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_TERMINATE);
}
// 更新合同状态
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
updateObj.setTerminateTime(LocalDateTime.now());
updateObj.setTerminateReason(reason);
contractMapper.updateById(updateObj);
// 记录变更历史
saveChangeHistory(id, "终止合同", "终止合同,原因:" + reason);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long renewContract(Long id, ContractSaveReqVO newContractReqVO) {
// 校验原合同存在
ContractDO oldContract = validateContractExists(id);
// 校验状态:只有进行中或已到期的合同才能续签
if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())
&& !ContractStatusEnum.EXPIRED.getStatus().equals(oldContract.getContractStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_RENEW);
}
// 创建新合同
Long newContractId = createContract(newContractReqVO);
// 更新原合同状态
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setContractStatus(ContractStatusEnum.RENEWED.getStatus());
updateObj.setRenewedContractId(newContractId);
contractMapper.updateById(updateObj);
// 更新新合同的原合同ID
ContractDO newContract = new ContractDO();
newContract.setId(newContractId);
newContract.setOriginalContractId(id);
contractMapper.updateById(newContract);
// 记录变更历史
saveChangeHistory(id, "续签合同", "续签合同新合同ID" + newContractId);
saveChangeHistory(newContractId, "续签合同", "由合同ID " + id + " 续签而来");
return newContractId;
}
@Override
public List<ContractDO> getContractSimpleList() {
return contractMapper.selectList();
}
@Override
public ContractDetailRespVO getContractDetail(Long id) {
ContractDO contract = validateContractExists(id);
ContractDetailRespVO result = ContractConvert.INSTANCE.convertDetail(contract);
// 查询车辆订单
List<ContractVehicleDO> vehicles = contractVehicleMapper.selectListByContractId(id);
List<ContractDetailRespVO.ContractVehicleDetailVO> vehicleDetails = ContractConvert.INSTANCE.convertVehicleDetailList(vehicles);
// 查询每个车辆的服务项目
for (int i = 0; i < vehicles.size(); i++) {
List<ContractVehicleServiceDO> services = contractVehicleServiceMapper.selectListByContractVehicleId(vehicles.get(i).getId());
vehicleDetails.get(i).setServices(ContractConvert.INSTANCE.convertServiceVOList(services));
}
result.setVehicles(vehicleDetails);
// 查询被授权人
List<ContractAuthorizedDO> authorized = contractAuthorizedMapper.selectListByContractId(id);
result.setAuthorizedPersons(ContractConvert.INSTANCE.convertAuthorizedVOList(authorized));
// 查询附件
List<ContractAttachmentDO> attachments = contractAttachmentMapper.selectListByContractId(id);
result.setAttachments(ContractConvert.INSTANCE.convertAttachmentVOList(attachments));
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long convertToThirdParty(Long id, ContractSaveReqVO newContractReqVO) {
// 校验原合同存在且进行中
ContractDO oldContract = validateContractExists(id);
if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_CONVERT);
}
// 设置三方合同标记
newContractReqVO.setThirdPartyEnabled(true);
// 创建新合同
Long newContractId = createContract(newContractReqVO);
// 更新新合同的原合同ID
ContractDO newContract = new ContractDO();
newContract.setId(newContractId);
newContract.setOriginalContractId(id);
contractMapper.updateById(newContract);
// 终止原合同
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
updateObj.setTerminateTime(LocalDateTime.now());
updateObj.setTerminateReason("变更为三方合同");
contractMapper.updateById(updateObj);
// 记录变更历史
saveChangeHistory(id, "变更为三方", "变更为三方合同新合同ID" + newContractId);
saveChangeHistory(newContractId, "变更为三方", "由合同ID " + id + " 变更而来");
return newContractId;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long convertToFormal(Long id, ContractSaveReqVO newContractReqVO) {
// 校验原合同存在且进行中
ContractDO oldContract = validateContractExists(id);
if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(oldContract.getContractStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_CONVERT);
}
// 校验原合同为试用合同
if (!Integer.valueOf(1).equals(oldContract.getContractType())) {
throw exception(CONTRACT_TYPE_NOT_TRIAL);
}
// 设置为正式合同
newContractReqVO.setContractType(2);
// 创建新合同
Long newContractId = createContract(newContractReqVO);
// 更新新合同的原合同ID
ContractDO newContract = new ContractDO();
newContract.setId(newContractId);
newContract.setOriginalContractId(id);
contractMapper.updateById(newContract);
// 终止原合同
ContractDO updateObj = new ContractDO();
updateObj.setId(id);
updateObj.setContractStatus(ContractStatusEnum.TERMINATED.getStatus());
updateObj.setTerminateTime(LocalDateTime.now());
updateObj.setTerminateReason("转正式合同");
contractMapper.updateById(updateObj);
// 记录变更历史
saveChangeHistory(id, "转正式", "转正式合同新合同ID" + newContractId);
saveChangeHistory(newContractId, "转正式", "由试用合同ID " + id + " 转正式而来");
return newContractId;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addVehiclesToContract(Long id, List<ContractSaveReqVO.ContractVehicleSaveVO> vehicles) {
// 校验合同存在且进行中
ContractDO contract = validateContractExists(id);
if (!ContractStatusEnum.IN_PROGRESS.getStatus().equals(contract.getContractStatus())) {
throw exception(CONTRACT_STATUS_NOT_ALLOW_ADD_VEHICLE);
}
// 追加车辆
saveContractVehicles(id, vehicles);
// 记录变更历史
saveChangeHistory(id, "新增车辆", "追加 " + vehicles.size() + " 辆车辆");
}
@Override
@Transactional(rollbackFor = Exception.class)
public void uploadSealedContract(Long id, String fileUrl, String fileName) {
// 校验合同存在
validateContractExists(id);
// 创建附件记录
ContractAttachmentDO attachment = new ContractAttachmentDO();
attachment.setContractId(id);
attachment.setAttachmentType(2); // 盖章合同
attachment.setFileName(fileName);
attachment.setFileUrl(fileUrl);
attachment.setUploadTime(LocalDateTime.now());
Long userId = SecurityFrameworkUtils.getLoginUserId();
attachment.setUploader(userId != null ? String.valueOf(userId) : "system");
contractAttachmentMapper.insert(attachment);
// 记录变更历史
saveChangeHistory(id, "上传盖章合同", "上传盖章合同附件:" + fileName);
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.asset.service.contract.listener;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
import cn.iocoder.yudao.module.asset.service.contract.ContractService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* 车辆租赁合同审批状态监听器
*
* @author 芋道源码
*/
@Component
public class ContractBpmListener extends BpmProcessInstanceStatusEventListener {
/**
* 流程定义 Key
*/
public static final String PROCESS_KEY = "asset_contract";
@Resource
private ContractService contractService;
@Override
protected String getProcessDefinitionKey() {
return PROCESS_KEY;
}
@Override
protected void onEvent(BpmProcessInstanceStatusEvent event) {
contractService.updateContractApprovalStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());
}
}

View File

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.asset.controller.admin.customer.vo.CustomerSaveRe
import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* 客户信息 Service 接口
@@ -52,4 +53,11 @@ public interface CustomerService {
*/
PageResult<CustomerDO> getCustomerPage(CustomerPageReqVO pageReqVO);
/**
* 获得客户精简列表(用于下拉选择)
*
* @return 客户列表
*/
List<CustomerDO> getCustomerSimpleList();
}

View File

@@ -98,6 +98,11 @@ public class CustomerServiceImpl implements CustomerService {
return customerMapper.selectPage(pageReqVO);
}
@Override
public List<CustomerDO> getCustomerSimpleList() {
return customerMapper.selectList();
}
/**
* 生成客户编号
*/

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.asset.service.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryOrderPageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryOrderRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryOrderSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryOrderDO;
import jakarta.validation.Valid;
public interface DeliveryOrderService {
Long createDeliveryOrder(@Valid DeliveryOrderSaveReqVO createReqVO);
void updateDeliveryOrder(@Valid DeliveryOrderSaveReqVO updateReqVO);
void deleteDeliveryOrder(Long id);
DeliveryOrderDO getDeliveryOrder(Long id);
DeliveryOrderRespVO getDeliveryOrderDetail(Long id);
PageResult<DeliveryOrderDO> getDeliveryOrderPage(DeliveryOrderPageReqVO pageReqVO);
void completeDeliveryOrder(Long id);
}

View File

@@ -8,6 +8,8 @@ import cn.iocoder.yudao.module.asset.dal.dataobject.inspection.InspectionRecordD
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 cn.iocoder.yudao.module.asset.service.delivery.event.DeliveryCompletedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -37,6 +39,9 @@ public class DeliveryOrderServiceImpl implements DeliveryOrderService {
@Resource
private InspectionRecordService inspectionRecordService;
@Resource
private ApplicationEventPublisher eventPublisher;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createDeliveryOrder(DeliveryOrderSaveReqVO createReqVO) {
@@ -169,6 +174,11 @@ public class DeliveryOrderServiceImpl implements DeliveryOrderService {
deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(task.getId()).deliveryStatus(1).build());
}
}
// Publish delivery completed events for each vehicle
for (DeliveryOrderVehicleDO ov : orderVehicles) {
eventPublisher.publishEvent(new DeliveryCompletedEvent(id, ov.getVehicleId()));
}
}
private DeliveryOrderDO validateDeliveryOrderExists(Long id) {

View File

@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.asset.service.delivery;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskActivateReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskContractGroupRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskPageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskDO;
import jakarta.validation.Valid;
import java.util.List;
public interface DeliveryTaskService {
Long createDeliveryTask(@Valid DeliveryTaskSaveReqVO createReqVO);
void updateDeliveryTask(@Valid DeliveryTaskSaveReqVO updateReqVO);
void deleteDeliveryTask(Long id);
DeliveryTaskDO getDeliveryTask(Long id);
DeliveryTaskRespVO getDeliveryTaskDetail(Long id);
PageResult<DeliveryTaskDO> getDeliveryTaskPage(DeliveryTaskPageReqVO pageReqVO);
void suspendDeliveryTask(Long id);
void activateDeliveryTask(Long id);
/**
* 激活交车任务(带日期参数)
*/
void activateDeliveryTask(DeliveryTaskActivateReqVO reqVO);
/**
* 获得按合同分组的交车任务分页
*/
PageResult<DeliveryTaskContractGroupRespVO> getDeliveryTaskContractPage(DeliveryTaskPageReqVO pageReqVO);
List<DeliveryTaskDO> getDeliveryTaskSimpleList();
}

View File

@@ -0,0 +1,264 @@
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.controller.admin.delivery.vo.DeliveryTaskActivateReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskContractGroupRespVO;
import cn.iocoder.yudao.module.asset.controller.admin.delivery.vo.DeliveryTaskVehicleVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.delivery.DeliveryTaskVehicleDO;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryTaskMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryTaskVehicleMapper;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.*;
@Service
@Validated
public class DeliveryTaskServiceImpl implements DeliveryTaskService {
@Resource
private DeliveryTaskMapper deliveryTaskMapper;
@Resource
private DeliveryTaskVehicleMapper deliveryTaskVehicleMapper;
@Resource
private ContractMapper contractMapper;
@Resource
private DeptApi deptApi;
@Resource
private AdminUserApi adminUserApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createDeliveryTask(DeliveryTaskSaveReqVO createReqVO) {
// 校验合同是否存在
if (contractMapper.selectById(createReqVO.getContractId()) == null) {
throw exception(CONTRACT_NOT_EXISTS);
}
// Generate task code: contractCode + JC + sequence
String taskCode = generateTaskCode(createReqVO.getContractId(), createReqVO.getContractCode());
DeliveryTaskDO task = BeanUtils.toBean(createReqVO, DeliveryTaskDO.class);
task.setTaskCode(taskCode);
task.setTaskStatus(0); // active
task.setDeliveryStatus(0); // not delivered
task.setVehicleCount(createReqVO.getVehicles() != null ? createReqVO.getVehicles().size() : 0);
deliveryTaskMapper.insert(task);
// Insert vehicles
if (createReqVO.getVehicles() != null) {
for (DeliveryTaskVehicleVO vehicleVO : createReqVO.getVehicles()) {
DeliveryTaskVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryTaskVehicleDO.class);
vehicle.setTaskId(task.getId());
vehicle.setIsDelivered(false);
deliveryTaskVehicleMapper.insert(vehicle);
}
}
return task.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateDeliveryTask(DeliveryTaskSaveReqVO updateReqVO) {
DeliveryTaskDO task = validateDeliveryTaskExists(updateReqVO.getId());
if (task.getDeliveryStatus().equals(1)) {
throw exception(DELIVERY_TASK_ALREADY_DELIVERED);
}
DeliveryTaskDO updateObj = BeanUtils.toBean(updateReqVO, DeliveryTaskDO.class);
updateObj.setVehicleCount(updateReqVO.getVehicles() != null ? updateReqVO.getVehicles().size() : 0);
deliveryTaskMapper.updateById(updateObj);
// Update vehicles: delete old, insert new
if (updateReqVO.getVehicles() != null) {
deliveryTaskVehicleMapper.deleteByTaskId(updateReqVO.getId());
for (DeliveryTaskVehicleVO vehicleVO : updateReqVO.getVehicles()) {
DeliveryTaskVehicleDO vehicle = BeanUtils.toBean(vehicleVO, DeliveryTaskVehicleDO.class);
vehicle.setTaskId(updateReqVO.getId());
vehicle.setIsDelivered(false);
deliveryTaskVehicleMapper.insert(vehicle);
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteDeliveryTask(Long id) {
validateDeliveryTaskExists(id);
deliveryTaskMapper.deleteById(id);
deliveryTaskVehicleMapper.deleteByTaskId(id);
}
@Override
public DeliveryTaskDO getDeliveryTask(Long id) {
return deliveryTaskMapper.selectById(id);
}
@Override
public DeliveryTaskRespVO getDeliveryTaskDetail(Long id) {
DeliveryTaskDO task = validateDeliveryTaskExists(id);
DeliveryTaskRespVO respVO = BeanUtils.toBean(task, DeliveryTaskRespVO.class);
List<DeliveryTaskVehicleDO> vehicles = deliveryTaskVehicleMapper.selectListByTaskId(id);
respVO.setVehicles(BeanUtils.toBean(vehicles, DeliveryTaskVehicleVO.class));
return respVO;
}
@Override
public PageResult<DeliveryTaskDO> getDeliveryTaskPage(DeliveryTaskPageReqVO pageReqVO) {
return deliveryTaskMapper.selectPage(pageReqVO);
}
@Override
public void suspendDeliveryTask(Long id) {
DeliveryTaskDO task = validateDeliveryTaskExists(id);
if (task.getDeliveryStatus().equals(1)) {
throw exception(DELIVERY_TASK_ALREADY_DELIVERED);
}
deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(id).taskStatus(1).build());
}
@Override
public void activateDeliveryTask(Long id) {
validateDeliveryTaskExists(id);
deliveryTaskMapper.updateById(DeliveryTaskDO.builder().id(id).taskStatus(0).build());
}
@Override
public void activateDeliveryTask(DeliveryTaskActivateReqVO reqVO) {
validateDeliveryTaskExists(reqVO.getId());
DeliveryTaskDO updateObj = DeliveryTaskDO.builder()
.id(reqVO.getId())
.taskStatus(0)
.expectedDeliveryDateStart(reqVO.getExpectedDeliveryDateStart())
.expectedDeliveryDateEnd(reqVO.getExpectedDeliveryDateEnd())
.billingStartDate(reqVO.getBillingStartDate())
.build();
deliveryTaskMapper.updateById(updateObj);
}
@Override
public PageResult<DeliveryTaskContractGroupRespVO> getDeliveryTaskContractPage(DeliveryTaskPageReqVO pageReqVO) {
// 1. 查询所有匹配的交车任务
List<DeliveryTaskDO> allTasks = deliveryTaskMapper.selectListByFilters(pageReqVO);
if (allTasks.isEmpty()) {
return new PageResult<>(Collections.emptyList(), 0L);
}
// 2. 按合同 ID 分组
Map<Long, List<DeliveryTaskDO>> groupedByContract = allTasks.stream()
.collect(Collectors.groupingBy(DeliveryTaskDO::getContractId,
LinkedHashMap::new, Collectors.toList()));
// 3. 对合同分组进行分页
List<Long> contractIds = new ArrayList<>(groupedByContract.keySet());
long total = contractIds.size();
int start = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize();
int end = Math.min(start + pageReqVO.getPageSize(), contractIds.size());
if (start >= contractIds.size()) {
return new PageResult<>(Collections.emptyList(), total);
}
List<Long> pageContractIds = contractIds.subList(start, end);
// 4. 批量查询合同信息
List<ContractDO> contracts = contractMapper.selectBatchIds(pageContractIds);
Map<Long, ContractDO> contractMap = contracts.stream()
.collect(Collectors.toMap(ContractDO::getId, c -> c, (a, b) -> a));
// 5. 批量查询部门和用户名称
Set<Long> deptIds = new HashSet<>();
Set<Long> userIds = new HashSet<>();
contracts.forEach(c -> {
if (c.getBusinessDeptId() != null) deptIds.add(c.getBusinessDeptId());
if (c.getBusinessManagerId() != null) userIds.add(c.getBusinessManagerId());
});
Map<Long, DeptRespDTO> deptMap = deptIds.isEmpty() ? Collections.emptyMap() : deptApi.getDeptMap(deptIds);
Map<Long, AdminUserRespDTO> userMap = userIds.isEmpty() ? Collections.emptyMap() : adminUserApi.getUserMap(userIds);
// 6. 批量查询车辆信息(用于 popover 展示)
List<Long> taskIds = pageContractIds.stream()
.flatMap(cid -> groupedByContract.get(cid).stream())
.map(DeliveryTaskDO::getId)
.toList();
List<DeliveryTaskVehicleDO> allVehicles = taskIds.isEmpty() ? Collections.emptyList()
: deliveryTaskVehicleMapper.selectList(
new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<DeliveryTaskVehicleDO>()
.in(DeliveryTaskVehicleDO::getTaskId, taskIds));
Map<Long, List<DeliveryTaskVehicleDO>> vehiclesByTask = allVehicles.stream()
.collect(Collectors.groupingBy(DeliveryTaskVehicleDO::getTaskId));
// 7. 组装响应
List<DeliveryTaskContractGroupRespVO> result = new ArrayList<>();
for (Long contractId : pageContractIds) {
DeliveryTaskContractGroupRespVO group = new DeliveryTaskContractGroupRespVO();
ContractDO contract = contractMap.get(contractId);
if (contract != null) {
group.setContractId(contract.getId());
group.setContractCode(contract.getContractCode());
group.setProjectName(contract.getProjectName());
group.setCustomerName(contract.getCustomerName());
group.setStartDate(contract.getStartDate());
group.setEndDate(contract.getEndDate());
DeptRespDTO dept = deptMap.get(contract.getBusinessDeptId());
group.setBusinessDeptName(dept != null ? dept.getName() : null);
AdminUserRespDTO user = userMap.get(contract.getBusinessManagerId());
group.setBusinessManagerName(user != null ? user.getNickname() : null);
} else {
DeliveryTaskDO firstTask = groupedByContract.get(contractId).get(0);
group.setContractId(contractId);
group.setContractCode(firstTask.getContractCode());
group.setProjectName(firstTask.getProjectName());
group.setCustomerName(firstTask.getCustomerName());
}
List<DeliveryTaskRespVO> taskVOs = groupedByContract.get(contractId).stream().map(task -> {
DeliveryTaskRespVO vo = BeanUtils.toBean(task, DeliveryTaskRespVO.class);
List<DeliveryTaskVehicleDO> vehicles = vehiclesByTask.getOrDefault(task.getId(), Collections.emptyList());
vo.setVehicles(BeanUtils.toBean(vehicles, DeliveryTaskVehicleVO.class));
return vo;
}).toList();
group.setTasks(taskVOs);
result.add(group);
}
return new PageResult<>(result, total);
}
@Override
public List<DeliveryTaskDO> getDeliveryTaskSimpleList() {
return deliveryTaskMapper.selectList();
}
private DeliveryTaskDO validateDeliveryTaskExists(Long id) {
DeliveryTaskDO task = deliveryTaskMapper.selectById(id);
if (task == null) {
throw exception(DELIVERY_TASK_NOT_EXISTS);
}
return task;
}
private String generateTaskCode(Long contractId, String contractCode) {
long count = deliveryTaskMapper.selectCountByContractId(contractId);
return contractCode + "JC" + String.format("%04d", count + 1);
}
}

View File

@@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.asset.service.event;
import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleStatusDO;
import cn.iocoder.yudao.module.asset.dal.mysql.vehicle.VehicleStatusMapper;
import cn.iocoder.yudao.module.asset.service.delivery.event.DeliveryCompletedEvent;
import cn.iocoder.yudao.module.asset.service.replacement.event.ReplacementReturnConfirmedEvent;
import cn.iocoder.yudao.module.asset.service.returnorder.event.ReturnApprovedEvent;
@@ -7,6 +9,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import jakarta.annotation.Resource;
import java.util.List;
/**
* 车辆状态事件监听器
* 负责在业务事件发生时更新车辆状态
@@ -17,22 +22,68 @@ import org.springframework.transaction.event.TransactionalEventListener;
@Slf4j
public class VehicleStatusEventListener {
@Resource
private VehicleStatusMapper vehicleStatusMapper;
/**
* 交车完成:更新车辆库位状态为「已交付车-租赁交车」,标记已交车
*/
@TransactionalEventListener
public void onDeliveryCompleted(DeliveryCompletedEvent event) {
log.info("[onDeliveryCompleted] vehicleId={}", event.getVehicleId());
// TODO: update vehicle status to DELIVERED when vehicle status management is implemented
log.info("[onDeliveryCompleted] deliveryOrderId={}, vehicleId={}", event.getDeliveryOrderId(), event.getVehicleId());
VehicleStatusDO vehicleStatus = vehicleStatusMapper.selectByVehicleId(event.getVehicleId());
if (vehicleStatus == null) {
log.warn("[onDeliveryCompleted] vehicle status not found, vehicleId={}", event.getVehicleId());
return;
}
vehicleStatusMapper.updateById(VehicleStatusDO.builder()
.id(vehicleStatus.getId())
.storageStatus(5) // 已交付车-租赁交车
.operateStatus(2) // 租赁
.isDelivered(true)
.build());
}
/**
* 还车审批通过:更新所有还车车辆的库位状态为「库存车-可交付车」,标记已还车
*/
@TransactionalEventListener
public void onReturnApproved(ReturnApprovedEvent event) {
log.info("[onReturnApproved] vehicleIds={}", event.getVehicleIds());
// TODO: update vehicle status to AVAILABLE when vehicle status management is implemented
log.info("[onReturnApproved] returnOrderId={}, vehicleIds={}", event.getReturnOrderId(), event.getVehicleIds());
List<Long> vehicleIds = event.getVehicleIds();
if (vehicleIds == null || vehicleIds.isEmpty()) {
return;
}
List<VehicleStatusDO> vehicleStatuses = vehicleStatusMapper.selectByVehicleIds(vehicleIds);
for (VehicleStatusDO vehicleStatus : vehicleStatuses) {
vehicleStatusMapper.updateById(VehicleStatusDO.builder()
.id(vehicleStatus.getId())
.storageStatus(2) // 库存车-可交付车
.operateStatus(1) // 库存
.isReturned(true)
.isDelivered(false)
.build());
}
}
/**
* 替换车换回确认:更新原车辆库位状态为「库存车-可交付车」
*/
@TransactionalEventListener
public void onReplacementReturnConfirmed(ReplacementReturnConfirmedEvent event) {
log.info("[onReplacementReturnConfirmed] vehicleId={}", event.getOriginalVehicleId());
// TODO: update vehicle status to AVAILABLE
log.info("[onReplacementReturnConfirmed] replacementId={}, originalVehicleId={}",
event.getReplacementId(), event.getOriginalVehicleId());
VehicleStatusDO vehicleStatus = vehicleStatusMapper.selectByVehicleId(event.getOriginalVehicleId());
if (vehicleStatus == null) {
log.warn("[onReplacementReturnConfirmed] vehicle status not found, vehicleId={}", event.getOriginalVehicleId());
return;
}
vehicleStatusMapper.updateById(VehicleStatusDO.builder()
.id(vehicleStatus.getId())
.storageStatus(2) // 库存车-可交付车
.operateStatus(1) // 库存
.isDelivered(false)
.build());
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.asset.service.prepare;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.prepare.VehiclePrepareDO;
import jakarta.validation.Valid;
public interface VehiclePrepareService {
Long createVehiclePrepare(@Valid VehiclePrepareSaveReqVO createReqVO);
void updateVehiclePrepare(@Valid VehiclePrepareSaveReqVO updateReqVO);
void deleteVehiclePrepare(Long id);
VehiclePrepareDO getVehiclePrepare(Long id);
PageResult<VehiclePrepareDO> getVehiclePreparePage(VehiclePreparePageReqVO pageReqVO);
void completeVehiclePrepare(Long id);
}

View File

@@ -6,7 +6,9 @@ import cn.iocoder.yudao.module.asset.controller.admin.prepare.vo.VehiclePrepareP
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.contract.ContractMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.prepare.VehiclePrepareMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.vehicle.VehicleBaseMapper;
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;
@@ -27,6 +29,12 @@ public class VehiclePrepareServiceImpl implements VehiclePrepareService {
@Resource
private VehiclePrepareMapper vehiclePrepareMapper;
@Resource
private ContractMapper contractMapper;
@Resource
private VehicleBaseMapper vehicleBaseMapper;
@Resource
private InspectionTemplateService inspectionTemplateService;
@@ -35,6 +43,19 @@ public class VehiclePrepareServiceImpl implements VehiclePrepareService {
@Override
public Long createVehiclePrepare(VehiclePrepareSaveReqVO createReqVO) {
// 校验合同是否存在
if (createReqVO.getContractId() != null) {
if (contractMapper.selectById(createReqVO.getContractId()) == null) {
throw exception(CONTRACT_NOT_EXISTS);
}
}
// 校验车辆是否存在
if (createReqVO.getVehicleId() != null) {
if (vehicleBaseMapper.selectById(createReqVO.getVehicleId()) == null) {
throw exception(VEHICLE_NOT_EXISTS);
}
}
VehiclePrepareDO prepare = BeanUtils.toBean(createReqVO, VehiclePrepareDO.class);
vehiclePrepareMapper.insert(prepare);

View File

@@ -11,12 +11,16 @@ import cn.iocoder.yudao.module.asset.dal.dataobject.inspection.InspectionRecordD
import cn.iocoder.yudao.module.asset.dal.dataobject.inspection.InspectionTemplateDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.returnorder.ReturnOrderDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.returnorder.ReturnOrderVehicleDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleBaseDO;
import cn.iocoder.yudao.module.asset.dal.dataobject.vehiclemodel.VehicleModelDO;
import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryOrderMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.inspection.InspectionRecordMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.delivery.DeliveryOrderVehicleMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.returnorder.ReturnOrderMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.returnorder.ReturnOrderVehicleMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.vehicle.VehicleBaseMapper;
import cn.iocoder.yudao.module.asset.dal.mysql.vehiclemodel.VehicleModelMapper;
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;
@@ -61,6 +65,12 @@ public class ReturnOrderServiceImpl implements ReturnOrderService {
@Resource
private ContractMapper contractMapper;
@Resource
private VehicleBaseMapper vehicleBaseMapper;
@Resource
private VehicleModelMapper vehicleModelMapper;
@Resource
private InspectionRecordMapper inspectionRecordMapper;
@@ -238,10 +248,26 @@ public class ReturnOrderServiceImpl implements ReturnOrderService {
returnOrderMapper.insert(order);
// 创建还车车辆(原车辆)
// TODO: 查询车辆信息获取 plateNo/vin/brand/model暂时只设置 vehicleId
VehicleBaseDO vehicleBase = vehicleBaseMapper.selectById(vehicleId);
if (vehicleBase == null) {
throw exception(VEHICLE_NOT_EXISTS);
}
ReturnOrderVehicleDO vehicle = new ReturnOrderVehicleDO();
vehicle.setReturnOrderId(order.getId());
vehicle.setVehicleId(vehicleId);
vehicle.setPlateNo(vehicleBase.getPlateNo());
vehicle.setVin(vehicleBase.getVin());
// 通过车型ID查询品牌和型号
if (vehicleBase.getVehicleModelId() != null) {
VehicleModelDO vehicleModel = vehicleModelMapper.selectById(vehicleBase.getVehicleModelId());
if (vehicleModel != null) {
vehicle.setBrand(vehicleModel.getBrand());
vehicle.setModel(vehicleModel.getModel());
}
}
returnOrderVehicleMapper.insert(vehicle);
return order.getId();

View File

@@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.asset.service.station;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationPageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* 加氢站 Service 接口
*
* @author 芋道源码
*/
public interface HydrogenStationService {
/**
* 创建加氢站
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createHydrogenStation(@Valid HydrogenStationSaveReqVO createReqVO);
/**
* 更新加氢站
*
* @param updateReqVO 更新信息
*/
void updateHydrogenStation(@Valid HydrogenStationSaveReqVO updateReqVO);
/**
* 删除加氢站
*
* @param id 编号
*/
void deleteHydrogenStation(Long id);
/**
* 获得加氢站
*
* @param id 编号
* @return 加氢站
*/
HydrogenStationDO getHydrogenStation(Long id);
/**
* 获得加氢站分页
*
* @param pageReqVO 分页查询
* @return 加氢站分页
*/
PageResult<HydrogenStationDO> getHydrogenStationPage(HydrogenStationPageReqVO pageReqVO);
/**
* 获得加氢站精简列表
*
* @return 加氢站列表
*/
List<HydrogenStationDO> getHydrogenStationSimpleList();
}

View File

@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.asset.service.station;
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.station.vo.HydrogenStationPageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO;
import cn.iocoder.yudao.module.asset.dal.mysql.station.HydrogenStationMapper;
import org.springframework.stereotype.Service;
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 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class HydrogenStationServiceImpl implements HydrogenStationService {
@Resource
private HydrogenStationMapper hydrogenStationMapper;
@Override
public Long createHydrogenStation(HydrogenStationSaveReqVO createReqVO) {
// 业务校验
validateHydrogenStationBusiness(createReqVO);
HydrogenStationDO hydrogenStation = BeanUtils.toBean(createReqVO, HydrogenStationDO.class);
hydrogenStationMapper.insert(hydrogenStation);
return hydrogenStation.getId();
}
@Override
public void updateHydrogenStation(HydrogenStationSaveReqVO updateReqVO) {
validateHydrogenStationExists(updateReqVO.getId());
// 业务校验
validateHydrogenStationBusiness(updateReqVO);
HydrogenStationDO updateObj = BeanUtils.toBean(updateReqVO, HydrogenStationDO.class);
hydrogenStationMapper.updateById(updateObj);
}
@Override
public void deleteHydrogenStation(Long id) {
validateHydrogenStationExists(id);
hydrogenStationMapper.deleteById(id);
}
private void validateHydrogenStationExists(Long id) {
if (hydrogenStationMapper.selectById(id) == null) {
throw exception(HYDROGEN_STATION_NOT_EXISTS);
}
}
/**
* 业务校验
*/
private void validateHydrogenStationBusiness(HydrogenStationSaveReqVO reqVO) {
// 校验营业时间
if (reqVO.getStartBusiness() != null && reqVO.getEndBusiness() != null) {
if (reqVO.getEndBusiness().isBefore(reqVO.getStartBusiness())) {
throw exception(HYDROGEN_STATION_END_BUSINESS_BEFORE_START_BUSINESS);
}
}
}
@Override
public HydrogenStationDO getHydrogenStation(Long id) {
return hydrogenStationMapper.selectById(id);
}
@Override
public PageResult<HydrogenStationDO> getHydrogenStationPage(HydrogenStationPageReqVO pageReqVO) {
return hydrogenStationMapper.selectPage(pageReqVO);
}
@Override
public List<HydrogenStationDO> getHydrogenStationSimpleList() {
return hydrogenStationMapper.selectSimpleList();
}
}

View File

@@ -68,4 +68,11 @@ public interface VehicleRegistrationService {
*/
void confirmRegistration(Long id);
/**
* 作废上牌记录
*
* @param id 上牌记录ID
*/
void voidRegistration(Long id);
}

View File

@@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.asset.dal.mysql.vehicleregistration.VehicleRegist
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.ocr.api.OcrApi;
import cn.iocoder.yudao.module.ocr.api.dto.VehicleLicenseReqDTO;
import cn.iocoder.yudao.module.ocr.api.dto.VehicleLicenseRespDTO;
import cn.hutool.core.codec.Base64;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -55,7 +56,9 @@ public class VehicleRegistrationServiceImpl implements VehicleRegistrationServic
String imageDataBase64 = Base64.encode(imageData);
// 调用 OCR API 识别
CommonResult<VehicleLicenseRespDTO> ocrResult = ocrApi.recognizeVehicleLicense(imageDataBase64, null);
VehicleLicenseReqDTO reqDTO = new VehicleLicenseReqDTO();
reqDTO.setImageData(imageDataBase64);
CommonResult<VehicleLicenseRespDTO> ocrResult = ocrApi.recognizeVehicleLicense(reqDTO);
long costTime = System.currentTimeMillis() - startTime;
@@ -100,11 +103,12 @@ public class VehicleRegistrationServiceImpl implements VehicleRegistrationServic
@Override
@Transactional(rollbackFor = Exception.class)
public Long createVehicleRegistration(VehicleRegistrationSaveReqVO createReqVO) {
// 验证车辆是否存在
VehicleBaseDO vehicle = validateVehicleExists(createReqVO.getVehicleId());
// 通过 VIN 验证车辆是否存在
VehicleBaseDO vehicle = validateVehicleExistsByVin(createReqVO.getVin());
// 插入
VehicleRegistrationDO registration = VehicleRegistrationConvert.INSTANCE.convert(createReqVO);
registration.setVehicleId(vehicle.getId());
registration.setStatus(0); // 待确认
vehicleRegistrationMapper.insert(registration);
@@ -120,9 +124,10 @@ public class VehicleRegistrationServiceImpl implements VehicleRegistrationServic
// 校验存在
validateVehicleRegistrationExists(updateReqVO.getId());
// 验证车辆是否存在
if (updateReqVO.getVehicleId() != null) {
validateVehicleExists(updateReqVO.getVehicleId());
// 通过 VIN 验证车辆是否存在
if (StrUtil.isNotBlank(updateReqVO.getVin())) {
VehicleBaseDO vehicle = validateVehicleExistsByVin(updateReqVO.getVin());
updateReqVO.setVehicleId(vehicle.getId());
}
// 更新
@@ -192,6 +197,20 @@ public class VehicleRegistrationServiceImpl implements VehicleRegistrationServic
log.info("[confirmRegistration][确认上牌记录成功ID{}车辆ID{}]", id, vehicle.getId());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void voidRegistration(Long id) {
VehicleRegistrationDO registration = validateVehicleRegistrationExists(id);
if (registration.getStatus() != 0) {
throw exception(new ErrorCode(400, "上牌记录已确认或已作废"));
}
VehicleRegistrationDO updateObj = new VehicleRegistrationDO();
updateObj.setId(id);
updateObj.setStatus(2); // 2=已作废
vehicleRegistrationMapper.updateById(updateObj);
log.info("[voidRegistration][作废上牌记录成功ID{}]", id);
}
// ==================== 私有方法 ====================
private VehicleRegistrationDO validateVehicleRegistrationExists(Long id) {
@@ -210,4 +229,17 @@ public class VehicleRegistrationServiceImpl implements VehicleRegistrationServic
return vehicle;
}
private VehicleBaseDO validateVehicleExistsByVin(String vin) {
if (StrUtil.isBlank(vin)) {
throw exception(new ErrorCode(400, "VIN不能为空"));
}
VehicleBaseDO vehicle = vehicleBaseMapper.selectOne(new LambdaQueryWrapper<VehicleBaseDO>()
.eq(VehicleBaseDO::getVin, vin)
.last("LIMIT 1"));
if (vehicle == null) {
throw exception(new ErrorCode(400, "车辆不存在,请先录入车辆信息"));
}
return vehicle;
}
}

View File

@@ -109,3 +109,14 @@ knife4j:
enable: true
setting:
language: zh_cn
# XXL-Job 配置
xxl:
job:
enabled: false # 本地开发禁用
admin:
addresses: http://47.103.115.36:9090/xxl-job-admin
executor:
appname: asset-server
logpath: ./logs/xxl-job
logretentiondays: 30

View File

@@ -3,7 +3,7 @@ spring:
name: asset-server
profiles:
active: dev
active: local
# 允许 Bean 覆盖
main:

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="asset_contract" name="车辆租赁合同审批" isExecutable="true">
<startEvent id="startEvent" name="开始"/>
<sequenceFlow sourceRef="startEvent" targetRef="deptManagerApproval"/>
<!-- 部门经理审批 -->
<userTask id="deptManagerApproval" name="部门经理审批" flowable:candidateGroups="dept_manager">
<extensionElements>
<flowable:formProperty id="approved" name="是否同意" type="boolean" required="true"/>
<flowable:formProperty id="comment" name="审批意见" type="string"/>
</extensionElements>
</userTask>
<sequenceFlow sourceRef="deptManagerApproval" targetRef="gateway1"/>
<!-- 网关:判断部门经理是否同意 -->
<exclusiveGateway id="gateway1"/>
<sequenceFlow sourceRef="gateway1" targetRef="financeApproval">
<conditionExpression xsi:type="tFormalExpression">${approved == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="gateway1" targetRef="endEventReject">
<conditionExpression xsi:type="tFormalExpression">${approved == false}</conditionExpression>
</sequenceFlow>
<!-- 财务审批 -->
<userTask id="financeApproval" name="财务审批" flowable:candidateGroups="finance">
<extensionElements>
<flowable:formProperty id="approved" name="是否同意" type="boolean" required="true"/>
<flowable:formProperty id="comment" name="审批意见" type="string"/>
</extensionElements>
</userTask>
<sequenceFlow sourceRef="financeApproval" targetRef="gateway2"/>
<!-- 网关:判断财务是否同意 -->
<exclusiveGateway id="gateway2"/>
<sequenceFlow sourceRef="gateway2" targetRef="endEventApprove">
<conditionExpression xsi:type="tFormalExpression">${approved == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="gateway2" targetRef="endEventReject">
<conditionExpression xsi:type="tFormalExpression">${approved == false}</conditionExpression>
</sequenceFlow>
<!-- 审批通过结束 -->
<endEvent id="endEventApprove" name="审批通过"/>
<!-- 审批拒绝结束 -->
<endEvent id="endEventReject" name="审批拒绝"/>
</process>
</definitions>

View File

@@ -1,228 +0,0 @@
package cn.iocoder.yudao.module.asset.service.customer;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.asset.controller.admin.customer.vo.CustomerPageReqVO;
import cn.iocoder.yudao.module.asset.controller.admin.customer.vo.CustomerSaveReqVO;
import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO;
import cn.iocoder.yudao.module.asset.dal.mysql.customer.CustomerMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.asset.enums.ErrorCodeConstants.CUSTOMER_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* 客户管理 Service 测试类
*
* @author 芋道源码
*/
@Import(CustomerServiceImpl.class)
public class CustomerServiceImplTest extends BaseDbUnitTest {
@Resource
private CustomerServiceImpl customerService;
@Resource
private CustomerMapper customerMapper;
@Test
public void testCreateCustomer_success() {
// 准备参数
CustomerSaveReqVO createReqVO = randomPojo(CustomerSaveReqVO.class, o -> {
o.setCustomerName("测试客户");
o.setCustomerType(0);
o.setContactPhone("13800138000");
o.setContactEmail("test@example.com");
o.setIdCardNo("310101199001011234");
o.setCreditLevel(1);
o.setDepositAmount(new BigDecimal("5000.00"));
o.setStatus(0);
});
// 调用
Long customerId = customerService.createCustomer(createReqVO);
// 断言
assertNotNull(customerId);
CustomerDO customer = customerMapper.selectById(customerId);
assertNotNull(customer);
assertEquals("测试客户", customer.getCustomerName());
assertEquals(0, customer.getCustomerType());
assertEquals("13800138000", customer.getContactPhone());
assertNotNull(customer.getCustomerNo());
assertTrue(customer.getCustomerNo().startsWith("CUST"));
}
@Test
public void testUpdateCustomer_success() {
// mock 数据
CustomerDO dbCustomer = new CustomerDO();
dbCustomer.setCustomerNo("CUST000001");
dbCustomer.setCustomerName("原客户名");
dbCustomer.setCustomerType(0);
dbCustomer.setContactPhone("13800138000");
dbCustomer.setCreditLevel(1);
dbCustomer.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer.setStatus(0);
customerMapper.insert(dbCustomer);
// 准备参数
CustomerSaveReqVO updateReqVO = new CustomerSaveReqVO();
updateReqVO.setId(dbCustomer.getId());
updateReqVO.setCustomerName("新客户名");
updateReqVO.setCustomerType(0);
updateReqVO.setContactPhone("13900139000");
updateReqVO.setCreditLevel(2);
updateReqVO.setDepositAmount(new BigDecimal("8000.00"));
updateReqVO.setStatus(0);
// 调用
customerService.updateCustomer(updateReqVO);
// 断言
CustomerDO customer = customerMapper.selectById(updateReqVO.getId());
assertEquals("新客户名", customer.getCustomerName());
assertEquals("13900139000", customer.getContactPhone());
assertEquals(2, customer.getCreditLevel());
}
@Test
public void testUpdateCustomer_notExists() {
// 准备参数
CustomerSaveReqVO updateReqVO = randomPojo(CustomerSaveReqVO.class, o -> {
o.setId(999999L);
o.setCustomerName("不存在的客户");
o.setCustomerType(0);
o.setStatus(0);
});
// 调用并断言异常
assertServiceException(() -> customerService.updateCustomer(updateReqVO), CUSTOMER_NOT_EXISTS);
}
@Test
public void testDeleteCustomer_success() {
// mock 数据
CustomerDO dbCustomer = new CustomerDO();
dbCustomer.setCustomerNo("CUST000001");
dbCustomer.setCustomerName("测试客户");
dbCustomer.setCustomerType(0);
dbCustomer.setCreditLevel(1);
dbCustomer.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer.setStatus(0);
customerMapper.insert(dbCustomer);
// 调用
customerService.deleteCustomer(dbCustomer.getId());
// 断言
assertNull(customerMapper.selectById(dbCustomer.getId()));
}
@Test
public void testDeleteCustomer_notExists() {
// 调用并断言异常
assertServiceException(() -> customerService.deleteCustomer(999999L), CUSTOMER_NOT_EXISTS);
}
@Test
public void testGetCustomer() {
// mock 数据
CustomerDO dbCustomer = new CustomerDO();
dbCustomer.setCustomerNo("CUST000001");
dbCustomer.setCustomerName("测试客户");
dbCustomer.setCustomerType(0);
dbCustomer.setCreditLevel(1);
dbCustomer.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer.setStatus(0);
customerMapper.insert(dbCustomer);
// 调用
CustomerDO customer = customerService.getCustomer(dbCustomer.getId());
// 断言
assertNotNull(customer);
assertEquals("测试客户", customer.getCustomerName());
}
@Test
public void testGetCustomerPage() {
// mock 数据
CustomerDO dbCustomer1 = new CustomerDO();
dbCustomer1.setCustomerNo("CUST000001");
dbCustomer1.setCustomerName("张三");
dbCustomer1.setCustomerType(0);
dbCustomer1.setContactPhone("13800138001");
dbCustomer1.setCreditLevel(1);
dbCustomer1.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer1.setStatus(0);
customerMapper.insert(dbCustomer1);
CustomerDO dbCustomer2 = new CustomerDO();
dbCustomer2.setCustomerNo("CUST000002");
dbCustomer2.setCustomerName("李四");
dbCustomer2.setCustomerType(1);
dbCustomer2.setContactPhone("13800138002");
dbCustomer2.setCreditLevel(1);
dbCustomer2.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer2.setStatus(0);
customerMapper.insert(dbCustomer2);
CustomerDO dbCustomer3 = new CustomerDO();
dbCustomer3.setCustomerNo("CUST000003");
dbCustomer3.setCustomerName("王五");
dbCustomer3.setCustomerType(0);
dbCustomer3.setContactPhone("13800138003");
dbCustomer3.setCreditLevel(1);
dbCustomer3.setDepositAmount(new BigDecimal("5000.00"));
dbCustomer3.setStatus(1);
customerMapper.insert(dbCustomer3);
// 测试 customerName 模糊查询
CustomerPageReqVO reqVO = new CustomerPageReqVO();
reqVO.setCustomerName("");
reqVO.setPageNo(1);
reqVO.setPageSize(10);
PageResult<CustomerDO> pageResult = customerService.getCustomerPage(reqVO);
assertEquals(1, pageResult.getTotal());
assertEquals("张三", pageResult.getList().get(0).getCustomerName());
// 测试 customerType 筛选
reqVO = new CustomerPageReqVO();
reqVO.setCustomerType(1);
reqVO.setPageNo(1);
reqVO.setPageSize(10);
pageResult = customerService.getCustomerPage(reqVO);
assertEquals(1, pageResult.getTotal());
assertEquals("李四", pageResult.getList().get(0).getCustomerName());
// 测试 status 筛选
reqVO = new CustomerPageReqVO();
reqVO.setStatus(1);
reqVO.setPageNo(1);
reqVO.setPageSize(10);
pageResult = customerService.getCustomerPage(reqVO);
assertEquals(1, pageResult.getTotal());
assertEquals("王五", pageResult.getList().get(0).getCustomerName());
// 测试 contactPhone 模糊查询
reqVO = new CustomerPageReqVO();
reqVO.setContactPhone("138001");
reqVO.setPageNo(1);
reqVO.setPageSize(10);
pageResult = customerService.getCustomerPage(reqVO);
assertEquals(3, pageResult.getTotal());
}
}

View File

@@ -1,30 +1,29 @@
-- 客户信息表
CREATE TABLE IF NOT EXISTS asset_customer (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
customer_no VARCHAR(50) NOT NULL,
customer_name VARCHAR(100) NOT NULL,
customer_type TINYINT NOT NULL DEFAULT 0,
contact_person VARCHAR(50),
contact_phone VARCHAR(20),
contact_email VARCHAR(100),
contact_address VARCHAR(255),
id_card_no VARCHAR(18),
id_card_front_url VARCHAR(255),
id_card_back_url VARCHAR(255),
driver_license_no VARCHAR(50),
driver_license_url VARCHAR(255),
company_name VARCHAR(200),
unified_social_credit_code VARCHAR(50),
business_license_url VARCHAR(255),
legal_person VARCHAR(50),
credit_level TINYINT DEFAULT 0,
deposit_amount DECIMAL(10,2) DEFAULT 0.00,
remark VARCHAR(500),
status TINYINT NOT NULL DEFAULT 0,
creator VARCHAR(64) DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) DEFAULT '',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted TINYINT NOT NULL DEFAULT 0,
tenant_id BIGINT NOT NULL DEFAULT 0
customer_code VARCHAR(50) COMMENT '客户编号',
coop_status VARCHAR(20) COMMENT '合作状态',
customer_name VARCHAR(100) NOT NULL COMMENT '客户名称',
province VARCHAR(50) COMMENT '省份',
city VARCHAR(50) COMMENT '城市',
address VARCHAR(255) COMMENT '地址',
region VARCHAR(20) COMMENT '区域',
contact VARCHAR(50) COMMENT '联系人',
contact_mobile VARCHAR(20) COMMENT '联系人手机',
contact_phone VARCHAR(20) COMMENT '联系人座机',
email VARCHAR(100) COMMENT '电子邮箱',
credit_code_or_id VARCHAR(50) COMMENT '统一社会信用代码/身份证',
remark VARCHAR(500) COMMENT '备注',
tax_id VARCHAR(50) COMMENT '纳税人识别号',
invoice_address VARCHAR(255) COMMENT '发票地址',
invoice_phone VARCHAR(20) COMMENT '发票电话',
account VARCHAR(50) COMMENT '银行账号',
opening_bank VARCHAR(100) COMMENT '开户行',
mailing_address VARCHAR(255) COMMENT '邮寄地址',
creator VARCHAR(64) DEFAULT '' COMMENT '创建者',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updater VARCHAR(64) DEFAULT '' COMMENT '更新者',
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
deleted TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除',
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号'
);