hsy
2026-08-28 a8e4132c3e2e9c3b3fcb0360901b35571b6464ea
feat: 综合业务模块功能补充与实体拓展

- feat(sales): 新增招投标、销售合同、诉讼管理业务线及相关实体,增加合同回款定时提醒任务
- feat(collaborativeApproval): 新增宽带与通信管理、房屋租赁管理审批流程支持
- feat(device): 新增设备状态变更记录与维修备件管理功能
- feat(project): 新增项目配置(ProjectConfig)及附件分组功能
- fix(approve): 优化财务报销(FinReimbursement)相关逻辑与映射
- fix(quality): 完善质量检验参数(QualityInspectParam)及检测标准配置
- fix(device): 修复设备台账、设备维修与保养的数据映射及任务逻辑
已添加104个文件
已修改63个文件
5953 ■■■■■ 文件已修改
.gitignore 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
alter_sales_ledger.sql 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/bidding_management.sql 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/broadband_communication.sql 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/house_lease.sql 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/project_management_config.sql 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/sales_contract.sql 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/sales_lawsuit.sql 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/sales_ledger_contract_rel.sql 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/vehicle_alter.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/vehicle_init.sql 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/FinReimbursementService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ICustomerService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/TypeEnums.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceRepair.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/controller/InfoController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/pojo/Roles.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/service/InfoService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityInspectController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/IQualityInspectService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesContractController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesContract.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesContractService.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/SalesContractVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/FuelCardService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-local.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/FinReimbursementMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerMapper.xml 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceLedgerMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceRepairMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/projectManagement/InfoMapper.xml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/BiddingManagementMapper.xml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesQuotationMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/vehicle/FuelCardMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/vehicle/FuelRecordMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/vehicle/VehicleCostMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/vehicle/VehicleInfoMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
update_fin_reimbursement.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -46,3 +46,9 @@
!*/build/*.java
!*/build/*.html
!*/build/*.xml
/graphify-out
/src/graphify-out
/build_graph.py
/gen_subagents.py
/subagents_args.json
alter_sales_ledger.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
ALTER TABLE `sales_ledger`
ADD COLUMN `project_id` BIGINT COMMENT '项目ID(关联项目管理)',
ADD COLUMN `year` INT COMMENT '年份(自动取签订日期)',
ADD COLUMN `signing_unit_id` BIGINT COMMENT '签单单位ID(组织架构)',
ADD COLUMN `office_id` BIGINT COMMENT '办事处ID(组织架构)',
ADD COLUMN `group_leader_id` BIGINT COMMENT '组负责人ID(员工)',
ADD COLUMN `is_invoice` TINYINT(1) DEFAULT 0 COMMENT '是否开发票(1是 0否)',
ADD COLUMN `bid_price` DECIMAL(15,2) COMMENT '中标价',
ADD COLUMN `is_bid` TINYINT(1) DEFAULT 0 COMMENT '是否中标(1是 0否)',
ADD COLUMN `unbid_reason` VARCHAR(500) COMMENT '未中标原因',
ADD COLUMN `contract_original_return_date` DATE COMMENT '合同原件约定回传日期';
doc/bidding_management.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
-- æ‹›æŠ•标管理表
DROP TABLE IF EXISTS `bidding_management`;
CREATE TABLE `bidding_management` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `project_id` bigint(20) DEFAULT NULL COMMENT '所属项目ID',
  `project_name` varchar(255) DEFAULT NULL COMMENT '所属项目名称',
  `bid_doc_name` varchar(255) DEFAULT NULL COMMENT '标书名称',
  `tender_unit` varchar(255) DEFAULT NULL COMMENT '招标单位',
  `bid_unit` varchar(255) DEFAULT NULL COMMENT '投标单位',
  `tender_date` date DEFAULT NULL COMMENT '招标日期',
  `bid_date` date DEFAULT NULL COMMENT '投标日期',
  `bid_doc_fee` decimal(18,2) DEFAULT NULL COMMENT '标书款金额',
  `bid_doc_fee_status` varchar(50) DEFAULT NULL COMMENT '标书款状态',
  `bid_doc_fee_status_change_date` date DEFAULT NULL COMMENT '标书款状态变更日期',
  `bid_deposit` decimal(18,2) DEFAULT NULL COMMENT '投标保证金金额',
  `bid_result` varchar(50) DEFAULT NULL COMMENT '招投标结果',
  `tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='招投标管理表';
doc/broadband_communication.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
    -- å®½å¸¦ä¸Žé€šä¿¡ç®¡ç†ä¸»è¡¨
    DROP TABLE IF EXISTS `broadband_communication`;
    CREATE TABLE `broadband_communication` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `area` varchar(100) NOT NULL COMMENT '区域',
      `office` varchar(100) NOT NULL COMMENT '办事处',
      `resident_staff` varchar(100) NOT NULL COMMENT '驻省人员',
      `address` varchar(255) NOT NULL COMMENT '地址',
      `install_date` date NOT NULL COMMENT '安装日期',
      `install_fee` decimal(18,2) DEFAULT '0.00' COMMENT '安装费',
      `payment_cycle` int(11) NOT NULL COMMENT '缴费周期(月数:1/3/6/12)',
      `payment_method` varchar(50) NOT NULL COMMENT '缴费方式',
      `prepaid_amount` decimal(18,2) DEFAULT '0.00' COMMENT '预存金额',
      `monthly_fee` decimal(18,2) DEFAULT '0.00' COMMENT '月资费',
      `package_content` varchar(500) DEFAULT NULL COMMENT '套餐内容',
      `next_payment_date` date DEFAULT NULL COMMENT '下次缴费日期',
      `create_user` int(11) DEFAULT NULL COMMENT '创建者',
      `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
      `update_user` int(11) DEFAULT NULL COMMENT '更新者',
      `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
      `tenant_id` int(11) DEFAULT NULL COMMENT '租户ID',
      `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
      `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宽带与通信管理表';
doc/house_lease.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
-- æˆ¿å±‹ç§Ÿèµä¸»è¡¨
DROP TABLE IF EXISTS `house_lease`;
CREATE TABLE `house_lease` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `office` varchar(100) NOT NULL COMMENT '办事处',
  `user_name` varchar(100) NOT NULL COMMENT '使用人',
  `resident_staff` varchar(100) NOT NULL COMMENT '驻省人员',
  `address` varchar(255) NOT NULL COMMENT '地址',
  `rent_start_date` date NOT NULL COMMENT '起租时间',
  `contract_start_date` date NOT NULL COMMENT '合同起租日',
  `contract_end_date` date NOT NULL COMMENT '合同到期日',
  `tenant` varchar(100) NOT NULL COMMENT '承租人',
  `party_a` varchar(100) NOT NULL COMMENT '合同甲方',
  `phone` varchar(20) DEFAULT NULL COMMENT '电话',
  `area` decimal(18,2) DEFAULT NULL COMMENT '面积',
  `payment_method` varchar(50) NOT NULL COMMENT '支付方式',
  `deposit` decimal(18,2) DEFAULT NULL COMMENT '押金',
  `rent` decimal(18,2) NOT NULL COMMENT '租金(元/月)',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `tenant_id` int DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房屋租赁管理主表';
-- æˆ¿å±‹ç§Ÿèµè´¹ç”¨å­è¡¨
DROP TABLE IF EXISTS `house_lease_cost`;
CREATE TABLE `house_lease_cost` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `lease_id` bigint(20) NOT NULL COMMENT '租赁表ID',
  `month` varchar(7) NOT NULL COMMENT '月份 (YYYY-MM)',
  `rent` decimal(18,2) DEFAULT '0.00' COMMENT '房租',
  `water_electricity` decimal(18,2) DEFAULT '0.00' COMMENT '水电',
  `property` decimal(18,2) DEFAULT '0.00' COMMENT '物业',
  `express` decimal(18,2) DEFAULT '0.00' COMMENT '快递',
  `broadband` decimal(18,2) DEFAULT '0.00' COMMENT '宽带',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `tenant_id` int DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_lease_month` (`lease_id`, `month`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房屋租赁管理费用子表';
doc/project_management_config.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
CREATE TABLE `project_management_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `contract_return_reminder_days` int(11) DEFAULT '3' COMMENT '合同原件约定回传提前提醒天数',
  `create_user` int(11) DEFAULT NULL COMMENT '创建用户',
  `create_user_name` varchar(64) DEFAULT NULL COMMENT '创建用户名称',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user` int(11) DEFAULT NULL COMMENT '更新用户',
  `update_user_name` varchar(64) DEFAULT NULL COMMENT '更新用户名称',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目管理配置表';
INSERT INTO `project_management_config` (`id`, `contract_return_reminder_days`) VALUES (1, 3);
doc/sales_contract.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
-- åˆåŒç®¡ç†ä¸»è¡¨
DROP TABLE IF EXISTS `sales_contract`;
CREATE TABLE `sales_contract` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `contract_no` varchar(100) NOT NULL COMMENT '合同编号',
  `sign_date` date NOT NULL COMMENT '签订时间',
  `signing_unit` varchar(255) NOT NULL COMMENT '签约单位简称',
  `contract_period` varchar(100) DEFAULT NULL COMMENT '签约周期',
  `archive_status` varchar(50) NOT NULL COMMENT '存档状态',
  `archive_remark` varchar(500) DEFAULT NULL COMMENT '存档情况说明',
  `performance_bond` decimal(18,2) DEFAULT NULL COMMENT '履约保证金',
  `warranty_deposit` decimal(18,2) DEFAULT NULL COMMENT '质保金情况',
  `output_value` decimal(18,2) DEFAULT NULL COMMENT '产值(自动汇总)',
  `received_amount` decimal(18,2) DEFAULT NULL COMMENT '已回款金额',
  `settlement_count` int(11) DEFAULT NULL COMMENT '结算次数',
  `receivable_amount` decimal(18,2) DEFAULT NULL COMMENT '应收金额',
  `payable_ratio` varchar(50) DEFAULT NULL COMMENT '应付比例',
  `node_receivable` decimal(18,2) DEFAULT NULL COMMENT '合同节点应收',
  `payment_nodes` varchar(255) DEFAULT NULL COMMENT '合同约定付款节点',
  `settlement_record` text COMMENT '结算记录',
  `invoice_requirement` varchar(255) DEFAULT NULL COMMENT '开票要求',
  `last_invoice_date` date DEFAULT NULL COMMENT '最近一次开票时间',
  `invoiced_amount` decimal(18,2) DEFAULT NULL COMMENT '已开票金额',
  `penalty_agreement` text COMMENT '违约金约定',
  `remark` text COMMENT '情况说明',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `tenant_id` int DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售合同主表';
-- åˆåŒäº§å“æ˜Žç»†è¡¨
DROP TABLE IF EXISTS `sales_contract_product`;
CREATE TABLE `sales_contract_product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `contract_id` bigint(20) NOT NULL COMMENT '关联销售合同 ID',
  `product_category` varchar(100) DEFAULT NULL COMMENT '产品大类',
  `specification_model` varchar(100) DEFAULT NULL COMMENT '规格型号',
  `product_model_id` bigint(20) DEFAULT NULL COMMENT '规格型号 ID',
  `tax_inclusive_unit_price` decimal(18,2) DEFAULT NULL COMMENT '含税单价',
  `agreed_quantity` decimal(18,2) DEFAULT NULL COMMENT '约定/暂估用量',
  `contract_unit` varchar(50) DEFAULT NULL COMMENT '合同计量单位',
  `shipped_quantity` decimal(18,2) DEFAULT NULL COMMENT '已发货数量',
  `output_value` decimal(18,2) DEFAULT NULL COMMENT '产值',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `tenant_id` int DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`),
  KEY `idx_contract_id` (`contract_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售合同产品明细表';
doc/sales_lawsuit.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
-- è¯‰è®¼ç®¡ç†è¡¨
DROP TABLE IF EXISTS `sales_lawsuit`;
CREATE TABLE `sales_lawsuit` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `case_no` varchar(100) NOT NULL COMMENT '案件编号',
  `project_id` bigint(20) NOT NULL COMMENT '所属项目 ID',
  `project_name` varchar(255) DEFAULT NULL COMMENT '所属项目名称',
  `contract_id` bigint(20) NOT NULL COMMENT '关联销售合同 ID',
  `contract_no` varchar(100) DEFAULT NULL COMMENT '合同编号',
  `lawsuit_status` varchar(50) NOT NULL COMMENT '诉讼状态',
  `filing_date` date NOT NULL COMMENT '立案时间',
  `lawyer` varchar(100) DEFAULT NULL COMMENT '承办律师/法务负责人',
  `hearing_date` date DEFAULT NULL COMMENT '开庭时间',
  `claim_amount` decimal(18,2) DEFAULT NULL COMMENT '诉讼标的金额',
  `judgment_summary` text COMMENT '判决结果摘要',
  `progress_remark` text COMMENT '诉讼进展备注',
  `create_user` int DEFAULT NULL COMMENT '创建者',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_user` int DEFAULT NULL COMMENT '更新者',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `tenant_id` int DEFAULT NULL COMMENT '租户ID',
  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='诉讼管理主表';
doc/sales_ledger_contract_rel.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
CREATE TABLE `sales_ledger_contract_rel` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `sales_ledger_id` bigint(20) NOT NULL COMMENT '销售台账ID',
  `contract_id` bigint(20) NOT NULL COMMENT '合同ID',
  `create_user` int(11) DEFAULT NULL COMMENT '创建用户',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user` int(11) DEFAULT NULL COMMENT '更新用户',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`),
  KEY `idx_sales_ledger_id` (`sales_ledger_id`),
  KEY `idx_contract_id` (`contract_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售台账与合同关联表';
pom.xml
@@ -427,6 +427,12 @@
            <version>${thumbnailator.version}</version>
        </dependency>
        <dependency>
            <groupId>e-iceblue</groupId>
            <artifactId>spire.doc.free</artifactId>
            <version>5.2.0</version>
        </dependency>
    </dependencies>
    <build>
@@ -464,6 +470,11 @@
                <enabled>true</enabled>
            </releases>
        </repository>
        <repository>
            <id>com.e-iceblue</id>
            <name>e-iceblue</name>
            <url>https://repo.e-iceblue.cn/repository/maven-public/</url>
        </repository>
    </repositories>
    <pluginRepositories>
sql/vehicle_alter.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
ALTER TABLE `p_fuel_record` ADD COLUMN `dept_id` bigint(20) DEFAULT NULL COMMENT '所属部门';
sql/vehicle_init.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
CREATE TABLE IF NOT EXISTS `p_vehicle_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `plate_number` varchar(50) NOT NULL COMMENT '车牌号',
  `dept_id` bigint(20) DEFAULT NULL COMMENT '所属销售团队(部门ID)',
  `user_id` bigint(20) DEFAULT NULL COMMENT '责任人ID(用户ID)',
  `vehicle_status` varchar(20) DEFAULT 'NORMAL' COMMENT '车辆状态(NORMAL:在用, IDLE:闲置, REPAIR:ç»´ä¿®)',
  `property_ownership` varchar(50) DEFAULT NULL COMMENT '产权所有',
  `inspection_expire_date` date DEFAULT NULL COMMENT '年检到期日期',
  `insurance_expire_date` date DEFAULT NULL COMMENT '保险到期日期',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user` bigint(20) DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `tenant_id` varchar(20) DEFAULT NULL COMMENT '租户ID',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_plate_number` (`plate_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆档案表';
CREATE TABLE IF NOT EXISTS `p_vehicle_cost` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID',
  `cost_type` varchar(20) NOT NULL COMMENT '费用类型(MAINTENANCE:保养, INSPECTION:年检, INSURANCE:保险, REPAIR:ç»´ä¿®)',
  `amount` decimal(10,2) NOT NULL COMMENT '费用金额',
  `cost_date` date NOT NULL COMMENT '发生日期',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注/详情',
  `create_user` bigint(20) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_user` bigint(20) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `tenant_id` varchar(20) DEFAULT NULL,
  `del_flag` char(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆费用记录表';
CREATE TABLE IF NOT EXISTS `p_fuel_card` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `card_number` varchar(100) NOT NULL COMMENT '加油卡号',
  `dept_id` bigint(20) DEFAULT NULL COMMENT '所属团队(部门ID)',
  `issuer` varchar(100) DEFAULT NULL COMMENT '发卡单位',
  `initial_balance` decimal(10,2) DEFAULT '0.00' COMMENT '期初余额',
  `current_balance` decimal(10,2) DEFAULT '0.00' COMMENT '当前剩余余额',
  `status` varchar(20) DEFAULT 'ACTIVE' COMMENT '状态(ACTIVE:正常, CANCELLED:注销)',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `create_user` bigint(20) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_user` bigint(20) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `tenant_id` varchar(20) DEFAULT NULL,
  `del_flag` char(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_card_number` (`card_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加油卡管理表';
CREATE TABLE IF NOT EXISTS `p_fuel_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `fuel_date` date NOT NULL COMMENT '加油日期',
  `amount` decimal(10,2) NOT NULL COMMENT '加油金额',
  `liters` decimal(10,2) NOT NULL COMMENT '加油升数',
  `vehicle_id` bigint(20) NOT NULL COMMENT '加油车辆ID',
  `fuel_card_id` bigint(20) NOT NULL COMMENT '加油卡ID',
  `start_mileage` decimal(12,2) DEFAULT NULL COMMENT '起始公里数',
  `end_mileage` decimal(12,2) DEFAULT NULL COMMENT '结束公里数',
  `invoice_no` varchar(100) DEFAULT NULL COMMENT '发票号码',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `create_user` bigint(20) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_user` bigint(20) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `tenant_id` varchar(20) DEFAULT NULL,
  `del_flag` char(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='加油记录表';
src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
@@ -45,8 +45,8 @@
                .createIndex(PineconeServerlessIndexConfig.builder()
                        .cloud("AWS")
                        .region("us-east-1")
                        .dimension(embeddingModel.dimension())
                        .dimension(1536) // ä¸´æ—¶å†™æ­»ç»´åº¦(qwen一般是1536),避免项目启动时调用API导致欠费报错
                        .build())
                .build();
    }
}
}
src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
@@ -28,6 +28,9 @@
    //审批流程
    private List<ApprovalTask> tasks;
    @com.ruoyi.framework.aspectj.lang.annotation.Excel(name = "归属部门")
    private String deptName;
    //审批记录
    private List<ApprovalRecord>  records;
    private List<StorageBlobVO> storageBlobVOList;
src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
@@ -7,10 +7,13 @@
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.ruoyi.common.utils.poi.ExcelUtil;
/**
 * <p>
@@ -57,4 +60,12 @@
    public R delete(@RequestBody List<Long> ids) {
        return R.ok(finReimbursementService.delete(ids));
    }
    @PostMapping("/export")
    @Operation(summary = "导出费用报销数据")
    public void export(HttpServletResponse response, FinReimbursementDto finReimbursementDto) {
        List<FinReimbursementVo> list = finReimbursementService.list(finReimbursementDto);
        ExcelUtil<FinReimbursementVo> util = new ExcelUtil<FinReimbursementVo>(FinReimbursementVo.class);
        util.exportExcel(response, list, "费用报销数据");
    }
}
src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
@@ -9,6 +9,8 @@
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
 * æŠ¥é”€å•主表 Mapper æŽ¥å£
@@ -21,4 +23,6 @@
public interface FinReimbursementMapper extends BaseMapper<FinReimbursement> {
    IPage<FinReimbursementVo> listPage(@Param("ew") FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
    List<FinReimbursementVo> list(@Param("ew") FinReimbursementDto finReimbursementDto);
}
src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
@@ -1,6 +1,7 @@
package com.ruoyi.approve.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
@@ -39,6 +40,7 @@
     * æŠ¥é”€å•号
     */
    @Schema(description = "报销单号")
    @Excel(name = "报销单号")
    private String billNo;
    /**
@@ -51,6 +53,7 @@
     * è´¹ç”¨ç±»åž‹ï¼šå·®æ—…è´¹/办公采购/业务招待/交通费/通讯费/其他
     */
    @Schema(description = "费用类型:差旅费/办公采购/业务招待/交通费/通讯费/其他")
    @Excel(name = "费用类型")
    private String expenseType;
    /**
@@ -63,12 +66,14 @@
     * å‘˜å·¥ç¼–号
     */
    @Schema(description = "员工编号")
    @Excel(name = "申请人编号")
    private String applicantCode;
    /**
     * å‘˜å·¥å§“名
     */
    @Schema(description = "员工姓名")
    @Excel(name = "申请人姓名")
    private String applicantName;
    /**
@@ -87,12 +92,14 @@
     * æŠ¥é”€åŽŸå› 
     */
    @Schema(description = "报销原因")
    @Excel(name = "报销原因")
    private String reason;
    /**
     * ç”³è¯·é‡‘额
     */
    @Schema(description = "申请金额")
    @Excel(name = "报销金额")
    private BigDecimal applyAmount;
    /**
@@ -100,6 +107,13 @@
     */
    @Schema(description = "明细汇总金额")
    private BigDecimal detailTotalAmount;
    /**
     * ä»˜æ¬¾æ–¹
     */
    @Schema(description = "付款方")
    @Excel(name = "付款方")
    private String payer;
    /**
     * æ”¶æ¬¾äºº
@@ -135,6 +149,7 @@
     * å•据状态:DRAFT-草稿,IN_APPROVAL-审批中,APPROVED-审批通过,REJECTED-审批驳回,WITHDRAWN-已撤回,PAID-已付款
     */
    @Schema(description = "单据状态:DRAFT-草稿,IN_APPROVAL-审批中,APPROVED-审批通过,REJECTED-审批驳回,WITHDRAWN-已撤回,PAID-已付款")
    @Excel(name = "单据状态")
    private String billStatus;
    /**
@@ -178,6 +193,7 @@
     * åˆ›å»ºæ—¶é—´
     */
    @Schema(description = "创建时间")
    @Excel(name = "申请时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    /**
src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
@@ -21,6 +21,8 @@
    IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
    List<FinReimbursementVo> list(FinReimbursementDto finReimbursementDto);
    Boolean add(FinReimbursementDto finReimbursementDto);
    FinReimbursementVo detail(Long id);
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -48,6 +48,11 @@
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.staff.mapper.HolidayApplicationMapper;
import com.ruoyi.staff.pojo.HolidayApplication;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
import com.ruoyi.device.pojo.DeviceLedger;
import com.ruoyi.device.service.IDeviceLedgerService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -94,6 +99,8 @@
    private final EnterpriseNewsMapper enterpriseNewsMapper;
    private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
    private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
    private final IDeviceStatusChangeRecordService deviceStatusChangeRecordService;
    private final IDeviceLedgerService deviceLedgerService;
    @Override
    public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
@@ -167,6 +174,63 @@
        approvalInstanceDto.setInstanceNo(instanceNo);
        approvalInstanceDto.setStatus("PENDING");
        approvalInstanceDto.setCurrentLevel(1);
        if (approvalInstanceDto.getApplicantId() == null) {
            approvalInstanceDto.setApplicantId(SecurityUtils.getUserId());
            LoginUser loginUser = SecurityUtils.getLoginUser();
            if (loginUser != null && loginUser.getUser() != null) {
                approvalInstanceDto.setApplicantName(loginUser.getUser().getNickName());
            }
        }
        // æ ¡éªŒè®¾å¤‡çŠ¶æ€å˜æ›´å®¡æ‰¹ï¼šå¦‚æžœè¯¥è®¾å¤‡å·²æœ‰æ­£åœ¨å®¡æ‰¹çš„ä»»åŠ¡ï¼Œæ— æ³•å†æ¬¡è¿›è¡Œå®¡æ‰¹
        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(approvalInstanceDto.getBusinessType())) {
            Long deviceId = approvalInstanceDto.getBusinessId();
            long pendingCount = deviceStatusChangeRecordService.count(Wrappers.<DeviceStatusChangeRecord>lambdaQuery()
                    .eq(DeviceStatusChangeRecord::getDeviceId, deviceId)
                    .eq(DeviceStatusChangeRecord::getApprovalStatus, "PENDING")
                    .eq(DeviceStatusChangeRecord::getDeleted, 0)
            );
            if (pendingCount > 0) {
                throw new ServiceException("该设备已有正在进行的状态变更审批,无法再次提交");
            }
            DeviceLedger device = deviceLedgerService.getById(deviceId);
            if (device == null) {
                throw new ServiceException("设备不存在");
            }
            Integer targetStatus = null;
            String reason = "";
            if (StringUtils.hasText(approvalInstanceDto.getFormConfig())) {
                try {
                    JSONObject formObj = JSONObject.parseObject(approvalInstanceDto.getFormConfig());
                    if (formObj.containsKey("targetStatus")) {
                        targetStatus = formObj.getInteger("targetStatus");
                    } else if (formObj.containsKey("status")) {
                        targetStatus = formObj.getInteger("status");
                    }
                    if (formObj.containsKey("reason")) {
                        reason = formObj.getString("reason");
                    }
                } catch (Exception e) {
                    log.error("解析状态变更表单失败", e);
                }
            }
            DeviceStatusChangeRecord record = new DeviceStatusChangeRecord();
            record.setDeviceId(deviceId);
            record.setOriginalStatus(Integer.valueOf(device.getStatus()));
            record.setTargetStatus(targetStatus);
            record.setReason(reason);
            record.setApprovalStatus("PENDING");
            deviceStatusChangeRecordService.save(record);
            // ç»‘定到关联记录的主键
            approvalInstanceDto.setBusinessId(record.getId());
        }
        boolean saved = this.save(approvalInstanceDto);
        if (!saved) {
            return false;
@@ -284,7 +348,7 @@
        );
        //审批拒绝的处理
        if ("REJECTED".equals(approveAction)) {
            return rejectCurrentNode(instance, currentNode, now);
            return rejectCurrentNode(instance, currentNode, approvalInstanceDto.getApproveComment(), now);
        }
        if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
            return R.ok("审批成功,等待其他审批人处理");
@@ -333,7 +397,7 @@
                currentInstance.setStatus("APPROVED");
                currentInstance.setFinishTime(LocalDateTime.now());
                this.updateById(currentInstance);
                handleBusinessAfterApprovalFinished(currentInstance);
                handleBusinessAfterApprovalFinished(currentInstance, null);
                return R.ok("审批已完成");
            }
@@ -410,7 +474,7 @@
        );
    }
    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, String approveComment, LocalDateTime now) {
        if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
            return R.ok("当前节点已处理完成");
        }
@@ -420,7 +484,7 @@
        instance.setFinishTime(now);
        this.updateById(instance);
        // ç»Ÿä¸€å¤„理业务状态更新
        handleBusinessAfterApprovalFinished(instance);
        handleBusinessAfterApprovalFinished(instance, approveComment);
        return R.ok("审批已驳回");
    }
@@ -484,7 +548,7 @@
            instance.setStatus("APPROVED");
            instance.setFinishTime(now);
            this.updateById(instance);
            handleBusinessAfterApprovalFinished(instance);
            handleBusinessAfterApprovalFinished(instance, null);
            return R.ok("审批已完成");
        }
@@ -521,7 +585,7 @@
        );
    }
    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance, String rejectReason) {
        String status = instance.getStatus();
        Long businessType = instance.getBusinessType();
        if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
@@ -549,7 +613,39 @@
        }
        if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
            handleNewsApprovalFinished(instance, status);
            return;
        }
        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(businessType)) {
            handleDeviceStatusChangeApprovalFinished(instance, status, rejectReason);
            return;
        }
    }
    private void handleDeviceStatusChangeApprovalFinished(ApprovalInstance instance, String status, String rejectReason) {
        if (instance == null || instance.getBusinessId() == null) {
            return;
        }
        DeviceStatusChangeRecord record = deviceStatusChangeRecordService.getById(instance.getBusinessId());
        if (record == null) {
            return;
        }
        if ("APPROVED".equals(status)) {
            record.setApprovalStatus("APPROVED");
            if (record.getTargetStatus() != null) {
                DeviceLedger device = new DeviceLedger();
                device.setId(record.getDeviceId());
                device.setStatus(String.valueOf(record.getTargetStatus()));
                deviceLedgerService.updateById(device);
            }
        } else if ("REJECTED".equals(status)) {
            record.setApprovalStatus("REJECTED");
            if (rejectReason != null) {
                record.setRejectReason(rejectReason);
            }
        } else if ("PENDING".equals(status)) {
            record.setApprovalStatus("PENDING");
        }
        deviceStatusChangeRecordService.updateById(record);
    }
    private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
@@ -24,6 +24,8 @@
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.project.system.service.ISysDeptService;
import com.ruoyi.project.system.domain.SysDept;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -60,6 +62,7 @@
    private final FinReimbursementDetailMapper finReimbursementDetailMapper;
    private final FileUtil fileUtil;
    private final ISysNoticeService sysNoticeService;
    private final ISysDeptService sysDeptService;
    @Override
    public IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page) {
        IPage<FinReimbursementVo> finReimbursementVoIPage = finReimbursementMapper.listPage(finReimbursementDto, page);
@@ -68,6 +71,11 @@
        });
        return finReimbursementVoIPage;
    }
    @Override
    public List<FinReimbursementVo> list(FinReimbursementDto finReimbursementDto) {
        return finReimbursementMapper.list(finReimbursementDto);
    }
    @Override
@@ -251,6 +259,13 @@
        vo.setDeptId(reimbursement.getDeptId());
        vo.setDeleted(reimbursement.getDeleted());
        if (reimbursement.getDeptId() != null) {
            SysDept dept = sysDeptService.selectDeptById(reimbursement.getDeptId());
            if (dept != null) {
                vo.setDeptName(dept.getDeptName());
            }
        }
        vo.setDetails(finReimbursementDetailMapper.selectList(
                new LambdaQueryWrapper<FinReimbursementDetail>()
                        .eq(FinReimbursementDetail::getReimbursementId, reimbursement.getId())
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -5,6 +5,7 @@
    INSPECTION_TASK("inspection_task"),
    PDA_VERSION("pda_version"),
    SALES_LEDGER("sales_ledger"),
    SALES_LAWSUIT("sales_lawsuit"),
    SUPPLIER_MANAGE("supplier_manage"),
    APPROVAL_PROCESS("approval_process"),
    QR_CODE_SCAN_RECORDS("qr_code_scan_records"),
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
@@ -8,10 +8,12 @@
import com.ruoyi.basic.vo.CustomerVo;
import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
import com.ruoyi.sales.vo.CustomerTransactionsVo;
import com.ruoyi.sales.vo.ProjectTransactionsVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
 * å®¢æˆ·æ¡£æ¡ˆMapper接口
@@ -74,7 +76,11 @@
    List<CustomerVo> list(@Param("c") CustomerDto customer, @Param("loginUserId") Long loginUserId);
    IPage<CustomerTransactionsVo> customewTransactions(Page page, @Param("customerName") String customerName);
    IPage<ProjectTransactionsVo> projectTransactions(Page page, @Param("projectName") String projectName, @Param("customerName") String customerName, @Param("contractName") String contractName);
    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, @Param("customerId") Long customerId);
    IPage<CustomerTransactionsVo> customewTransactions(Page page, @Param("projectId") Long projectId, @Param("customerName") String customerName, @Param("contractName") String contractName);
    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, @Param("projectId") Long projectId, @Param("customerId") Long customerId, @Param("contractName") String contractName);
    Map<String, Object> transactionsSummary();
}
src/main/java/com/ruoyi/basic/service/ICustomerService.java
@@ -9,6 +9,7 @@
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
import com.ruoyi.sales.vo.CustomerTransactionsVo;
import com.ruoyi.sales.vo.ProjectTransactionsVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@@ -101,14 +102,11 @@
     * @param page
     * @param customerName
     * @return
     */
    IPage<CustomerTransactionsVo> customewTransactions(Page page, String customerName);
     */    IPage<ProjectTransactionsVo> projectTransactions(Page page, String projectName, String customerName, String contractName);
    /**
     * æŸ¥è¯¢å®¢æˆ·å¾€æ¥æ˜Žç»†åˆ—表
     * @param page
     * @param customerId
     * @return
     */
    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long customerId);
    IPage<CustomerTransactionsVo> customewTransactions(Page page, Long projectId, String customerName, String contractName);
    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName);
    Map<String, Object> transactionsSummary();
}
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -29,6 +29,7 @@
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
import com.ruoyi.sales.vo.CustomerTransactionsVo;
import com.ruoyi.sales.vo.ProjectTransactionsVo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -452,13 +453,23 @@
    }
    @Override
    public IPage<CustomerTransactionsVo> customewTransactions(Page page, String customerName) {
        return customerMapper.customewTransactions(page, customerName);
    public IPage<ProjectTransactionsVo> projectTransactions(Page page, String projectName, String customerName, String contractName) {
        return customerMapper.projectTransactions(page, projectName, customerName, contractName);
    }
    @Override
    public IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long customerId) {
        return customerMapper.customewTransactionsDetails(page, customerId);
    public IPage<CustomerTransactionsVo> customewTransactions(Page page, Long projectId, String customerName, String contractName) {
        return customerMapper.customewTransactions(page, projectId, customerName, contractName);
    }
    @Override
    public IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName) {
        return customerMapper.customewTransactionsDetails(page, projectId, customerId, contractName);
    }
    @Override
    public Map<String, Object> transactionsSummary() {
        return customerMapper.transactionsSummary();
    }
    /**
src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.ruoyi.collaborativeApproval.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
import com.ruoyi.collaborativeApproval.service.BroadbandCommunicationService;
import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
import com.ruoyi.framework.web.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/broadband")
public class BroadbandCommunicationController {
    @Autowired
    private BroadbandCommunicationService broadbandCommunicationService;
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto) {
        IPage<BroadbandCommunicationVO> result = broadbandCommunicationService.listPage(page, dto);
        return AjaxResult.success(result);
    }
    @GetMapping("/getDetail")
    public AjaxResult getDetail(@RequestParam("id") Long id) {
        return AjaxResult.success(broadbandCommunicationService.getDetail(id));
    }
    @PostMapping("/addOrUpdate")
    public AjaxResult addOrUpdate(@RequestBody BroadbandCommunicationDTO dto) {
        boolean success = broadbandCommunicationService.addOrUpdate(dto);
        if (success) {
            return AjaxResult.success("操作成功");
        }
        return AjaxResult.error("操作失败");
    }
    @DeleteMapping("/del")
    public AjaxResult del(@RequestBody List<Long> ids) {
        boolean success = broadbandCommunicationService.deleteByIds(ids);
        if (success) {
            return AjaxResult.success("删除成功");
        }
        return AjaxResult.error("删除失败");
    }
}
src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.collaborativeApproval.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import com.ruoyi.collaborativeApproval.service.HouseLeaseService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç† Controller
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/houseLease")
@RequiredArgsConstructor
@Tag(name = "房屋租赁管理", description = "房屋租赁相关接口")
public class HouseLeaseController {
    private final HouseLeaseService houseLeaseService;
    @GetMapping("/listPage")
    @Operation(summary = "分页查询")
    public AjaxResult listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO) {
        return AjaxResult.success(houseLeaseService.listPage(page, houseLeaseDTO));
    }
    @GetMapping("/getDetail")
    @Operation(summary = "查询详情(含费用子表)")
    public AjaxResult getDetail(@RequestParam("id") Long id) {
        return AjaxResult.success(houseLeaseService.getDetail(id));
    }
    @PostMapping("/addOrUpdate")
    @Operation(summary = "新增/修改(含费用子表)")
    public AjaxResult addOrUpdate(@RequestBody HouseLeaseDTO houseLeaseDTO) {
        return AjaxResult.success(houseLeaseService.addOrUpdate(houseLeaseDTO));
    }
    @DeleteMapping("/del")
    @Operation(summary = "批量删除")
    public AjaxResult del(@RequestBody List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return AjaxResult.error("请传入要删除的ID");
        }
        return AjaxResult.success(houseLeaseService.removeByIds(ids));
    }
    @PostMapping("/export")
    @Operation(summary = "导出数据")
    public void export(HttpServletResponse response, HouseLeaseDTO houseLeaseDTO) {
        List<HouseLease> list = houseLeaseService.getList(houseLeaseDTO);
        ExcelUtil<HouseLease> util = new ExcelUtil<>(HouseLease.class);
        util.exportExcel(response, list, "房屋租赁数据");
    }
}
src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.collaborativeApproval.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class BroadbandCommunicationDTO {
    private Long id;
    private String area;
    private String office;
    private String residentStaff;
    private String address;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate installDate;
    private BigDecimal installFee;
    private Integer paymentCycle;
    private String paymentMethod;
    private BigDecimal prepaidAmount;
    private BigDecimal monthlyFee;
    private String packageContent;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextPaymentDate;
    /**
     * normal: æ­£å¸¸ (>15天)
     * expiring: å³å°†åˆ°æœŸ (0~15天)
     * overdue: å·²é€¾æœŸ (<0天)
     */
    private String warnStatus;
}
src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.collaborativeApproval.dto;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
 * æˆ¿å±‹ç§Ÿèµè¯·æ±‚ DTO
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class HouseLeaseDTO extends HouseLease {
    private static final long serialVersionUID = 1L;
    /**
     * è´¹ç”¨æ˜Žç»†åˆ—表 (子表)
     */
    private List<HouseLeaseCost> costList;
    /**
     * åˆ°æœŸé¢„警状态:normal(正常), expiring(即将到期 0-30天), overdue(已逾期 <0天)
     * ä»…用作列表查询条件
     */
    private String warnStatus;
}
src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package com.ruoyi.collaborativeApproval.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
public interface BroadbandCommunicationMapper extends BaseMapper<BroadbandCommunication> {
}
src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.collaborativeApproval.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
import org.apache.ibatis.annotations.Mapper;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç†è´¹ç”¨å­è¡¨ Mapper æŽ¥å£
 *
 * @author ruoyi
 */
@Mapper
public interface HouseLeaseCostMapper extends BaseMapper<HouseLeaseCost> {
}
src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.collaborativeApproval.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import org.apache.ibatis.annotations.Mapper;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç† Mapper æŽ¥å£
 *
 * @author ruoyi
 */
@Mapper
public interface HouseLeaseMapper extends BaseMapper<HouseLease> {
}
src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.ruoyi.collaborativeApproval.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("broadband_communication")
public class BroadbandCommunication {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String area;
    private String office;
    private String residentStaff;
    private String address;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate installDate;
    private BigDecimal installFee;
    private Integer paymentCycle;
    private String paymentMethod;
    private BigDecimal prepaidAmount;
    private BigDecimal monthlyFee;
    private String packageContent;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextPaymentDate;
    private String delFlag;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package com.ruoyi.collaborativeApproval.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç†ä¸»è¡¨å¯¹è±¡ house_lease
 *
 * @author ruoyi
 */
@Data
@TableName("house_lease")
public class HouseLease {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** åŠžäº‹å¤„ */
    @Excel(name = "办事处")
    private String office;
    /** ä½¿ç”¨äºº */
    @Excel(name = "使用人")
    private String userName;
    /** é©»çœäººå‘˜ */
    @Excel(name = "驻省人员")
    private String residentStaff;
    /** åœ°å€ */
    @Excel(name = "地址")
    private String address;
    /** èµ·ç§Ÿæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "起租时间", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate rentStartDate;
    /** åˆåŒèµ·ç§Ÿæ—¥ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "合同起租日", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate contractStartDate;
    /** åˆåŒåˆ°æœŸæ—¥ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "合同到期日", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate contractEndDate;
    /** æ‰¿ç§Ÿäºº */
    @Excel(name = "承租人")
    private String tenant;
    /** åˆåŒç”²æ–¹ */
    @Excel(name = "合同甲方")
    private String partyA;
    /** ç”µè¯ */
    @Excel(name = "电话")
    private String phone;
    /** é¢ç§¯ */
    @Excel(name = "面积")
    private BigDecimal area;
    /** æ”¯ä»˜æ–¹å¼ */
    @Excel(name = "支付方式")
    private String paymentMethod;
    /** æŠ¼é‡‘ */
    @Excel(name = "押金")
    private BigDecimal deposit;
    /** ç§Ÿé‡‘(元/月) */
    @Excel(name = "租金(元/月)")
    private BigDecimal rent;
    /** å¤‡æ³¨ */
    @Excel(name = "备注")
    private String remark;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    /** ç§Ÿæˆ·ID */
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    /** éƒ¨é—¨ID */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
package com.ruoyi.collaborativeApproval.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç†è´¹ç”¨å­è¡¨å¯¹è±¡ house_lease_cost
 *
 * @author ruoyi
 */
@Data
@TableName("house_lease_cost")
public class HouseLeaseCost {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** ç§Ÿèµè¡¨ID */
    private Long leaseId;
    /** æœˆä»½ (YYYY-MM) */
    private String month;
    /** æˆ¿ç§Ÿ */
    private BigDecimal rent;
    /** æ°´ç”µ */
    private BigDecimal waterElectricity;
    /** ç‰©ä¸š */
    private BigDecimal property;
    /** å¿«é€’ */
    private BigDecimal express;
    /** å®½å¸¦ */
    private BigDecimal broadband;
    /** å¤‡æ³¨ */
    private String remark;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    /** ç§Ÿæˆ·ID */
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    /** éƒ¨é—¨ID */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.collaborativeApproval.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
import java.util.List;
public interface BroadbandCommunicationService extends IService<BroadbandCommunication> {
    IPage<BroadbandCommunicationVO> listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto);
    BroadbandCommunicationVO getDetail(Long id);
    boolean addOrUpdate(BroadbandCommunicationDTO dto);
    boolean deleteByIds(List<Long> ids);
}
src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.collaborativeApproval.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç†è´¹ç”¨å­è¡¨ Service æŽ¥å£
 *
 * @author ruoyi
 */
public interface HouseLeaseCostService extends IService<HouseLeaseCost> {
}
src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.collaborativeApproval.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import com.ruoyi.collaborativeApproval.vo.HouseLeaseVO;
import java.util.List;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç† Service æŽ¥å£
 *
 * @author ruoyi
 */
public interface HouseLeaseService extends IService<HouseLease> {
    /**
     * åˆ†é¡µæŸ¥è¯¢
     *
     * @param page åˆ†é¡µå¯¹è±¡
     * @param houseLeaseDTO æŸ¥è¯¢æ¡ä»¶
     * @return åˆ†é¡µç»“æžœ
     */
    Page<HouseLeaseVO> listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO);
    /**
     * æŸ¥è¯¢åˆ—表 (用于导出)
     *
     * @param houseLeaseDTO æŸ¥è¯¢æ¡ä»¶
     * @return åˆ—表结果
     */
    List<HouseLease> getList(HouseLeaseDTO houseLeaseDTO);
    /**
     * æŸ¥è¯¢è¯¦æƒ…
     *
     * @param id ç§ŸèµID
     * @return è¯¦æƒ…VO
     */
    HouseLeaseVO getDetail(Long id);
    /**
     * æ–°å¢žæˆ–修改
     *
     * @param houseLeaseDTO æ•°æ®
     * @return æ˜¯å¦æˆåŠŸ
     */
    boolean addOrUpdate(HouseLeaseDTO houseLeaseDTO);
}
src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package com.ruoyi.collaborativeApproval.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
import com.ruoyi.collaborativeApproval.mapper.BroadbandCommunicationMapper;
import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
import com.ruoyi.collaborativeApproval.service.BroadbandCommunicationService;
import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List;
@Service
public class BroadbandCommunicationServiceImpl extends ServiceImpl<BroadbandCommunicationMapper, BroadbandCommunication> implements BroadbandCommunicationService {
    @Override
    public IPage<BroadbandCommunicationVO> listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto) {
        LambdaQueryWrapper<BroadbandCommunication> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BroadbandCommunication::getDelFlag, "0");
        if (StringUtils.isNotBlank(dto.getArea())) {
            queryWrapper.like(BroadbandCommunication::getArea, dto.getArea());
        }
        if (StringUtils.isNotBlank(dto.getOffice())) {
            queryWrapper.like(BroadbandCommunication::getOffice, dto.getOffice());
        }
        if (StringUtils.isNotBlank(dto.getResidentStaff())) {
            queryWrapper.like(BroadbandCommunication::getResidentStaff, dto.getResidentStaff());
        }
        if (StringUtils.isNotBlank(dto.getWarnStatus())) {
            LocalDate today = LocalDate.now();
            if ("expiring".equals(dto.getWarnStatus())) {
                // å³å°†åˆ°æœŸ: nextPaymentDate åœ¨ä»Šå¤©åˆ°15天之后之间,且不包括小于今天
                LocalDate future15 = today.plusDays(15);
                queryWrapper.ge(BroadbandCommunication::getNextPaymentDate, today)
                            .le(BroadbandCommunication::getNextPaymentDate, future15);
            } else if ("overdue".equals(dto.getWarnStatus())) {
                // å·²é€¾æœŸ: nextPaymentDate < ä»Šå¤©
                queryWrapper.lt(BroadbandCommunication::getNextPaymentDate, today);
            } else if ("normal".equals(dto.getWarnStatus())) {
                // æ­£å¸¸: nextPaymentDate > 15天之后
                LocalDate future15 = today.plusDays(15);
                queryWrapper.gt(BroadbandCommunication::getNextPaymentDate, future15);
            }
        }
        queryWrapper.orderByDesc(BroadbandCommunication::getCreateTime);
        IPage<BroadbandCommunication> p = this.page(page, queryWrapper);
        return p.convert(this::convertToVO);
    }
    @Override
    public BroadbandCommunicationVO getDetail(Long id) {
        BroadbandCommunication entity = this.getById(id);
        if (entity != null && "0".equals(entity.getDelFlag())) {
            return convertToVO(entity);
        }
        return null;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addOrUpdate(BroadbandCommunicationDTO dto) {
        BroadbandCommunication entity = new BroadbandCommunication();
        BeanUtils.copyProperties(dto, entity);
        // Calculate nextPaymentDate automatically if installDate and paymentCycle are present
        if (entity.getInstallDate() != null && entity.getPaymentCycle() != null) {
            entity.setNextPaymentDate(entity.getInstallDate().plusMonths(entity.getPaymentCycle()));
        }
        if (entity.getId() == null) {
            return this.save(entity);
        } else {
            return this.updateById(entity);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByIds(List<Long> ids) {
        if (ids != null && !ids.isEmpty()) {
            return this.removeByIds(ids);
        }
        return false;
    }
    private BroadbandCommunicationVO convertToVO(BroadbandCommunication entity) {
        BroadbandCommunicationVO vo = new BroadbandCommunicationVO();
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }
}
src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.collaborativeApproval.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.collaborativeApproval.mapper.HouseLeaseCostMapper;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
import com.ruoyi.collaborativeApproval.service.HouseLeaseCostService;
import org.springframework.stereotype.Service;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç†è´¹ç”¨å­è¡¨ Service ä¸šåŠ¡å±‚å¤„ç†
 *
 * @author ruoyi
 */
@Service
public class HouseLeaseCostServiceImpl extends ServiceImpl<HouseLeaseCostMapper, HouseLeaseCost> implements HouseLeaseCostService {
}
src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,159 @@
package com.ruoyi.collaborativeApproval.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
import com.ruoyi.collaborativeApproval.mapper.HouseLeaseMapper;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
import com.ruoyi.collaborativeApproval.service.HouseLeaseCostService;
import com.ruoyi.collaborativeApproval.service.HouseLeaseService;
import com.ruoyi.collaborativeApproval.vo.HouseLeaseVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * æˆ¿å±‹ç§Ÿèµç®¡ç† Service ä¸šåŠ¡å±‚å¤„ç†
 *
 * @author ruoyi
 */
@Service
@RequiredArgsConstructor
public class HouseLeaseServiceImpl extends ServiceImpl<HouseLeaseMapper, HouseLease> implements HouseLeaseService {
    private final HouseLeaseCostService houseLeaseCostService;
    @Override
    public Page<HouseLeaseVO> listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO) {
        LambdaQueryWrapper<HouseLease> lqw = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(houseLeaseDTO.getOffice())) {
            lqw.like(HouseLease::getOffice, houseLeaseDTO.getOffice());
        }
        if (StringUtils.isNotBlank(houseLeaseDTO.getUserName())) {
            lqw.like(HouseLease::getUserName, houseLeaseDTO.getUserName());
        }
        // å¤„理到期预警状态
        String warnStatus = houseLeaseDTO.getWarnStatus();
        if (StringUtils.isNotBlank(warnStatus)) {
            LocalDate today = LocalDate.now();
            LocalDate in30Days = today.plusDays(30);
            if ("overdue".equals(warnStatus)) {
                // å·²é€¾æœŸ: åˆåŒåˆ°æœŸæ—¥ < ä»Šå¤©
                lqw.lt(HouseLease::getContractEndDate, today);
            } else if ("expiring".equals(warnStatus)) {
                // å³å°†åˆ°æœŸ: 0 <= åˆåŒåˆ°æœŸæ—¥ - ä»Šå¤© <= 30
                lqw.ge(HouseLease::getContractEndDate, today)
                   .le(HouseLease::getContractEndDate, in30Days);
            } else if ("normal".equals(warnStatus)) {
                // æ­£å¸¸: åˆåŒåˆ°æœŸæ—¥ > ä»Šå¤© + 30天
                lqw.gt(HouseLease::getContractEndDate, in30Days);
            }
        }
        lqw.orderByDesc(HouseLease::getCreateTime);
        Page<HouseLease> entityPage = this.page(page, lqw);
        // è½¬æ¢æˆVO分页对象
        Page<HouseLeaseVO> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
        List<HouseLeaseVO> voRecords = entityPage.getRecords().stream()
                .map(entity -> BeanUtil.copyProperties(entity, HouseLeaseVO.class))
                .collect(Collectors.toList());
        voPage.setRecords(voRecords);
        return voPage;
    }
    @Override
    public List<HouseLease> getList(HouseLeaseDTO houseLeaseDTO) {
        LambdaQueryWrapper<HouseLease> lqw = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(houseLeaseDTO.getOffice())) {
            lqw.like(HouseLease::getOffice, houseLeaseDTO.getOffice());
        }
        if (StringUtils.isNotBlank(houseLeaseDTO.getUserName())) {
            lqw.like(HouseLease::getUserName, houseLeaseDTO.getUserName());
        }
        // å¤„理到期预警状态
        String warnStatus = houseLeaseDTO.getWarnStatus();
        if (StringUtils.isNotBlank(warnStatus)) {
            LocalDate today = LocalDate.now();
            LocalDate in30Days = today.plusDays(30);
            if ("overdue".equals(warnStatus)) {
                lqw.lt(HouseLease::getContractEndDate, today);
            } else if ("expiring".equals(warnStatus)) {
                lqw.ge(HouseLease::getContractEndDate, today)
                   .le(HouseLease::getContractEndDate, in30Days);
            } else if ("normal".equals(warnStatus)) {
                lqw.gt(HouseLease::getContractEndDate, in30Days);
            }
        }
        lqw.orderByDesc(HouseLease::getCreateTime);
        return this.list(lqw);
    }
    @Override
    public HouseLeaseVO getDetail(Long id) {
        HouseLease houseLease = this.getById(id);
        if (houseLease == null) {
            return null;
        }
        HouseLeaseVO vo = BeanUtil.copyProperties(houseLease, HouseLeaseVO.class);
        // æŸ¥è¯¢å­è¡¨
        List<HouseLeaseCost> costList = houseLeaseCostService.list(
                Wrappers.<HouseLeaseCost>lambdaQuery()
                        .eq(HouseLeaseCost::getLeaseId, id)
                        .orderByAsc(HouseLeaseCost::getMonth)
        );
        vo.setCostList(costList);
        return vo;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addOrUpdate(HouseLeaseDTO houseLeaseDTO) {
        HouseLease houseLease = BeanUtil.copyProperties(houseLeaseDTO, HouseLease.class);
        // 1. ä¿å­˜ä¸»è¡¨
        this.saveOrUpdate(houseLease);
        // 2. å¤„理子表 (全删全插模式)
        // åˆ é™¤æ—§æ•°æ®
        houseLeaseCostService.remove(
                Wrappers.<HouseLeaseCost>lambdaQuery()
                        .eq(HouseLeaseCost::getLeaseId, houseLease.getId())
        );
        // æ’入新数据
        List<HouseLeaseCost> costList = houseLeaseDTO.getCostList();
        if (costList != null && !costList.isEmpty()) {
            for (HouseLeaseCost cost : costList) {
                cost.setId(null); // ç¡®ä¿æ˜¯æ–°å¢ž
                cost.setLeaseId(houseLease.getId());
            }
            houseLeaseCostService.saveBatch(costList);
        }
        return true;
    }
}
src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.collaborativeApproval.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class BroadbandCommunicationVO {
    private Long id;
    private String area;
    private String office;
    private String residentStaff;
    private String address;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate installDate;
    private BigDecimal installFee;
    private Integer paymentCycle;
    private String paymentMethod;
    private BigDecimal prepaidAmount;
    private BigDecimal monthlyFee;
    private String packageContent;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextPaymentDate;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}
src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.collaborativeApproval.vo;
import com.ruoyi.collaborativeApproval.pojo.HouseLease;
import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
 * æˆ¿å±‹ç§Ÿèµå“åº” VO
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class HouseLeaseVO extends HouseLease {
    private static final long serialVersionUID = 1L;
    /**
     * è´¹ç”¨æ˜Žç»†åˆ—表 (子表)
     */
    private List<HouseLeaseCost> costList;
}
src/main/java/com/ruoyi/common/enums/TypeEnums.java
@@ -24,7 +24,8 @@
    OVERTIME_APPROVAL(15L, "加班审批"),
    TRAVEL_REIMBURSEMENT_APPROVAL(16L, "出差报销审批"),
    EXPENSE_APPROVAL(17L, "费用审批"),
    ENTERPRISE_NEWS_APPROVAL(18L, "企业新闻审批");
    ENTERPRISE_NEWS_APPROVAL(18L, "企业新闻审批"),
    DEVICE_STATUS_CHANGE_APPROVAL(19L, "设备状态变更审批");
src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.device.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "设备状态变更记录")
@RestController
@RequestMapping("/device/statusChangeRecord")
public class DeviceStatusChangeRecordController extends BaseController {
    @Autowired
    private IDeviceStatusChangeRecordService deviceStatusChangeRecordService;
    @Operation(summary = "分页查询设备状态变更记录")
    @GetMapping("/list")
    public AjaxResult list(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record) {
        IPage<DeviceStatusChangeRecord> pageList = deviceStatusChangeRecordService.selectPageList(page, record);
        return AjaxResult.success(pageList);
    }
}
src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
@@ -57,6 +57,42 @@
     */
    private String supplierName;
    @Schema(description = "设备编号")
    private String uniqueCode;
    @Schema(description = "出厂编号")
    private String factoryNumber;
    @Schema(description = "制造厂商")
    private String manufacturer;
    @Schema(description = "资产原值")
    private BigDecimal originalAssetValue;
    @Schema(description = "购置日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate purchaseDate;
    @Schema(description = "启用日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate activationDate;
    @Schema(description = "主要技术参数")
    private String technicalParameters;
    @Schema(description = "主要用途")
    private String mainPurpose;
    @Schema(description = "责任人")
    private String responsiblePerson;
    @Schema(description = "建档日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate filingDate;
    /**
     * è®¾å¤‡é™„ä»¶(用于接收)
     */
src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java
@@ -84,10 +84,42 @@
    /**
     * å½•入人
     */
    @Excel(name = "录入人",sort = 9)
    @Excel(name = "录入人",sort = 10)
    private String createUser;
    @Excel(name = "设备编号", sort = 11)
    private String uniqueCode;
    @Excel(name = "出厂编号", sort = 12)
    private String factoryNumber;
    @Excel(name = "制造厂商", sort = 13)
    private String manufacturer;
    @Excel(name = "资产原值", sort = 14)
    private BigDecimal originalAssetValue;
    @Excel(name = "购置日期", sort = 15, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date purchaseDate;
    @Excel(name = "启用日期", sort = 16, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date activationDate;
    @Excel(name = "主要技术参数", sort = 17)
    private String technicalParameters;
    @Excel(name = "主要用途", sort = 18)
    private String mainPurpose;
    @Excel(name = "责任人", sort = 19)
    private String responsiblePerson;
    @Excel(name = "建档日期", sort = 20, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date filingDate;
}
src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package com.ruoyi.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface DeviceRepairSparePartMapper extends BaseMapper<DeviceRepairSparePart> {
    @Select("SELECT d.*, s.name as spare_part_name FROM device_repair_spare_part d LEFT JOIN spare_parts s ON d.spare_part_id = s.id WHERE d.source_id = #{sourceId} AND d.source_type = #{sourceType}")
    List<DeviceRepairSparePart> selectListWithPartName(Long sourceId, Integer sourceType);
}
src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeviceStatusChangeRecordMapper extends BaseMapper<DeviceStatusChangeRecord> {
}
src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
@@ -55,6 +55,42 @@
     */
    private String supplierName;
    @Schema(description = "设备编号")
    private String uniqueCode;
    @Schema(description = "出厂编号")
    private String factoryNumber;
    @Schema(description = "制造厂商")
    private String manufacturer;
    @Schema(description = "资产原值")
    private BigDecimal originalAssetValue;
    @Schema(description = "购置日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate purchaseDate;
    @Schema(description = "启用日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate activationDate;
    @Schema(description = "主要技术参数")
    private String technicalParameters;
    @Schema(description = "主要用途")
    private String mainPurpose;
    @Schema(description = "责任人")
    private String responsiblePerson;
    @Schema(description = "建档日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate filingDate;
    /**
     * å•位
     */
src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
@@ -61,6 +61,18 @@
    @Schema(description = "保养结果 0 ç»´ä¿® 1 å®Œå¥½")
    private String maintenanceResult;
    @Schema(description = "保养类型")
    private String maintenanceType;
    @Schema(description = "工作内容描述")
    private String workContent;
    @Schema(description = "运行状况")
    private String runningStatus;
    @Schema(description = "实际执行人ID")
    private Long executorId;
    @Schema(description = "状态 0 å¾…保养 1 å®Œç»“ 2 å¤±è´¥")
    private Integer status;
src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
@@ -61,6 +61,15 @@
    @Schema(description = "验收备注")
    private String acceptanceRemark;
    @Schema(description = "报修类型(字典值)")
    private String repairType;
    @Schema(description = "原因分析")
    private String reasonAnalysis;
    @Schema(description = "处理措施")
    private String handlingMeasures;
    @Schema(description = "状态 0 å¾…ç»´ä¿® 1完结 2 å¤±è´¥")
    // 0:待维修 1:完结 2:失败 3:待验收
    private Integer status;
src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.device.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@TableName("device_repair_spare_part")
public class DeviceRepairSparePart {
    @Schema(description = "主键ID")
    @TableId(type = IdType.AUTO)
    private Long id;
    @Schema(description = "来源ID(报修单ID或保养单ID)")
    private Long sourceId;
    @Schema(description = "来源类型:1-报修,2-保养")
    private Integer sourceType;
    @Schema(description = "配件ID")
    private Long sparePartId;
    @Schema(description = "使用数量")
    private Integer quantity;
    @Schema(description = "配件名称(非表字段)")
    @TableField(exist = false)
    private String sparePartName;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @Schema(description = "创建人")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @Schema(description = "更新人")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @Schema(description = "租户id")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.device.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldFill;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@TableName("device_status_change_record")
public class DeviceStatusChangeRecord implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private Long deviceId;
    private Integer originalStatus;
    private Integer targetStatus;
    private String reason;
    private String rejectReason;
    private String approvalStatus;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(exist = false)
    private String createUserName;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    private Integer deleted;
}
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -69,6 +69,12 @@
    @Excel(name = "备注")
    private String remarks;
    @Schema(description = "保养类型")
    private String maintenanceType;
    @Schema(description = "执行人ID")
    private Long executorId;
    @Schema(description = "录入人id")
    private Long registrantId;
src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.device.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import java.util.List;
public interface IDeviceRepairSparePartService extends IService<DeviceRepairSparePart> {
    List<DeviceRepairSparePart> getListWithPartName(Long sourceId, Integer sourceType);
}
src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
package com.ruoyi.device.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
public interface IDeviceStatusChangeRecordService extends IService<DeviceStatusChangeRecord> {
    IPage<DeviceStatusChangeRecord> selectPageList(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record);
}
src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.device.dto.DeviceLedgerDto;
@@ -31,6 +32,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.time.format.DateTimeFormatter;
import java.time.LocalDate;
@Service
@RequiredArgsConstructor
@@ -56,6 +59,28 @@
        }
        DeviceLedger deviceLedger = new DeviceLedger();
        BeanUtils.copyProperties(deviceLedgerDto, deviceLedger);
        if (StringUtils.isEmpty(deviceLedger.getUniqueCode())) {
            String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            LambdaQueryWrapper<DeviceLedger> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.likeRight(DeviceLedger::getUniqueCode, "SB" + dateStr);
            queryWrapper.orderByDesc(DeviceLedger::getUniqueCode);
            queryWrapper.last("limit 1");
            DeviceLedger lastRecord = this.getOne(queryWrapper);
            int sequence = 1;
            if (lastRecord != null && StringUtils.isNotEmpty(lastRecord.getUniqueCode()) && lastRecord.getUniqueCode().length() >= 13) {
                String lastSeqStr = lastRecord.getUniqueCode().substring(10);
                try {
                    sequence = Integer.parseInt(lastSeqStr) + 1;
                } catch (NumberFormatException ignored) {}
            }
            deviceLedger.setUniqueCode("SB" + dateStr + String.format("%03d", sequence));
        }
        if (this.count(new LambdaQueryWrapper<DeviceLedger>().eq(DeviceLedger::getUniqueCode, deviceLedger.getUniqueCode())) > 0) {
            return AjaxResult.error("设备编号已存在:" + deviceLedger.getUniqueCode());
        }
        boolean save = this.save(deviceLedger);
        if (save){
            if (deviceLedgerDto.getStorageBlobDTOs() != null) {
@@ -73,6 +98,14 @@
    @Override
    public AjaxResult updateDeviceLedger(DeviceLedgerDto deviceLedgerDto) {
        if (StringUtils.isNotEmpty(deviceLedgerDto.getUniqueCode())) {
            if (this.count(new LambdaQueryWrapper<DeviceLedger>()
                    .eq(DeviceLedger::getUniqueCode, deviceLedgerDto.getUniqueCode())
                    .ne(DeviceLedger::getId, deviceLedgerDto.getId())) > 0) {
                return AjaxResult.error("设备编号已存在:" + deviceLedgerDto.getUniqueCode());
            }
        }
        DeviceLedger deviceLedger = new DeviceLedger();
        BeanUtils.copyProperties(deviceLedgerDto, deviceLedger);
        if (this.updateById(deviceLedger)) {
@@ -141,6 +174,25 @@
    public Boolean importData(MultipartFile file) throws IOException {
        ExcelUtil<DeviceLedgerExeclDto> util = new ExcelUtil<DeviceLedgerExeclDto>(DeviceLedgerExeclDto.class);
        List<DeviceLedgerExeclDto> userList = util.importExcel(file.getInputStream());
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        LambdaQueryWrapper<DeviceLedger> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.likeRight(DeviceLedger::getUniqueCode, "SB" + dateStr);
        queryWrapper.orderByDesc(DeviceLedger::getUniqueCode);
        queryWrapper.last("limit 1");
        DeviceLedger lastRecord = this.getOne(queryWrapper);
        // åº”对Java lambda表达式内变量必须是final的问题,用数组或类的成员包裹
        final int[] maxSequence = {0};
        if (lastRecord != null && StringUtils.isNotEmpty(lastRecord.getUniqueCode()) && lastRecord.getUniqueCode().length() >= 13) {
            String lastSeqStr = lastRecord.getUniqueCode().substring(10);
            try {
                maxSequence[0] = Integer.parseInt(lastSeqStr);
            } catch (NumberFormatException ignored) {}
        }
        List<String> importUniqueCodes = new ArrayList<>();
        userList.forEach(c -> {
            DeviceLedger deviceLedger = new DeviceLedger();
            SysUser sysUser = sysUserMapper.selectUserByUserName(c.getCreateUser());
@@ -150,6 +202,18 @@
                deviceLedger.setCreateUser(SecurityUtils.getUserId().intValue());
            }
            BeanUtils.copyProperties(c,deviceLedger);
            if (StringUtils.isEmpty(deviceLedger.getUniqueCode())) {
                maxSequence[0]++;
                deviceLedger.setUniqueCode("SB" + dateStr + String.format("%03d", maxSequence[0]));
            }
            if (importUniqueCodes.contains(deviceLedger.getUniqueCode())) {
                throw new RuntimeException("导入文件中存在重复的设备编号:" + deviceLedger.getUniqueCode());
            }
            if (this.count(new LambdaQueryWrapper<DeviceLedger>().eq(DeviceLedger::getUniqueCode, deviceLedger.getUniqueCode())) > 0) {
                throw new RuntimeException("设备编号已存在:" + deviceLedger.getUniqueCode());
            }
            importUniqueCodes.add(deviceLedger.getUniqueCode());
            // é€šè¿‡å«ç¨Žå•价、数量、税率计算含税总价,不含税总价
            deviceLedger.setTaxIncludingPriceTotal(c.getTaxIncludingPriceUnit());
            deviceLedger.setNumber(BigDecimal.ONE);
@@ -158,6 +222,17 @@
            if (deviceLedger.getTaxIncludingPriceTotal() != null && c.getTaxRate() != null) {
                deviceLedger.setUnTaxIncludingPriceTotal(deviceLedger.getTaxIncludingPriceTotal().divide(BigDecimal.ONE.add(c.getTaxRate()), 2, RoundingMode.HALF_UP));
            }
            if (c.getPurchaseDate() != null) {
                deviceLedger.setPurchaseDate(DateUtils.toLocalDate(c.getPurchaseDate()));
            }
            if (c.getActivationDate() != null) {
                deviceLedger.setActivationDate(DateUtils.toLocalDate(c.getActivationDate()));
            }
            if (c.getFilingDate() != null) {
                deviceLedger.setFilingDate(DateUtils.toLocalDate(c.getFilingDate()));
            }
            deviceLedgerMapper.insert(deviceLedger);
        });
src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -21,7 +21,11 @@
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import com.ruoyi.device.service.IDeviceRepairSparePartService;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -38,6 +42,7 @@
    private final DeviceMaintenanceMapper deviceMaintenanceMapper;
    private final SparePartsMapper sparePartsMapper;
    private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
    private final IDeviceRepairSparePartService deviceRepairSparePartService;
    private final FileUtil fileUtil;
    @Override
@@ -62,6 +67,12 @@
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult updateDeviceDeviceMaintenance(DeviceMaintenanceDto deviceMaintenance) {
        DeviceMaintenance oldDeviceMaintenance = this.getById(deviceMaintenance.getId());
        // æ¸…理旧的备件使用记录,防止重复累计
        deviceRepairSparePartService.remove(new QueryWrapper<DeviceRepairSparePart>()
                .eq("source_id", deviceMaintenance.getId())
                .eq("source_type", 2));
        // å¤„理备件使用情况
        if (com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(deviceMaintenance.getSparePartsUseList())) {
            List<Long> sparePartIds = new ArrayList<>();
@@ -84,6 +95,14 @@
                        record.setSparePartsId(sparePartUse.getId());
                        record.setQuantity(sparePartUse.getQuantity());
                        sparePartsRequisitionRecordService.save(record);
                        // æ’入记录到 device_repair_spare_part
                        DeviceRepairSparePart partRecord = new DeviceRepairSparePart();
                        partRecord.setSourceId(deviceMaintenance.getId());
                        partRecord.setSourceType(2); // 2 ä¿å…»
                        partRecord.setSparePartId(sparePartUse.getId());
                        partRecord.setQuantity(sparePartUse.getQuantity());
                        deviceRepairSparePartService.save(partRecord);
                    } else {
                        return AjaxResult.error("备件 " + spareParts.getName() + " æ•°é‡ä¸è¶³");
                    }
@@ -122,6 +141,8 @@
    public DeviceMaintenanceVo detailById(Long id) {
        DeviceMaintenanceVo vo = deviceMaintenanceMapper.detailById(id);
        vo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.DEVICE_MAINTENANCE, id));
        List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(id, 2);
        vo.setSparePartsUseListDetails(parts);
        return vo;
    }
}
src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
@@ -6,6 +6,7 @@
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.device.dto.DeviceDefectRecordDto;
@@ -21,6 +22,11 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
import com.ruoyi.approve.pojo.KnowledgeBase;
import com.ruoyi.approve.service.KnowledgeBaseService;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import com.ruoyi.device.service.IDeviceRepairSparePartService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
import jakarta.servlet.http.HttpServletResponse;
@@ -45,6 +51,8 @@
    private final IDeviceLedgerService deviceLedgerService;
    private final SparePartsMapper sparePartsMapper;
    private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
    private final IDeviceRepairSparePartService deviceRepairSparePartService;
    private final KnowledgeBaseService knowledgeBaseService;
    private final FileUtil fileUtil;
    private static final int STATUS_PENDING_REPAIR = 0;
@@ -92,6 +100,11 @@
                || oldDeviceRepair.getStatus() != STATUS_COMPLETED)) {
            return AjaxResult.error("请先提交验收审批,验收通过后才可完结");
        }
        // æ¸…理旧的备件使用记录,防止重复累计
        deviceRepairSparePartService.remove(new QueryWrapper<DeviceRepairSparePart>()
                .eq("source_id", deviceRepairDto.getId())
                .eq("source_type", 1));
        // å¤„理备件使用情况
        if (CollectionUtils.isNotEmpty(deviceRepairDto.getSparePartsUseList())) {
            List<Long> sparePartIds = new ArrayList<>();
@@ -114,6 +127,14 @@
                        record.setSparePartsId(sparePartUse.getId());
                        record.setQuantity(sparePartUse.getQuantity());
                        sparePartsRequisitionRecordService.save(record);
                        // æ’入记录到 device_repair_spare_part
                        DeviceRepairSparePart partRecord = new DeviceRepairSparePart();
                        partRecord.setSourceId(deviceRepairDto.getId());
                        partRecord.setSourceType(1); // 1 æŠ¥ä¿®
                        partRecord.setSparePartId(sparePartUse.getId());
                        partRecord.setQuantity(sparePartUse.getQuantity());
                        deviceRepairSparePartService.save(partRecord);
                    } else {
                        return AjaxResult.error("备件 " + spareParts.getName() + " æ•°é‡ä¸è¶³");
                    }
@@ -194,6 +215,47 @@
        update.setAcceptanceRemark(deviceRepairDto.getAcceptanceRemark());
        update.setStatus(STATUS_COMPLETED);
        if (this.updateById(update)) {
            try {
                DeviceLedger deviceLedger = deviceLedgerService.getById(oldDeviceRepair.getDeviceLedgerId());
                List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(deviceRepairDto.getId(), 1);
                String sparePartsInfo = "";
                if (CollectionUtils.isNotEmpty(parts)) {
                    StringBuilder sb = new StringBuilder();
                    for (DeviceRepairSparePart p : parts) {
                        sb.append(p.getSparePartName()).append("(").append(p.getQuantity()).append("个) ");
                    }
                    sparePartsInfo = " é…ä»¶ä½¿ç”¨æƒ…况: " + sb.toString();
                }
                KnowledgeBase kb = new KnowledgeBase();
                String typeName = deviceLedger.getType();
                String deviceModel = deviceLedger.getDeviceModel();
                String repairTypeStr = oldDeviceRepair.getRepairType();
                String repairTypeLabel = DictUtils.getDictLabel("device_repair_type", repairTypeStr);
                if (StringUtils.isBlank(repairTypeLabel)) {
                    repairTypeLabel = "ç»´ä¿®";
                }
                kb.setTitle((typeName != null ? typeName : "设备") + "_" + (deviceModel != null ? deviceModel : "") + " çš„ " + repairTypeLabel + "记录");
                kb.setScenario("设备维修");
                kb.setType("解决方案");
                String remarkStr = oldDeviceRepair.getRemark() == null ? "" : oldDeviceRepair.getRemark();
                kb.setProblem(String.format("设备号: %s, è®¾å¤‡åç§°: %s, æ•…障描述: %s",
                        deviceLedger.getUniqueCode(), deviceLedger.getDeviceName(),
                        remarkStr));
                String reasonStr = oldDeviceRepair.getReasonAnalysis() == null ? "" : oldDeviceRepair.getReasonAnalysis();
                String handlingStr = oldDeviceRepair.getHandlingMeasures() == null ? "" : oldDeviceRepair.getHandlingMeasures();
                kb.setSolution(String.format("原因分析: %s. å¤„理措施: %s.%s",
                        reasonStr, handlingStr, sparePartsInfo));
                kb.setCreator(oldDeviceRepair.getMaintenanceName());
                knowledgeBaseService.save(kb);
            } catch(Exception e) {
                log.error("保存知识库失败", e);
            }
            return AjaxResult.success();
        }
        return AjaxResult.error("验收审批失败");
@@ -251,6 +313,8 @@
    public DeviceRepairVo detailById(Long id) {
        DeviceRepairVo vo = deviceRepairMapper.detailById(id);
        vo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.DEVICE_REPAIR, id));
        List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(id, 1);
        vo.setSparePartsUseListDetails(parts);
        return vo;
    }
src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.device.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.device.mapper.DeviceRepairSparePartMapper;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import com.ruoyi.device.service.IDeviceRepairSparePartService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeviceRepairSparePartServiceImpl extends ServiceImpl<DeviceRepairSparePartMapper, DeviceRepairSparePart> implements IDeviceRepairSparePartService {
    @Override
    public List<DeviceRepairSparePart> getListWithPartName(Long sourceId, Integer sourceType) {
        return baseMapper.selectListWithPartName(sourceId, sourceType);
    }
}
src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.device.mapper.DeviceStatusChangeRecordMapper;
import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeviceStatusChangeRecordServiceImpl extends ServiceImpl<DeviceStatusChangeRecordMapper, DeviceStatusChangeRecord> implements IDeviceStatusChangeRecordService {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    public IPage<DeviceStatusChangeRecord> selectPageList(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record) {
        LambdaQueryWrapper<DeviceStatusChangeRecord> wrapper = Wrappers.lambdaQuery();
        if (record.getDeviceId() != null) {
            wrapper.eq(DeviceStatusChangeRecord::getDeviceId, record.getDeviceId());
        }
        wrapper.eq(DeviceStatusChangeRecord::getDeleted, 0);
        wrapper.orderByDesc(DeviceStatusChangeRecord::getCreateTime);
        IPage<DeviceStatusChangeRecord> result = this.page(page, wrapper);
        List<DeviceStatusChangeRecord> records = result.getRecords();
        if (records != null && !records.isEmpty()) {
            for (DeviceStatusChangeRecord r : records) {
                if (r.getCreateUser() != null) {
                    SysUser user = sysUserMapper.selectUserById(r.getCreateUser());
                    if (user != null) {
                        r.setCreateUserName(user.getNickName());
                    }
                }
            }
        }
        return result;
    }
}
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -8,6 +8,8 @@
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.project.system.domain.SysNotice;
import java.io.Serializable;
import java.time.DayOfWeek;
@@ -28,6 +30,9 @@
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private ISysNoticeService sysNoticeService;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
@@ -52,6 +57,19 @@
            // 2. åˆ›å»ºå¹¶ä¿å­˜å·¡æ£€ä»»åŠ¡è®°å½• - è¿™å°±æ˜¯æ‚¨æä¾›çš„代码应该放的位置
            DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask);
            deviceMaintenanceService.save(deviceMaintenance);
            // ç»™æ‰§è¡Œäººå‘送系统通知
            if (timingTask.getExecutorId() != null) {
                SysNotice notice = new SysNotice();
                notice.setNoticeTitle("新保养任务提醒");
                notice.setNoticeType("1"); // 1 é€šçŸ¥
                notice.setNoticeContent("您有一个新的设备保养任务需要执行,设备名称:" + timingTask.getTaskName());
                notice.setStatus("0"); // æ­£å¸¸
                notice.setConsigneeId(timingTask.getExecutorId());
                notice.setSenderId(0L); // 0代表系统发送
                notice.setTenantId(timingTask.getTenantId());
                sysNoticeService.insertNotice(notice);
            }
            // 3. æ›´æ–°å®šæ—¶ä»»åŠ¡çš„æ‰§è¡Œæ—¶é—´
            if (!tasks.isEmpty()) {
@@ -94,6 +112,8 @@
        inspectionTask.setDeviceLedgerId(timingTask.getTaskId());
        inspectionTask.setMaintenancePlanTime(LocalDateTime.now());
        inspectionTask.setMaintenanceActuallyName(timingTask.getMaintenancePerson());
        inspectionTask.setExecutorId(timingTask.getExecutorId());
        inspectionTask.setMaintenanceType(timingTask.getMaintenanceType());
        inspectionTask.setFrequencyType(timingTask.getFrequencyType());
        inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
        inspectionTask.setTenantId(timingTask.getTenantId());
src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java
@@ -48,4 +48,7 @@
    @Schema(description = "保养图片列表")
    private List<StorageBlobVO> storageBlobVOs;
    @Schema(description = "使用备件详情列表")
    private List<com.ruoyi.device.pojo.DeviceRepairSparePart> sparePartsUseListDetails;
}
src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java
@@ -2,6 +2,7 @@
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.device.pojo.DeviceRepairSparePart;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -16,5 +17,11 @@
    @Schema(description = "维修时间字符串")
    private String maintenanceTimeStr;
    @Schema(description = "处理措施")
    private String handlingMeasures;
    @Schema(description = "备件使用详情列表")
    private List<DeviceRepairSparePart> sparePartsUseListDetails;
    private List<StorageBlobVO> storageBlobVOs;
}
src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java
@@ -39,5 +39,29 @@
    @Schema(description = "检定人")
    private Long userId;
    @Schema(description = "溯源/核查类型")
    private String traceabilityType;
    @Schema(description = "实施机构")
    private String implementingAgency;
    @Schema(description = "证书编号")
    private String certificateNumber;
    @Schema(description = "核心结果")
    private String coreResult;
    @Schema(description = "结果确认")
    private String resultConfirmation;
    @Schema(description = "确认人id")
    private Long confirmerId;
    @Schema(description = "确认人")
    private String confirmerName;
    @Schema(description = "有效期至")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date validUntil;
}
src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java
@@ -81,6 +81,39 @@
    @Schema(description = "有效期(单位天)")
    private Integer valid;
    @Schema(description = "溯源/核查类型")
    @Excel(name = "溯源/核查类型")
    private String traceabilityType;
    @Schema(description = "实施机构")
    @Excel(name = "实施机构")
    private String implementingAgency;
    @Schema(description = "证书编号")
    @Excel(name = "证书编号")
    private String certificateNumber;
    @Schema(description = "核心结果")
    @Excel(name = "核心结果")
    private String coreResult;
    @Schema(description = "结果确认")
    @Excel(name = "结果确认")
    private String resultConfirmation;
    @Schema(description = "有效期至")
    @Excel(name = "有效期至", width = 30, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date validUntil;
    @Schema(description = "确认人id")
    private Long confirmerId;
    @Schema(description = "确认人")
    @Excel(name = "确认人")
    private String confirmerName;
    /**
     * çŠ¶æ€ï¼ˆ1-有效 2-逾期)
src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
@@ -78,6 +78,17 @@
            measuringInstrumentLedgerRecord.setValid(req.getValid());
            measuringInstrumentLedgerRecord.setUserId(req.getUserId());
            measuringInstrumentLedgerRecord.setUserName(sysUser.getUserName());
            // Set the new fields
            measuringInstrumentLedgerRecord.setTraceabilityType(req.getTraceabilityType());
            measuringInstrumentLedgerRecord.setImplementingAgency(req.getImplementingAgency());
            measuringInstrumentLedgerRecord.setCertificateNumber(req.getCertificateNumber());
            measuringInstrumentLedgerRecord.setCoreResult(req.getCoreResult());
            measuringInstrumentLedgerRecord.setResultConfirmation(req.getResultConfirmation());
            measuringInstrumentLedgerRecord.setConfirmerId(req.getConfirmerId());
            measuringInstrumentLedgerRecord.setConfirmerName(req.getConfirmerName());
            measuringInstrumentLedgerRecord.setValidUntil(req.getValidUntil());
            measuringInstrumentLedgerRecordMapper.insert(measuringInstrumentLedgerRecord);
            // å°è´¦ç»‘定一次
//            if(!CollectionUtils.isEmpty(req.getTempFileIds())){
src/main/java/com/ruoyi/projectManagement/controller/InfoController.java
@@ -8,6 +8,7 @@
import com.ruoyi.projectManagement.vo.SaveInfoStageVo;
import com.ruoyi.projectManagement.vo.SaveInfoVo;
import com.ruoyi.projectManagement.vo.SearchInfoVo;
import com.ruoyi.projectManagement.vo.AttachmentGroupVo;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
@@ -83,5 +84,11 @@
        return AjaxResult.success();
    }
    @GetMapping("/attachments/{id}")
    @Operation(summary = "文档归档:获取项目所有附件")
    public AjaxResult getAttachments(@PathVariable("id") Long id) {
        return AjaxResult.success(infoService.getProjectAttachments(id));
    }
}
src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.projectManagement.controller;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.projectManagement.pojo.ProjectConfig;
import com.ruoyi.projectManagement.service.ProjectConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/projectManagement/config")
@Tag(name = "项目管理配置表")
@RequiredArgsConstructor
public class ProjectConfigController {
    private final ProjectConfigService projectConfigService;
    @GetMapping
    @Operation(summary = "获取全局配置")
    public AjaxResult getConfig() {
        return AjaxResult.success(projectConfigService.getGlobalConfig());
    }
    @PutMapping
    @Operation(summary = "修改全局配置")
    public AjaxResult updateConfig(@RequestBody ProjectConfig projectConfig) {
        projectConfigService.updateGlobalConfig(projectConfig);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.projectManagement.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.projectManagement.pojo.ProjectConfig;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProjectConfigMapper extends BaseMapper<ProjectConfig> {
}
src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.projectManagement.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * é¡¹ç›®ç®¡ç†é…ç½®è¡¨
 */
@Data
@TableName("project_management_config")
@Schema(description = "项目管理配置对象")
public class ProjectConfig implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * åˆåŒåŽŸä»¶çº¦å®šå›žä¼ æå‰æé†’å¤©æ•°
     */
    @Schema(description = "合同原件约定回传提前提醒天数")
    @TableField("contract_return_reminder_days")
    private Integer contractReturnReminderDays;
    @Schema(description = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(value = "create_user_name", fill = FieldFill.INSERT)
    private String createUserName;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    @Schema(description = "更新用户")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(value = "update_user_name", fill = FieldFill.INSERT_UPDATE)
    private String updateUserName;
    @Schema(description = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
    @Schema(description = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @Schema(description = "部门ID")
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/projectManagement/pojo/Roles.java
@@ -38,6 +38,7 @@
    private Integer status;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "创建用户")
src/main/java/com/ruoyi/projectManagement/service/InfoService.java
@@ -46,4 +46,11 @@
    InfoVo getInfoById(@NotNull Long id);
    /**
     * èŽ·å–é¡¹ç›®ä¸‹æ‰€æœ‰é™„ä»¶
     * @param id é¡¹ç›®id
     * @return é™„件按模块分组的集合
     */
    java.util.List<com.ruoyi.projectManagement.vo.AttachmentGroupVo> getProjectAttachments(@NotNull Long id);
}
src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.projectManagement.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.projectManagement.pojo.ProjectConfig;
public interface ProjectConfigService extends IService<ProjectConfig> {
    /**
     * èŽ·å–å…¨å±€é…ç½®ï¼ˆå¦‚ä¸å­˜åœ¨åˆ™è¿”å›žå¸¦æœ‰é»˜è®¤å€¼çš„å®žä¾‹ï¼‰
     */
    ProjectConfig getGlobalConfig();
    /**
     * æ›´æ–°å…¨å±€é…ç½®
     */
    void updateGlobalConfig(ProjectConfig projectConfig);
}
src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java
@@ -18,8 +18,18 @@
import com.ruoyi.projectManagement.vo.ListInfoVo;
import com.ruoyi.projectManagement.vo.SaveInfoVo;
import com.ruoyi.projectManagement.vo.SearchInfoVo;
import com.ruoyi.projectManagement.vo.AttachmentGroupVo;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.SalesLedger;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.basic.dto.StorageAttachmentDTO;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.service.StorageAttachmentService;
import com.ruoyi.sales.service.ISalesLedgerService;
import com.ruoyi.sales.pojo.SalesLawsuit;
import com.ruoyi.sales.service.ISalesLawsuitService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
@@ -43,6 +53,8 @@
    private final ContractInfoHandleService contractInfoHandleService;
    private final ShippingAddressHandleService shippingAddressHandleService;
    private final ISalesLedgerService salesLedgerService;
    private final StorageAttachmentService storageAttachmentService;
    private final ISalesLawsuitService salesLawsuitService;
    @Transactional(rollbackFor = Exception.class)
@@ -128,4 +140,55 @@
        return vo;
    }
    @Override
    public List<AttachmentGroupVo> getProjectAttachments(Long id) {
        List<AttachmentGroupVo> result = new java.util.ArrayList<>();
        // é¡¹ç›®ç®¡ç†é™„ä»¶
        StorageAttachmentDTO infoDto = new StorageAttachmentDTO();
        infoDto.setRecordType(RecordTypeEnum.INFO.getType());
        infoDto.setRecordId(id);
        List<StorageBlobVO> infoFiles = storageAttachmentService.list(infoDto);
        if (infoFiles != null && !infoFiles.isEmpty()) {
            result.add(new AttachmentGroupVo("项目管理", infoFiles));
        }
        // é”€å”®å°è´¦é™„ä»¶
        List<SalesLedger> ledgers = salesLedgerService.list(Wrappers.<SalesLedger>lambdaQuery().eq(SalesLedger::getProjectId, id));
        if (ledgers != null && !ledgers.isEmpty()) {
            List<StorageBlobVO> ledgerFiles = new java.util.ArrayList<>();
            for (SalesLedger ledger : ledgers) {
                StorageAttachmentDTO dto = new StorageAttachmentDTO();
                dto.setRecordType(RecordTypeEnum.SALES_LEDGER.getType());
                dto.setRecordId(ledger.getId());
                List<StorageBlobVO> files = storageAttachmentService.list(dto);
                if (files != null && !files.isEmpty()) {
                    ledgerFiles.addAll(files);
                }
            }
            if (!ledgerFiles.isEmpty()) {
                result.add(new AttachmentGroupVo("销售台账", ledgerFiles));
            }
        }
        // è¯‰è®¼ç®¡ç†é™„ä»¶
        List<SalesLawsuit> lawsuits = salesLawsuitService.list(Wrappers.<SalesLawsuit>lambdaQuery().eq(SalesLawsuit::getProjectId, id));
        if (lawsuits != null && !lawsuits.isEmpty()) {
            List<StorageBlobVO> lawsuitFiles = new java.util.ArrayList<>();
            for (SalesLawsuit lawsuit : lawsuits) {
                StorageAttachmentDTO dto = new StorageAttachmentDTO();
                dto.setRecordType(RecordTypeEnum.SALES_LAWSUIT.getType());
                dto.setRecordId(lawsuit.getId());
                List<StorageBlobVO> files = storageAttachmentService.list(dto);
                if (files != null && !files.isEmpty()) {
                    lawsuitFiles.addAll(files);
                }
            }
            if (!lawsuitFiles.isEmpty()) {
                result.add(new AttachmentGroupVo("诉讼管理", lawsuitFiles));
            }
        }
        return result;
    }
}
src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.projectManagement.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.projectManagement.mapper.ProjectConfigMapper;
import com.ruoyi.projectManagement.pojo.ProjectConfig;
import com.ruoyi.projectManagement.service.ProjectConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProjectConfigServiceImpl extends ServiceImpl<ProjectConfigMapper, ProjectConfig> implements ProjectConfigService {
    @Override
    public ProjectConfig getGlobalConfig() {
        ProjectConfig config = this.getById(1L);
        if (config == null) {
            config = new ProjectConfig();
            config.setId(1L);
            config.setContractReturnReminderDays(3);
            this.save(config);
        }
        return config;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGlobalConfig(ProjectConfig projectConfig) {
        projectConfig.setId(1L); // å¼ºåˆ¶æ›´æ–° ID=1 çš„记录
        this.saveOrUpdate(projectConfig);
    }
}
src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.projectManagement.vo;
import com.ruoyi.basic.dto.StorageBlobVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "附件归档分组对象")
public class AttachmentGroupVo {
    @Schema(description = "模块名称(如:项目管理、销售台账等)")
    private String moduleName;
    @Schema(description = "模块内对应的附件列表")
    private List<StorageBlobVO> files;
    public AttachmentGroupVo() {
    }
    public AttachmentGroupVo(String moduleName, List<StorageBlobVO> files) {
        this.moduleName = moduleName;
        this.files = files;
    }
}
src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java
@@ -41,6 +41,8 @@
    private String createUserName; // åˆ›å»ºäººåç§°
    private String updateUserName; // æ›´æ–°äººåç§°
    // é”€å”®åˆåŒæ€»æ•°
    private Integer salesContractCount;
    // å…¨éƒ¨é˜¶æ®µä¿¡æ¯
    private List<PlanStageDto> planStage;
}
src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
@@ -154,4 +154,17 @@
    public void down(HttpServletResponse response, @RequestBody QualityInspect qualityInspect) {
        qualityInspectService.down(response, qualityInspect);
    }
    /**
     * ä¸‹è½½PDF
     *
     * @param response
     * @param qualityInspect
     */
    @PostMapping("/downPdf")
    @Operation(summary = "下载检验PDF")
    @Log(title = "下载检验PDF", businessType = BusinessType.OTHER)
    public void downPdf(HttpServletResponse response, @RequestBody QualityInspect qualityInspect) {
        qualityInspectService.downPdf(response, qualityInspect);
    }
}
src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java
@@ -7,6 +7,7 @@
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
@@ -50,6 +51,24 @@
    private String controlValue;
    /**
     * å‚数类型(0-文字, 1-数字)
     */
    @Excel(name = "参数类型")
    private Integer parameterType;
    /**
     * æœ€å¤§å€¼
     */
    @Excel(name = "最大值")
    private BigDecimal maxValue;
    /**
     * æœ€å°å€¼
     */
    @Excel(name = "最小值")
    private BigDecimal minValue;
    /**
     * æ£€éªŒå€¼
     */
    @Excel(name = "检验值")
src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
@@ -42,6 +43,15 @@
    @Schema(description = "内控值")
    private String controlValue;
    @Schema(description = "参数类型(0-文字, 1-数字)")
    private Integer parameterType;
    @Schema(description = "最大值")
    private BigDecimal maxValue;
    @Schema(description = "最小值")
    private BigDecimal minValue;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
src/main/java/com/ruoyi/quality/service/IQualityInspectService.java
@@ -27,4 +27,6 @@
    R autoSubmit(Long id);
    void down(HttpServletResponse response, QualityInspect qualityInspect);
    void downPdf(HttpServletResponse response, QualityInspect qualityInspect);
}
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -244,6 +244,12 @@
        for (QualityInspectParam detail : paramList) {
            detail.setIndex(index);
            index++;
            if (detail.getParameterType() != null && detail.getParameterType() == 1) {
                String min = detail.getMinValue() != null ? detail.getMinValue().stripTrailingZeros().toPlainString() : "";
                String max = detail.getMaxValue() != null ? detail.getMaxValue().stripTrailingZeros().toPlainString() : "";
                detail.setStandardValue(min + "~" + max);
            }
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/static/report-template.docx");
        Configure configure = Configure.builder()
@@ -276,6 +282,73 @@
    }
    @Override
    public void downPdf(HttpServletResponse response, QualityInspect qualityInspect) {
        QualityInspect inspect = qualityInspectMapper.selectById(qualityInspect.getId());
        String inspectType = "";
        switch (inspect.getInspectType()) {
            case 0:
                inspectType = "原材料检验";
                break;
            case 1:
                inspectType = "过程检验";
                break;
            case 2:
                inspectType = "出厂检验";
                break;
        }
        List<QualityInspectParam> paramList = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
        int index = 1;
        for (QualityInspectParam detail : paramList) {
            detail.setIndex(index);
            index++;
            if (detail.getParameterType() != null && detail.getParameterType() == 1) {
                String min = detail.getMinValue() != null ? detail.getMinValue().stripTrailingZeros().toPlainString() : "";
                String max = detail.getMaxValue() != null ? detail.getMaxValue().stripTrailingZeros().toPlainString() : "";
                detail.setStandardValue(min + "~" + max);
            }
        }
        InputStream inputStream = this.getClass().getResourceAsStream("/static/report-template.docx");
        Configure configure = Configure.builder()
                .bind("paramList", new HackLoopTableRenderPolicy())
                .build();
        String finalInspectType = inspectType;
        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                new HashMap<String, Object>() {{
                    put("inspect", inspect);
                    put("inspectType", finalInspectType);
                    put("paramList", paramList);
                }});
        try {
            // å…ˆå°†æ¨¡æ¿å†™å…¥åˆ° ByteArrayOutputStream ä¸­
            java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
            template.write(bos);
            bos.flush();
            bos.close();
            inputStream.close();
            // ä½¿ç”¨ Spire.Doc è¯»å– docx æ•°æ®æµå¹¶è½¬ä¸º PDF
            java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(bos.toByteArray());
            com.spire.doc.Document document = new com.spire.doc.Document(bis);
            response.setContentType("application/pdf");
            String fileName = URLEncoder.encode("检验报告", "UTF-8");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
            java.io.OutputStream os = response.getOutputStream();
            document.saveToStream(os, com.spire.doc.FileFormat.PDF);
            os.flush();
            os.close();
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导出失败");
        }
    }
    @Override
    public int updateQualityInspect(QualityInspectDto qualityInspectDto) {
        if (ObjectUtils.isNotNull(qualityInspectDto.getQualityInspectParams())) {
            qualityInspectParamService.remove(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, qualityInspectDto.getId()));
src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.ruoyi.sales.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.sales.dto.BiddingManagementDto;
import com.ruoyi.sales.pojo.BiddingManagement;
import com.ruoyi.sales.service.IBiddingManagementService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * æ‹›æŠ•标管理Controller
 */
@RestController
@RequestMapping("/sales/bidding")
@AllArgsConstructor
@Tag(name = "招投标管理")
public class BiddingManagementController extends BaseController {
    private final IBiddingManagementService biddingManagementService;
    /**
     * æŸ¥è¯¢æ‹›æŠ•标管理列表
     */
    @GetMapping("/listPage")
    @Operation(summary = "查询招投标管理列表(分页)")
    public AjaxResult listPage(Page<BiddingManagement> page, BiddingManagementDto dto) {
        IPage<BiddingManagement> iPage = biddingManagementService.selectBiddingManagementList(page, dto);
        return AjaxResult.success(iPage);
    }
    /**
     * èŽ·å–æ‹›æŠ•æ ‡ç®¡ç†è¯¦ç»†ä¿¡æ¯
     */
    @GetMapping("/getDetail")
    @Operation(summary = "获取招投标管理详细信息")
    public AjaxResult getDetail(@RequestParam("id") Long id) {
        return AjaxResult.success(biddingManagementService.getById(id));
    }
    /**
     * æ–°å¢žæˆ–修改招投标管理
     */
    @Log(title = "招投标管理", businessType = BusinessType.INSERT)
    @PostMapping("/addOrUpdate")
    @Operation(summary = "新增或修改招投标管理")
    public AjaxResult addOrUpdate(@RequestBody BiddingManagementDto dto) {
        return toAjax(biddingManagementService.addOrUpdateBiddingManagement(dto));
    }
    /**
     * åˆ é™¤æ‹›æŠ•标管理
     */
    @Log(title = "招投标管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "删除招投标管理")
    public AjaxResult remove(@RequestBody Long[] ids) {
        if (ids == null || ids.length == 0) {
            return AjaxResult.error("请传入要删除的ID");
        }
        return toAjax(biddingManagementService.deleteBiddingManagementByIds(ids));
    }
    /**
     * å¯¼å‡ºæ‹›æŠ•标管理列表
     */
    @Log(title = "招投标管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @Operation(summary = "导出招投标管理")
    public void export(HttpServletResponse response, BiddingManagementDto dto) {
        List<BiddingManagement> list = biddingManagementService.selectBiddingManagementList(dto);
        ExcelUtil<BiddingManagement> util = new ExcelUtil<>(BiddingManagement.class);
        util.exportExcel(response, list, "招投标管理数据");
    }
}
src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java
@@ -41,18 +41,27 @@
        return metricStatisticsService.statisticsTable(statisticsTableDto);
    }
    @GetMapping("/projectTransactions")
    @Operation(summary = "营销管理--客户往来-项目汇总")
    public R projectTransactions(Page page, String projectName, String customerName, String contractName) {
        return R.ok(customerService.projectTransactions(page, projectName, customerName, contractName));
    }
    @GetMapping("/customewTransactions")
    @Log(title = "客户往来", businessType = BusinessType.OTHER)
    @Operation(summary = "客户往来")
    public R customewTransactions(Page page, String customerName) {
        return R.ok(customerService.customewTransactions(page,customerName));
    @Operation(summary = "营销管理--客户往来-客户汇总")
    public R customewTransactions(Page page, Long projectId, String customerName, String contractName) {
        return R.ok(customerService.customewTransactions(page, projectId, customerName, contractName));
    }
    @GetMapping("/customewTransactionsDetails")
    @Log(title = "客户往来明细", businessType = BusinessType.OTHER)
    @Operation(summary = "客户往来明细")
    public R customewTransactionsDetails(Page page, Long customerId) {
        return R.ok(customerService.customewTransactionsDetails(page,customerId));
    @Operation(summary = "营销管理--客户往来明细-合同级")
    public R customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName) {
        return R.ok(customerService.customewTransactionsDetails(page, projectId, customerId, contractName));
    }
    @GetMapping("/transactionsSummary")
    @Operation(summary = "营销管理--客户往来-大盘总览")
    public R transactionsSummary() {
        return R.ok(customerService.transactionsSummary());
    }
}
src/main/java/com/ruoyi/sales/controller/SalesContractController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package com.ruoyi.sales.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.SalesContractDTO;
import com.ruoyi.sales.pojo.SalesContract;
import com.ruoyi.sales.service.ISalesContractService;
import com.ruoyi.sales.vo.SalesContractVO;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * é”€å”®åˆåŒ Controller
 */
@RestController
@RequestMapping("/sales/contract")
public class SalesContractController extends BaseController {
    @Resource
    private ISalesContractService salesContractService;
    /**
     * æŸ¥è¯¢é”€å”®åˆåŒåˆ†é¡µåˆ—表
     */
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<SalesContract> page, SalesContractDTO dto) {
        return AjaxResult.success(salesContractService.listPage(page, dto));
    }
    /**
     * èŽ·å–é”€å”®åˆåŒè¯¦æƒ…
     */
    @GetMapping("/getDetail")
    public AjaxResult getDetail(@RequestParam("id") Long id) {
        return salesContractService.getDetail(id);
    }
    /**
     * æŸ¥è¯¢é”€å”®åˆåŒä¸åˆ†é¡µåˆ—表
     */
    @GetMapping("/listNoPage")
    public AjaxResult listNoPage(SalesContractDTO dto) {
        return AjaxResult.success(salesContractService.selectList(dto));
    }
    /**
     * æ–°å¢žæˆ–修改销售合同
     */
    @Log(title = "销售合同", businessType = BusinessType.INSERT)
    @PostMapping("/addOrUpdate")
    public AjaxResult addOrUpdate(@RequestBody SalesContractDTO dto) {
        return salesContractService.addOrUpdate(dto);
    }
    /**
     * åˆ é™¤é”€å”®åˆåŒ
     */
    @Log(title = "销售合同", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult del(@RequestBody List<Long> ids) {
        return salesContractService.del(ids);
    }
    /**
     * å¯¼å‡ºé”€å”®åˆåŒ
     */
    @Log(title = "销售合同", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesContractDTO dto) {
        List<SalesContract> list = salesContractService.selectList(dto);
        ExcelUtil<SalesContract> util = new ExcelUtil<>(SalesContract.class);
        util.exportExcel(response, list, "销售合同数据");
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.ruoyi.sales.controller;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.SalesLawsuitDTO;
import com.ruoyi.sales.pojo.SalesLawsuit;
import com.ruoyi.sales.service.ISalesLawsuitService;
import com.ruoyi.sales.vo.SalesLawsuitVO;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * è¯‰è®¼ç®¡ç† Controller
 */
@RestController
@RequestMapping("/sales/lawsuit")
public class SalesLawsuitController extends BaseController {
    @Autowired
    private ISalesLawsuitService salesLawsuitService;
    /**
     * åˆ†é¡µæŸ¥è¯¢è¯‰è®¼åˆ—表
     */
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto) {
        return AjaxResult.success(salesLawsuitService.listPage(page, dto));
    }
    /**
     * è¯‰è®¼è¯¦æƒ…回显
     */
    @GetMapping("/getDetail")
    public AjaxResult getDetail(@RequestParam("id") Long id) {
        return salesLawsuitService.getDetail(id);
    }
    /**
     * æ–°å¢žæˆ–更新诉讼
     */
    @PostMapping("/addOrUpdate")
    public AjaxResult addOrUpdate(@RequestBody SalesLawsuitDTO dto) {
        return salesLawsuitService.addOrUpdate(dto);
    }
    /**
     * æ‰¹é‡åˆ é™¤è¯‰è®¼
     */
    @DeleteMapping("/del")
    public AjaxResult del(@RequestBody List<Long> ids) {
        return salesLawsuitService.del(ids);
    }
    /**
     * å¯¼å‡ºè¯‰è®¼è®°å½•
     */
    @PostMapping("/export")
    public void export(HttpServletResponse response, SalesLawsuitDTO dto) {
        List<SalesLawsuit> list = salesLawsuitService.selectList(dto);
        ExcelUtil<SalesLawsuit> util = new ExcelUtil<>(SalesLawsuit.class);
        util.exportExcel(response, list, "诉讼数据");
    }
}
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -70,6 +70,11 @@
        if (byId == null) {
            return AjaxResult.error("发货信息不存在");
        }
        if ("审核通过".equals(byId.getStatus()) && !"已发货".equals(req.getStatus())) {
            // Note: If the request is from outbound management trying to change status to "已发货", it's allowed.
            // But if it's a normal edit, it's blocked. We just check if it's already å®¡æ ¸é€šè¿‡ and they are updating other fields.
            return AjaxResult.error("已审核通过,不能编辑发货信息");
        }
        boolean update = shippingInfoService.updateById(req);
        return update ? AjaxResult.success() : AjaxResult.error();
    }
@@ -79,7 +84,12 @@
    @Transactional(rollbackFor = Exception.class)
    @Log(title = "发货信息管理", businessType = BusinessType.DELETE)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        for (Long id : ids) {
            ShippingInfo byId = shippingInfoService.getById(id);
            if (byId != null && ("已发货".equals(byId.getStatus()) || "审核通过".equals(byId.getStatus()))) {
                return AjaxResult.error("存在已发货或审核通过的产品,不能删除");
            }
        }
        return shippingInfoService.delete(ids) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
    }
@@ -88,8 +98,8 @@
     */
    @PostMapping("/export")
    @Operation(summary = "导出发货信息")
    public void export(HttpServletResponse response) {
        List<ShippingInfo> list = shippingInfoMapper.listAll();
    public void export(HttpServletResponse response, ShippingInfo req) {
        List<ShippingInfo> list = shippingInfoMapper.listAll(req);
        ExcelUtil<ShippingInfo> util = new ExcelUtil<ShippingInfo>(ShippingInfo.class);
        util.exportExcel(response, list, "发货信息");
    }
src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.sales.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class BiddingManagementDto {
    private Long id;
    private Long projectId;
    private String projectName;
    private String bidDocName;
    private String tenderUnit;
    private String bidUnit;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date tenderDate;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date bidDate;
    private String bidDateStart;
    private String bidDateEnd;
    private BigDecimal bidDocFee;
    private String bidDocFeeStatus;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date bidDocFeeStatusChangeDate;
    private BigDecimal bidDeposit;
    private String bidResult;
}
src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.sales.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.sales.pojo.SalesContract;
import com.ruoyi.sales.pojo.SalesContractProduct;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class SalesContractDTO extends SalesContract {
    /**
     * äº§å“æ˜Žç»†åˆ—表
     */
    private List<SalesContractProduct> productList;
    /**
     * ç­¾è®¢æ—¶é—´ï¼ˆæŸ¥è¯¢å¼€å§‹æ—¶é—´ï¼‰
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate signDateStart;
    /**
     * ç­¾è®¢æ—¶é—´ï¼ˆæŸ¥è¯¢ç»“束时间)
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate signDateEnd;
}
src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.sales.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.sales.pojo.SalesLawsuit;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class SalesLawsuitDTO extends SalesLawsuit {
    /**
     * é™„件列表
     */
    private List<StorageBlobDTO> storageBlobDTOs;
    /**
     * ç«‹æ¡ˆæ—¶é—´ï¼ˆæŸ¥è¯¢å¼€å§‹æ—¶é—´ï¼‰
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate filingDateStart;
    /**
     * ç«‹æ¡ˆæ—¶é—´ï¼ˆæŸ¥è¯¢ç»“束时间)
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate filingDateEnd;
}
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -8,7 +8,9 @@
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
@@ -63,4 +65,39 @@
    private List<StorageBlobDTO> storageBlobDTOs;
    private List<StorageBlobVO> StorageBlobVOs;
    @Schema(description = "项目ID")
    private Long projectId;
    @Schema(description = "年份")
    private Integer year;
    @Schema(description = "签单单位ID")
    private Long signingUnitId;
    @Schema(description = "办事处ID")
    private Long officeId;
    @Schema(description = "组负责人ID")
    private Long groupLeaderId;
    @Schema(description = "是否开发票(1是 0否)")
    private Integer isInvoice;
    @Schema(description = "中标价")
    private BigDecimal bidPrice;
    @Schema(description = "是否中标(1是 0否)")
    private Integer isBid;
    @Schema(description = "未中标原因")
    private String unbidReason;
    @Schema(description = "合同原件约定回传日期")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate contractOriginalReturnDate;
    @Schema(description = "关联合同ID列表")
    private List<Long> associatedContractIds;
}
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -19,4 +19,7 @@
    private String approveUserIds;
    private Long templateId;
    @Schema(description = "项目名称")
    private String projectName;
}
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -6,6 +6,7 @@
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.pojo.ShippingProductDetail;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@@ -31,6 +32,14 @@
    @TableField(exist = false)
    private List<CommonFile> commonFileList;
    @Schema(description = "退货单号集合")
    @TableField(exist = false)
    private List<String> returnNoList;
    @Schema(description = "退货单号字符串")
    @TableField(exist = false)
    private String returnNos;
    private String  specificationModel;
    private String productName;
src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.BiddingManagement;
import org.apache.ibatis.annotations.Mapper;
/**
 * æ‹›æŠ•标管理Mapper接口
 */
@Mapper
public interface BiddingManagementMapper extends BaseMapper<BiddingManagement> {
}
src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.SalesContract;
import org.apache.ibatis.annotations.Mapper;
/**
 * é”€å”®åˆåŒ Mapper æŽ¥å£
 */
@Mapper
public interface SalesContractMapper extends BaseMapper<SalesContract> {
}
src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.SalesContractProduct;
import org.apache.ibatis.annotations.Mapper;
/**
 * é”€å”®åˆåŒäº§å“æ˜Žç»† Mapper æŽ¥å£
 */
@Mapper
public interface SalesContractProductMapper extends BaseMapper<SalesContractProduct> {
}
src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.SalesLawsuit;
import org.apache.ibatis.annotations.Mapper;
/**
 * è¯‰è®¼ç®¡ç† Mapper æŽ¥å£
 */
@Mapper
public interface SalesLawsuitMapper extends BaseMapper<SalesLawsuit> {
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.sales.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.sales.pojo.SalesLedgerContractRel;
import org.apache.ibatis.annotations.Mapper;
/**
 * é”€å”®å°è´¦ä¸ŽåˆåŒå…³è”表Mapper接口
 */
@Mapper
public interface SalesLedgerContractRelMapper extends BaseMapper<SalesLedgerContractRel> {
}
src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
@@ -17,7 +17,7 @@
public interface ShippingInfoMapper extends BaseMapper<ShippingInfo> {
    IPage<ShippingInfoDto> listPage(Page page, @Param("req") ShippingInfo req);
    List<ShippingInfo> listAll();
    List<ShippingInfo> listAll(@Param("req") ShippingInfo req);
    List<ShippingProductVo> getReturnManagementDtoById(@Param("shippingId")Long shippingId);
src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
/**
 * æ‹›æŠ•标管理对象 bidding_management
 */
@TableName(value = "bidding_management")
@Data
public class BiddingManagement implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * æ‰€å±žé¡¹ç›®ID
     */
    private Long projectId;
    /**
     * æ‰€å±žé¡¹ç›®åç§°
     */
    @Excel(name = "所属项目名称")
    private String projectName;
    /**
     * æ ‡ä¹¦åç§°
     */
    @Excel(name = "标书名称")
    private String bidDocName;
    /**
     * æ‹›æ ‡å•位
     */
    @Excel(name = "招标单位")
    private String tenderUnit;
    /**
     * æŠ•标单位
     */
    @Excel(name = "投标单位")
    private String bidUnit;
    /**
     * æ‹›æ ‡æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "招标日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date tenderDate;
    /**
     * æŠ•标日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "投标日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date bidDate;
    /**
     * æ ‡ä¹¦æ¬¾é‡‘额
     */
    @Excel(name = "标书款金额")
    private BigDecimal bidDocFee;
    /**
     * æ ‡ä¹¦æ¬¾çŠ¶æ€
     */
    @Excel(name = "标书款状态")
    private String bidDocFeeStatus;
    /**
     * æ ‡ä¹¦æ¬¾çŠ¶æ€å˜æ›´æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "标书款状态变更日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date bidDocFeeStatusChangeDate;
    /**
     * æŠ•标保证金金额
     */
    @Excel(name = "投标保证金金额")
    private BigDecimal bidDeposit;
    /**
     * æ‹›æŠ•标结果
     */
    @Excel(name = "招投标结果")
    private String bidResult;
    /**
     * ç§Ÿæˆ·ID
     */
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    /**
     * éƒ¨é—¨ID
     */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /**
     * åˆ é™¤æ ‡å¿—(0代表存在 2代表删除)
     */
    @TableLogic
    private String delFlag;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
    /**
     * å¤‡æ³¨
     */
    @Excel(name = "备注")
    private String remark;
}
src/main/java/com/ruoyi/sales/pojo/SalesContract.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * é”€å”®åˆåŒä¸»è¡¨å®žä½“
 */
@Data
@TableName("sales_contract")
public class SalesContract {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** åˆåŒç¼–号 */
    @Excel(name = "合同编号")
    private String contractNo;
    /** ç­¾è®¢æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "签订时间", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate signDate;
    /** ç­¾çº¦å•位简称 */
    @Excel(name = "签约单位简称")
    private String signingUnit;
    /** ç­¾çº¦å‘¨æœŸ */
    @Excel(name = "签约周期")
    private String contractPeriod;
    /** å­˜æ¡£çŠ¶æ€ */
    @Excel(name = "存档状态")
    private String archiveStatus;
    /** å­˜æ¡£æƒ…况说明 */
    @Excel(name = "存档情况说明")
    private String archiveRemark;
    /** å±¥çº¦ä¿è¯é‡‘ */
    @Excel(name = "履约保证金")
    private BigDecimal performanceBond;
    /** è´¨ä¿é‡‘情况 */
    @Excel(name = "质保金情况")
    private BigDecimal warrantyDeposit;
    /** äº§å€¼ï¼ˆè‡ªåŠ¨æ±‡æ€»ï¼‰ */
    @Excel(name = "产值")
    private BigDecimal outputValue;
    /** å·²å›žæ¬¾é‡‘额 */
    @Excel(name = "已回款金额")
    private BigDecimal receivedAmount;
    /** ç»“算次数 */
    @Excel(name = "结算次数")
    private Integer settlementCount;
    /** åº”收金额 */
    @Excel(name = "应收金额")
    private BigDecimal receivableAmount;
    /** åº”付比例 */
    @Excel(name = "应付比例")
    private String payableRatio;
    /** åˆåŒèŠ‚ç‚¹åº”æ”¶ */
    @Excel(name = "合同节点应收")
    private BigDecimal nodeReceivable;
    /** åˆåŒçº¦å®šä»˜æ¬¾èŠ‚ç‚¹ */
    @Excel(name = "合同约定付款节点")
    private String paymentNodes;
    /** ç»“算记录 */
    @Excel(name = "结算记录")
    private String settlementRecord;
    /** å¼€ç¥¨è¦æ±‚ */
    @Excel(name = "开票要求")
    private String invoiceRequirement;
    /** æœ€è¿‘一次开票时间 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "最近一次开票时间", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate lastInvoiceDate;
    /** å·²å¼€ç¥¨é‡‘额 */
    @Excel(name = "已开票金额")
    private BigDecimal invoicedAmount;
    /** è¿çº¦é‡‘约定 */
    @Excel(name = "违约金约定")
    private String penaltyAgreement;
    /** æƒ…况说明 */
    @Excel(name = "情况说明")
    private String remark;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /** ç§Ÿæˆ·ID */
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    /** éƒ¨é—¨ID */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /** åˆ é™¤æ ‡å¿— */
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
/**
 * é”€å”®åˆåŒäº§å“æ˜Žç»†è¡¨å®žä½“
 */
@Data
@TableName("sales_contract_product")
public class SalesContractProduct {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** å…³è”销售合同 ID */
    private Long contractId;
    /** äº§å“å¤§ç±» */
    private String productCategory;
    /** è§„格型号 */
    private String specificationModel;
    /** è§„格型号 ID */
    private Long productModelId;
    /** å«ç¨Žå•ä»· */
    private BigDecimal taxInclusiveUnitPrice;
    /** çº¦å®š/暂估用量 */
    private BigDecimal agreedQuantity;
    /** åˆåŒè®¡é‡å•位 */
    private String contractUnit;
    /** å·²å‘货数量 */
    private BigDecimal shippedQuantity;
    /** äº§å€¼ */
    private BigDecimal outputValue;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /** ç§Ÿæˆ·ID */
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    /** éƒ¨é—¨ID */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /** åˆ é™¤æ ‡å¿— */
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import lombok.Data;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * è¯‰è®¼ç®¡ç† ä¸»è¡¨å®žä½“
 */
@Data
@TableName("sales_lawsuit")
public class SalesLawsuit {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** æ¡ˆä»¶ç¼–号 */
    @Excel(name = "案件编号")
    private String caseNo;
    /** æ‰€å±žé¡¹ç›® ID */
    private Long projectId;
    /** æ‰€å±žé¡¹ç›®åç§° */
    @Excel(name = "所属项目名称")
    private String projectName;
    /** å…³è”销售合同 ID */
    private Long contractId;
    /** åˆåŒç¼–号 */
    @Excel(name = "合同编号")
    private String contractNo;
    /** è¯‰è®¼çŠ¶æ€ */
    @Excel(name = "诉讼状态")
    private String lawsuitStatus;
    /** ç«‹æ¡ˆæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "立案时间", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate filingDate;
    /** æ‰¿åŠžå¾‹å¸ˆ/法务负责人 */
    @Excel(name = "承办律师/法务负责人")
    private String lawyer;
    /** å¼€åº­æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "开庭时间", width = 30, dateFormat = "yyyy-MM-dd")
    private LocalDate hearingDate;
    /** è¯‰è®¼æ ‡çš„金额 */
    @Excel(name = "诉讼标的金额")
    private BigDecimal claimAmount;
    /** åˆ¤å†³ç»“果摘要 */
    @Excel(name = "判决结果摘要")
    private String judgmentSummary;
    /** è¯‰è®¼è¿›å±•备注 */
    @Excel(name = "诉讼进展备注")
    private String progressRemark;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.UPDATE)
    private Integer updateUser;
    /** ç§Ÿæˆ·ID */
    @TableField(fill = FieldFill.INSERT)
    private Integer tenantId;
    /** éƒ¨é—¨ID */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /** åˆ é™¤æ ‡å¿— */
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -10,6 +10,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
/**
 * é”€å”®å°è´¦å¯¹è±¡ sales_ledger
@@ -143,5 +144,43 @@
    @TableField(exist = false)
    private Boolean hasProductionRecord;
    @Schema(description = "项目ID")
    private Long projectId;
    @Schema(description = "年份")
    @Excel(name = "年份")
    private Integer year;
    @Schema(description = "签单单位ID")
    private Long signingUnitId;
    @Schema(description = "办事处ID")
    private Long officeId;
    @Schema(description = "组负责人ID")
    private Long groupLeaderId;
    @Schema(description = "是否开发票(1是 0否)")
    private Integer isInvoice;
    @Schema(description = "中标价")
    @Excel(name = "中标价")
    private BigDecimal bidPrice;
    @Schema(description = "是否中标(1是 0否)")
    private Integer isBid;
    @Schema(description = "未中标原因")
    private String unbidReason;
    @Schema(description = "合同原件约定回传日期")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @Excel(name = "合同原件约定回传日期", width = 30, dateFormat = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate contractOriginalReturnDate;
    @TableField(exist = false)
    @Schema(description = "关联合同ID列表")
    private List<Long> associatedContractIds;
}
src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.sales.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * é”€å”®å°è´¦ä¸ŽåˆåŒå…³è”表实体
 */
@Data
@TableName("sales_ledger_contract_rel")
public class SalesLedgerContractRel implements Serializable {
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    @TableId(type = IdType.AUTO)
    private Long id;
    /** é”€å”®å°è´¦ID */
    private Long salesLedgerId;
    /** åˆåŒID */
    private Long contractId;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java
@@ -45,6 +45,10 @@
    private String deliveryPeriod;
    @ApiModelProperty(value = "状态")
    private String status;
    @ApiModelProperty(value = "项目ID")
    private Long projectId;
    @ApiModelProperty(value = "报价总金额")
    @Excel(name = "报价金额")
    private BigDecimal totalAmount;
src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
@@ -91,4 +91,44 @@
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @Schema(description = "发货单据(文本)")
    @Excel(name = "发货单据")
    private String shippingDocument;
    @Schema(description = "是否对账(勾选框,0否 1是)")
    @Excel(name = "是否对账")
    private Integer isReconciled;
    @Schema(description = "对账单归档情况(文本域)")
    @Excel(name = "对账单归档情况")
    private String statementArchivingStatus;
    @Schema(description = "是否结算(勾选框,0否 1是)")
    @Excel(name = "是否结算")
    private Integer isSettled;
    @Schema(description = "结算单归档情况(文本域)")
    @Excel(name = "结算单归档情况")
    private String settlementArchivingStatus;
    @Schema(description = "是否已开票(勾选框,0否 1是)")
    @Excel(name = "是否已开票")
    private Integer isInvoiced;
    @Schema(description = "项目ID")
    private Long projectId;
    @TableField(exist = false)
    @Schema(description = "项目名称")
    @Excel(name = "项目名称")
    private String projectName;
    @TableField(exist = false)
    @Schema(description = "开始发货日期")
    private String beginShippingDate;
    @TableField(exist = false)
    @Schema(description = "结束发货日期")
    private String endShippingDate;
}
src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.sales.dto.BiddingManagementDto;
import com.ruoyi.sales.pojo.BiddingManagement;
import java.util.List;
/**
 * æ‹›æŠ•标管理Service接口
 */
public interface IBiddingManagementService extends IService<BiddingManagement> {
    /**
     * æŸ¥è¯¢æ‹›æŠ•标管理列表 (分页)
     *
     * @param page åˆ†é¡µå¯¹è±¡
     * @param dto æŸ¥è¯¢æ¡ä»¶
     * @return æ‹›æŠ•标管理集合
     */
    IPage<BiddingManagement> selectBiddingManagementList(Page<BiddingManagement> page, BiddingManagementDto dto);
    /**
     * æŸ¥è¯¢æ‹›æŠ•标管理列表 (不分页)
     *
     * @param dto æŸ¥è¯¢æ¡ä»¶
     * @return æ‹›æŠ•标管理集合
     */
    List<BiddingManagement> selectBiddingManagementList(BiddingManagementDto dto);
    /**
     * æ–°å¢žæˆ–修改招投标管理
     *
     * @param dto æ‹›æŠ•标管理信息
     * @return ç»“æžœ
     */
    int addOrUpdateBiddingManagement(BiddingManagementDto dto);
    /**
     * æ‰¹é‡åˆ é™¤æ‹›æŠ•标管理
     *
     * @param ids éœ€è¦åˆ é™¤çš„ID数组
     * @return ç»“æžœ
     */
    int deleteBiddingManagementByIds(Long[] ids);
}
src/main/java/com/ruoyi/sales/service/ISalesContractService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.SalesContractDTO;
import com.ruoyi.sales.pojo.SalesContract;
import com.ruoyi.sales.vo.SalesContractVO;
import java.util.List;
/**
 * é”€å”®åˆåŒ Service æŽ¥å£
 */
public interface ISalesContractService extends IService<SalesContract> {
    /**
     * åˆ†é¡µæŸ¥è¯¢åˆåŒåˆ—表
     */
    IPage<SalesContractVO> listPage(Page<SalesContract> page, SalesContractDTO dto);
    /**
     * èŽ·å–åˆåŒè¯¦æƒ…ï¼ˆå«æ˜Žç»†ï¼‰
     */
    AjaxResult getDetail(Long id);
    /**
     * æ–°å¢žæˆ–更新合同
     */
    AjaxResult addOrUpdate(SalesContractDTO dto);
    /**
     * åˆ é™¤åˆåŒ
     */
    AjaxResult del(List<Long> ids);
    /**
     * å¯¼å‡ºæŸ¥è¯¢åˆ—表 (无分页)
     */
    List<SalesContract> selectList(SalesContractDTO dto);
}
src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.sales.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.sales.dto.SalesLawsuitDTO;
import com.ruoyi.sales.pojo.SalesLawsuit;
import com.ruoyi.sales.vo.SalesLawsuitVO;
import java.util.List;
/**
 * è¯‰è®¼ç®¡ç† Service æŽ¥å£
 */
public interface ISalesLawsuitService extends IService<SalesLawsuit> {
    /**
     * åˆ†é¡µæŸ¥è¯¢
     */
    IPage<SalesLawsuitVO> listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto);
    /**
     * èŽ·å–è¯¦æƒ…
     */
    AjaxResult getDetail(Long id);
    /**
     * æ–°å¢žæˆ–修改
     */
    AjaxResult addOrUpdate(SalesLawsuitDTO dto);
    /**
     * åˆ é™¤
     */
    AjaxResult del(List<Long> ids);
    /**
     * å¯¼å‡ºæŸ¥è¯¢åˆ—表 (无分页)
     */
    List<SalesLawsuit> selectList(SalesLawsuitDTO dto);
}
src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.sales.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.sales.dto.BiddingManagementDto;
import com.ruoyi.sales.mapper.BiddingManagementMapper;
import com.ruoyi.sales.pojo.BiddingManagement;
import com.ruoyi.sales.service.IBiddingManagementService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
 * æ‹›æŠ•标管理Service业务层处理
 */
@Service
public class BiddingManagementServiceImpl extends ServiceImpl<BiddingManagementMapper, BiddingManagement> implements IBiddingManagementService {
    @Override
    public IPage<BiddingManagement> selectBiddingManagementList(Page<BiddingManagement> page, BiddingManagementDto dto) {
        LambdaQueryWrapper<BiddingManagement> queryWrapper = buildQueryWrapper(dto);
        queryWrapper.orderByDesc(BiddingManagement::getCreateTime);
        return this.page(page, queryWrapper);
    }
    @Override
    public List<BiddingManagement> selectBiddingManagementList(BiddingManagementDto dto) {
        LambdaQueryWrapper<BiddingManagement> queryWrapper = buildQueryWrapper(dto);
        queryWrapper.orderByDesc(BiddingManagement::getCreateTime);
        return this.list(queryWrapper);
    }
    @Override
    public int addOrUpdateBiddingManagement(BiddingManagementDto dto) {
        BiddingManagement biddingManagement = new BiddingManagement();
        BeanUtils.copyProperties(dto, biddingManagement);
        if (biddingManagement.getId() == null) {
            return this.save(biddingManagement) ? 1 : 0;
        } else {
            return this.updateById(biddingManagement) ? 1 : 0;
        }
    }
    @Override
    public int deleteBiddingManagementByIds(Long[] ids) {
        return this.removeByIds(Arrays.asList(ids)) ? ids.length : 0;
    }
    private LambdaQueryWrapper<BiddingManagement> buildQueryWrapper(BiddingManagementDto dto) {
        LambdaQueryWrapper<BiddingManagement> queryWrapper = new LambdaQueryWrapper<>();
        if (dto != null) {
            queryWrapper.like(StringUtils.isNotEmpty(dto.getProjectName()), BiddingManagement::getProjectName, dto.getProjectName())
                        .like(StringUtils.isNotEmpty(dto.getTenderUnit()), BiddingManagement::getTenderUnit, dto.getTenderUnit())
                        .eq(StringUtils.isNotEmpty(dto.getBidResult()), BiddingManagement::getBidResult, dto.getBidResult())
                        .ge(StringUtils.isNotEmpty(dto.getBidDateStart()), BiddingManagement::getBidDate, dto.getBidDateStart())
                        .le(StringUtils.isNotEmpty(dto.getBidDateEnd()), BiddingManagement::getBidDate, dto.getBidDateEnd());
        }
        return queryWrapper;
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,183 @@
package com.ruoyi.sales.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.sales.dto.SalesContractDTO;
import com.ruoyi.sales.mapper.SalesContractMapper;
import com.ruoyi.sales.mapper.SalesContractProductMapper;
import com.ruoyi.sales.pojo.SalesContract;
import com.ruoyi.sales.pojo.SalesContractProduct;
import com.ruoyi.sales.service.ISalesContractService;
import com.ruoyi.sales.vo.SalesContractVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * é”€å”®åˆåŒ Service å®žçް
 */
@Slf4j
@Service
public class SalesContractServiceImpl extends ServiceImpl<SalesContractMapper, SalesContract> implements ISalesContractService {
    @Resource
    private SalesContractProductMapper salesContractProductMapper;
    @Override
    public IPage<SalesContractVO> listPage(Page<SalesContract> page, SalesContractDTO dto) {
        LambdaQueryWrapper<SalesContract> queryWrapper = buildQueryWrapper(dto);
        IPage<SalesContract> iPage = this.page(page, queryWrapper);
        IPage<SalesContractVO> voPage = iPage.convert(item -> {
            return BeanUtil.copyProperties(item, SalesContractVO.class);
        });
        return voPage;
    }
    @Override
    public AjaxResult getDetail(Long id) {
        SalesContract contract = this.getById(id);
        if (contract == null) {
            return AjaxResult.error("合同数据不存在");
        }
        SalesContractVO vo = BeanUtil.copyProperties(contract, SalesContractVO.class);
        // èŽ·å–æ˜Žç»†
        LambdaQueryWrapper<SalesContractProduct> productQuery = new LambdaQueryWrapper<>();
        productQuery.eq(SalesContractProduct::getContractId, id);
        List<SalesContractProduct> productList = salesContractProductMapper.selectList(productQuery);
        vo.setProductList(productList);
        return AjaxResult.success(vo);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult addOrUpdate(SalesContractDTO dto) {
        SalesContract contract = BeanUtil.copyProperties(dto, SalesContract.class);
        boolean isUpdate = contract.getId() != null;
        List<SalesContractProduct> productList = dto.getProductList();
        if (productList == null) {
            productList = new ArrayList<>();
        }
        // é‡æ–°è®¡ç®—各产品的产值,并汇总合同总产值
        BigDecimal totalOutputValue = BigDecimal.ZERO;
        for (SalesContractProduct product : productList) {
            BigDecimal price = product.getTaxInclusiveUnitPrice() != null ? product.getTaxInclusiveUnitPrice() : BigDecimal.ZERO;
            BigDecimal shipped = product.getShippedQuantity() != null ? product.getShippedQuantity() : BigDecimal.ZERO;
            BigDecimal productOutputValue = price.multiply(shipped);
            product.setOutputValue(productOutputValue);
            totalOutputValue = totalOutputValue.add(productOutputValue);
        }
        contract.setOutputValue(totalOutputValue);
        if (isUpdate) {
            this.updateById(contract);
            // å·®é‡æ›´æ–°é€»è¾‘
            handleProductListUpdate(contract.getId(), productList);
        } else {
            this.save(contract);
            // æ‰¹é‡æ’入明细
            for (SalesContractProduct product : productList) {
                product.setContractId(contract.getId());
                salesContractProductMapper.insert(product);
            }
        }
        return AjaxResult.success();
    }
    /**
     * å¤„理产品明细的差量更新
     */
    private void handleProductListUpdate(Long contractId, List<SalesContractProduct> newList) {
        // èŽ·å–æ•°æ®åº“ä¸­æ—§çš„æ˜Žç»†åˆ—è¡¨
        LambdaQueryWrapper<SalesContractProduct> productQuery = new LambdaQueryWrapper<>();
        productQuery.eq(SalesContractProduct::getContractId, contractId);
        List<SalesContractProduct> oldList = salesContractProductMapper.selectList(productQuery);
        Map<Long, SalesContractProduct> oldMap = oldList.stream()
                .collect(Collectors.toMap(SalesContractProduct::getId, p -> p));
        // éåŽ†æ–°åˆ—è¡¨è¿›è¡Œæ’å…¥æˆ–æ›´æ–°
        for (SalesContractProduct newProduct : newList) {
            newProduct.setContractId(contractId);
            if (newProduct.getId() != null && oldMap.containsKey(newProduct.getId())) {
                // æ›´æ–°
                salesContractProductMapper.updateById(newProduct);
                oldMap.remove(newProduct.getId()); // ä»Ž Map ä¸­ç§»é™¤ï¼Œå‰©ä¸‹çš„就是需要删除的
            } else {
                // æ–°å¢ž
                newProduct.setId(null); // é˜²å¾¡æ€§å¤„理
                salesContractProductMapper.insert(newProduct);
            }
        }
        // åˆ é™¤åœ¨å‰ç«¯åˆ—表中不存在的旧记录
        if (!oldMap.isEmpty()) {
            salesContractProductMapper.deleteBatchIds(oldMap.keySet());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult del(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return AjaxResult.error("参数不能为空");
        }
        // åˆ é™¤ä¸»è¡¨
        this.removeByIds(ids);
        // çº§è”删除子表
        LambdaQueryWrapper<SalesContractProduct> delQuery = new LambdaQueryWrapper<>();
        delQuery.in(SalesContractProduct::getContractId, ids);
        salesContractProductMapper.delete(delQuery);
        return AjaxResult.success();
    }
    @Override
    public List<SalesContract> selectList(SalesContractDTO dto) {
        return this.list(buildQueryWrapper(dto));
    }
    /**
     * æž„建查询条件
     */
    private LambdaQueryWrapper<SalesContract> buildQueryWrapper(SalesContractDTO dto) {
        LambdaQueryWrapper<SalesContract> queryWrapper = new LambdaQueryWrapper<>();
        if (dto == null) {
            return queryWrapper;
        }
        queryWrapper.like(StringUtils.isNotBlank(dto.getContractNo()), SalesContract::getContractNo, dto.getContractNo());
        queryWrapper.like(StringUtils.isNotBlank(dto.getSigningUnit()), SalesContract::getSigningUnit, dto.getSigningUnit());
        queryWrapper.eq(StringUtils.isNotBlank(dto.getArchiveStatus()), SalesContract::getArchiveStatus, dto.getArchiveStatus());
        queryWrapper.ge(dto.getSignDateStart() != null, SalesContract::getSignDate, dto.getSignDateStart());
        queryWrapper.le(dto.getSignDateEnd() != null, SalesContract::getSignDate, dto.getSignDateEnd());
        queryWrapper.orderByDesc(SalesContract::getCreateTime);
        return queryWrapper;
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package com.ruoyi.sales.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.sales.dto.SalesLawsuitDTO;
import com.ruoyi.sales.mapper.SalesLawsuitMapper;
import com.ruoyi.sales.pojo.SalesLawsuit;
import com.ruoyi.sales.service.ISalesLawsuitService;
import com.ruoyi.sales.vo.SalesLawsuitVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * è¯‰è®¼ç®¡ç† Service å®žçް
 */
@Slf4j
@Service
public class SalesLawsuitServiceImpl extends ServiceImpl<SalesLawsuitMapper, SalesLawsuit> implements ISalesLawsuitService {
    @Resource
    private FileUtil fileUtil;
    @Override
    public IPage<SalesLawsuitVO> listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto) {
        LambdaQueryWrapper<SalesLawsuit> queryWrapper = buildQueryWrapper(dto);
        IPage<SalesLawsuit> iPage = this.page(page, queryWrapper);
        IPage<SalesLawsuitVO> voPage = iPage.convert(item -> {
            SalesLawsuitVO vo = BeanUtil.copyProperties(item, SalesLawsuitVO.class);
            return vo;
        });
        return voPage;
    }
    @Override
    public AjaxResult getDetail(Long id) {
        SalesLawsuit lawsuit = this.getById(id);
        if (lawsuit == null) {
            return AjaxResult.error("数据不存在");
        }
        SalesLawsuitVO vo = BeanUtil.copyProperties(lawsuit, SalesLawsuitVO.class);
        // ç»„装附件
        List<StorageBlobVO> storageBlobVOs = fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(
                ApplicationTypeEnum.FILE,
                RecordTypeEnum.SALES_LAWSUIT,
                id
        );
        vo.setStorageBlobVOs(storageBlobVOs != null ? storageBlobVOs : new ArrayList<>());
        return AjaxResult.success(vo);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult addOrUpdate(SalesLawsuitDTO dto) {
        SalesLawsuit lawsuit = BeanUtil.copyProperties(dto, SalesLawsuit.class);
        boolean isUpdate = lawsuit.getId() != null;
        if (isUpdate) {
            this.updateById(lawsuit);
            log.info("TODO: åŒæ­¥æ›´æ–°é¡¹ç›®æ—¶é—´è½´ä¸­å¯¹åº”的诉讼事件节点 (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
        } else {
            this.save(lawsuit);
            log.info("TODO: åœ¨é¡¹ç›®æ—¶é—´è½´ä¸­è¿½åŠ ä¸€æ¡è¯‰è®¼èŠ‚ç‚¹ (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
        }
        // å¤„理附件
        List<StorageBlobDTO> storageBlobDTOs = dto.getStorageBlobDTOs();
        if (storageBlobDTOs == null) {
            storageBlobDTOs = new ArrayList<>();
        }
        fileUtil.saveStorageAttachment(
                ApplicationTypeEnum.FILE,
                RecordTypeEnum.SALES_LAWSUIT,
                lawsuit.getId(),
                storageBlobDTOs
        );
        return AjaxResult.success();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult del(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return AjaxResult.error("参数不能为空");
        }
        List<SalesLawsuit> lawsuits = this.listByIds(ids);
        this.removeByIds(ids);
        for (SalesLawsuit lawsuit : lawsuits) {
            log.info("TODO: åŒæ­¥ç§»é™¤é¡¹ç›®æ—¶é—´è½´ä¸­å¯¹åº”的诉讼事件节点 (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
        }
        return AjaxResult.success();
    }
    @Override
    public List<SalesLawsuit> selectList(SalesLawsuitDTO dto) {
        return this.list(buildQueryWrapper(dto));
    }
    /**
     * æž„建查询条件
     */
    private LambdaQueryWrapper<SalesLawsuit> buildQueryWrapper(SalesLawsuitDTO dto) {
        LambdaQueryWrapper<SalesLawsuit> queryWrapper = new LambdaQueryWrapper<>();
        if (dto == null) {
            return queryWrapper;
        }
        queryWrapper.like(StringUtils.isNotBlank(dto.getCaseNo()), SalesLawsuit::getCaseNo, dto.getCaseNo());
        queryWrapper.eq(dto.getProjectId() != null, SalesLawsuit::getProjectId, dto.getProjectId());
        queryWrapper.like(StringUtils.isNotBlank(dto.getProjectName()), SalesLawsuit::getProjectName, dto.getProjectName());
        queryWrapper.eq(dto.getContractId() != null, SalesLawsuit::getContractId, dto.getContractId());
        queryWrapper.like(StringUtils.isNotBlank(dto.getContractNo()), SalesLawsuit::getContractNo, dto.getContractNo());
        queryWrapper.eq(StringUtils.isNotBlank(dto.getLawsuitStatus()), SalesLawsuit::getLawsuitStatus, dto.getLawsuitStatus());
        queryWrapper.like(StringUtils.isNotBlank(dto.getLawyer()), SalesLawsuit::getLawyer, dto.getLawyer());
        queryWrapper.ge(dto.getFilingDateStart() != null, SalesLawsuit::getFilingDate, dto.getFilingDateStart());
        queryWrapper.le(dto.getFilingDateEnd() != null, SalesLawsuit::getFilingDate, dto.getFilingDateEnd());
        queryWrapper.orderByDesc(SalesLawsuit::getCreateTime);
        return queryWrapper;
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -1,9 +1,9 @@
package com.ruoyi.sales.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -75,6 +75,9 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.ruoyi.sales.mapper.SalesLedgerContractRelMapper;
import com.ruoyi.sales.pojo.SalesLedgerContractRel;
import org.springframework.util.CollectionUtils;
/**
 * é”€å”®å°è´¦Service业务层处理
@@ -106,6 +109,7 @@
    private final FileUtil fileUtil;
    private final AccountInvoiceApplicationMapper accountInvoiceApplicationMapper;
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    private final SalesLedgerContractRelMapper salesLedgerContractRelMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
@@ -159,6 +163,15 @@
        SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerDto.getId());
        if (salesLedger == null) {
            throw new BaseException("台账不存在");
        }
        // æŸ¥è¯¢å…³è”的合同
        List<SalesLedgerContractRel> rels = salesLedgerContractRelMapper.selectList(
                new LambdaQueryWrapper<SalesLedgerContractRel>().eq(SalesLedgerContractRel::getSalesLedgerId, salesLedger.getId())
        );
        if (!CollectionUtils.isEmpty(rels)) {
            List<Long> contractIds = rels.stream().map(SalesLedgerContractRel::getContractId).collect(Collectors.toList());
            salesLedger.setAssociatedContractIds(contractIds);
        }
        // 2. æŸ¥è¯¢å­è¡¨
@@ -530,7 +543,7 @@
        // åˆ é™¤å‘货台账记录
        List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                .in(ShippingInfo::getSalesLedgerId, idList));
        if (CollectionUtils.isNotEmpty(shippingInfos)) {
        if (CollUtil.isNotEmpty(shippingInfos)) {
            shippingInfoServiceImpl.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList()));
        }
        // åˆ é™¤é™„件表
@@ -539,7 +552,7 @@
        // åˆ é™¤ç”Ÿäº§ç®¡æŽ§æ•°æ®
        //查询生产报工id
        ArrayList<Long> mainIdList = productionProductMainService.listMain(idList);
        if (CollectionUtils.isNotEmpty(mainIdList)) {
        if (CollUtil.isNotEmpty(mainIdList)) {
            mainIdList.stream().forEach(mainId -> {
                productionProductMainService.removeProductMain(mainId);
            });
@@ -577,6 +590,18 @@
            salesLedgerMapper.updateById(salesLedger);
        }
        // å¤„理关联合同数据
        salesLedgerContractRelMapper.delete(
                new LambdaQueryWrapper<SalesLedgerContractRel>().eq(SalesLedgerContractRel::getSalesLedgerId, salesLedger.getId())
        );
        if (!CollectionUtils.isEmpty(salesLedgerDto.getAssociatedContractIds())) {
            for (Long contractId : salesLedgerDto.getAssociatedContractIds()) {
                SalesLedgerContractRel rel = new SalesLedgerContractRel();
                rel.setSalesLedgerId(salesLedger.getId());
                rel.setContractId(contractId);
                salesLedgerContractRelMapper.insert(rel);
            }
        }
        // 4. å¤„理子表数据
        List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -8,6 +8,7 @@
import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
import com.ruoyi.approve.pojo.ApprovalTemplate;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.service.ApprovalInstanceService;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
@@ -23,9 +24,11 @@
import com.ruoyi.sales.dto.ShippingApproveDto;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.dto.ShippingProductDetailDto;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.mapper.ShippingProductDetailMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.pojo.ShippingProductDetail;
import com.ruoyi.sales.service.ShippingInfoService;
@@ -36,6 +39,7 @@
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.stream.Collectors;
import java.util.List;
/**
@@ -52,6 +56,7 @@
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final SalesLedgerMapper salesLedgerMapper;
    private final StockUtils stockUtils;
@@ -68,6 +73,11 @@
        IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
        listPage.getRecords().forEach(item -> {
            item.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, item.getId()));
            if (item.getReturnNos() != null && !item.getReturnNos().isEmpty()) {
                item.setReturnNoList(java.util.Arrays.asList(item.getReturnNos().split(",")));
            } else {
                item.setReturnNoList(new java.util.ArrayList<>());
            }
        });
        return listPage;
    }
@@ -123,6 +133,20 @@
                stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
            }
        }
        // åˆ é™¤å·¥ä½œæµå®žä¾‹
        List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>()
                .in(ApprovalInstance::getBusinessId, ids)
                .eq(ApprovalInstance::getBusinessType, 7L)
        );
        if(!approvalInstances.isEmpty()){
            approvalInstanceService.delete(
                    approvalInstances.stream()
                            .map(ApprovalInstance::getId)
                            .collect(Collectors.toList())
            );
        }
        //删除发货明细
        shippingProductDetailMapper.delete(new LambdaQueryWrapper<ShippingProductDetail>().in(ShippingProductDetail::getShippingInfoId, ids));
@@ -188,6 +212,14 @@
        // å…ˆä¿å­˜å‘货单,再发起审批;无审核人自动通过时需要按发货编号回写发货状态。
        req.setShippingNo(sh);
        req.setStatus("待审核");
        // Fetch projectId from SalesLedger
        if (req.getSalesLedgerId() != null) {
            SalesLedger salesLedger = salesLedgerMapper.selectById(req.getSalesLedgerId());
            if (salesLedger != null && salesLedger.getProjectId() != null) {
                req.setProjectId(salesLedger.getProjectId());
            }
        }
        boolean save = this.add(req);
        // å‘货审批
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.ruoyi.sales.task;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.projectManagement.vo.InfoVo;
import com.ruoyi.projectManagement.pojo.ProjectConfig;
import com.ruoyi.projectManagement.service.InfoService;
import com.ruoyi.projectManagement.service.ProjectConfigService;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.service.ISalesLedgerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
/**
 * é”€å”®å°è´¦-合同原件约定回传日期预警任务
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ContractReturnReminderTask {
    private final ProjectConfigService projectConfigService;
    private final ISalesLedgerService salesLedgerService;
    private final InfoService infoService;
    private final ISysNoticeService sysNoticeService;
    // æ¯å¤©ä¸Šåˆ 8 ç‚¹æ‰§è¡Œ
    @Scheduled(cron = "0 0 8 * * ?")
    public void processReminders() {
        log.info("开始执行:合同原件回传预警扫描");
        ProjectConfig config = projectConfigService.getGlobalConfig();
        Integer reminderDays = config.getContractReturnReminderDays();
        if (reminderDays == null) {
            reminderDays = 3;
        }
        LocalDate targetDate = LocalDate.now().plusDays(reminderDays);
        // æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„销售台账
        List<SalesLedger> ledgers = salesLedgerService.list(Wrappers.<SalesLedger>lambdaQuery()
                .eq(SalesLedger::getContractOriginalReturnDate, targetDate));
        if (ledgers == null || ledgers.isEmpty()) {
            log.info("今日无需要预警的合同回传记录");
            return;
        }
        log.info("发现 {} æ¡é¢„警记录", ledgers.size());
        for (SalesLedger ledger : ledgers) {
            if (ledger.getProjectId() == null) {
                continue;
            }
            InfoVo projectInfo = infoService.getInfoById(ledger.getProjectId());
            if (projectInfo != null && projectInfo.getInfo() != null && projectInfo.getInfo().getManagerId() != null) {
                sendNotice(ledger, projectInfo);
            }
        }
        log.info("合同原件回传预警扫描结束");
    }
    private void sendNotice(SalesLedger ledger, InfoVo projectInfo) {
        SysNotice notice = new SysNotice();
        notice.setNoticeTitle("合同原件回传预警");
        notice.setNoticeType("1"); // 1为通知
        String content = String.format("项目【%s】关联的销售台账(%s)的合同原件约定回传日期(%s)即将到期,请及时处理!",
                projectInfo.getInfo().getTitle(),
                ledger.getSalesContractNo(),
                ledger.getContractOriginalReturnDate().toString());
        notice.setNoticeContent(content);
        notice.setStatus("0"); // æ­£å¸¸
        notice.setConsigneeId(projectInfo.getInfo().getManagerId());
        // å¯ä»¥è®¾ç½® senderId ä¸ºç³»ç»Ÿç®¡ç†å‘˜ (例如 1L)
        notice.setSenderId(1L);
        try {
            sysNoticeService.save(notice);
        } catch (Exception e) {
            log.error("发送预警通知失败: salesLedgerId={}, error={}", ledger.getId(), e.getMessage());
        }
    }
}
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
@@ -33,4 +33,10 @@
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "开票金额")
    private BigDecimal invoiceAmount;
    @Schema(description = "是否回款超时")
    private Boolean isPaymentTimeout;
}
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
@@ -12,6 +12,9 @@
    @Schema(description = "客户ID")
    private Long customerId;
    @Schema(description = "项目ID")
    private Long projectId;
    @Schema(description = "客户名称")
    private String customerName;
@@ -30,4 +33,13 @@
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "开票金额")
    private BigDecimal invoiceAmount;
    @Schema(description = "合同总数")
    private Integer contractCount;
    @Schema(description = "超期合同数")
    private Integer overdueCount;
}
src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.sales.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Schema(name = "ProjectTransactionsVo", description = "营销管理--客户往来-项目层级(返回)")
public class ProjectTransactionsVo {
    @Schema(description = "项目ID")
    private Long projectId;
    @Schema(description = "项目编号")
    private String projectNo;
    @Schema(description = "项目名称")
    private String projectName;
    @Schema(description = "合同总金额")
    private BigDecimal contractAmounts;
    @Schema(description = "收款金额")
    private BigDecimal receiptPaymentAmount;
    @Schema(description = "应收金额")
    private BigDecimal receiptableAmount;
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "开票金额")
    private BigDecimal invoiceAmount;
    @Schema(description = "关联客户数")
    private Integer customerCount;
    @Schema(description = "合同总数")
    private Integer contractCount;
    @Schema(description = "超期合同数")
    private Integer overdueCount;
    @Schema(description = "项目状态 (0进行中 1已完成等)")
    private Integer status;
}
src/main/java/com/ruoyi/sales/vo/SalesContractVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.sales.vo;
import com.ruoyi.sales.pojo.SalesContract;
import com.ruoyi.sales.pojo.SalesContractProduct;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class SalesContractVO extends SalesContract {
    /**
     * äº§å“æ˜Žç»†åˆ—表
     */
    private List<SalesContractProduct> productList;
}
src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.sales.vo;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.sales.pojo.SalesLawsuit;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
public class SalesLawsuitVO extends SalesLawsuit {
    /**
     * é™„件回显列表
     */
    private List<StorageBlobVO> storageBlobVOs;
}
src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.vehicle.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.vehicle.pojo.FuelCard;
import com.ruoyi.vehicle.service.FuelCardService;
import com.ruoyi.vehicle.mapper.FuelCardMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@RestController
@RequestMapping("/vehicle/fuelCard")
public class FuelCardController {
    @Autowired
    private FuelCardService fuelCardService;
    @Autowired
    private FuelCardMapper fuelCardMapper;
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<FuelCard> page, FuelCard query) {
        IPage<FuelCard> pageInfo = fuelCardMapper.selectFuelCardPage(page, query);
        return AjaxResult.success(pageInfo);
    }
    @GetMapping("/listAll")
    public AjaxResult listAll() {
        LambdaQueryWrapper<FuelCard> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(FuelCard::getId);
        List<FuelCard> list = fuelCardService.list(wrapper);
        return AjaxResult.success(list);
    }
    @PostMapping
    public AjaxResult add(@RequestBody FuelCard entity) {
        if (entity.getInitialBalance() != null) {
            entity.setCurrentBalance(entity.getInitialBalance());
        }
        return AjaxResult.success(fuelCardService.save(entity));
    }
    @PutMapping
    public AjaxResult edit(@RequestBody FuelCard entity) {
        return AjaxResult.success(fuelCardService.updateById(entity));
    }
    @DeleteMapping("/{id}")
    public AjaxResult remove(@PathVariable Long id) {
        return AjaxResult.success(fuelCardService.removeById(id));
    }
}
src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.vehicle.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.vehicle.pojo.FuelCardRecharge;
import com.ruoyi.vehicle.service.IFuelCardRechargeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/vehicle/fuelCardRecharge")
public class FuelCardRechargeController {
    @Autowired
    private IFuelCardRechargeService fuelCardRechargeService;
    @GetMapping("/list")
    public AjaxResult list(Page<FuelCardRecharge> page, @RequestParam("cardId") Long cardId) {
        IPage<FuelCardRecharge> pageList = fuelCardRechargeService.selectRechargePage(page, cardId);
        return AjaxResult.success(pageList);
    }
    @PostMapping("/add")
    public AjaxResult add(@RequestBody FuelCardRecharge recharge) {
        if (recharge.getCardId() == null || recharge.getRechargeAmount() == null || recharge.getRechargeDate() == null) {
            return AjaxResult.error("参数不完整");
        }
        fuelCardRechargeService.addRecharge(recharge);
        return AjaxResult.success();
    }
    @DeleteMapping("/{id}")
    public AjaxResult remove(@PathVariable Long id) {
        fuelCardRechargeService.deleteRecharge(id);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.vehicle.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.vehicle.pojo.FuelRecord;
import com.ruoyi.vehicle.service.FuelRecordService;
import com.ruoyi.vehicle.mapper.FuelRecordMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/vehicle/fuelRecord")
public class FuelRecordController {
    @Autowired
    private FuelRecordService fuelRecordService;
    @Autowired
    private FuelRecordMapper fuelRecordMapper;
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<FuelRecord> page, FuelRecord query, String beginDate, String endDate) {
        IPage<FuelRecord> pageInfo = fuelRecordMapper.selectFuelRecordPage(page, query, beginDate, endDate);
        return AjaxResult.success(pageInfo);
    }
    @PostMapping
    public AjaxResult add(@RequestBody FuelRecord entity) {
        return AjaxResult.success(fuelRecordService.saveRecord(entity));
    }
    @PutMapping
    public AjaxResult edit(@RequestBody FuelRecord entity) {
        return AjaxResult.success(fuelRecordService.updateRecord(entity));
    }
    @DeleteMapping("/{id}")
    public AjaxResult remove(@PathVariable Long id) {
        return AjaxResult.success(fuelRecordService.removeRecordById(id));
    }
    @PostMapping("/submit/{id}")
    public AjaxResult submit(@PathVariable Long id) {
        try {
            fuelRecordService.submitRecord(id);
            return AjaxResult.success();
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
    @PostMapping("/revoke/{id}")
    public AjaxResult revoke(@PathVariable Long id) {
        try {
            fuelRecordService.revokeRecord(id);
            return AjaxResult.success();
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }
}
src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package com.ruoyi.vehicle.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.vehicle.pojo.VehicleCost;
import com.ruoyi.vehicle.service.VehicleCostService;
import com.ruoyi.vehicle.mapper.VehicleCostMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.util.StringUtils;
@RestController
@RequestMapping("/vehicle/cost")
public class VehicleCostController {
    @Autowired
    private VehicleCostService vehicleCostService;
    @Autowired
    private VehicleCostMapper vehicleCostMapper;
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<VehicleCost> page, VehicleCost query) {
        LambdaQueryWrapper<VehicleCost> wrapper = new LambdaQueryWrapper<>();
        if (query.getVehicleId() != null) {
            wrapper.eq(VehicleCost::getVehicleId, query.getVehicleId());
        }
        if (StringUtils.hasText(query.getCostType())) {
            wrapper.eq(VehicleCost::getCostType, query.getCostType());
        }
        wrapper.orderByDesc(VehicleCost::getCostDate);
        IPage<VehicleCost> pageInfo = vehicleCostService.page(page, wrapper);
        return AjaxResult.success(pageInfo);
    }
    @GetMapping("/summaryByMonth")
    public AjaxResult summaryByMonth() {
        return AjaxResult.success(vehicleCostMapper.summaryByMonth());
    }
    @GetMapping("/summaryByVehicle")
    public AjaxResult summaryByVehicle() {
        return AjaxResult.success(vehicleCostMapper.summaryByVehicle());
    }
    @PostMapping
    public AjaxResult add(@RequestBody VehicleCost entity) {
        return AjaxResult.success(vehicleCostService.save(entity));
    }
    @PutMapping
    public AjaxResult edit(@RequestBody VehicleCost entity) {
        return AjaxResult.success(vehicleCostService.updateById(entity));
    }
    @DeleteMapping("/{id}")
    public AjaxResult remove(@PathVariable Long id) {
        return AjaxResult.success(vehicleCostService.removeById(id));
    }
}
src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.vehicle.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.vehicle.pojo.VehicleInfo;
import com.ruoyi.vehicle.service.VehicleInfoService;
import com.ruoyi.vehicle.mapper.VehicleInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.List;
@RestController
@RequestMapping("/vehicle/info")
public class VehicleInfoController {
    @Autowired
    private VehicleInfoService vehicleInfoService;
    @Autowired
    private VehicleInfoMapper vehicleInfoMapper;
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<VehicleInfo> page, VehicleInfo query) {
        IPage<VehicleInfo> pageInfo = vehicleInfoMapper.selectVehiclePage(page, query);
        return AjaxResult.success(pageInfo);
    }
    @GetMapping("/warningList")
    public AjaxResult warningList() {
        LocalDate nextMonth = LocalDate.now().plusDays(30);
        LambdaQueryWrapper<VehicleInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.le(VehicleInfo::getInspectionExpireDate, nextMonth).or().le(VehicleInfo::getInsuranceExpireDate, nextMonth);
        List<VehicleInfo> list = vehicleInfoService.list(wrapper);
        return AjaxResult.success(list);
    }
    @PostMapping
    public AjaxResult add(@RequestBody VehicleInfo entity) {
        return AjaxResult.success(vehicleInfoService.save(entity));
    }
    @PutMapping
    public AjaxResult edit(@RequestBody VehicleInfo entity) {
        return AjaxResult.success(vehicleInfoService.updateById(entity));
    }
    @DeleteMapping("/{id}")
    public AjaxResult remove(@PathVariable Long id) {
        return AjaxResult.success(vehicleInfoService.removeById(id));
    }
}
src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.vehicle.pojo.FuelCard;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface FuelCardMapper extends BaseMapper<FuelCard> {
    IPage<FuelCard> selectFuelCardPage(Page<FuelCard> page, @Param("query") FuelCard query);
}
src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.vehicle.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.vehicle.pojo.FuelCardRecharge;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface FuelCardRechargeMapper extends BaseMapper<FuelCardRecharge> {
    IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, @Param("cardId") Long cardId);
}
src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.vehicle.pojo.FuelRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface FuelRecordMapper extends BaseMapper<FuelRecord> {
    IPage<FuelRecord> selectFuelRecordPage(Page<FuelRecord> page, @Param("query") FuelRecord query, @Param("beginDate") String beginDate, @Param("endDate") String endDate);
}
src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.vehicle.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.vehicle.pojo.VehicleCost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface VehicleCostMapper extends BaseMapper<VehicleCost> {
    @Select("SELECT DATE_FORMAT(cost_date, '%Y-%m') as month, cost_type as costType, sum(amount) as totalAmount " +
            "FROM p_vehicle_cost WHERE del_flag = '0' GROUP BY DATE_FORMAT(cost_date, '%Y-%m'), cost_type")
    List<Map<String, Object>> summaryByMonth();
    @Select("SELECT v.plate_number as plateNumber, c.cost_type as costType, sum(c.amount) as totalAmount " +
            "FROM p_vehicle_cost c LEFT JOIN p_vehicle_info v ON c.vehicle_id = v.id " +
            "WHERE c.del_flag = '0' GROUP BY v.plate_number, c.cost_type")
    List<Map<String, Object>> summaryByVehicle();
}
src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.vehicle.pojo.VehicleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface VehicleInfoMapper extends BaseMapper<VehicleInfo> {
    IPage<VehicleInfo> selectVehiclePage(Page<VehicleInfo> page, @Param("query") VehicleInfo query);
}
src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.vehicle.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("p_fuel_card")
public class FuelCard {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String cardNumber;
    private Long deptId;
    private String issuer;
    private BigDecimal initialBalance;
    private BigDecimal currentBalance;
    private String status;
    private String remark;
    @TableField(exist = false)
    private String deptName;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private String tenantId;
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package com.ruoyi.vehicle.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("p_fuel_card_recharge")
public class FuelCardRecharge {
    @TableId
    private Long id;
    private Long cardId;
    private BigDecimal rechargeAmount;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date rechargeDate;
    private String remark;
    private String delFlag;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    @TableField(exist = false)
    private String createUserName;
}
src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.vehicle.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("p_fuel_record")
public class FuelRecord {
    @TableId(type = IdType.AUTO)
    private Long id;
    private LocalDate fuelDate;
    private BigDecimal amount;
    private BigDecimal liters;
    private Long vehicleId;
    private Long fuelCardId;
    private Long deptId;
    private BigDecimal startMileage;
    private BigDecimal endMileage;
    private String invoiceNo;
    private String remark;
    @TableField(exist = false)
    private String deptName;
    @TableField(exist = false)
    private String plateNumber;
    @TableField(exist = false)
    private String cardNumber;
    private String status;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private String tenantId;
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.vehicle.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("p_vehicle_cost")
public class VehicleCost {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long vehicleId;
    private String costType;
    private BigDecimal amount;
    private LocalDate costDate;
    private String remark;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private String tenantId;
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package com.ruoyi.vehicle.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("p_vehicle_info")
public class VehicleInfo {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String plateNumber;
    private String propertyOwnership;
    private Long deptId;
    private Long userId;
    private String vehicleStatus;
    private LocalDate inspectionExpireDate;
    private LocalDate insuranceExpireDate;
    private String remark;
    @TableField(exist = false)
    private String deptName;
    @TableField(exist = false)
    private String userName;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private String tenantId;
    @TableLogic
    private String delFlag;
}
src/main/java/com/ruoyi/vehicle/service/FuelCardService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
package com.ruoyi.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.vehicle.pojo.FuelCard;
public interface FuelCardService extends IService<FuelCard> {
}
src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.vehicle.pojo.FuelRecord;
public interface FuelRecordService extends IService<FuelRecord> {
    boolean saveRecord(FuelRecord record);
    boolean updateRecord(FuelRecord record);
    boolean removeRecordById(Long id);
    // æäº¤è®°å½•并扣款
    boolean submitRecord(Long id);
    // æ’¤å›žè®°å½•并退款
    boolean revokeRecord(Long id);
}
src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.vehicle.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.vehicle.pojo.FuelCardRecharge;
public interface IFuelCardRechargeService extends IService<FuelCardRecharge> {
    IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, Long cardId);
    void addRecharge(FuelCardRecharge recharge);
    void deleteRecharge(Long id);
}
src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
package com.ruoyi.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.vehicle.pojo.VehicleCost;
public interface VehicleCostService extends IService<VehicleCost> {
}
src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
package com.ruoyi.vehicle.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.vehicle.pojo.VehicleInfo;
public interface VehicleInfoService extends IService<VehicleInfo> {
}
src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.vehicle.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.vehicle.mapper.FuelCardMapper;
import com.ruoyi.vehicle.mapper.FuelCardRechargeMapper;
import com.ruoyi.vehicle.pojo.FuelCard;
import com.ruoyi.vehicle.pojo.FuelCardRecharge;
import com.ruoyi.vehicle.service.IFuelCardRechargeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class FuelCardRechargeServiceImpl extends ServiceImpl<FuelCardRechargeMapper, FuelCardRecharge> implements IFuelCardRechargeService {
    @Autowired
    private FuelCardMapper fuelCardMapper;
    @Override
    public IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, Long cardId) {
        return baseMapper.selectRechargePage(page, cardId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRecharge(FuelCardRecharge recharge) {
        // ä¿å­˜å……值记录
        recharge.setDelFlag("0");
        this.save(recharge);
        // æ›´æ–°åŠ æ²¹å¡ä½™é¢
        FuelCard card = fuelCardMapper.selectById(recharge.getCardId());
        if (card != null && card.getCurrentBalance() != null) {
            card.setCurrentBalance(card.getCurrentBalance().add(recharge.getRechargeAmount()));
            fuelCardMapper.updateById(card);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRecharge(Long id) {
        FuelCardRecharge recharge = this.getById(id);
        if (recharge != null && "0".equals(recharge.getDelFlag())) {
            // é€»è¾‘删除
            recharge.setDelFlag("2");
            this.updateById(recharge);
            // æ‰£å›žä½™é¢
            FuelCard card = fuelCardMapper.selectById(recharge.getCardId());
            if (card != null && card.getCurrentBalance() != null) {
                card.setCurrentBalance(card.getCurrentBalance().subtract(recharge.getRechargeAmount()));
                fuelCardMapper.updateById(card);
            }
        }
    }
}
src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.vehicle.mapper.FuelCardMapper;
import com.ruoyi.vehicle.pojo.FuelCard;
import com.ruoyi.vehicle.service.FuelCardService;
import org.springframework.stereotype.Service;
@Service
public class FuelCardServiceImpl extends ServiceImpl<FuelCardMapper, FuelCard> implements FuelCardService {
}
src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package com.ruoyi.vehicle.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.vehicle.mapper.FuelRecordMapper;
import com.ruoyi.vehicle.pojo.FuelCard;
import com.ruoyi.vehicle.pojo.FuelRecord;
import com.ruoyi.vehicle.pojo.VehicleInfo;
import com.ruoyi.vehicle.service.FuelCardService;
import com.ruoyi.vehicle.service.FuelRecordService;
import com.ruoyi.vehicle.service.VehicleInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class FuelRecordServiceImpl extends ServiceImpl<FuelRecordMapper, FuelRecord> implements FuelRecordService {
    @Autowired
    private FuelCardService fuelCardService;
    @Autowired
    private VehicleInfoService vehicleInfoService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRecord(FuelRecord record) {
        // å¦‚果没有传入部门,尝试从加油卡或车辆继承
        if (record.getDeptId() == null) {
            if (record.getFuelCardId() != null) {
                FuelCard card = fuelCardService.getById(record.getFuelCardId());
                if (card != null && card.getDeptId() != null) {
                    record.setDeptId(card.getDeptId());
                }
            } else if (record.getVehicleId() != null) {
                VehicleInfo vehicle = vehicleInfoService.getById(record.getVehicleId());
                if (vehicle != null && vehicle.getDeptId() != null) {
                    record.setDeptId(vehicle.getDeptId());
                }
            }
        }
        record.setStatus("0"); // é»˜è®¤è‰ç¨¿
        return this.save(record);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRecord(FuelRecord record) {
        FuelRecord oldRecord = this.getById(record.getId());
        if (oldRecord != null && "1".equals(oldRecord.getStatus())) {
            throw new RuntimeException("已提交的记录无法修改");
        }
        return this.updateById(record);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeRecordById(Long id) {
        FuelRecord oldRecord = this.getById(id);
        if (oldRecord != null && "1".equals(oldRecord.getStatus())) {
            throw new RuntimeException("已提交的记录无法删除");
        }
        return this.removeById(id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submitRecord(Long id) {
        FuelRecord record = this.getById(id);
        if (record == null) return false;
        if ("1".equals(record.getStatus())) {
            throw new RuntimeException("该记录已经提交");
        }
        record.setStatus("1");
        boolean updated = this.updateById(record);
        if (updated && record.getFuelCardId() != null && record.getAmount() != null) {
            FuelCard card = fuelCardService.getById(record.getFuelCardId());
            if (card != null) {
                card.setCurrentBalance(card.getCurrentBalance().subtract(record.getAmount()));
                fuelCardService.updateById(card);
            }
        }
        return updated;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean revokeRecord(Long id) {
        FuelRecord record = this.getById(id);
        if (record == null) return false;
        if ("0".equals(record.getStatus())) {
            throw new RuntimeException("草稿状态无需撤回");
        }
        record.setStatus("0");
        boolean updated = this.updateById(record);
        if (updated && record.getFuelCardId() != null && record.getAmount() != null) {
            FuelCard card = fuelCardService.getById(record.getFuelCardId());
            if (card != null) {
                card.setCurrentBalance(card.getCurrentBalance().add(record.getAmount()));
                fuelCardService.updateById(card);
            }
        }
        return updated;
    }
}
src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.vehicle.mapper.VehicleCostMapper;
import com.ruoyi.vehicle.pojo.VehicleCost;
import com.ruoyi.vehicle.service.VehicleCostService;
import org.springframework.stereotype.Service;
@Service
public class VehicleCostServiceImpl extends ServiceImpl<VehicleCostMapper, VehicleCost> implements VehicleCostService {
}
src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.vehicle.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.vehicle.mapper.VehicleInfoMapper;
import com.ruoyi.vehicle.pojo.VehicleInfo;
import com.ruoyi.vehicle.service.VehicleInfoService;
import org.springframework.stereotype.Service;
@Service
public class VehicleInfoServiceImpl extends ServiceImpl<VehicleInfoMapper, VehicleInfo> implements VehicleInfoService {
}
src/main/resources/application-local.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /Users/mac/txkj/product-inventory/file
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: NEW
  # ä¸ªæŽ¨ Unipush é…ç½®
  getui:
    appId: PfjyAAE0FK64FaO1w2CMb1
    appKey: zTMb831OEL6J4GK1uE3Ob4
    masterSecret: K1GFtsv42v61tXGnF7SGE5
    domain: https://restapi.getui.cn/v2/
    # ç¦»çº¿æŽ¨é€ä½¿ç”¨çš„包名/组件名
    intentComponent: uni.app.UNI099A590/io.dcloud.PandoraEntry
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 9003
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# æ—¥å¿—配置
logging:
  level:
    com.ruoyi: warn
    org.springframework: warn
minio:
  endpoint: http://114.132.189.42/
  port: 7019
  secure: false
  accessKey: admin
  secretKey: 12345678
  preview-expiry: 24 # é¢„览地址默认24小时
  default-bucket: jxc
# ç”¨æˆ·é…ç½®
user:
  password:
    # å¯†ç æœ€å¤§é”™è¯¯æ¬¡æ•°
    maxRetryCount: 5
    # å¯†ç é”å®šæ—¶é—´ï¼ˆé»˜è®¤10分钟)
    lockTime: 10
# Spring配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/product-inventory-management-new-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: root@2026
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
        enabled: false
        url:
        username:
        password:
      # åˆå§‹è¿žæŽ¥æ•°
      initialSize: 5
      # æœ€å°è¿žæŽ¥æ± æ•°é‡
      minIdle: 10
      # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
      maxActive: 20
      # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´
      maxWait: 60000
      # é…ç½®è¿žæŽ¥è¶…æ—¶æ—¶é—´
      connectTimeout: 30000
      # é…ç½®ç½‘络超时时间
      socketTimeout: 60000
      # é…ç½®é—´éš”多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      minEvictableIdleTimeMillis: 300000
      # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å¤§ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # è®¾ç½®ç™½åå•,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # æŽ§åˆ¶å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç 
        login-username: ruoyi
        login-password: 123456
      filter:
        stat:
          enabled: true
          # æ…¢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
  # èµ„源信息
  messages:
    # å›½é™…化资源文件路径
    basename: i18n/messages
  # æ–‡ä»¶ä¸Šä¼ 
  servlet:
    multipart:
      # å•个文件大小
      max-file-size: 1GB
      # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å°
      max-request-size: 2GB
  # æœåŠ¡æ¨¡å—
  devtools:
    restart:
      # çƒ­éƒ¨ç½²å¼€å…³
      enabled: false
  # redis é…ç½®
  data:
    mongodb:
      uri: mongodb://114.132.189.42:9028/chat_memory_db
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      host: 127.0.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password: root@2026
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      timeout: 10s
      lettuce:
        pool:
          # è¿žæŽ¥æ± ä¸­çš„æœ€å°ç©ºé—²è¿žæŽ¥
          min-idle: 0
          # è¿žæŽ¥æ± ä¸­çš„æœ€å¤§ç©ºé—²è¿žæŽ¥
          max-idle: 8
          # è¿žæŽ¥æ± çš„æœ€å¤§æ•°æ®åº“连接数
          max-active: 8
          # #连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
  # Quartz定时任务配置(新增部分)
  quartz:
    job-store-type: jdbc  # ä½¿ç”¨æ•°æ®åº“存储
    jdbc:
      initialize-schema: never  # é¦–次运行时自动创建表结构,成功后改为never
      schema: classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql  # MySQL表结构脚本
    properties:
      org:
        quartz:
          scheduler:
            instanceName: RuoYiScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  # MySQL适配
            tablePrefix: qrtz_  # è¡¨åå‰ç¼€ï¼Œä¸Žè„šæœ¬ä¸€è‡´
            isClustered: false  # å•节点模式(集群需改为true)
            clusterCheckinInterval: 10000
            txIsolationLevelSerializable: true
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10  # çº¿ç¨‹æ± å¤§å°
            threadPriority: 5
            makeThreadsDaemons: true
          updateCheck: false  # å…³é—­ç‰ˆæœ¬æ£€æŸ¥
# token配置
token:
  # ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
  header: Authorization
  # ä»¤ç‰Œå¯†é’¥
  secret: xpAVjhCjQDaDB7mjPAzMDSbQWXNu2zYkTdDNUsPMS5Xx8QMmQVYN7n74eZrYJxDJ
  # ä»¤ç‰Œæœ‰æ•ˆæœŸï¼ˆé»˜è®¤30分钟)
  expireTime: 450
# MyBatis Plus配置
mybatis-plus:
  # æœç´¢æŒ‡å®šåŒ…别名   æ ¹æ®è‡ªå·±çš„项目来
  typeAliasesPackage: com.ruoyi.**.pojo
  # é…ç½®mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # åŠ è½½å…¨å±€çš„é…ç½®æ–‡ä»¶
  configLocation: classpath:mybatis/mybatis-config.xml
  global-config:
    enable-sql-runner: true
    db-config:
      id-type: auto
# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  supportMethodsArguments: true
  params: count=countSql
# Swagger配置
swagger:
  # æ˜¯å¦å¼€å¯swagger
  enabled: true
  # è¯·æ±‚前缀
  pathMapping: /dev-api
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
# ä»£ç ç”Ÿæˆ
gen:
  # ä½œè€…
  author: ruoyi
  # é»˜è®¤ç”ŸæˆåŒ…路径 system éœ€æ”¹æˆè‡ªå·±çš„æ¨¡å—名称 å¦‚ system monitor tool
  packageName: com.ruoyi.project.system
  # è‡ªåŠ¨åŽ»é™¤è¡¨å‰ç¼€ï¼Œé»˜è®¤æ˜¯true
  autoRemovePre: false
  # è¡¨å‰ç¼€ï¼ˆç”Ÿæˆç±»åä¸ä¼šåŒ…含表前缀,多个用逗号分隔)
  tablePrefix: sys_
  # æ˜¯å¦å…è®¸ç”Ÿæˆæ–‡ä»¶è¦†ç›–到本地(自定义路径),默认不允许
  allowOverwrite: false
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: /Users/mac/txkj/product-inventory/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /Users/mac/txkj/product-inventory/file/prod/uploads # æ­£å¼ç›®å½•
  path: /Users/mac/txkj/product-inventory/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://1.15.17.182:9049 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application.yml
@@ -6,7 +6,7 @@
  main:
    allow-circular-references: true
  profiles:
    active: dev
    active: local
# Pinecone å‘量数据库配置
pinecone:
src/main/resources/mapper/approve/FinReimbursementMapper.xml
@@ -37,10 +37,12 @@
    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
        select fin_reimbursement.*,
        fin_reimbursement_travel.start_time ,
        fin_reimbursement_travel.end_time
        fin_reimbursement_travel.end_time,
        sys_dept.dept_name as deptName
               from
                fin_reimbursement
            left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
            left join sys_dept on fin_reimbursement.dept_id = sys_dept.dept_id
        <where>
            <if test="ew.billNo != null and ew.billNo != ''">
                bill_no like concat('%',#{ew.billNo},'%')
@@ -55,8 +57,46 @@
                and create_time &gt;= #{ew.createTimeStart}
                and create_time &lt;= #{ew.createTimeEnd}
            </if>
            <if test="ew.deptId != null">
                and fin_reimbursement.dept_id = #{ew.deptId}
            </if>
            <if test="ew.expenseType != null and ew.expenseType != ''">
                and fin_reimbursement.expense_type = #{ew.expenseType}
            </if>
        </where>
        order by create_time desc
    </select>
    <select id="list" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
        select fin_reimbursement.*,
        fin_reimbursement_travel.start_time ,
        fin_reimbursement_travel.end_time,
        sys_dept.dept_name as deptName
               from
                fin_reimbursement
            left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
            left join sys_dept on fin_reimbursement.dept_id = sys_dept.dept_id
        <where>
            <if test="ew.billNo != null and ew.billNo != ''">
                bill_no like concat('%',#{ew.billNo},'%')
            </if>
            <if test="ew.applicantName != null and ew.applicantName != ''">
                and applicant_name like concat('%',#{ew.applicantName},'%')
            </if>
            <if test="ew.applicantCode != null and ew.applicantCode != ''">
                and applicant_code like concat('%',#{ew.applicantCode},'%')
            </if>
            <if test="ew.createTimeStart != null and ew.createTimeStart !='' and ew.createTimeEnd != null and ew.createTimeEnd != ''">
                and create_time &gt;= #{ew.createTimeStart}
                and create_time &lt;= #{ew.createTimeEnd}
            </if>
            <if test="ew.deptId != null">
                and fin_reimbursement.dept_id = #{ew.deptId}
            </if>
            <if test="ew.expenseType != null and ew.expenseType != ''">
                and fin_reimbursement.expense_type = #{ew.expenseType}
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>
src/main/resources/mapper/basic/CustomerMapper.xml
@@ -123,29 +123,175 @@
        </where>
        order by c.id desc
    </select>
    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
        select T1.customer_id,
               c.customer_name,
    <select id="projectTransactions" resultType="com.ruoyi.sales.vo.ProjectTransactionsVo">
        select T1.project_id AS projectId,
               IFNULL(p.title, '未分类') AS projectName,
               p.no AS projectNo,
               p.status AS status,
               T1.contractAmounts AS contractAmounts,
               IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
               T1.contractAmounts - IFNULL(T2.receiptPaymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount,
               IFNULL(T4.returnAmount, 0) AS returnAmount
        from (select customer_id, sum(contract_amount) as contractAmounts from sales_ledger group by customer_id) T1
        left join (select customer_id, sum(collection_amount) as receiptPaymentAmount from account_sales_collection group by customer_id) T2 on T1.customer_id = T2.customer_id
               IFNULL(T4.returnAmount, 0) AS returnAmount,
               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
               IFNULL(T6.contractCount, 0) AS contractCount,
               IFNULL(T6.customerCount, 0) AS customerCount,
               IFNULL(T6.overdueCount, 0) AS overdueCount
        from (select IFNULL(project_id, -1) as project_id, sum(contract_amount) as contractAmounts from sales_ledger group by IFNULL(project_id, -1)) T1
        left join project_management_info p on T1.project_id = p.id
        left join (
            select
                sl.customer_id,
                IFNULL(sl.project_id, -1) as project_id,
                sum(ascc.collection_amount) as receiptPaymentAmount
            from account_sales_collection ascc
            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
            WHERE sor.record_type='13' and sor.approval_status=1
            group by IFNULL(sl.project_id, -1)
        ) T2 on T1.project_id = T2.project_id
        left join (
            select
                IFNULL(sl.project_id, -1) as project_id,
                sum(rm.refund_amount) as returnAmount
            from return_management rm
            left join shipping_info si on rm.shipping_id = si.id
            left join sales_ledger sl on si.sales_ledger_id = sl.id
            where rm.status=1
            group by sl.customer_id
        ) T4 on T4.customer_id=T1.customer_id
            group by IFNULL(sl.project_id, -1)
        ) T4 on T4.project_id = T1.project_id
        left join (
            select
                IFNULL(sl.project_id, -1) as project_id,
                sum(asi.tax_inclusive_price) as invoiceAmount
            from account_sales_invoice asi
            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            left join sales_ledger sl on s.sales_ledger_id = sl.id
            where asi.status=0
            group by IFNULL(sl.project_id, -1)
        ) T5 on T5.project_id = T1.project_id
        left join (
            select IFNULL(sl.project_id, -1) as project_id,
                   count(sl.id) as contractCount,
                   count(distinct sl.customer_id) as customerCount,
                   sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(T1_rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end) as overdueCount
            from sales_ledger sl
            left join (
                select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
                from account_sales_collection ascc
                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
                left join shipping_info s on sor.record_id = s.id
                LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
                WHERE sor.record_type='13' and sor.approval_status=1
                group by sl_in.id
            ) T1_rcpt on T1_rcpt.id = sl.id
            group by IFNULL(sl.project_id, -1)
        ) T6 on T6.project_id = T1.project_id
        <where>
            <if test="projectName!=null and projectName!=''">
                AND IFNULL(p.title, '未分类') LIKE CONCAT('%', #{projectName}, '%')
            </if>
            <if test="customerName!=null and customerName!=''">
                AND EXISTS (
                    select 1 from sales_ledger sl2
                    left join customer c on sl2.customer_id = c.id
                    where IFNULL(sl2.project_id, -1) = T1.project_id
                    and c.customer_name like concat('%', #{customerName}, '%')
                )
            </if>
            <if test="contractName!=null and contractName!=''">
                AND EXISTS (
                    select 1 from sales_ledger sl3
                    where IFNULL(sl3.project_id, -1) = T1.project_id
                    and sl3.sales_contract_no like concat('%', #{contractName}, '%')
                )
            </if>
        </where>
        order by CASE WHEN T1.project_id = -1 THEN 1 ELSE 0 END DESC, T1.project_id desc
    </select>
    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
        select T1.customer_id,
               T1.project_id AS projectId,
               c.customer_name,
               T1.contractAmounts AS contractAmounts,
               IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
               T1.contractAmounts - IFNULL(T2.receiptPaymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount,
               IFNULL(T4.returnAmount, 0) AS returnAmount,
               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
               IFNULL(T6.contractCount, 0) AS contractCount,
               IFNULL(T6.overdueCount, 0) AS overdueCount
        from (
            select customer_id, IFNULL(project_id, -1) as project_id, sum(contract_amount) as contractAmounts
            from sales_ledger
            <where>
                <if test="projectId!=null">
                    and IFNULL(project_id, -1) = #{projectId}
                </if>
            </where>
            group by customer_id, IFNULL(project_id, -1)
        ) T1
        left join (
            select
                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
                sum(ascc.collection_amount) as receiptPaymentAmount
            from account_sales_collection ascc
            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
            WHERE sor.record_type='13' and sor.approval_status=1
            group by sl.customer_id, IFNULL(sl.project_id, -1)
        ) T2 on T1.customer_id = T2.customer_id and T1.project_id = T2.project_id
        left join (
            select
                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
                sum(rm.refund_amount) as returnAmount
            from return_management rm
            left join shipping_info si on rm.shipping_id = si.id
            left join sales_ledger sl on si.sales_ledger_id = sl.id
            where rm.status=1
            group by sl.customer_id, IFNULL(sl.project_id, -1)
        ) T4 on T4.customer_id = T1.customer_id and T4.project_id = T1.project_id
        left join (
            select
                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
                sum(asi.tax_inclusive_price) as invoiceAmount
            from account_sales_invoice asi
            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            left join sales_ledger sl on s.sales_ledger_id = sl.id
            where asi.status=0
            group by sl.customer_id, IFNULL(sl.project_id, -1)
        ) T5 on T5.customer_id = T1.customer_id and T5.project_id = T1.project_id
        left join (
            select sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
                   count(sl.id) as contractCount,
                   sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(T1_rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end) as overdueCount
            from sales_ledger sl
            left join (
                select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
                from account_sales_collection ascc
                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
                left join shipping_info s on sor.record_id = s.id
                LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
                WHERE sor.record_type='13' and sor.approval_status=1
                group by sl_in.id
            ) T1_rcpt on T1_rcpt.id = sl.id
            group by sl.customer_id, IFNULL(sl.project_id, -1)
        ) T6 on T6.customer_id = T1.customer_id and T6.project_id = T1.project_id
        left join customer c on T1.customer_id = c.id
        <where>
            <if test="customerName!=null and customerName!=''">
                AND c.customer_name LIKE CONCAT('%', #{customerName}, '%')
            </if>
            <if test="contractName!=null and contractName!=''">
                AND EXISTS (
                    select 1 from sales_ledger sl3
                    where sl3.customer_id = T1.customer_id
                    and IFNULL(sl3.project_id, -1) = T1.project_id
                    and sl3.sales_contract_no like concat('%', #{contractName}, '%')
                )
            </if>
        </where>
        order by T1.customer_id desc
@@ -158,7 +304,12 @@
               sl.contract_amount AS contract_amount,
               IFNULL(T1.receiptPaymentAmount, 0) AS receiptPaymentAmount,
               sl.contract_amount - IFNULL(T1.receiptPaymentAmount, 0) - IFNULL(T3.returnAmount, 0) AS receiptableAmount,
               IFNULL(T3.returnAmount, 0) AS returnAmount
               IFNULL(T3.returnAmount, 0) AS returnAmount,
               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
               CASE
                   WHEN DATEDIFF(NOW(), sl.execution_date) > 30 AND IFNULL(T1.receiptPaymentAmount, 0) &lt; sl.contract_amount THEN 1
                   ELSE 0
               END AS isPaymentTimeout
        from sales_ledger sl
        left join (
            select
@@ -170,7 +321,7 @@
            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
            WHERE sor.record_type='13'
              and sor.approval_status=1
            group by  sl.id
            group by sl.id
        )T1 on T1.id = sl.id
        left join (
            select sl.id,
@@ -181,7 +332,43 @@
            where rm.status=1
            group by sl.id
        )T3 on T3.id = sl.id
        left join (
            select
                sl.id,
                sum(asi.tax_inclusive_price) as invoiceAmount
            from account_sales_invoice asi
            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            left join sales_ledger sl on s.sales_ledger_id = sl.id
            where asi.status=0
            group by sl.id
        ) T5 on T5.id = sl.id
        where sl.customer_id = #{customerId}
        <if test="projectId!=null">
            and IFNULL(sl.project_id, -1) = #{projectId}
        </if>
        <if test="contractName!=null and contractName!=''">
            and sl.sales_contract_no like concat('%', #{contractName}, '%')
        </if>
        order by sl.id desc
    </select>
    <select id="transactionsSummary" resultType="java.util.Map">
        select count(distinct IFNULL(sl.project_id, -1)) as totalProjects,
               count(distinct sl.customer_id) as totalCustomers,
               count(sl.id) as totalContracts,
               IFNULL(sum(sl.contract_amount), 0) as totalAmount,
               IFNULL(sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end), 0) as totalOverdueCount
        from sales_ledger sl
        left join (
            select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
            from account_sales_collection ascc
            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
            left join shipping_info s on sor.record_id = s.id
            LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
            WHERE sor.record_type='13' and sor.approval_status=1
            group by sl_in.id
        ) rcpt on rcpt.id = sl.id
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.collaborativeApproval.mapper.BroadbandCommunicationMapper">
</mapper>
src/main/resources/mapper/device/DeviceLedgerMapper.xml
@@ -8,6 +8,16 @@
    <select id="queryPage" resultType="com.ruoyi.device.dto.DeviceLedgerDto">
        SELECT
        dl.id,
        dl.unique_code,
        dl.factory_number,
        dl.manufacturer,
        dl.original_asset_value,
        dl.purchase_date,
        dl.activation_date,
        dl.technical_parameters,
        dl.main_purpose,
        dl.responsible_person,
        dl.filing_date,
        dl.device_name,
        dl.device_model,
        dl.supplier_name,
@@ -35,6 +45,11 @@
        FROM device_ledger dl
        left join sys_user su on dl.create_user = su.user_id
        <where>
            <!-- è®¾å¤‡ç¼–号 -->
            <if test="deviceLedger.uniqueCode != null and deviceLedger.uniqueCode != ''">
                AND unique_code LIKE CONCAT('%', #{deviceLedger.uniqueCode}, '%')
            </if>
            <!-- è®¾å¤‡åç§° -->
            <if test="deviceLedger.deviceName != null and deviceLedger.deviceName != ''">
                AND device_name LIKE CONCAT('%', #{deviceLedger.deviceName}, '%')
src/main/resources/mapper/device/DeviceRepairMapper.xml
@@ -18,6 +18,10 @@
                 dr.acceptance_time,
                 dr.acceptance_remark,
                 dr.status,
                 dr.repair_type,
                 dr.reason_analysis,
                 dr.handling_measures,
                 dr.spare_parts_ids,
                dr.create_time,
                dr.update_time,
                dr.create_user,
@@ -67,6 +71,10 @@
               dr.acceptance_time,
               dr.acceptance_remark,
               dr.status,
               dr.repair_type,
               dr.reason_analysis,
               dr.handling_measures,
               dr.spare_parts_ids,
               dr.create_time,
               dr.update_time,
               dr.create_user,
src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.device.mapper.DeviceStatusChangeRecordMapper">
</mapper>
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
@@ -13,7 +13,15 @@
        t2.model,
        t1.valid,
        t1.record_date,
        t1.entry_date
        t1.entry_date,
        t1.traceability_type,
        t1.implementing_agency,
        t1.certificate_number,
        t1.core_result,
        t1.result_confirmation,
        t1.confirmer_id,
        t1.confirmer_name,
        t1.valid_until
        FROM
        measuring_instrument_ledger_record t1
        left join measuring_instrument_ledger t2 on t1.measuring_instrument_ledger_id = t2.id
@@ -44,7 +52,15 @@
        t2.model,
        t1.valid,
        t1.record_date,
        t1.entry_date
        t1.entry_date,
        t1.traceability_type,
        t1.implementing_agency,
        t1.certificate_number,
        t1.core_result,
        t1.result_confirmation,
        t1.confirmer_id,
        t1.confirmer_name,
        t1.valid_until
        FROM
        measuring_instrument_ledger_record t1
        left join measuring_instrument_ledger t2 on t1.measuring_instrument_ledger_id = t2.id
src/main/resources/mapper/projectManagement/InfoMapper.xml
@@ -49,17 +49,22 @@
        remark,attachment
    </sql>
    <resultMap id="ListInfoVoMap" type="com.ruoyi.projectManagement.vo.ListInfoVo">
        <result column="plan_stage"
                property="planStage"
                typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
    </resultMap>
    <select id="searchListInfo" resultMap="ListInfoVoMap">
        select t1.*, t2.name as project_management_plan_name,t3.title as projectManagementInfoParentName
        select t1.*, t2.name as project_management_plan_name,t3.title as projectManagementInfoParentName,
               IFNULL(t4.sales_contract_count, 0) as salesContractCount
        from project_management_info as t1
        left join project_management_plan as t2 on t1.project_management_plan_id = t2.id
        left join project_management_info as t3 on t1.project_management_info_parent_id = t3.id
        left join (
            SELECT project_id, COUNT(DISTINCT sales_contract_no) AS sales_contract_count
            FROM sales_ledger
            WHERE sales_contract_no IS NOT NULL AND sales_contract_no != ''
            GROUP BY project_id
        ) as t4 on t1.id = t4.project_id
        where t1.is_delete = 0
        <if test="vo.noOrName != null and vo.noOrName != ''">
            and ( t1.no like concat('%', #{vo.noOrName}, '%') OR t1.title like concat('%', #{vo.noOrName}, '%') )
src/main/resources/mapper/sales/BiddingManagementMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.sales.mapper.BiddingManagementMapper">
    <resultMap type="BiddingManagement" id="BiddingManagementResult">
        <result property="id"    column="id"    />
        <result property="projectId"    column="project_id"    />
        <result property="projectName"    column="project_name"    />
        <result property="bidDocName"    column="bid_doc_name"    />
        <result property="tenderUnit"    column="tender_unit"    />
        <result property="bidUnit"    column="bid_unit"    />
        <result property="tenderDate"    column="tender_date"    />
        <result property="bidDate"    column="bid_date"    />
        <result property="bidDocFee"    column="bid_doc_fee"    />
        <result property="bidDocFeeStatus"    column="bid_doc_fee_status"    />
        <result property="bidDocFeeStatusChangeDate"    column="bid_doc_fee_status_change_date"    />
        <result property="bidDeposit"    column="bid_deposit"    />
        <result property="bidResult"    column="bid_result"    />
        <result property="tenantId"    column="tenant_id"    />
        <result property="deptId"    column="dept_id"    />
        <result property="delFlag"    column="del_flag"    />
        <result property="createBy"    column="create_by"    />
        <result property="createTime"    column="create_time"    />
        <result property="updateBy"    column="update_by"    />
        <result property="updateTime"    column="update_time"    />
        <result property="remark"    column="remark"    />
    </resultMap>
    <sql id="selectBiddingManagementVo">
        select id, project_id, project_name, bid_doc_name, tender_unit, bid_unit, tender_date, bid_date, bid_doc_fee, bid_doc_fee_status, bid_doc_fee_status_change_date, bid_deposit, bid_result, tenant_id, dept_id, del_flag, create_by, create_time, update_by, update_time, remark from bidding_management
    </sql>
</mapper>
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -19,6 +19,16 @@
        T1.sales_contract_no,
        T1.customer_contract_no,
        T1.project_name,
        T1.project_id,
        T1.year,
        T1.signing_unit_id,
        T1.office_id,
        T1.group_leader_id,
        T1.is_invoice,
        T1.bid_price,
        T1.is_bid,
        T1.unbid_reason,
        T1.contract_original_return_date,
        T1.entry_date,
        T1.salesman,
        T1.customer_id,
@@ -37,7 +47,10 @@
        LEFT JOIN sys_user T2 ON T1.entry_person = T2.user_id
        <where>
            <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
                T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
                AND T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
            </if>
            <if test="salesLedgerDto.projectId != null">
                AND T1.project_id = #{salesLedgerDto.projectId}
            </if>
        </where>
    </select>
@@ -47,6 +60,16 @@
        T1.sales_contract_no,
        T1.customer_contract_no,
        T1.project_name,
        T1.project_id,
        T1.year,
        T1.signing_unit_id,
        T1.office_id,
        T1.group_leader_id,
        T1.is_invoice,
        T1.bid_price,
        T1.is_bid,
        T1.unbid_reason,
        T1.contract_original_return_date,
        T1.entry_date,
        T1.salesman,
        T1.customer_id,
@@ -80,6 +103,9 @@
            <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
                AND T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
            </if>
            <if test="salesLedgerDto.projectId != null">
                AND T1.project_id = #{salesLedgerDto.projectId}
            </if>
            <if test="salesLedgerDto.customerContractNo != null and salesLedgerDto.customerContractNo !='' ">
                AND T1.customer_contract_no LIKE CONCAT('%',#{salesLedgerDto.customerContractNo},'%')
            </if>
src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -4,18 +4,22 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.sales.mapper.SalesQuotationMapper">
    <select id="listPage" resultType="com.ruoyi.sales.dto.SalesQuotationDto">
        SELECT t1.*
        SELECT t1.*, p.title as projectName
        FROM sales_quotation t1
        LEFT JOIN project_management_info p ON t1.project_id = p.id
        <where>
            <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' ">
                AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%')
            </if>
            <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' ">
                AND t1.customer = #{salesQuotationDto.customer}
                AND t1.customer LIKE CONCAT('%',#{salesQuotationDto.customer},'%')
            </if>
            <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
                AND t1.status = #{salesQuotationDto.status}
            </if>
            <if test="salesQuotationDto.projectId != null">
                AND t1.project_id = #{salesQuotationDto.projectId}
            </if>
        </where>
        order by t1.id desc
    </select>
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -18,13 +18,22 @@
        s.create_user,
        s.update_user,
        s.tenant_id,
        s.shipping_document,
        s.is_reconciled,
        s.statement_archiving_status,
        s.is_settled,
        s.settlement_archiving_status,
        s.is_invoiced,
        s.project_id,
        sl.project_name as projectName,
        sl.sales_contract_no,
        pm.model as specification_model,
        pm.unit,
        p.product_name,
        sl.customer_name,
        spd.totalQuantity,
        sor.outboundBatches
        sor.outboundBatches,
        rm_agg.returnNos
        FROM shipping_info s
        LEFT JOIN (select shipping_info_id,sum(quantity) totalQuantity from shipping_product_detail GROUP BY shipping_info_id) spd ON spd.shipping_info_id = s.id
        LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
@@ -35,6 +44,9 @@
                   from stock_out_record
                   where record_type='13'and approval_status=1
                   group by record_id)sor on sor.record_id= s.id
        left join (select shipping_id, GROUP_CONCAT(DISTINCT return_no SEPARATOR ',') AS returnNos
                   from return_management
                   group by shipping_id) rm_agg on rm_agg.shipping_id = s.id
        WHERE 1=1
        <if test="req.salesContractNo != null and req.salesContractNo != ''">
            AND sl.sales_contract_no LIKE CONCAT('%',#{req.salesContractNo},'%')
@@ -47,6 +59,15 @@
        </if>
        <if test="req.expressNumber != null and req.expressNumber != ''">
            AND s.express_number LIKE CONCAT('%',#{req.expressNumber},'%')
        </if>
        <if test="req.projectId != null">
            AND s.project_id = #{req.projectId}
        </if>
        <if test="req.beginShippingDate != null and req.beginShippingDate != ''">
            AND DATE(s.shipping_date) &gt;= DATE(#{req.beginShippingDate})
        </if>
        <if test="req.endShippingDate != null and req.endShippingDate != ''">
            AND DATE(s.shipping_date) &lt;= DATE(#{req.endShippingDate})
        </if>
        order by create_time DESC
    </select>
@@ -61,10 +82,41 @@
            s.create_user,
            s.update_user,
            s.tenant_id,
            s.shipping_document,
            s.is_reconciled,
            s.statement_archiving_status,
            s.is_settled,
            s.settlement_archiving_status,
            s.is_invoiced,
            s.project_id,
            sl.project_name as projectName,
            sl.sales_contract_no,
            sl.customer_name
        FROM shipping_info s
                 LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
        WHERE 1=1
        <if test="req.salesContractNo != null and req.salesContractNo != ''">
            AND sl.sales_contract_no LIKE CONCAT('%',#{req.salesContractNo},'%')
        </if>
        <if test="req.shippingCarNumber != null and req.shippingCarNumber != ''">
            AND s.shipping_car_number LIKE CONCAT('%',#{req.shippingCarNumber},'%')
        </if>
        <if test="req.shippingNo != null and req.shippingNo != ''">
            AND s.shipping_no LIKE CONCAT('%',#{req.shippingNo},'%')
        </if>
        <if test="req.expressNumber != null and req.expressNumber != ''">
            AND s.express_number LIKE CONCAT('%',#{req.expressNumber},'%')
        </if>
        <if test="req.projectId != null">
            AND s.project_id = #{req.projectId}
        </if>
        <if test="req.beginShippingDate != null and req.beginShippingDate != ''">
            AND DATE(s.shipping_date) &gt;= DATE(#{req.beginShippingDate})
        </if>
        <if test="req.endShippingDate != null and req.endShippingDate != ''">
            AND DATE(s.shipping_date) &lt;= DATE(#{req.endShippingDate})
        </if>
        order by create_time DESC
    </select>
    <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.vo.ShippingProductVo">
        SELECT distinct
src/main/resources/mapper/vehicle/FuelCardMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.vehicle.mapper.FuelCardMapper">
    <select id="selectFuelCardPage" resultType="com.ruoyi.vehicle.pojo.FuelCard">
        SELECT c.*, d.dept_name as deptName
        FROM p_fuel_card c
        LEFT JOIN sys_dept d ON c.dept_id = d.dept_id
        WHERE c.del_flag = '0'
        <if test="query.cardNumber != null and query.cardNumber != ''">
            AND c.card_number LIKE concat('%', #{query.cardNumber}, '%')
        </if>
        <if test="query.deptId != null">
            AND (c.dept_id = #{query.deptId} OR c.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
        </if>
        ORDER BY c.id DESC
    </select>
</mapper>
src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.vehicle.mapper.FuelCardRechargeMapper">
    <select id="selectRechargePage" resultType="com.ruoyi.vehicle.pojo.FuelCardRecharge">
        SELECT r.*, u.nick_name as createUserName
        FROM p_fuel_card_recharge r
        LEFT JOIN sys_user u ON r.create_user = u.user_id
        WHERE r.del_flag = '0'
        <if test="cardId != null">
            AND r.card_id = #{cardId}
        </if>
        ORDER BY r.recharge_date DESC, r.create_time DESC
    </select>
</mapper>
src/main/resources/mapper/vehicle/FuelRecordMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.vehicle.mapper.FuelRecordMapper">
    <select id="selectFuelRecordPage" resultType="com.ruoyi.vehicle.pojo.FuelRecord">
        SELECT r.*, d.dept_name as deptName, v.plate_number as plateNumber, c.card_number as cardNumber
        FROM p_fuel_record r
        LEFT JOIN sys_dept d ON r.dept_id = d.dept_id
        LEFT JOIN p_vehicle_info v ON r.vehicle_id = v.id
        LEFT JOIN p_fuel_card c ON r.fuel_card_id = c.id
        WHERE r.del_flag = '0'
        <if test="query.vehicleId != null">
            AND r.vehicle_id = #{query.vehicleId}
        </if>
        <if test="query.fuelCardId != null">
            AND r.fuel_card_id = #{query.fuelCardId}
        </if>
        <if test="query.deptId != null">
            AND (r.dept_id = #{query.deptId} OR r.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
        </if>
        <if test="beginDate != null and beginDate != '' and endDate != null and endDate != ''">
            AND r.fuel_date BETWEEN #{beginDate} AND #{endDate}
        </if>
        <if test="query.status != null and query.status != ''">
            AND r.status = #{query.status}
        </if>
ORDER BY r.fuel_date DESC
    </select>
</mapper>
src/main/resources/mapper/vehicle/VehicleCostMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.vehicle.mapper.VehicleCostMapper">
</mapper>
src/main/resources/mapper/vehicle/VehicleInfoMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.vehicle.mapper.VehicleInfoMapper">
    <select id="selectVehiclePage" resultType="com.ruoyi.vehicle.pojo.VehicleInfo">
        SELECT v.*, d.dept_name as deptName, u.nick_name as userName
        FROM p_vehicle_info v
        LEFT JOIN sys_dept d ON v.dept_id = d.dept_id
        LEFT JOIN sys_user u ON v.user_id = u.user_id
        WHERE v.del_flag = '0'
        <if test="query.plateNumber != null and query.plateNumber != ''">
            AND v.plate_number LIKE concat('%', #{query.plateNumber}, '%')
        </if>
        <if test="query.deptId != null">
            AND (v.dept_id = #{query.deptId} OR v.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
        </if>
        <if test="query.vehicleStatus != null and query.vehicleStatus != ''">
            AND v.vehicle_status = #{query.vehicleStatus}
        </if>
        ORDER BY
        CASE WHEN
            (v.inspection_expire_date IS NOT NULL AND DATEDIFF(v.inspection_expire_date, NOW()) &lt;= 30)
            OR
            (v.insurance_expire_date IS NOT NULL AND DATEDIFF(v.insurance_expire_date, NOW()) &lt;= 30)
        THEN 0 ELSE 1 END,
        v.create_time DESC
    </select>
</mapper>
update_fin_reimbursement.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
ALTER TABLE fin_reimbursement ADD COLUMN payer VARCHAR(50) DEFAULT NULL COMMENT '付款方' AFTER detail_total_amount;