yuan
2 天以前 47806d9e390ee00e1d29ad1da8c1aa908882a758
用河南鹤壁代码覆盖山西长治
已修改280个文件
已添加26个文件
13899 ■■■■■ 文件已修改
.gitignore 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.vscode/settings.json 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/RuoYiApplication.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/bean/vo/purchase/PurchaseReturnVo.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/bean/vo/sales/SalesOutboundVo.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/bean/vo/sales/SalesReturnVo.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/controller/sales/AccountInvoiceApplicationController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/mapper/sales/AccountInvoiceApplicationMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/sales/AccountInvoiceApplicationServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/impl/sales/AccountSalesCollectionServiceImpl.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/service/sales/AccountInvoiceApplicationService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/aftersalesservice/dto/AfterSalesServiceExeclDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/aftersalesservice/pojo/AfterSalesService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/aftersalesservice/service/impl/AfterSalesServiceServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApproveNodeController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApproveProcessConfigNodeController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApproveProcessController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/HolidaySettingsController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/KnowledgeBaseController.java 146 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/NotificationManagementController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/RpaProcessAutomationController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/KnowledgeBase.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java 132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java 363 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/SupplierManageController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/SupplierManageFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/SupplierServiceImpl.java 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/vo/CustomerVo.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/vo/ProductModelVo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/DutyPlanController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/MeetingController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/NoticeTypeController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/StaffContactsPersonalController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/vo/SearchMeetingUseVo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/AmountUtils.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/DateUtils.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/OrderUtils.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/customervisits/service/impl/CustomerVisitsServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceDefectRecordController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceLedgerController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/controller/DeviceRepairController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/framework/config/ApplicationConfig.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/controller/HomeController.java 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/QrCodeController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/QrCodeScanRecordController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/officesupplies/controller/OfficeSuppliesController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/other/controller/PdaVersionController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/GasTankWarningController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/InboundManagementController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementPlanController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementPriceManagementController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/controller/ReturnSaleProductController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/pojo/CustomStorage.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanImportDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProcessRouteStatusVo.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderProcessTaskVo.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionBomStructureController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderPickController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingOperationParamController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/enums/ProductOrderStatusEnum.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionOrder.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java 142 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/common/CommonController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/domain/SysUser.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/tool/swagger/TestController.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/controller/InfoController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/controller/PlanController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/controller/RolesController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/projectManagement/pojo/Roles.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderHasAllInfoDto.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrders.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/ProcurementBusinessSummaryServiceImpl.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 746 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityInspectFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityTestStandardParamController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityUnqualified.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/IQualityInspectService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardBindingServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeCertificationController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeCertificationFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeContingencyPlanController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeHazardController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeHazardRecordController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeHiddenController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeHiddenFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/PaymentShippingController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesQuotationController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalespersonManagementController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesTrendDto.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/PaymentShipping.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/MetricStatisticsServiceImpl.java 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 177 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/PersonalShiftController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffLeaveController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffSalaryMainController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/SchemeApplicableStaffMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/StaffSalaryMainMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/StaffSalaryDetail.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/StaffSalaryMainService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffSalaryMainServiceImpl.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInRecordService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInventoryService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockOutRecordService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyBomStructureController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingOperationController.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingOperationParamController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/pojo/TechnologyParam.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/pojo/TechnologyRouting.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/service/TechnologyOperationService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/service/impl/TechnologyOperationServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/warehouse/controller/DocumentationFileController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-btyxpro.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-ddgw.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dtzg.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dyzn.yml 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hyjc.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-hylq.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jhjm.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-jhsn.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-lmjn.yml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-qxy.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-sdks.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-sdtx.yml 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-skjs.yml 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-tddq.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xcdq.yml 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xhzg.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xlzn.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-xswh.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-yjyy.yml 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-ytjz.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zqsy.yml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zxzn.yml 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/financial-agent-prompt.txt 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/purchase/AccountPaymentApplicationMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/purchase/AccountPurchaseInvoiceMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/purchase/AccountPurchasePaymentMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/sales/AccountInvoiceApplicationMapper.xml 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/sales/AccountSalesCollectionMapper.xml 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/account/sales/AccountSalesInvoiceMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/aftersalesservice/AfterSalesNearExpiryMapper.xml 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/aftersalesservice/AfterSalesServiceMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApproveProcessMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/KnowledgeBaseMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/CustomerMapper.xml 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/ProductModelMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/basic/SupplierManageMapper.xml 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/NoticeMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/RulesRegulationsManagementMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/device/DeviceRepairMapper.xml 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/measuringinstrumentledger/SparePartsMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionAccountMapper.xml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionOrderMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionPlanMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductInputMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductOutputMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml 89 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityInspectMapper.xml 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityTestStandardMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesQuotationMapper.xml 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/ShippingInfoMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/PersonalShiftMapper.xml 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/SchemeApplicableStaffMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffLeaveMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffOnJobMapper.xml 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffSalaryMainMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInRecordMapper.xml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockOutRecordMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysDeptMapper.xml 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysDictTypeMapper.xml 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysNoticeMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysPostMapper.xml 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysRoleMapper.xml 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysUserMapper.xml 612 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/technology/TechnologyOperationMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/technology/TechnologyOperationParamMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/warehouse/DocumentationBorrowManagementMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/warehouse/DocumentationMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/warehouse/DocumentationReturnManagementMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/warehouse/WarehouseGoodsShelvesRowcolMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -6,13 +6,11 @@
!gradle/wrapper/gradle-wrapper.jar
claude.md
target/
src/test/
test/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# Claude Code
.claude/
# IDE
### STS ###
.apt_generated
.vscode/settings.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
{
    "java.compile.nullAnalysis.mode": "disabled"
}
pom.xml
@@ -53,27 +53,10 @@
        <getui-sdk.version>1.0.7.0</getui-sdk.version>
        <jsqlparser.version>4.9</jsqlparser.version>
        <thumbnailator.version>0.4.20</thumbnailator.version>
        <langchain4j.version>1.0.0-beta3</langchain4j.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <!-- ruoyi-springboot2 / swagger knife4j é…ç½® -->
@@ -133,10 +116,7 @@
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <!-- pool å¯¹è±¡æ±  -->
        <dependency>
@@ -144,38 +124,7 @@
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-pinecone</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-mcp</artifactId>
        </dependency>
        <!-- Mysql驱动包 -->
        <dependency>
src/main/java/com/ruoyi/RuoYiApplication.java
@@ -7,11 +7,11 @@
/**
 * å¯åŠ¨ç¨‹åº
 *
 *
 * @author ruoyi
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableScheduling
//@EnableScheduling
public class RuoYiApplication
{
    public static void main(String[] args)
src/main/java/com/ruoyi/account/bean/vo/purchase/PurchaseReturnVo.java
@@ -29,22 +29,6 @@
    @Excel(name = "关联入库单号")
    private String inboundBatches;
    @Schema(description = "发货类型")
    @Excel(name = "发货类型")
    private Integer shippingType;
    @Schema(description = "发货车牌号")
    @Excel(name = "发货车牌号")
    private String truckPlateNo;
    @Schema(description = "快递公司")
    @Excel(name = "快递公司")
    private String expressCompany;
    @Schema(description = "快递单号")
    @Excel(name = "快递单号")
    private String expressNo;
    @Schema(description = "退货日期")
    @Excel(name = "退货日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
src/main/java/com/ruoyi/account/bean/vo/sales/SalesOutboundVo.java
@@ -1,6 +1,5 @@
package com.ruoyi.account.bean.vo.sales;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
@@ -53,8 +52,4 @@
    @Schema(description = "销售订单号")
    @Excel(name = "销售订单号")
    private String salesContractNo;
    @Schema(description = "已收款金额")
    @ExcelIgnore
    private BigDecimal amountReceived;
}
src/main/java/com/ruoyi/account/bean/vo/sales/SalesReturnVo.java
@@ -29,22 +29,6 @@
    @Excel(name = "关联发货单号")
    private String shippingNo;
    @Schema(description = "发货类型")
    @Excel(name = "发货类型")
    private Integer shippingType;
    @Schema(description = "发货车牌号")
    @Excel(name = "发货车牌号")
    private String truckPlateNo;
    @Schema(description = "快递公司")
    @Excel(name = "快递公司")
    private String expressCompany;
    @Schema(description = "快递单号")
    @Excel(name = "快递单号")
    private String expressNo;
    @Schema(description = "退货日期")
    @Excel(name = "退货日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
src/main/java/com/ruoyi/account/controller/sales/AccountInvoiceApplicationController.java
@@ -16,7 +16,6 @@
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
 * <p>
@@ -40,13 +39,6 @@
    public R<IPage<AccountInvoiceApplicationVo>> listPageAccountInvoiceApplication(Page page, AccountInvoiceApplicationDto accountInvoiceApplicationDto) {
        IPage<AccountInvoiceApplicationVo> listPage = accountInvoiceApplicationService.listPageAccountInvoiceApplication(page,accountInvoiceApplicationDto);
        return R.ok(listPage);
    }
    @GetMapping("/getAccountInvoiceApplicationList")
    @Log(title = "根据客户id查询开票申请台账", businessType = BusinessType.OTHER)
    @Operation(summary = "财务管理--开票申请台账")
    public R<List<AccountInvoiceApplicationVo>> getAccountInvoiceApplicationList(@RequestParam Integer customerId){
        return R.ok(accountInvoiceApplicationService.getAccountInvoiceApplicationList(customerId));
    }
    @GetMapping("/getOutboundBatchesByCustomer")
src/main/java/com/ruoyi/account/mapper/sales/AccountInvoiceApplicationMapper.java
@@ -29,6 +29,4 @@
    //判断该出库记录是否有开票申请
    boolean existsByStockOutRecordId(@Param("stockOutRecordIds") List<Long> stockOutRecordIds);
    List<AccountInvoiceApplicationVo> selectAccountInvoiceApplicationList(@Param("customerId") Integer customerId);
}
src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java
@@ -174,7 +174,7 @@
        if (accountSubjectDto != null && accountSubjectDto.getStatus() != null) {
            queryWrapper.eq(AccountSubject::getStatus, accountSubjectDto.getStatus());
        }
        queryWrapper.orderByDesc(AccountSubject::getSubjectCode).orderByDesc(AccountSubject::getId);
        queryWrapper.orderByAsc(AccountSubject::getSubjectCode).orderByAsc(AccountSubject::getId);
        return queryWrapper;
    }
@@ -296,8 +296,8 @@
        }
        List<AccountSubject> sortedSubjects = new ArrayList<>(subjects);
        sortedSubjects.sort(Comparator
                .comparing(AccountSubject::getSubjectCode, Comparator.nullsFirst(String::compareTo)).reversed()
                .thenComparing(AccountSubject::getId, Comparator.nullsFirst(Long::compareTo)).reversed());
                .comparing(AccountSubject::getSubjectCode, Comparator.nullsLast(String::compareTo))
                .thenComparing(AccountSubject::getId, Comparator.nullsLast(Long::compareTo)));
        Map<Long, AccountSubjectVo> subjectVoMap = new LinkedHashMap<>();
        for (AccountSubject subject : sortedSubjects) {
src/main/java/com/ruoyi/account/service/impl/sales/AccountInvoiceApplicationServiceImpl.java
@@ -91,11 +91,6 @@
        return removeBatchByIds(ids);
    }
    @Override
    public List<AccountInvoiceApplicationVo> getAccountInvoiceApplicationList(Integer customerId) {
        return accountInvoiceApplicationMapper.selectAccountInvoiceApplicationList(customerId);
    }
    private String genInvoiceApplicationNo() {
        return "KP" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
    }
src/main/java/com/ruoyi/account/service/impl/sales/AccountSalesCollectionServiceImpl.java
@@ -53,15 +53,15 @@
        if (StringUtils.isEmpty(accountSalesCollection.getCollectionNumber())) {
            accountSalesCollection.setCollectionNumber(genAccountSalesCollectionNo());
        }
//        String stockOutRecordIds = accountSalesCollection.getStockOutRecordIds();
//        if (stockOutRecordIds != null && !stockOutRecordIds.isEmpty()) {
//            List<Long> ids = Arrays.stream(stockOutRecordIds.split(","))
//                    .map(Long::valueOf)
//                    .toList();
//            if (accountSalesCollectionMapper.existsByStockOutRecordId(ids)){
//                throw new ServiceException("存在重复的出库单");
//            }
//        }
        String stockOutRecordIds = accountSalesCollection.getStockOutRecordIds();
        if (stockOutRecordIds != null && !stockOutRecordIds.isEmpty()) {
            List<Long> ids = Arrays.stream(stockOutRecordIds.split(","))
                    .map(Long::valueOf)
                    .toList();
            if (accountSalesCollectionMapper.existsByStockOutRecordId(ids)){
                throw new ServiceException("存在重复的出库单");
            }
        }
        return save(accountSalesCollection);
    }
src/main/java/com/ruoyi/account/service/sales/AccountInvoiceApplicationService.java
@@ -30,6 +30,4 @@
    void exportAccountInvoiceApplication(HttpServletResponse response, AccountInvoiceApplicationDto accountInvoiceApplicationDto);
    boolean deleteAccountInvoiceApplication(List<Long> ids);
    List<AccountInvoiceApplicationVo> getAccountInvoiceApplicationList(Integer customerId);
}
src/main/java/com/ruoyi/aftersalesservice/dto/AfterSalesServiceExeclDto.java
@@ -62,4 +62,12 @@
    @Excel(name = "关联部门")
    private String deptName;
    @Schema(description = "评分")
    @Excel(name = "评分")
    private String rating;
    @Schema(description = "评价内容描述")
    @Excel(name = "评价内容描述")
    private String evaluation;
}
src/main/java/com/ruoyi/aftersalesservice/pojo/AfterSalesService.java
@@ -115,6 +115,7 @@
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
@@ -156,8 +157,16 @@
    @Schema(description = "产品型号IDs")
    private String productModelIds;
    @Schema(description = "产品型号数量,逗号分隔")
    private String productModelQuantities;
    @Schema(description = "评分(1-5分)")
    @Excel(name = "评分")
    private BigDecimal rating;
    @Schema(description = "评价内容描述")
    @Excel(name = "评价内容描述")
    private String evaluation;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
src/main/java/com/ruoyi/aftersalesservice/service/impl/AfterSalesServiceServiceImpl.java
@@ -11,8 +11,6 @@
import com.ruoyi.aftersalesservice.service.AfterSalesServiceService;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import java.time.LocalDateTime;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
@@ -46,6 +44,7 @@
    private final SysUserMapper sysUserMapper;
    private final ISalesLedgerProductService salesLedgerProductService;
    private final ISalesLedgerService salesLedgerService;
    private final com.ruoyi.sales.mapper.ShippingInfoMapper shippingInfoMapper;
    @Override
    public IPage<AfterSalesServiceNewDto> listPage(Page page, AfterSalesServiceNewDto afterSalesService) {
@@ -71,7 +70,7 @@
        if(sysUser == null) throw new RuntimeException("审核人不存在");
        afterSalesServiceNewDto.setCheckNickName(sysUser.getNickName());
        if (StringUtils.isEmpty(afterSalesServiceNewDto.getAfterSalesServiceNo())) {
            String string = OrderUtils.countAfterServiceTodayByCreateTime(afterSalesServiceMapper, "SH_", afterSalesServiceNewDto.getCreateTime() != null ? afterSalesServiceNewDto.getCreateTime() : LocalDateTime.now());
            String string = OrderUtils.countAfterServiceTodayByCreateTime(afterSalesServiceMapper, "SH_");
            afterSalesServiceNewDto.setAfterSalesServiceNo(string);
        }
        return this.save(afterSalesServiceNewDto);
@@ -83,6 +82,38 @@
        SalesLedger byId = salesLedgerService.getById(afterSalesService.getSalesLedgerId());
        List<Long> collect = Arrays.stream(afterSalesService.getProductModelIds().split(",")).map(Long::valueOf).collect(Collectors.toList());
        List<SalesLedgerProduct> list = salesLedgerProductService.list(new QueryWrapper<SalesLedgerProduct>().lambda().in(SalesLedgerProduct::getId, collect));
        if (StringUtils.isNotEmpty(afterSalesService.getProductModelQuantities())) {
            String[] quantities = afterSalesService.getProductModelQuantities().split(",");
            for (int i = 0; i < collect.size(); i++) {
                Long productId = collect.get(i);
                for (SalesLedgerProduct product : list) {
                    if (product.getId().equals(productId)) {
                        if (i < quantities.length && StringUtils.isNotEmpty(quantities[i])) {
                            product.setQuantity(new java.math.BigDecimal(quantities[i]));
                        }
                        break;
                    }
                }
            }
        }
        for (SalesLedgerProduct product : list) {
            com.ruoyi.sales.pojo.ShippingInfo shippingInfo = shippingInfoMapper.selectOne(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.ruoyi.sales.pojo.ShippingInfo>()
                    .eq(com.ruoyi.sales.pojo.ShippingInfo::getSalesLedgerProductId, product.getId())
                    .orderByDesc(com.ruoyi.sales.pojo.ShippingInfo::getCreateTime)
                    .last("limit 1")
            );
            if (shippingInfo != null) {
                product.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                product.setShippingDate(shippingInfo.getShippingDate());
                product.setShippingStatus(shippingInfo.getStatus());
                product.setExpressCompany(shippingInfo.getExpressCompany());
                product.setExpressNumber(shippingInfo.getExpressNumber());
            }
        }
        AfterSalesServiceNewDto afterSalesServiceNewDto = new AfterSalesServiceNewDto();
        BeanUtils.copyProperties(afterSalesService, afterSalesServiceNewDto);
        SalesLedgerDto salesLedgerDto = new SalesLedgerDto();
src/main/java/com/ruoyi/approve/controller/ApproveNodeController.java
@@ -2,6 +2,8 @@
import com.ruoyi.approve.pojo.ApproveNode;
import com.ruoyi.approve.service.IApproveNodeService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -35,6 +37,7 @@
     * @param approveNode
     * @return
     */
    @Log(title = "审批节点", businessType = BusinessType.UPDATE)
    @PostMapping("/updateApproveNode")
    @Transactional(rollbackFor = Exception.class)
    @Operation(summary = "审批节点")
@@ -48,6 +51,7 @@
     * @param id
     * @return
     */
    @Log(title = "初始化审批节点", businessType = BusinessType.INSERT)
    @PostMapping("/init")
    public AjaxResult init(String id) {
        approveNodeService.initApproveNodes("",id,1L);
src/main/java/com/ruoyi/approve/controller/ApproveProcessConfigNodeController.java
@@ -2,6 +2,8 @@
import com.ruoyi.approve.pojo.ApproveProcessConfigNode;
import com.ruoyi.approve.service.ApproveProcessConfigNodeService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
@@ -41,6 +43,7 @@
     * @return
     */
    @ApiOperation("添加审批节点")
    @Log(title = "审批流程节点", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R addApproveProcessConfigNodes(@RequestBody List<ApproveProcessConfigNode> approveProcessConfigNodes) {
        return R.ok(approveProcessConfigNodeService.addApproveProcessConfigNodes(approveProcessConfigNodes));
src/main/java/com/ruoyi/approve/controller/ApproveProcessController.java
@@ -8,6 +8,8 @@
import com.ruoyi.approve.bean.vo.ApproveProcessVO;
import com.ruoyi.common.utils.SecurityUtils;
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.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysDept;
@@ -47,6 +49,7 @@
     * @param approveProcessVO
     * @return
     */
    @Log(title = "添加审批", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    @Operation(summary = "添加审批")
@@ -78,6 +81,7 @@
     * @param approveGetAndUpdateVo
     * @return
     */
    @Log(title = "更新审批", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Transactional(rollbackFor = Exception.class)
    @Operation(summary = "更新审批")
@@ -103,6 +107,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除审批", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteIds")
    @Operation(summary = "删除审批")
    @Transactional(rollbackFor = Exception.class)
@@ -115,6 +120,7 @@
    }
    @Operation(summary = "公出管理导出")
    @Log(title = "公出管理导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportOne")
    public void exportOne(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -125,6 +131,7 @@
    }
    @Operation(summary = "请假管理导出")
    @Log(title = "请假管理导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportTwo")
    public void exportTwo(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -135,6 +142,7 @@
    }
    @Operation(summary = "出差管理导出")
    @Log(title = "出差管理导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportThree")
    public void exportThree(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -145,6 +153,7 @@
    }
    @Operation(summary = "报销管理导出")
    @Log(title = "报销管理导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportFour")
    public void exportFour(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -155,6 +164,7 @@
    }
    @Operation(summary = "采购申请导出")
    @Log(title = "采购申请导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportFive")
    public void exportFive(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -165,6 +175,7 @@
    }
    @Operation(summary = "协同审批导出")
    @Log(title = "协同审批导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportZero")
    public void exportZero(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
@@ -175,6 +186,7 @@
    }
    @Operation(summary = "危险作业审批导出")
    @Log(title = "危险作业审批导出", businessType = BusinessType.EXPORT)
    @PostMapping("/exportEight")
    public void exportEight(HttpServletResponse response) {
        List<ApproveProcess> accountExpenses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
src/main/java/com/ruoyi/approve/controller/HolidaySettingsController.java
@@ -7,6 +7,8 @@
import com.ruoyi.approve.mapper.WorkingHoursSettingMapper;
import com.ruoyi.approve.pojo.*;
import com.ruoyi.approve.service.HolidaySettingsService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -37,6 +39,7 @@
     * å¢žæ·»
     * @return
     */
    @Log(title = "增添假期设置", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody HolidaySettings holidaySettings){
        return AjaxResult.success(holidaySettingsService.save(holidaySettings));
@@ -45,6 +48,7 @@
     * æ›´æ–°
     * @return
     */
    @Log(title = "更新假期设置", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody HolidaySettings holidaySettings){
        return AjaxResult.success(holidaySettingsService.updateById(holidaySettings));
@@ -53,6 +57,7 @@
     * åˆ é™¤
     * @return
     */
    @Log(title = "删除假期设置", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
@@ -72,6 +77,7 @@
     * å¢žæ·»å¹´å‡è§„则
     * @return
     */
    @Log(title = "增添年假规则", businessType = BusinessType.INSERT)
    @PostMapping("/addAnnualLeaveSetting")
    public AjaxResult addAnnualLeaveSetting(@RequestBody AnnualLeaveSetting annualLeaveSetting){
        return AjaxResult.success(annualLeaveSettingMapper.insert(annualLeaveSetting));
@@ -80,6 +86,7 @@
     * æ›´æ–°å¹´å‡è§„则
     * @return
     */
    @Log(title = "更新年假规则", businessType = BusinessType.UPDATE)
    @PostMapping("/updateAnnualLeaveSetting")
    public AjaxResult updateAnnualLeaveSetting(@RequestBody AnnualLeaveSetting annualLeaveSetting){
        return AjaxResult.success(annualLeaveSettingMapper.updateById(annualLeaveSetting));
@@ -88,6 +95,7 @@
     * åˆ é™¤å¹´å‡è§„则
     * @return
     */
    @Log(title = "删除年假规则", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteAnnualLeaveSetting")
    public AjaxResult deleteAnnualLeaveSetting(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
@@ -108,6 +116,7 @@
     * å¢žæ·»åŠ ç­è§„åˆ™
     * @return
     */
    @Log(title = "增添加班规则", businessType = BusinessType.INSERT)
    @PostMapping("/addOvertimeSetting")
    public AjaxResult addOvertimeSetting(@RequestBody OvertimeSetting overtimeSetting){
        return AjaxResult.success(overtimeSettingMapper.insert(overtimeSetting));
@@ -116,6 +125,7 @@
     * æ›´æ–°åŠ ç­è§„åˆ™
     * @return
     */
    @Log(title = "更新加班规则", businessType = BusinessType.UPDATE)
    @PostMapping("/updateOvertimeSetting")
    public AjaxResult updateOvertimeSetting(@RequestBody OvertimeSetting overtimeSetting){
        return AjaxResult.success(overtimeSettingMapper.updateById(overtimeSetting));
@@ -124,6 +134,7 @@
     * åˆ é™¤åŠ ç­è§„åˆ™
     * @return
     */
    @Log(title = "删除加班规则", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteOvertimeSetting")
    public AjaxResult deleteOvertimeSetting(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
@@ -143,6 +154,7 @@
     * å¢žæ·»ç­åˆ¶è§„则
     * @return
     */
    @Log(title = "增添班制规则", businessType = BusinessType.INSERT)
    @PostMapping("/addWorkingHoursSetting")
    public AjaxResult addWorkingHoursSetting(@RequestBody WorkingHoursSetting workingHoursSetting){
        return AjaxResult.success(workingHoursSettingMapper.insert(workingHoursSetting));
@@ -151,6 +163,7 @@
     * æ›´æ–°ç­åˆ¶è§„则
     * @return
     */
    @Log(title = "更新班制规则", businessType = BusinessType.UPDATE)
    @PostMapping("/updateWorkingHoursSetting")
    public AjaxResult updateWorkingHoursSetting(@RequestBody WorkingHoursSetting workingHoursSetting){
        return AjaxResult.success(workingHoursSettingMapper.updateById(workingHoursSetting));
@@ -159,6 +172,7 @@
     * åˆ é™¤ç­åˆ¶è§„则
     * @return
     */
    @Log(title = "删除班制规则", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteWorkingHoursSetting")
    public AjaxResult deleteWorkingHoursSetting(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
src/main/java/com/ruoyi/approve/controller/KnowledgeBaseController.java
@@ -2,25 +2,18 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.approve.dto.KnowledgeBaseVectorVO;
import com.ruoyi.approve.pojo.KnowledgeBase;
import com.ruoyi.approve.pojo.KnowledgeBaseVector;
import com.ruoyi.approve.service.KnowledgeBaseService;
import com.ruoyi.approve.service.KnowledgeBaseVectorService;
import com.ruoyi.basic.dto.StorageAttachmentDTO;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.service.StorageAttachmentService;
import com.ruoyi.basic.service.StorageBlobService;
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.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@@ -29,45 +22,47 @@
@Tag(name = "知识库管理")
public class KnowledgeBaseController {
    private KnowledgeBaseService knowledgeBaseService;
    private KnowledgeBaseVectorService knowledgeBaseVectorService;
    private StorageAttachmentService storageAttachmentService;
    private StorageBlobService storageBlobService;
    /**
    /**、
     * èŽ·å–åˆ—è¡¨
     * @return
     */
    @GetMapping("/getList")
    public AjaxResult getList(@RequestParam(defaultValue = "1") long current,
                              @RequestParam(defaultValue = "10") long size, KnowledgeBase knowledgeBase) {
        Page page = new Page(current, size);
        return AjaxResult.success(knowledgeBaseService.listpage(page, knowledgeBase));
        return AjaxResult.success(knowledgeBaseService.listpage(page,knowledgeBase));
    }
    /**
     * æ–°å¢žçŸ¥è¯†åº“
    /**、
     * å¢žæ·»
     * @return
     */
    @Log(title = "知识库", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody KnowledgeBase knowledgeBase) {
    public AjaxResult add(@RequestBody KnowledgeBase knowledgeBase){
        return AjaxResult.success(knowledgeBaseService.save(knowledgeBase));
    }
    /**
     * æ›´æ–°çŸ¥è¯†åº“
     * æ›´æ–°
     * @return
     */
    @Log(title = "知识库", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody KnowledgeBase knowledgeBase) {
    public AjaxResult update(@RequestBody KnowledgeBase knowledgeBase){
        return AjaxResult.success(knowledgeBaseService.updateById(knowledgeBase));
    }
    /**
     * åˆ é™¤çŸ¥è¯†åº“
     * åˆ é™¤
     * @return
     */
    @Log(title = "知识库", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
    public AjaxResult delete(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
        return AjaxResult.success(knowledgeBaseService.removeByIds(ids));
    }
    @Log(title = "知识库", businessType = BusinessType.EXPORT)
    @Operation(summary = "知识库管理导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
@@ -76,101 +71,4 @@
        util.exportExcel(response, accountExpenses, "知识库管理导出");
    }
    /**
     * æŸ¥è¯¢çŸ¥è¯†åº“文件向量化状态
     */
    @GetMapping("/vector/status/{knowledgeBaseId}")
    @Operation(summary = "查询知识库文件向量化状态")
    public AjaxResult getVectorStatus(@PathVariable Long knowledgeBaseId) {
        List<KnowledgeBaseVectorVO> list = knowledgeBaseVectorService.getVectorStatusByKnowledgeBaseId(knowledgeBaseId);
        return AjaxResult.success(list);
    }
    /**
     * é‡æ–°å‘量化文件
     */
    @PostMapping("/vector/reprocess/{vectorId}")
    @Operation(summary = "重新向量化文件")
    public AjaxResult reprocessVector(@PathVariable Long vectorId) {
        knowledgeBaseVectorService.reprocessVector(vectorId);
        return AjaxResult.success("已重新提交向量化任务");
    }
    /**
     * ä¿å­˜çŸ¥è¯†åº“文件关联(文件上传后调用)
     * ä¸Šä¼ æµç¨‹ï¼š
     * 1. å…ˆè°ƒç”¨ /common/upload ä¸Šä¼ æ–‡ä»¶ï¼ŒèŽ·å– storageBlobDTOs
     * 2. å†è°ƒç”¨æ­¤æŽ¥å£å…³è”文件到知识库并触发向量化
     */
    @PostMapping("/file/save")
    @Operation(summary = "保存知识库文件关联")
    public AjaxResult saveKnowledgeBaseFiles(@RequestBody KnowledgeBaseFileDTO dto) {
        if (dto.getKnowledgeBaseId() == null) {
            return AjaxResult.error("知识库ID不能为空");
        }
        if (CollectionUtils.isEmpty(dto.getStorageBlobIds())) {
            return AjaxResult.error("文件ID不能为空");
        }
        // ä¿å­˜é™„件关联
        StorageAttachmentDTO attachmentDTO = new StorageAttachmentDTO();
        attachmentDTO.setRecordType("knowledge_base");
        attachmentDTO.setRecordId(dto.getKnowledgeBaseId());
        attachmentDTO.setApplication("rag_file");
        List<StorageBlobDTO> blobDTOs = new ArrayList<>();
        for (Long blobId : dto.getStorageBlobIds()) {
            StorageBlobDTO blobDTO = new StorageBlobDTO();
            blobDTO.setId(blobId);
            blobDTOs.add(blobDTO);
        }
        attachmentDTO.setStorageBlobDTOs(blobDTOs);
        storageAttachmentService.saveStorageAttachment(attachmentDTO);
        // åˆ›å»ºå‘量记录并触发向量化
        for (Long blobId : dto.getStorageBlobIds()) {
            StorageBlob blob = storageBlobService.getById(blobId);
            if (blob != null) {
                String fileName = blob.getOriginalFilename();
                String fileType = getFileExtension(fileName);
                knowledgeBaseVectorService.createVectorRecord(
                        dto.getKnowledgeBaseId(),
                        blobId,
                        fileName,
                        fileType
                );
            }
        }
        return AjaxResult.success();
    }
    private String getFileExtension(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            return "unknown";
        }
        return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
    }
    /**
     * åˆ é™¤çŸ¥è¯†åº“文件
     */
    @DeleteMapping("/file/delete")
    @Operation(summary = "删除知识库文件")
    public AjaxResult deleteKnowledgeBaseFiles(@RequestBody List<Long> vectorIds) {
        if (CollectionUtils.isEmpty(vectorIds)) {
            return AjaxResult.error("请选择要删除的文件");
        }
        knowledgeBaseVectorService.deleteVectors(vectorIds);
        return AjaxResult.success();
    }
    /**
     * çŸ¥è¯†åº“文件DTO
     */
    @lombok.Data
    public static class KnowledgeBaseFileDTO {
        private Long knowledgeBaseId;
        private List<Long> storageBlobIds;
    }
}
}
src/main/java/com/ruoyi/approve/controller/NotificationManagementController.java
@@ -8,6 +8,8 @@
import com.ruoyi.approve.pojo.NotificationManagement;
import com.ruoyi.approve.pojo.OnlineMeeting;
import com.ruoyi.approve.service.NotificationManagementService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,6 +38,7 @@
     * å¢žæ·»
     * @return
     */
    @Log(title = "增添通知管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody NotificationManagement notificationManagement){
        return AjaxResult.success(notificationManagementService.save(notificationManagement));
@@ -44,6 +47,7 @@
     * æ›´æ–°
     * @return
     */
    @Log(title = "更新通知管理", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody NotificationManagement notificationManagement){
        return AjaxResult.success(notificationManagementService.updateById(notificationManagement));
@@ -52,6 +56,7 @@
     * åˆ é™¤
     * @return
     */
    @Log(title = "删除通知管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
@@ -62,6 +67,7 @@
     * @param onlineMeeting
     * @return
     */
    @Log(title = "新增线上会议", businessType = BusinessType.INSERT)
    @PostMapping("/addOnlineMeeting")
    public AjaxResult addOnlineMeeting(@RequestBody OnlineMeeting onlineMeeting){
        return AjaxResult.success(onlineMeetingMapper.insert(onlineMeeting));
@@ -70,6 +76,7 @@
     *新增文件共享
     *
     */
    @Log(title = "新增文件共享", businessType = BusinessType.INSERT)
    @PostMapping("/addFileSharing")
    public AjaxResult addFileSharing(@RequestBody FileSharing fileSharing){
        return AjaxResult.success(fileSharingMapper.insert(fileSharing));
src/main/java/com/ruoyi/approve/controller/RpaProcessAutomationController.java
@@ -5,6 +5,8 @@
import com.ruoyi.approve.pojo.RpaProcessAutomation;
import com.ruoyi.approve.service.RpaProcessAutomationService;
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.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -34,6 +36,7 @@
     * å¢žæ·»
     * @return
     */
    @Log(title = "RPA流程自动化", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody RpaProcessAutomation rpaProcessAutomation){
        return AjaxResult.success(rpaProcessAutomationService.save(rpaProcessAutomation));
@@ -42,6 +45,7 @@
     * æ›´æ–°
     * @return
     */
    @Log(title = "RPA流程自动化", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody RpaProcessAutomation rpaProcessAutomation){
        return AjaxResult.success(rpaProcessAutomationService.updateById(rpaProcessAutomation));
@@ -50,12 +54,14 @@
     * åˆ é™¤
     * @return
     */
    @Log(title = "RPA流程自动化", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestBody List<Long> ids){
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
        return AjaxResult.success(rpaProcessAutomationService.removeByIds(ids));
    }
    @Log(title = "RPA流程自动化", businessType = BusinessType.EXPORT)
    @Operation(summary = "RPA流程自动化导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
src/main/java/com/ruoyi/approve/pojo/KnowledgeBase.java
@@ -91,19 +91,4 @@
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
    /**
     * æ–‡ä»¶æ•°é‡
     */
    private Integer fileCount;
    /**
     * æ€»åˆ‡ç‰‡æ•°é‡
     */
    private Integer totalChunkCount;
    /**
     * çŸ¥è¯†åº“描述
     */
    private String description;
}
src/main/java/com/ruoyi/approve/service/impl/ApproveNodeServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.approve.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -13,12 +14,26 @@
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityInspectParamMapper;
import com.ruoyi.quality.mapper.QualityTestStandardMapper;
import com.ruoyi.quality.mapper.QualityTestStandardParamMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityInspectParam;
import com.ruoyi.quality.pojo.QualityTestStandard;
import com.ruoyi.quality.pojo.QualityTestStandardParam;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.*;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -40,8 +55,18 @@
    private final SysUserMapper sysUserMapper;
    private final ISysNoticeService sysNoticeService;
    private final CommonFileMapper fileMapper;
    private final DeviceRepairMapper deviceRepairMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    private final SalesQuotationMapper salesQuotationMapper;
    private final ShippingInfoMapper shippingInfoMapper;
    private final ShippingProductDetailMapper shippingProductDetailMapper;
    private final CommonFileServiceImpl commonFileService;
    private final ApproveBusinessStatusService approveBusinessStatusService;
    private final StockUtils stockUtils;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final QualityInspectMapper qualityInspectMapper;
    private final QualityTestStandardMapper qualityTestStandardMapper;
    private final QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private final QualityInspectParamMapper qualityInspectParamMapper;
    private final FileUtil fileUtil;
@@ -137,7 +162,74 @@
        }
        approveProcessMapper.updateById(approveProcess);
        approveBusinessStatusService.syncBusinessStatus(approveProcess.getApproveType(), approveProcess.getApproveReason(), status);
        //采购审核
        if (approveProcess.getApproveType().equals(5)) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                    .eq(PurchaseLedger::getPurchaseContractNumber, approveProcess.getApproveReason())
                    .last("limit 1"));
            if (purchaseLedger != null) {
                if (status.equals(2)) {
                    // åŒæ„
                    purchaseLedger.setApprovalStatus(3);
                    List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>()
                            .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2));
                    for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
                        // è´¨æ£€
                        if (salesLedgerProduct.getIsChecked()) {
                            addQualityInspect(purchaseLedger, salesLedgerProduct);
                        } else {
                            //直接入库
                            stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId());
                        }
                    }
                } else if (status.equals(3)) {
                    // æ‹’绝
                    purchaseLedger.setApprovalStatus(4);
                } else if (status.equals(1)) {
                    // å®¡æ ¸ä¸­
                    purchaseLedger.setApprovalStatus(2);
                }
                purchaseLedgerMapper.updateById(purchaseLedger);
            }
        }
        // é”€å”®æŠ¥ä»·çŠ¶æ€ä¿®æ”¹
        if (approveProcess.getApproveType().equals(6)) {
            SalesQuotation salesQuote = salesQuotationMapper.selectOne(new LambdaQueryWrapper<SalesQuotation>()
                    .eq(SalesQuotation::getQuotationNo, approveProcess.getApproveReason())
                    .last("limit 1"));
            // åŒæ„
            if (status.equals(2) && salesQuote != null) {
                salesQuote.setStatus("通过");
            } else if (status.equals(3) && salesQuote != null) {
                salesQuote.setStatus("拒绝");
            } else if (status.equals(1) && salesQuote != null) {
                salesQuote.setStatus("审核中");
            }
            salesQuotationMapper.updateById(salesQuote);
        }
        // å‡ºåº“审批修改=发货审批
        if (approveProcess.getApproveType().equals(7)) {
            ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                    .eq(ShippingInfo::getShippingNo, approveProcess.getApproveReason())
                    .orderByDesc(ShippingInfo::getCreateTime)
                    .last("limit 1"));
            if (shippingInfo != null) {
                if (status.equals(2)) {
                    shippingInfo.setStatus("审核通过");
                    shippingInfo.setShippingDate(new Date());
                    //更改出库审核状态(待确认改成待审核)
                    //stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
                } else if (status.equals(3)) {
                    //删除原本(待确认)的出库审核状态
                    //stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
                    //shippingInfo.setStatus("审核拒绝");
                } else if (status.equals(1)) {
                    //shippingInfo.setStatus("审核中");
                }
                shippingInfoMapper.updateById(shippingInfo);
            }
        }
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_NODE, approveNode.getId(), approveNode.getStorageBlobDTOS());
    }
@@ -215,4 +307,32 @@
        return null;
    }
    private void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) {
        QualityInspect qualityInspect = new QualityInspect();
        qualityInspect.setInspectType(0);
        qualityInspect.setSupplier(purchaseLedger.getSupplierName());
        qualityInspect.setPurchaseLedgerId(purchaseLedger.getId());
        qualityInspect.setProductId(saleProduct.getProductId());
        qualityInspect.setProductName(saleProduct.getProductCategory());
        qualityInspect.setModel(saleProduct.getSpecificationModel());
        qualityInspect.setProductModelId(saleProduct.getProductModelId());
        qualityInspect.setUnit(saleProduct.getUnit());
        qualityInspect.setQuantity(saleProduct.getQuantity());
        qualityInspectMapper.insert(qualityInspect);
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null);
        if (qualityTestStandard.size() > 0) {
            qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
            qualityInspectMapper.updateById(qualityInspect);
            qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
                            .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))
                    .forEach(qualityTestStandardParam -> {
                        QualityInspectParam param = new QualityInspectParam();
                        com.ruoyi.common.utils.bean.BeanUtils.copyProperties(qualityTestStandardParam, param);
                        param.setId(null);
                        param.setInspectId(qualityInspect.getId());
                        qualityInspectParamMapper.insert(param);
                    });
        }
    }
}
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -1,6 +1,8 @@
package com.ruoyi.approve.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -23,8 +25,11 @@
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysNotice;
import com.ruoyi.project.system.domain.SysUser;
@@ -34,8 +39,10 @@
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import lombok.RequiredArgsConstructor;
@@ -47,12 +54,15 @@
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ApproveProcessServiceImpl extends ServiceImpl<ApproveProcessMapper, ApproveProcess> implements IApproveProcessService {
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
    private final SysDeptMapper sysDeptMapper;
    private final IApproveNodeService approveNodeService;
    private final SysUserMapper sysUserMapper;
@@ -61,10 +71,11 @@
    private final CommonFileServiceImpl commonFileService;
    private final ISysNoticeService sysNoticeService;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final StockUtils stockUtils;
    private final ShippingInfoMapper shippingInfoMapper;
    private final ApproveNodeMapper approveNodeMapper;
    private final ApproveProcessConfigNodeService approveProcessConfigNodeService;
    private final ApproveBusinessStatusService approveBusinessStatusService;
    private final FileUtil fileUtil;
    private final ApproveProcessConfigNodeMapper approveProcessConfigNodeMapper;
@@ -72,34 +83,63 @@
    public void addApprove(ApproveProcessVO approveProcessVO) throws Exception {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        SysDept sysDept = sysDeptMapper.selectDeptById(SecurityUtils.getLoginUser().getCurrentDeptId());
        if (sysDept == null) throw new RuntimeException("部门不存在");
        if (sysUser == null) throw new RuntimeException("申请人不存在");
        List<ApproveProcessConfigNodeVo> list = Optional.ofNullable(approveProcessConfigNodeService.listNode(approveProcessVO.getApproveType()))
                .orElse(Collections.emptyList());
        List<ApproveProcessConfigNodeVo> list = approveProcessConfigNodeService.listNode(approveProcessVO.getApproveType());
        List<Long> nodeIds = list.stream()
                .map(ApproveProcessConfigNodeVo::getApproverId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        // å®¡æ‰¹é…ç½®æ²¡æœ‰æœ‰æ•ˆå®¡æ ¸äººæ—¶ï¼Œä¸æ–°å¢žååŒå®¡æ‰¹æµç¨‹ï¼Œç›´æŽ¥æ‰§è¡Œä¸šåŠ¡å®¡æ ¸é€šè¿‡é€»è¾‘ã€‚
        // æ— å®¡æ ¸äººé€»è¾‘添加
        if (CollectionUtils.isEmpty(nodeIds)) {
            approveBusinessStatusService.syncBusinessStatus(approveProcessVO.getApproveType(), approveProcessVO.getApproveReason(), 2);
            autoPassPurchaseApproveIfNoApprover(approveProcessVO); // é‡‡è´­å•无审核人逻辑
            return;
        }
        List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds);
        if (CollectionUtils.isEmpty(sysUsers)) throw new RuntimeException("审核用户不存在");
        // æœ‰å®¡æ ¸äººæ—¶ï¼ŒæŒ‰æ­£å¸¸ååŒå®¡æ‰¹æµç¨‹åˆ›å»ºå®¡æ‰¹ä¸»è¡¨ã€å®¡æ‰¹èŠ‚ç‚¹å¹¶é€šçŸ¥é¦–ä¸ªå®¡æ ¸äººã€‚
        ApproveProcess approveProcess = buildApproveProcess(approveProcessVO, sysUser, sysDept, nodeIds, sysUsers, 0);
        markResubmitted(approveProcessVO);
        if (sysDept == null) throw new RuntimeException("部门不存在");
        if (sysUser == null) throw new RuntimeException("申请人不存在");
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        ApproveProcess approveProcess = new ApproveProcess();
        String no = OrderUtils.countTodayByCreateTime(approveProcessMapper, "", "approve_id");
        approveProcess.setApproveId(no);
        approveProcess.setApproveUser(sysUser.getUserId());
        approveProcess.setApproveUserName(sysUser.getNickName());
        approveProcess.setApproveDeptId(sysDept.getDeptId());
        approveProcess.setApproveUserIds(nodeIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        approveProcess.setApproveDeptName(sysDept.getDeptName());
        approveProcess.setApproveUserNames(sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining(",")));
        approveProcess.setApproveTime(StringUtils.isEmpty(approveProcessVO.getApproveTime()) ? new Date() : dateFormat.parse(approveProcessVO.getApproveTime()));
        approveProcess.setApproveReason(approveProcessVO.getApproveReason());
        approveProcess.setDeviceRepairId(approveProcessVO.getDeviceRepairId());
        approveProcess.setMaintenancePrice(AmountUtils.scaleAmount(approveProcessVO.getMaintenancePrice()));
        approveProcess.setPrice(AmountUtils.scaleAmount(approveProcessVO.getPrice()));
        approveProcess.setStartDate(approveProcessVO.getStartDate());
        approveProcess.setEndDate(approveProcessVO.getEndDate());
        approveProcess.setStartDateTime(approveProcessVO.getStartDateTime());
        approveProcess.setEndDateTime(approveProcessVO.getEndDateTime());
        approveProcess.setApproveStatus(0);
        approveProcess.setApproveDelete(0);
        approveProcess.setApproveType(approveProcessVO.getApproveType());
        approveProcess.setCreateTime(LocalDateTime.now());
        approveProcess.setTenantId(approveProcessVO.getApproveDeptId());
        approveProcess.setApproveUserCurrentId(nodeIds.get(0));
        approveProcess.setApproveUserCurrentName(sysUsers
                .stream()
                .filter(SysUser -> SysUser.getUserId().equals(nodeIds.get(0)))
                .collect(Collectors.toList())
                .get(0)
                .getNickName());
        // è®¾ç½®çŠ¶æ€ä¸ºé‡æ–°æäº¤
        if (approveProcessVO.getId() != null) {
            ApproveProcess approveProcess1 = approveProcessMapper.selectById(approveProcessVO.getId());
            approveProcess1.setApproveStatus(4);
            approveProcessMapper.updateById(approveProcess1);
        }
        save(approveProcess);
        //初始化审批节点
        String nodeIdStr = nodeIds.stream()
                .map(String::valueOf)
                .collect(Collectors.joining(","));
        approveNodeService.initApproveNodes(nodeIdStr, approveProcess.getApproveId(), approveProcessVO.getApproveDeptId());
        approveNodeService.initApproveNodes(nodeIdStr, no, approveProcessVO.getApproveDeptId());
        // é™„件绑定
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_PROCESS, approveProcess.getId(), approveProcessVO.getStorageBlobDTOS());
        /*消息通知*/
@@ -117,56 +157,24 @@
        }
    }
    private ApproveProcess buildApproveProcess(ApproveProcessVO approveProcessVO, SysUser sysUser, SysDept sysDept,
                                               List<Long> nodeIds, List<SysUser> sysUsers, Integer approveStatus) throws Exception {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        ApproveProcess approveProcess = new ApproveProcess();
        String no = OrderUtils.countTodayByCreateTime(approveProcessMapper, "", "approve_id", approveProcess.getCreateTime() != null ? approveProcess.getCreateTime() : LocalDateTime.now());
        approveProcess.setApproveId(no);
        approveProcess.setApproveUser(sysUser.getUserId());
        approveProcess.setApproveUserName(sysUser.getNickName());
        approveProcess.setApproveDeptId(sysDept.getDeptId());
        approveProcess.setApproveDeptName(sysDept.getDeptName());
        approveProcess.setApproveUserIds(nodeIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        approveProcess.setApproveUserNames(sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining(",")));
        approveProcess.setApproveTime(StringUtils.isEmpty(approveProcessVO.getApproveTime()) ? new Date() : dateFormat.parse(approveProcessVO.getApproveTime()));
        approveProcess.setApproveReason(approveProcessVO.getApproveReason());
        approveProcess.setDeviceRepairId(approveProcessVO.getDeviceRepairId());
        approveProcess.setMaintenancePrice(approveProcessVO.getMaintenancePrice());
        approveProcess.setPrice(approveProcessVO.getPrice());
        approveProcess.setStartDate(approveProcessVO.getStartDate());
        approveProcess.setEndDate(approveProcessVO.getEndDate());
        approveProcess.setStartDateTime(approveProcessVO.getStartDateTime());
        approveProcess.setEndDateTime(approveProcessVO.getEndDateTime());
        approveProcess.setApproveStatus(approveStatus);
        approveProcess.setApproveDelete(0);
        approveProcess.setApproveType(approveProcessVO.getApproveType());
        approveProcess.setCreateTime(LocalDateTime.now());
        approveProcess.setTenantId(approveProcessVO.getApproveDeptId());
        if (!CollectionUtils.isEmpty(nodeIds)) {
            SysUser currentUser = sysUsers.stream()
                    .filter(user -> user.getUserId().equals(nodeIds.get(0)))
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("审核用户不存在"));
            approveProcess.setApproveUserCurrentId(currentUser.getUserId());
            approveProcess.setApproveUserCurrentName(currentUser.getNickName());
    private void autoPassPurchaseApproveIfNoApprover(ApproveProcessVO approveProcessVO) {
        if (!Objects.equals(approveProcessVO.getApproveType(), 5)
                || !StringUtils.hasText(approveProcessVO.getApproveReason())) {
            throw new RuntimeException("审核用户不存在");
        }
        if (approveStatus.equals(2) || approveStatus.equals(3) || approveStatus.equals(4)) {
            approveProcess.setApproveOverTime(new Date());
        }
        return approveProcess;
    }
    private void markResubmitted(ApproveProcessVO approveProcessVO) {
        if (approveProcessVO.getId() == null) {
            return;
        purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>()
                .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason())
                .set(PurchaseLedger::getApprovalStatus, 3));
        //采购入库
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason())
                .last("limit 1"));
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>()
                .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2));
        for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
            stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(),purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId());
        }
        ApproveProcess approveProcess = approveProcessMapper.selectById(approveProcessVO.getId());
        if (approveProcess == null) {
            return;
        }
        approveProcess.setApproveStatus(4);
        approveProcessMapper.updateById(approveProcess);
    }
    @Override
src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
@@ -1,370 +1,9 @@
package com.ruoyi.approve.utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalInstanceNode;
import com.ruoyi.approve.pojo.ApprovalRecord;
import com.ruoyi.approve.pojo.ApprovalTask;
import com.ruoyi.approve.pojo.ApprovalTemplateNode;
import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
import com.ruoyi.approve.service.ApprovalInstanceNodeService;
import com.ruoyi.approve.service.ApprovalRecordService;
import com.ruoyi.approve.service.ApprovalTaskService;
import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService;
import com.ruoyi.approve.service.ApprovalTemplateNodeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * å®¡æ‰¹æµç¨‹èŠ‚ç‚¹å·¥å…·ç±»
 */
@Component
@RequiredArgsConstructor
public class ApproveProcessConfigNodeUtils {
    private final ApprovalInstanceNodeService instanceNodeService;
    private final ApprovalTaskService approvalTaskService;
    private final ApprovalRecordService approvalRecordService;
    private final ApprovalTemplateNodeService approvalTemplateNodeService;
    private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService;
    /**
     * æŒ‰å½“前层级创建审批节点和审批任务。
     * è¯¥é‡è½½ä¼šåŒæ—¶å†™å…¥ä¸€æ¡å‘起审批记录。
     */
    @Transactional(rollbackFor = Exception.class)
    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance) {
        return createCurrentNodeAndTasks(instance, true);
    }
    /**
     * æŒ‰å½“前层级创建审批节点和审批任务。
     *
     * @param instance å®¡æ‰¹å®žä¾‹
     * @param createSubmitRecord æ˜¯å¦åˆ›å»ºå‘起审批记录
     * @return åˆ›å»ºå‡ºçš„当前节点实例
     */
    @Transactional(rollbackFor = Exception.class)
    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance, boolean createSubmitRecord) {
        if (instance == null || instance.getId() == null) {
            throw new RuntimeException("审批实例不能为空");
        }
        if (instance.getTemplateId() == null) {
            throw new RuntimeException("审批模板不能为空");
        }
        Integer currentLevel = instance.getCurrentLevel() == null ? 1 : instance.getCurrentLevel();
        ApprovalInstanceNode existsNode = instanceNodeService.getOne(
                new LambdaQueryWrapper<ApprovalInstanceNode>()
                        .eq(ApprovalInstanceNode::getInstanceId, instance.getId())
                        .eq(ApprovalInstanceNode::getLevelNo, currentLevel)
                        .eq(ApprovalInstanceNode::getDeleted, 0)
                        .last("LIMIT 1")
        );
        if (existsNode != null) {
            return existsNode;
        }
        ApprovalTemplateNode templateNode = approvalTemplateNodeService.getOne(
                new LambdaQueryWrapper<ApprovalTemplateNode>()
                        .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
                        .eq(ApprovalTemplateNode::getLevelNo, currentLevel)
                        .orderByAsc(ApprovalTemplateNode::getId)
                        .last("LIMIT 1")
        );
        if (templateNode == null) {
            throw new RuntimeException("未找到当前层级对应的审批模板节点");
        }
        List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverService.list(
                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
                        .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId())
                        .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId())
                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
                        .orderByAsc(ApprovalTemplateNodeApprover::getSortNo)
        );
        if (approvers == null || approvers.isEmpty()) {
            throw new RuntimeException("当前审批节点未配置审批人");
        }
        ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
        instanceNode.setInstanceId(instance.getId());
        instanceNode.setLevelNo(templateNode.getLevelNo());
        instanceNode.setApproveType(templateNode.getApproveType());
        instanceNode.setStatus("PENDING");
        instanceNode.setStartTime(LocalDateTime.now());
        instanceNode.setDeleted((byte) 0);
        instanceNodeService.save(instanceNode);
        List<ApprovalTask> taskList = new ArrayList<>(approvers.size());
        for (ApprovalTemplateNodeApprover approver : approvers) {
            ApprovalTask task = new ApprovalTask();
            task.setInstanceId(instance.getId());
            task.setNodeId(instanceNode.getId());
            task.setLevelNo(instanceNode.getLevelNo());
            task.setApproverId(approver.getApproverId());
            task.setApproverName(approver.getApproverName());
            task.setTaskStatus("PENDING");
            task.setIsRead((byte) 0);
            task.setDeleted((byte) 0);
            taskList.add(task);
        }
        approvalTaskService.saveBatch(taskList);
        if (createSubmitRecord) {
            ApprovalRecord record = new ApprovalRecord();
            record.setInstanceId(instance.getId());
            record.setNodeId(instanceNode.getId());
            record.setOperatorId(instance.getApplicantId());
            record.setOperatorName(instance.getApplicantName());
            record.setAction("SUBMIT");
            record.setComment("发起审批");
            record.setDeleted((byte) 0);
            approvalRecordService.save(record);
        }
        return instanceNode;
    }
    /**
     * æŸ¥è¯¢å½“前待处理节点。
     */
    public ApprovalInstanceNode getCurrentNode(Long instanceId) {
        if (instanceId == null) {
            return null;
        }
        return instanceNodeService.getOne(
                new LambdaQueryWrapper<ApprovalInstanceNode>()
                        .eq(ApprovalInstanceNode::getInstanceId, instanceId)
                        .eq(ApprovalInstanceNode::getStatus, "PENDING")
                        .eq(ApprovalInstanceNode::getDeleted, 0)
                        .orderByAsc(ApprovalInstanceNode::getLevelNo)
                        .last("LIMIT 1")
        );
    }
    /**
     * æŸ¥è¯¢å½“前审批层级。
     */
    public Integer getCurrentLevel(Long instanceId) {
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        return currentNode != null ? currentNode.getLevelNo() : null;
    }
    /**
     * æŸ¥è¯¢å½“前节点下的待审批任务。
     */
    public List<ApprovalTask> getCurrentPendingTasks(Long instanceId) {
        if (instanceId == null) {
            return List.of();
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return List.of();
        }
        return approvalTaskService.list(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getInstanceId, instanceId)
                        .eq(ApprovalTask::getNodeId, currentNode.getId())
                        .eq(ApprovalTask::getTaskStatus, "PENDING")
                        .eq(ApprovalTask::getDeleted, 0)
                        .orderByAsc(ApprovalTask::getLevelNo)
        );
    }
    /**
     * æŸ¥è¯¢å½“前用户在当前节点上的待审批任务。
     */
    public ApprovalTask getCurrentUserTask(Long instanceId, Long userId) {
        if (instanceId == null || userId == null) {
            return null;
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return null;
        }
        return approvalTaskService.getOne(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getInstanceId, instanceId)
                        .eq(ApprovalTask::getNodeId, currentNode.getId())
                        .eq(ApprovalTask::getApproverId, userId)
                        .eq(ApprovalTask::getTaskStatus, "PENDING")
                        .eq(ApprovalTask::getDeleted, 0)
                        .last("LIMIT 1")
        );
    }
    /**
     * åˆ¤æ–­å½“前用户是否是当前审批人。
     */
    public boolean isCurrentApprover(Long instanceId, Long userId) {
        return getCurrentUserTask(instanceId, userId) != null;
    }
    /**
     * æŸ¥è¯¢å½“前节点的审批人 ID åˆ—表。
     */
    public List<Long> getCurrentNodeApproverIds(Long instanceId) {
        return getCurrentPendingTasks(instanceId).stream()
                .map(ApprovalTask::getApproverId)
                .distinct()
                .collect(Collectors.toList());
    }
    /**
     * æŸ¥è¯¢å½“前节点剩余待审批人数。
     */
    public int getRemainingApproverCount(Long instanceId) {
        return getCurrentPendingTasks(instanceId).size();
    }
    /**
     * æŸ¥è¯¢å½“前节点已同意人数。
     */
    public int getApprovedCount(Long instanceId) {
        if (instanceId == null) {
            return 0;
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return 0;
        }
        return Math.toIntExact(approvalTaskService.count(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getInstanceId, instanceId)
                        .eq(ApprovalTask::getNodeId, currentNode.getId())
                        .eq(ApprovalTask::getTaskStatus, "APPROVED")
                        .eq(ApprovalTask::getDeleted, 0)
        ));
    }
    /**
     * æŸ¥è¯¢å½“前节点已拒绝人数。
     */
    public int getRejectedCount(Long instanceId) {
        if (instanceId == null) {
            return 0;
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return 0;
        }
        return Math.toIntExact(approvalTaskService.count(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getInstanceId, instanceId)
                        .eq(ApprovalTask::getNodeId, currentNode.getId())
                        .eq(ApprovalTask::getTaskStatus, "REJECTED")
                        .eq(ApprovalTask::getDeleted, 0)
        ));
    }
    /**
     * åˆ¤æ–­å½“前节点是否可以流转到下一层。
     */
    public boolean canProceedToNextLevel(Long instanceId, String approveType) {
        if (instanceId == null || approveType == null) {
            return false;
        }
        if (getRejectedCount(instanceId) > 0) {
            return false;
        }
        int totalApproverCount = getCurrentPendingTasks(instanceId).size() + getApprovedCount(instanceId);
        int approvedCount = getApprovedCount(instanceId);
        if ("AND".equalsIgnoreCase(approveType)) {
            return approvedCount > 0 && approvedCount == totalApproverCount;
        }
        if ("OR".equalsIgnoreCase(approveType)) {
            return approvedCount > 0;
        }
        return false;
    }
    /**
     * æŸ¥è¯¢å½“前用户在当前节点上的任务状态。
     */
    public String getUserTaskStatus(Long instanceId, Long userId) {
        if (instanceId == null || userId == null) {
            return null;
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return null;
        }
        ApprovalTask task = approvalTaskService.getOne(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getInstanceId, instanceId)
                        .eq(ApprovalTask::getNodeId, currentNode.getId())
                        .eq(ApprovalTask::getApproverId, userId)
                        .eq(ApprovalTask::getDeleted, 0)
                        .last("LIMIT 1")
        );
        return task != null ? task.getTaskStatus() : null;
    }
    /**
     * æŸ¥è¯¢æŒ‡å®šç”¨æˆ·çš„全部待审批任务。
     */
    public List<ApprovalTask> getUserAllPendingTasks(Long userId) {
        if (userId == null) {
            return List.of();
        }
        return approvalTaskService.list(
                new LambdaQueryWrapper<ApprovalTask>()
                        .eq(ApprovalTask::getApproverId, userId)
                        .eq(ApprovalTask::getTaskStatus, "PENDING")
                        .eq(ApprovalTask::getDeleted, 0)
                        .orderByDesc(ApprovalTask::getCreateTime)
        );
    }
    /**
     * æŸ¥è¯¢å®¡æ‰¹å®žä¾‹çš„进度摘要。
     */
    public String getApprovalProgress(Long instanceId) {
        if (instanceId == null) {
            return "无效的审批实例";
        }
        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
        if (currentNode == null) {
            return "审批已完成或尚未开始";
        }
        int approvedCount = getApprovedCount(instanceId);
        int rejectedCount = getRejectedCount(instanceId);
        int pendingCount = getRemainingApproverCount(instanceId);
        int totalCount = approvedCount + rejectedCount + pendingCount;
        return String.format(
                "第%d级审批:总人数=%d,已同意=%d,已拒绝=%d,待审批=%d",
                currentNode.getLevelNo(),
                totalCount,
                approvedCount,
                rejectedCount,
                pendingCount
        );
    }
}
}
src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java
@@ -2,6 +2,8 @@
import com.ruoyi.basic.dto.StorageAttachmentDTO;
import com.ruoyi.basic.service.StorageAttachmentService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -35,6 +37,7 @@
     * @param ids æ–‡ä»¶id列表
     * @return åˆ é™¤ç»“æžœ
     */
    @Log(title = "存储附件", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除通用文件上传的附件信息")
    public R batchDelete(@RequestBody List<Long> ids) {
@@ -44,6 +47,7 @@
    /**
     * ä¿å­˜é€šç”¨æ–‡ä»¶ä¸Šä¼ çš„附件信息
     */
    @Log(title = "存储附件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "保存通用文件上传的附件信息")
    public R add(@RequestBody StorageAttachmentDTO storageAttachmentDTO) {
src/main/java/com/ruoyi/basic/controller/SupplierManageController.java
@@ -28,6 +28,7 @@
     * @param supplierManage
     * @return
     */
    @Log(title = "供应商管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody SupplierManage supplierManage) {
        supplierService.saveSupplier(supplierManage);
@@ -39,6 +40,7 @@
     * @param ids
     * @return
     */
    @Log(title = "供应商管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delSupplier(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
@@ -63,6 +65,7 @@
     * @param supplierManage
     * @return
     */
    @Log(title = "供应商管理", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody SupplierManage supplierManage) {
        supplierService.supplierUpdate(supplierManage);
@@ -85,6 +88,7 @@
     * @param response
     * @param supplierManageDto
     */
    @Log(title = "供应商管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void supplierExport(HttpServletResponse response,SupplierManageDto supplierManageDto) {
        supplierService.supplierExport(response, supplierManageDto);
src/main/java/com/ruoyi/basic/controller/SupplierManageFileController.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.pojo.SupplierManageFile;
import com.ruoyi.basic.service.SupplierManageFileService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
@@ -27,6 +29,7 @@
     * @param supplierManageFile
     * @return
     */
    @Log(title = "新增供应商附件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody SupplierManageFile supplierManageFile) {
        return AjaxResult.success(supplierManageFileService.save(supplierManageFile));
@@ -37,6 +40,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除供应商附件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delSupplierManageFile(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java
@@ -5,8 +5,7 @@
    FILE("file"),
    AFTER_FILE("after_file"),
    BEFORE_FILE("before_file"),
    APK("apk"),
    RAG_FILE("rag_file");
    APK("apk");
    private final String type;
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -194,15 +194,9 @@
    // Account
    SALES_REFUND_AMOUNT_ORDER("sales_refund_amount_order"),
    SALES_RECEIPT_RETURN("sales_receipt_return"),
    ACCOUNT_EXPENSE("account_expense"),
    FIN_REIMBURSEMENT("fin_reimbursement"),
    FIN_VOUCHER("fin_voucher"),
    ACCOUNT_FILE("account_file"),
    ENTERPRISE_NEWS("enterprise_news"),
    APPROVAL_INSTANCE("approval_instance"),
    ACCOUNT_INVOICE_APPLICATION("account_invoice_application"),
    ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice"),
    APPROVAL_TEMPLATE("approval_template");
    ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice");
    private final String type;
    RecordTypeEnum(String type) { this.type = type; }
src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
@@ -12,6 +12,7 @@
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
 * å®¢æˆ·æ¡£æ¡ˆMapper接口
@@ -20,55 +21,7 @@
 * @date 2025-05-07
 */
@Mapper
public interface CustomerMapper extends BaseMapper<Customer>
{
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param id å®¢æˆ·æ¡£æ¡ˆä¸»é”®
     * @return å®¢æˆ·æ¡£æ¡ˆ
     */
    Customer selectCustomerById(Long id);
    /**
     * æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆåˆ—表
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return å®¢æˆ·æ¡£æ¡ˆé›†åˆ
     */
    List<Customer> selectCustomerList(Customer customer);
    /**
     * æ–°å¢žå®¢æˆ·æ¡£æ¡ˆ
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return ç»“æžœ
     */
    int insertCustomer(Customer customer);
    /**
     * ä¿®æ”¹å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param customer å®¢æˆ·æ¡£æ¡ˆ
     * @return ç»“æžœ
     */
    int updateCustomer(Customer customer);
    /**
     * åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param id å®¢æˆ·æ¡£æ¡ˆä¸»é”®
     * @return ç»“æžœ
     */
    int deleteCustomerById(Long id);
    /**
     * æ‰¹é‡åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    int deleteCustomerByIds(Long[] ids);
public interface CustomerMapper extends BaseMapper<Customer> {
    IPage<CustomerVo> listPage(Page<CustomerDto> page, @Param("c") CustomerDto customer, @Param("loginUserId") Long loginUserId);
@@ -77,4 +30,8 @@
    IPage<CustomerTransactionsVo> customewTransactions(Page page, @Param("customerName") String customerName);
    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, @Param("customerId") Long customerId);
    List<Map<String, Object>> getShippedAmountByCustomerIds(@Param("customerIds") List<Long> customerIds);
    List<Map<String, Object>> getShippedAmountBySalesLedgerIds(@Param("salesLedgerIds") List<Long> salesLedgerIds);
}
src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -16,17 +16,14 @@
import com.ruoyi.basic.pojo.CustomerUser;
import com.ruoyi.basic.service.*;
import com.ruoyi.basic.vo.CustomerVo;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesQuotationMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
import com.ruoyi.sales.vo.CustomerTransactionsVo;
import lombok.AllArgsConstructor;
@@ -38,6 +35,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
@@ -54,11 +52,7 @@
@Slf4j
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService {
    @Autowired
    private SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private SalesQuotationMapper salesQuotationMapper;
    @Autowired
    private ReturnManagementMapper returnManagementMapper;
    private  SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private CustomerMapper customerMapper;
@@ -212,7 +206,6 @@
    public int updateCustomer(Customer customer) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long tenantId = loginUser.getTenantId();
        validateCustomerEditPermission(customer.getId());
        customer.setTenantId(tenantId);
        return customerMapper.updateById(customer);
    }
@@ -227,26 +220,12 @@
    @Transactional(rollbackFor = Exception.class)
    public int deleteCustomerByIds(Long[] ids) {
        List<Long> idList = Arrays.asList(ids);
        // æ£€æŸ¥æ˜¯å¦æœ‰é”€å”®å°è´¦å…³è”
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new QueryWrapper<SalesLedger>().lambda().in(SalesLedger::getCustomerId, idList));
        if (!salesLedgers.isEmpty()) {
            throw new RuntimeException("客户档案下有销售台账,请先删除销售台账");
            throw new RuntimeException("客户档案下有销售合同,请先删除销售合同");
        }
        // æ£€æŸ¥æ˜¯å¦æœ‰é”€å”®é€€è´§å…³è”
        List<ReturnManagement> returnManagements = returnManagementMapper.selectList(new QueryWrapper<ReturnManagement>().lambda().in(ReturnManagement::getCustomerId, idList));
        if (!returnManagements.isEmpty()) {
            throw new RuntimeException("客户档案下有销售退货,请先删除销售退货");
        }
        // æ£€æŸ¥æ˜¯å¦æœ‰é”€å”®æŠ¥ä»·å…³è”
        List<SalesQuotation> salesQuotations = salesQuotationMapper.selectList(new QueryWrapper<SalesQuotation>().lambda().in(SalesQuotation::getCustomerId, idList));
        if (!salesQuotations.isEmpty()) {
            throw new RuntimeException("客户档案下有销售报价,请先删除销售报价");
        }
        // æŸ¥è¯¢æ˜¯å¦æœ‰å·²åˆ†é…çš„公海客户
        List<Customer> assignedPools = customerMapper.selectList(
        /*List<Customer> assignedPools = customerMapper.selectList(
                new QueryWrapper<Customer>().lambda()
                        .in(Customer::getId, idList)
                        .eq(Customer::getType, 1).
@@ -254,7 +233,7 @@
        );
        if (!assignedPools.isEmpty()) {
            throw new RuntimeException("客户档案下有已分配的公海客户,请先收回");
        }
        }*/
        // åˆ é™¤å®¢æˆ·çš„同时也需要删除对应的客户跟随、附件和回访提醒
        for (Long id : ids) {
            customerFollowUpService.deleteByCustomerId(id);
@@ -294,11 +273,11 @@
            }
            // æ ¹æ® type å‚数设置客户类型(私海/公海)
            if (type != null) {
            /*if (type != null) {
                userList.forEach(customer -> {
                    customer.setType(type);
                });
            }
            }*/
            this.saveOrUpdateBatch(userList);
            return R.ok(true);
        } catch (Exception e) {
@@ -310,26 +289,18 @@
    @Override
    public List<Map<String, Object>> customerList(Customer customer) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long loginUserId = loginUser.getUserId();
        CustomerDto query = new CustomerDto();
        if (customer != null) {
            BeanUtils.copyProperties(customer, query);
        }
        LambdaQueryWrapper<Customer> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(Customer::getId, Customer::getCustomerName, Customer::getTaxpayerIdentificationNumber);
        // å¤ç”¨å®¢æˆ·åˆ—表的可见性规则,避免把别人的私海客户暴露给当前账号
        List<CustomerVo> visibleCustomers = customerMapper.list(query, loginUserId);
        if (CollectionUtils.isEmpty(visibleCustomers)) {
            return Collections.emptyList();
        }
        // èŽ·å–åŽŸå§‹æŸ¥è¯¢ç»“æžœ
        List<Map<String, Object>> result = customerMapper.selectMaps(queryWrapper);
        return visibleCustomers.stream().map(item -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("id", item.getId());
            map.put("customerName", item.getCustomerName());
            map.put("taxpayerIdentificationNumber", item.getTaxpayerIdentificationNumber());
            return map;
        }).collect(Collectors.toList());
        // å°†ä¸‹åˆ’线命名转换为驼峰命名
        return result.stream().map(map -> map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> underlineToCamel(entry.getKey()),
                        Map.Entry::getValue))
        ).collect(Collectors.toList());
    }
    // åˆ†é…å…¬æµ·å®¢æˆ·ç»™ç§æµ·
@@ -350,7 +321,6 @@
    public void recycleCustomer(CustomerDto customerDto) {
        Customer customer = customerMapper.selectById(customerDto.getId());
        if (customer.getType() == 1 && customer.getIsAssigned() == 1) {  // å…¬æµ·ä¸”已分配
            validatePublicSeaRecyclePermission(customer.getId());
            customer.setIsAssigned(0);
            customer.setUsageStatus(0L);
            customer.setUsageUser(0L);
@@ -413,52 +383,60 @@
        return this.updateById(customer);
    }
    private void validateCustomerEditPermission(Long customerId) {
        Customer customer = customerMapper.selectById(customerId);
        if (customer == null) {
            throw new RuntimeException("客户档案不存在");
        }
        if (!hasSalesModuleReference(customerId)) {
            return;
        }
        if (Objects.equals(customer.getType(), 0)) {
            throw new RuntimeException("已被销售模块引用的私海客户不能编辑");
        }
        if (Objects.equals(customer.getType(), 1) && Objects.equals(customer.getIsAssigned(), 1)) {
            throw new RuntimeException("已领用并且被销售模块引用的公海客户不能编辑");
        }
    }
    private void validatePublicSeaRecyclePermission(Long customerId) {
        if (hasSalesModuleReference(customerId)) {
            throw new RuntimeException("已领用并且被销售模块引用的公海客户不能回收");
        }
    }
    private boolean hasSalesModuleReference(Long customerId) {
        Long salesLedgerCount = salesLedgerMapper.selectCount(new QueryWrapper<SalesLedger>().lambda()
                .eq(SalesLedger::getCustomerId, customerId));
        if (salesLedgerCount != null && salesLedgerCount > 0) {
            return true;
        }
        Long salesQuotationCount = salesQuotationMapper.selectCount(new QueryWrapper<SalesQuotation>().lambda()
                .eq(SalesQuotation::getCustomerId, customerId));
        if (salesQuotationCount != null && salesQuotationCount > 0) {
            return true;
        }
        Long returnManagementCount = returnManagementMapper.selectCount(new QueryWrapper<ReturnManagement>().lambda()
                .eq(ReturnManagement::getCustomerId, customerId));
        return returnManagementCount != null && returnManagementCount > 0;
    }
    @Override
    public IPage<CustomerTransactionsVo> customewTransactions(Page page, String customerName) {
        return customerMapper.customewTransactions(page, customerName);
        IPage<CustomerTransactionsVo> p = customerMapper.customewTransactions(page, customerName);
        List<CustomerTransactionsVo> records = p.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<Long> customerIds = records.stream().map(CustomerTransactionsVo::getCustomerId).collect(Collectors.toList());
            List<Map<String, Object>> shippedAmountList = customerMapper.getShippedAmountByCustomerIds(customerIds);
            Map<Long, BigDecimal> shippedAmountMap = new HashMap<>();
            for (Map<String, Object> map : shippedAmountList) {
                if (map.get("outboundAmount") != null) {
                    shippedAmountMap.put(((Number) map.get("customerId")).longValue(), new BigDecimal(String.valueOf(map.get("outboundAmount"))));
                }
            }
            for (CustomerTransactionsVo vo : records) {
                BigDecimal outboundAmount = shippedAmountMap.getOrDefault(vo.getCustomerId(), BigDecimal.ZERO);
                vo.setShippedAmount(outboundAmount);
                BigDecimal contractAmounts = vo.getContractAmounts() == null ? BigDecimal.ZERO : vo.getContractAmounts();
                BigDecimal unshippedAmount = contractAmounts.subtract(outboundAmount);
                if (unshippedAmount.compareTo(BigDecimal.ZERO) < 0) {
                    unshippedAmount = BigDecimal.ZERO;
                }
                vo.setUnshippedAmount(unshippedAmount);
                AmountUtils.normalizeCustomerTransaction(vo);
            }
        }
        return p;
    }
    @Override
    public IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long customerId) {
        return customerMapper.customewTransactionsDetails(page, customerId);
        IPage<CustomerTransactionsDetailsVo> p = customerMapper.customewTransactionsDetails(page, customerId);
        List<CustomerTransactionsDetailsVo> records = p.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<Long> salesLedgerIds = records.stream().map(CustomerTransactionsDetailsVo::getSalesLedgerId).collect(Collectors.toList());
            List<Map<String, Object>> shippedAmountList = customerMapper.getShippedAmountBySalesLedgerIds(salesLedgerIds);
            Map<Long, BigDecimal> shippedAmountMap = new HashMap<>();
            for (Map<String, Object> map : shippedAmountList) {
                if (map.get("outboundAmount") != null) {
                    shippedAmountMap.put(((Number) map.get("salesLedgerId")).longValue(), new BigDecimal(String.valueOf(map.get("outboundAmount"))));
                }
            }
            for (CustomerTransactionsDetailsVo vo : records) {
                BigDecimal outboundAmount = shippedAmountMap.getOrDefault(vo.getSalesLedgerId(), BigDecimal.ZERO);
                vo.setShippedAmount(outboundAmount);
                BigDecimal contractAmount = vo.getContractAmount() == null ? BigDecimal.ZERO : vo.getContractAmount();
                BigDecimal unshippedAmount = contractAmount.subtract(outboundAmount);
                if (unshippedAmount.compareTo(BigDecimal.ZERO) < 0) {
                    unshippedAmount = BigDecimal.ZERO;
                }
                vo.setUnshippedAmount(unshippedAmount);
                AmountUtils.normalizeCustomerTransactionDetail(vo);
            }
        }
        return p;
    }
    /**
src/main/java/com/ruoyi/basic/service/impl/SupplierServiceImpl.java
@@ -10,38 +10,54 @@
import com.ruoyi.basic.mapper.SupplierManageMapper;
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.basic.service.ISupplierService;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.vo.SupplierTransactionsDetailsVo;
import com.ruoyi.purchase.vo.SupplierTransactionsVo;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityUnqualified;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.pojo.StockInRecord;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class SupplierServiceImpl extends ServiceImpl<SupplierManageMapper,SupplierManage> implements ISupplierService {
public class SupplierServiceImpl extends ServiceImpl<SupplierManageMapper, SupplierManage> implements ISupplierService {
    private final SupplierManageMapper supplierMapper;
    private final PurchaseLedgerMapper purchaseLedgerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final StockInRecordMapper stockInRecordMapper;
    private final QualityInspectMapper qualityInspectMapper;
    private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
    /**
     * ä¾›åº”商新增
     *
     * @param supplierManage
     * @return
     */
    @Override
    public void saveSupplier(SupplierManage supplierManage) {
        LambdaQueryWrapper<SupplierManage> supplierManageLambdaQueryWrapper = new LambdaQueryWrapper<>();
        supplierManageLambdaQueryWrapper.eq(SupplierManage::getSupplierName,supplierManage.getSupplierName());
        supplierManageLambdaQueryWrapper.eq(SupplierManage::getSupplierName, supplierManage.getSupplierName());
        if (supplierMapper.selectCount(supplierManageLambdaQueryWrapper) > 0) {
            throw new RuntimeException("供应商已存在");
        }
@@ -50,6 +66,7 @@
    /**
     * ä¾›åº”商删除
     *
     * @param ids
     * @return
     */
@@ -67,6 +84,7 @@
    /**
     * ä¾›åº”商详情
     *
     * @param id
     * @return
     */
@@ -77,6 +95,7 @@
    /**
     * ä¾›åº”商修改
     *
     * @param supplierManage
     * @return
     */
@@ -87,17 +106,19 @@
    /**
     * ä¾›åº”商分页查询
     *
     * @param page
     * @param supplierManageDto
     * @return
     */
    @Override
    public IPage<SupplierManage> supplierListPage(Page page, SupplierManageDto supplierManageDto) {
        return supplierMapper.supplierListPage(page,supplierManageDto);
        return supplierMapper.supplierListPage(page, supplierManageDto);
    }
    /**
     * ä¾›åº”商导出
     *
     * @param response
     * @param supplierManageDto
     */
@@ -115,19 +136,19 @@
            ExcelUtil<SupplierManageExcelDto> util = new ExcelUtil<SupplierManageExcelDto>(SupplierManageExcelDto.class);
            List<SupplierManageExcelDto> list = util.importExcel(file.getInputStream());
            ArrayList<SupplierManage> supplierManages = new ArrayList<>();
            list.stream().forEach(dto -> {
            list.forEach(dto -> {
                SupplierManage supplierManage = new SupplierManage();
                BeanUtils.copyProperties(dto,supplierManage);
                BeanUtils.copyProperties(dto, supplierManage);
                supplierManage.setMaintainTime(LocalDate.now());
                Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
                supplierManage.setMaintainUserId(Integer.parseInt(userId+""));
                supplierManage.setMaintainUserId(Integer.parseInt(userId + ""));
                supplierManages.add(supplierManage);
            });
            this.saveOrUpdateBatch(supplierManages);
            return true;
        }catch (Exception e){
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
@@ -135,11 +156,221 @@
    @Override
    public IPage<SupplierTransactionsVo> supplierTransactions(Page page, String supplierName) {
        return supplierMapper.supplierTransactions(page,supplierName);
        IPage<SupplierTransactionsVo> plPage = supplierMapper.supplierTransactions(page, supplierName);
        List<SupplierTransactionsVo> resultList = plPage.getRecords();
        if (resultList.isEmpty()) {
            return plPage;
        }
        List<Long> supplierIds = resultList.stream().map(SupplierTransactionsVo::getSupplierId).collect(Collectors.toList());
        List<PurchaseLedger> purchaseLedgers = purchaseLedgerMapper.selectList(new LambdaQueryWrapper<PurchaseLedger>()
                .in(PurchaseLedger::getSupplierId, supplierIds));
        if (purchaseLedgers.isEmpty()) {
            for (SupplierTransactionsVo vo : resultList) {
                vo.setShippedAmount(BigDecimal.ZERO);
                BigDecimal contractAmount = vo.getContractAmounts() == null ? BigDecimal.ZERO : vo.getContractAmounts();
                vo.setUnshippedAmount(contractAmount);
                AmountUtils.normalizeSupplierTransaction(vo);
            }
            return plPage;
        }
        List<Long> ledgerIds = purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList());
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
                .in(SalesLedgerProduct::getSalesLedgerId, ledgerIds)
                .eq(SalesLedgerProduct::getType, 2));
        //  ä¸è´¨æ£€ç›´æŽ¥å…¥åº“çš„ - 7
        List<StockInRecord> directInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                .eq(StockInRecord::getRecordType, "7")
                .eq(StockInRecord::getApprovalStatus, 1)
                .in(StockInRecord::getRecordId, ledgerIds));
        //  é€šè¿‡è´¨æ£€å…¥åº“çš„
        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .in(QualityInspect::getPurchaseLedgerId, ledgerIds));
        List<Long> inspectIds = qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList());
        //  è´¨æ£€åˆæ ¼å…¥åº“ - 10
        List<StockInRecord> qualifiedInboundRecords = new ArrayList<>();
        List<QualityUnqualified> qualityUnqualifieds = new ArrayList<>();
        if (!inspectIds.isEmpty()) {
            qualifiedInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                    .eq(StockInRecord::getRecordType, "10")
                    .eq(StockInRecord::getApprovalStatus, 1)
                    .in(StockInRecord::getRecordId, inspectIds));
            qualityUnqualifieds = qualityUnqualifiedMapper.selectList(new LambdaQueryWrapper<QualityUnqualified>()
                    .in(QualityUnqualified::getInspectId, inspectIds));
        }
        List<Long> unqualifiedIds = qualityUnqualifieds.stream().map(QualityUnqualified::getId).collect(Collectors.toList());
        //  è´¨æ£€ä¸åˆæ ¼å…¥åº“ - 11
        List<StockInRecord> unqualifiedInboundRecords = new ArrayList<>();
        if (!unqualifiedIds.isEmpty()) {
            unqualifiedInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                    .eq(StockInRecord::getRecordType, "11")
                    .eq(StockInRecord::getApprovalStatus, 1)
                    .in(StockInRecord::getRecordId, unqualifiedIds));
        }
        for (SupplierTransactionsVo vo : resultList) {
            BigDecimal shippedAmount = BigDecimal.ZERO;
            List<PurchaseLedger> currentLedgers = purchaseLedgers.stream().filter(pl -> pl.getSupplierId().equals(vo.getSupplierId())).toList();
            for (PurchaseLedger ledger : currentLedgers) {
                Long ledgerId = ledger.getId();
                List<StockInRecord> currentDirectRecords = directInboundRecords.stream().filter(s -> s.getRecordId().equals(ledgerId)).toList();
                for (StockInRecord sir : currentDirectRecords) {
                    List<SalesLedgerProduct> currentProducts = salesLedgerProducts.stream().filter(s -> s.getSalesLedgerId().equals(ledgerId)).toList();
                    for (SalesLedgerProduct slp : currentProducts) {
                        if (sir.getStockInNum() != null && slp.getTaxInclusiveUnitPrice() != null) {
                            shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(slp.getTaxInclusiveUnitPrice()));
                        }
                    }
                }
                List<QualityInspect> currentInspects = qualityInspects.stream().filter(q -> q.getPurchaseLedgerId().equals(ledgerId)).toList();
                for (QualityInspect inspect : currentInspects) {
                    List<StockInRecord> currentQualifiedRecords = qualifiedInboundRecords.stream().filter(s -> s.getRecordId().equals(inspect.getId())).toList();
                    for (StockInRecord sir : currentQualifiedRecords) {
                        BigDecimal minPrice = getMinTaxInclusiveUnitPrice(salesLedgerProducts, ledgerId, sir.getProductModelId());
                        if (sir.getStockInNum() != null && minPrice != null) {
                            shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(minPrice));
                        }
                    }
                    List<QualityUnqualified> currentUnqualifieds = qualityUnqualifieds.stream().filter(q -> q.getInspectId().equals(inspect.getId())).toList();
                    for (QualityUnqualified unqualified : currentUnqualifieds) {
                        List<StockInRecord> currentUnqualifiedRecords = unqualifiedInboundRecords.stream().filter(s -> s.getRecordId().equals(unqualified.getId())).toList();
                        for (StockInRecord sir : currentUnqualifiedRecords) {
                            BigDecimal minPrice = getMinTaxInclusiveUnitPrice(salesLedgerProducts, ledgerId, sir.getProductModelId());
                            if (sir.getStockInNum() != null && minPrice != null) {
                                shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(minPrice));
                            }
                        }
                    }
                }
            }
            BigDecimal contractAmount = vo.getContractAmounts() == null ? BigDecimal.ZERO : vo.getContractAmounts();
            if (shippedAmount.subtract(contractAmount).compareTo(BigDecimal.ZERO) > 0) {
                shippedAmount = contractAmount;
            }
            vo.setShippedAmount(shippedAmount);
            vo.setUnshippedAmount(contractAmount.subtract(shippedAmount));
            AmountUtils.normalizeSupplierTransaction(vo);
        }
        return plPage;
    }
    @Override
    public IPage<SupplierTransactionsDetailsVo> supplierTransactionsDetails(Page page, Long supplierId) {
        return supplierMapper.supplierTransactionsDetails(page,supplierId);
        IPage<SupplierTransactionsDetailsVo> plPage = supplierMapper.supplierTransactionsDetails(page, supplierId);
        List<SupplierTransactionsDetailsVo> resultList = plPage.getRecords();
        if (resultList.isEmpty()) {
            return plPage;
        }
        List<Long> ledgerIds = resultList.stream().map(SupplierTransactionsDetailsVo::getPurchaseLedgerId).collect(Collectors.toList());
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
                .in(SalesLedgerProduct::getSalesLedgerId, ledgerIds)
                .eq(SalesLedgerProduct::getType, 2));
        //  ä¸è´¨æ£€ç›´æŽ¥å…¥åº“çš„ - 7
        List<StockInRecord> directInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                .eq(StockInRecord::getRecordType, "7")
                .eq(StockInRecord::getApprovalStatus, 1)
                .in(StockInRecord::getRecordId, ledgerIds));
        //  é€šè¿‡è´¨æ£€å…¥åº“çš„
        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .in(QualityInspect::getPurchaseLedgerId, ledgerIds));
        List<Long> inspectIds = qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList());
        //  è´¨æ£€åˆæ ¼å…¥åº“ - 10
        List<StockInRecord> qualifiedInboundRecords = new ArrayList<>();
        List<QualityUnqualified> qualityUnqualifieds = new ArrayList<>();
        if (!inspectIds.isEmpty()) {
            qualifiedInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                    .eq(StockInRecord::getRecordType, "10")
                    .eq(StockInRecord::getApprovalStatus, 1)
                    .in(StockInRecord::getRecordId, inspectIds));
            qualityUnqualifieds = qualityUnqualifiedMapper.selectList(new LambdaQueryWrapper<QualityUnqualified>()
                    .in(QualityUnqualified::getInspectId, inspectIds));
        }
        List<Long> unqualifiedIds = qualityUnqualifieds.stream().map(QualityUnqualified::getId).collect(Collectors.toList());
        //  è´¨æ£€ä¸åˆæ ¼å…¥åº“ - 11
        List<StockInRecord> unqualifiedInboundRecords = new ArrayList<>();
        if (!unqualifiedIds.isEmpty()) {
            unqualifiedInboundRecords = stockInRecordMapper.selectList(new LambdaQueryWrapper<StockInRecord>()
                    .eq(StockInRecord::getRecordType, "11")
                    .eq(StockInRecord::getApprovalStatus, 1)
                    .in(StockInRecord::getRecordId, unqualifiedIds));
        }
        for (SupplierTransactionsDetailsVo vo : resultList) {
            BigDecimal shippedAmount = BigDecimal.ZERO;
            List<StockInRecord> currentDirectRecords = directInboundRecords.stream().filter(s -> s.getRecordId().equals(vo.getPurchaseLedgerId())).toList();
            for (StockInRecord sir : currentDirectRecords) {
                List<SalesLedgerProduct> currentProducts = salesLedgerProducts.stream().filter(s -> s.getSalesLedgerId().equals(vo.getPurchaseLedgerId())).toList();
                for (SalesLedgerProduct slp : currentProducts) {
                    if (sir.getStockInNum() != null && slp.getTaxInclusiveUnitPrice() != null) {
                        shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(slp.getTaxInclusiveUnitPrice()));
                    }
                }
            }
            List<QualityInspect> currentInspects = qualityInspects.stream().filter(q -> q.getPurchaseLedgerId().equals(vo.getPurchaseLedgerId())).toList();
            for (QualityInspect inspect : currentInspects) {
                List<StockInRecord> currentQualifiedRecords = qualifiedInboundRecords.stream().filter(s -> s.getRecordId().equals(inspect.getId())).toList();
                for (StockInRecord sir : currentQualifiedRecords) {
                    BigDecimal minPrice = getMinTaxInclusiveUnitPrice(salesLedgerProducts, vo.getPurchaseLedgerId(), sir.getProductModelId());
                    if (sir.getStockInNum() != null && minPrice != null) {
                        shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(minPrice));
                    }
                }
                List<QualityUnqualified> currentUnqualifieds = qualityUnqualifieds.stream().filter(q -> q.getInspectId().equals(inspect.getId())).toList();
                for (QualityUnqualified unqualified : currentUnqualifieds) {
                    List<StockInRecord> currentUnqualifiedRecords = unqualifiedInboundRecords.stream().filter(s -> s.getRecordId().equals(unqualified.getId())).toList();
                    for (StockInRecord sir : currentUnqualifiedRecords) {
                        BigDecimal minPrice = getMinTaxInclusiveUnitPrice(salesLedgerProducts, vo.getPurchaseLedgerId(), sir.getProductModelId());
                        if (sir.getStockInNum() != null && minPrice != null) {
                            shippedAmount = shippedAmount.add(sir.getStockInNum().multiply(minPrice));
                        }
                    }
                }
            }
            BigDecimal contractAmount = vo.getContractAmount() == null ? BigDecimal.ZERO : vo.getContractAmount();
            if (shippedAmount.subtract(contractAmount).compareTo(BigDecimal.ZERO) > 0) {
                shippedAmount = contractAmount;
            }
            vo.setShippedAmount(shippedAmount);
            vo.setUnshippedAmount(contractAmount.subtract(shippedAmount));
            AmountUtils.normalizeSupplierTransactionDetail(vo);
        }
        return plPage;
    }
    private BigDecimal getMinTaxInclusiveUnitPrice(List<SalesLedgerProduct> allSlp, Long salesLedgerId, Long productModelId) {
        return allSlp.stream()
                .filter(s -> s.getSalesLedgerId().equals(salesLedgerId) && s.getProductModelId() != null && s.getProductModelId().equals(productModelId))
                .map(SalesLedgerProduct::getTaxInclusiveUnitPrice)
                .filter(java.util.Objects::nonNull)
                .min(BigDecimal::compareTo)
                .orElse(null);
    }
}
src/main/java/com/ruoyi/basic/vo/CustomerVo.java
@@ -25,9 +25,4 @@
     * å…±äº«ç”¨æˆ·ID字符串(SQL查询返回,用于转换为List)
     */
    private String userIdsStr;
    /**
     * æ˜¯å¦è¢«é”€å”®æ¨¡å—引用:0-否,1-是
     */
    private Integer salesReferenceFlag;
}
src/main/java/com/ruoyi/basic/vo/ProductModelVo.java
@@ -1,5 +1,6 @@
package com.ruoyi.basic.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.basic.pojo.ProductModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -10,5 +11,8 @@
@Data
public class ProductModelVo extends ProductModel {
    @TableField(exist = false)
    private String topProductName;
    private List<String> batchNoList;
}
src/main/java/com/ruoyi/collaborativeApproval/controller/DutyPlanController.java
@@ -7,6 +7,8 @@
import com.ruoyi.collaborativeApproval.pojo.RulesRegulationsManagement;
import com.ruoyi.collaborativeApproval.service.DutyPlanService;
import com.ruoyi.common.utils.excel.ExcelUtils;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
@@ -34,18 +36,21 @@
        return AjaxResult.success(dutyPlanService.getNum());
    }
    @Log(title = "新增值班计划", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public AjaxResult add(@RequestBody DutyPlan dutyPlan){
        return AjaxResult.success(dutyPlanService.save(dutyPlan));
    }
    @Log(title = "修改值班计划", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改")
    public AjaxResult update(@RequestBody DutyPlan dutyPlan){
        return AjaxResult.success(dutyPlanService.updateById(dutyPlan));
    }
    @Log(title = "删除值班计划", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除")
    public AjaxResult delete(@RequestBody List<Long> ids){
@@ -54,6 +59,7 @@
        }
        return AjaxResult.success(dutyPlanService.removeBatchByIds(ids));
    }
    @Log(title = "导出值班计划", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @Operation(summary = "导出")
    public void exportData(HttpServletResponse response, DutyPlanDTO dutyPlanDTO){
src/main/java/com/ruoyi/collaborativeApproval/controller/MeetingController.java
@@ -13,6 +13,8 @@
import com.ruoyi.collaborativeApproval.vo.SearchMeetingRoomVo;
import com.ruoyi.collaborativeApproval.vo.SearchMeetingUseVo;
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.domain.R;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -42,6 +44,7 @@
        return R.ok(meetingService.getMeetingRoomList(vo));
    }
    @Log(title = "新增会议室", businessType = BusinessType.INSERT)
    @PostMapping("/saveRoom")
    public R saveRoom(@RequestBody MeetingRoom meetingRoom) {
        meetingService.saveMeetRoom(meetingRoom);
@@ -53,6 +56,7 @@
        return R.ok(meetingService.findMeetRoomById(id));
    }
    @Log(title = "删除会议室", businessType = BusinessType.DELETE)
    @DeleteMapping("/delRoom/{id}")
    public R deleteRoom(@PathVariable Long id) {
        meetingService.deleteMeetingRoom(id);
@@ -69,18 +73,21 @@
        return R.ok(meetingService.getMeetingDraftList(vo));
    }
    @Log(title = "保存会议草稿", businessType = BusinessType.INSERT)
    @PostMapping("/saveDraft")
    public R saveMeetingDraft(@RequestBody MeetDraft meetDraft) {
        meetingService.saveMeetDraft(meetDraft);
        return R.ok();
    }
    @Log(title = "删除会议草稿", businessType = BusinessType.DELETE)
    @DeleteMapping("/delDraft/{id}")
    public R deleteMeetingDraft(@PathVariable Long id) {
        meetingService.deleteMeetingDraft(id);
        return R.ok();
    }
    @Log(title = "新增会议申请", businessType = BusinessType.INSERT)
    @PostMapping("/saveMeetingApplication")
    public R saveMeetApplication(@RequestBody MeetApplication meetApplication) {
       return meetingService.saveMeetApplication(meetApplication);
@@ -110,6 +117,7 @@
        return R.ok(meetingService.getMeetingMinutesById(id));
    }
    @Log(title = "保存会议纪要", businessType = BusinessType.INSERT)
    @PostMapping("/saveMeetingMinutes")
    public R saveMeetingMinutes(@RequestBody MeetingMinutes meetingMinutes) {
        meetingService.saveMeetingMinutes(meetingMinutes);
@@ -127,6 +135,7 @@
    }
    @Operation(summary = "会议室设置导出")
    @Log(title = "导出会议室设置", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        List<MeetingRoom> accountExpenses = meetingService.list();
@@ -137,6 +146,7 @@
    private final MeetDraftMapper meetDraftMapper;
    @Operation(summary = "会议草稿导出")
    @Log(title = "导出会议草稿", businessType = BusinessType.EXPORT)
    @PostMapping("/exportOne")
    public void exportOne(HttpServletResponse response) {
        List<MeetDraft> accountExpenses = meetDraftMapper.selectList(new LambdaQueryWrapper<MeetDraft>());
src/main/java/com/ruoyi/collaborativeApproval/controller/NoticeTypeController.java
@@ -5,6 +5,8 @@
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.collaborativeApproval.pojo.NoticeType;
import com.ruoyi.collaborativeApproval.service.NoticeTypeService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +35,7 @@
     * @param noticeType
     * @return
     */
    @Log(title = "新增公告类型", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody NoticeType noticeType) {
        return AjaxResult.success(noticeTypeService.saveOrUpdate(noticeType));
@@ -43,6 +46,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除公告类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delNoticeType(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementController.java
@@ -8,6 +8,8 @@
import com.ruoyi.collaborativeApproval.pojo.SealApplicationManagement;
import com.ruoyi.collaborativeApproval.service.RulesRegulationsManagementService;
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.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -33,6 +35,7 @@
        return AjaxResult.success(rulesRegulationsManagementService.listPage(page, rulesRegulationsManagement));
    }
    @Log(title = "新增规章制度", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public AjaxResult add(@RequestBody RulesRegulationsManagement rulesRegulationsManagement){
@@ -40,12 +43,14 @@
        return AjaxResult.success(rulesRegulationsManagement.getId());
    }
    @Log(title = "修改规章制度", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改")
    public AjaxResult update(@RequestBody RulesRegulationsManagement rulesRegulationsManagement){
        return AjaxResult.success(rulesRegulationsManagementService.updateById(rulesRegulationsManagement));
    }
    @Log(title = "删除规章制度", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除")
    public AjaxResult delete(@PathVariable("ids") List<Long> ids){
@@ -55,11 +60,13 @@
        return AjaxResult.success(rulesRegulationsManagementService.removeBatchByIds(ids));
    }
    //规则查看时新增阅读状态
    @Log(title = "新增阅读状态", businessType = BusinessType.INSERT)
    @PostMapping("/addReadingStatus")
    @Operation(summary = "新增阅读状态")
    public AjaxResult addReadingStatus(@RequestBody ReadingStatus readingStatus){
        return AjaxResult.success(readingStatusMapper.insert(readingStatus));
    }
    @Log(title = "修改阅读状态", businessType = BusinessType.UPDATE)
    @PostMapping("/updateReadingStatus")
    @Operation(summary = "修改阅读状态")
    public AjaxResult updateReadingStatus(@RequestBody ReadingStatus readingStatus){
@@ -77,6 +84,7 @@
    }
    @Operation(summary = "规章制度管理导出")
    @Log(title = "导出规章制度", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        List<RulesRegulationsManagement> accountExpenses = rulesRegulationsManagementService.list();
src/main/java/com/ruoyi/collaborativeApproval/controller/RulesRegulationsManagementFileController.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.collaborativeApproval.pojo.RulesRegulationsManagementFile;
import com.ruoyi.collaborativeApproval.service.RulesRegulationsManagementFileService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.quality.pojo.QualityInspectFile;
import com.ruoyi.quality.service.IQualityInspectFileService;
@@ -33,6 +35,7 @@
     * @param rulesRegulationsManagementFile
     * @return
     */
    @Log(title = "规章制度文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody RulesRegulationsManagementFile rulesRegulationsManagementFile) {
        return AjaxResult.success(rulesRegulationsManagementFileService.save(rulesRegulationsManagementFile));
@@ -43,6 +46,7 @@
     * @param ids
     * @return
     */
    @Log(title = "规章制度文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delQualityUnqualified(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/collaborativeApproval/controller/SealApplicationManagementController.java
@@ -10,6 +10,7 @@
import com.ruoyi.collaborativeApproval.service.SealApplicationManagementService;
import com.ruoyi.common.utils.SecurityUtils;
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.domain.AjaxResult;
import com.ruoyi.project.system.service.ISysNoticeService;
@@ -39,6 +40,7 @@
        return AjaxResult.success(sealApplicationManagementService.listPage(page,sealApplicationManagement));
    }
    @Log(title = "用章申请", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public AjaxResult add(@RequestBody SealApplicationManagementDTO sealApplicationManagement){
@@ -57,6 +59,7 @@
        return AjaxResult.success();
    }
    @Log(title = "用章申请", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改")
    public AjaxResult update(@RequestBody SealApplicationManagementDTO sealApplicationManagement){
@@ -68,6 +71,7 @@
        return AjaxResult.success(sealApplicationManagementService.updateById(sealApplicationManagement));
    }
    @Log(title = "用章申请", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除")
    public AjaxResult delete(@PathVariable("ids") List<Long> ids){
@@ -80,6 +84,7 @@
        return AjaxResult.success(sealApplicationManagementService.removeBatchByIds(ids));
    }
    @Log(title = "用章申请", businessType = BusinessType.EXPORT)
    @Operation(summary = "用印申请管理导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
src/main/java/com/ruoyi/collaborativeApproval/controller/StaffContactsPersonalController.java
@@ -4,6 +4,8 @@
import com.ruoyi.collaborativeApproval.dto.StaffContactsPersonalDTO;
import com.ruoyi.collaborativeApproval.pojo.StaffContactsPersonal;
import com.ruoyi.collaborativeApproval.service.StaffContactsPersonalService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
@@ -21,12 +23,14 @@
        return AjaxResult.success(staffContactsPersonalService.listPage(page, staffContactsPersonalDTO));
    }
    @Log(title = "新增员工通讯录", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public AjaxResult add(@RequestBody StaffContactsPersonal staffContactsPersonal) {
        return AjaxResult.success(staffContactsPersonalService.save(staffContactsPersonal));
    }
    @Log(title = "删除员工通讯录", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete/{id}")
    @Operation(summary = "删除")
    public AjaxResult delete(@PathVariable("id") Long id) {
src/main/java/com/ruoyi/collaborativeApproval/service/impl/MeetingServiceImpl.java
@@ -238,9 +238,9 @@
                    .or()
                    .eq(MeetApplication::getApplicationType, "notification");
        });
        if (Objects.nonNull(vo.getDate())) {
        if (Objects.nonNull(vo.getMeetingDate())) {
            alWrapper.and(wrapper -> {
                wrapper.eq(MeetApplication::getMeetingDate, vo.getDate());
                wrapper.eq(MeetApplication::getMeetingDate, vo.getMeetingDate());
            });
        }
        alWrapper.orderByAsc(MeetApplication::getStartTime);
src/main/java/com/ruoyi/collaborativeApproval/vo/SearchMeetingUseVo.java
@@ -16,6 +16,4 @@
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date meetingDate;
    private String date;
}
src/main/java/com/ruoyi/common/enums/StockOutQualifiedRecordTypeEnum.java
@@ -7,6 +7,7 @@
public enum StockOutQualifiedRecordTypeEnum implements BaseEnum<String> {
    CUSTOMIZATION_STOCK_OUT("1", "合格自定义出库"),
    PRODUCTION_REPORT_STOCK_OUT("3", "生产报工-出库"),
    SALE_STOCK_OUT("8", "销售-出库"),
    PURCHASE_RETURN_STOCK_OUT("9", "采购退货"),
    CUSTOMIZATION_UNSTOCK_OUT("10", "不合格自定义出库"),
    SALE_SHIP_STOCK_OUT("13", "销售-发货出库"),
src/main/java/com/ruoyi/common/utils/AmountUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package com.ruoyi.common.utils;
import com.ruoyi.purchase.dto.ProcurementBusinessSummaryDto;
import com.ruoyi.purchase.vo.SupplierTransactionsDetailsVo;
import com.ruoyi.purchase.vo.SupplierTransactionsVo;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
import com.ruoyi.sales.vo.CustomerTransactionsVo;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.List;
/**
 * é‡‘额精度工具:统一保留 6 ä½å°æ•°
 */
public final class AmountUtils {
    public static final int AMOUNT_SCALE = 6;
    public static final RoundingMode AMOUNT_ROUNDING = RoundingMode.HALF_UP;
    private AmountUtils() {
    }
    public static BigDecimal scaleAmount(BigDecimal value) {
        if (value == null) {
            return BigDecimal.ZERO.setScale(AMOUNT_SCALE, AMOUNT_ROUNDING);
        }
        return value.setScale(AMOUNT_SCALE, AMOUNT_ROUNDING);
    }
    public static BigDecimal calcTaxExclusiveTotalPrice(BigDecimal taxInclusiveTotalPrice, BigDecimal taxRate) {
        if (taxInclusiveTotalPrice == null || taxRate == null) {
            return scaleAmount(BigDecimal.ZERO);
        }
        BigDecimal divisor = BigDecimal.ONE.add(
                taxRate.divide(BigDecimal.valueOf(100), AMOUNT_SCALE, AMOUNT_ROUNDING)
        );
        return scaleAmount(taxInclusiveTotalPrice.divide(divisor, AMOUNT_SCALE, AMOUNT_ROUNDING));
    }
    public static void normalizeSalesLedgerProduct(SalesLedgerProduct product) {
        if (product == null) {
            return;
        }
        product.setTaxInclusiveUnitPrice(scaleAmount(product.getTaxInclusiveUnitPrice()));
        product.setTaxInclusiveTotalPrice(scaleAmount(product.getTaxInclusiveTotalPrice()));
        product.setTaxExclusiveTotalPrice(scaleAmount(product.getTaxExclusiveTotalPrice()));
    }
    public static void normalizeSalesLedgerProducts(Collection<SalesLedgerProduct> products) {
        if (products == null) {
            return;
        }
        products.forEach(AmountUtils::normalizeSalesLedgerProduct);
    }
    public static void normalizeContractAmount(Object entity) {
        if (entity == null) {
            return;
        }
        try {
            java.lang.reflect.Field field = entity.getClass().getDeclaredField("contractAmount");
            field.setAccessible(true);
            field.set(entity, scaleAmount((BigDecimal) field.get(entity)));
        } catch (NoSuchFieldException ignored) {
        } catch (Exception e) {
            throw new RuntimeException("标准化合同金额失败", e);
        }
    }
    public static void normalizeSupplierTransaction(SupplierTransactionsVo vo) {
        if (vo == null) {
            return;
        }
        vo.setContractAmounts(scaleAmount(vo.getContractAmounts()));
        vo.setShippedAmount(scaleAmount(vo.getShippedAmount()));
        vo.setUnshippedAmount(scaleAmount(vo.getUnshippedAmount()));
    }
    public static void normalizeSupplierTransactionDetail(SupplierTransactionsDetailsVo vo) {
        if (vo == null) {
            return;
        }
        vo.setContractAmount(scaleAmount(vo.getContractAmount()));
        vo.setShippedAmount(scaleAmount(vo.getShippedAmount()));
        vo.setUnshippedAmount(scaleAmount(vo.getUnshippedAmount()));
    }
    public static void normalizeCustomerTransaction(CustomerTransactionsVo vo) {
        if (vo == null) {
            return;
        }
        vo.setContractAmounts(scaleAmount(vo.getContractAmounts()));
        vo.setShippedAmount(scaleAmount(vo.getShippedAmount()));
        vo.setUnshippedAmount(scaleAmount(vo.getUnshippedAmount()));
    }
    public static void normalizeCustomerTransactionDetail(CustomerTransactionsDetailsVo vo) {
        if (vo == null) {
            return;
        }
        vo.setContractAmount(scaleAmount(vo.getContractAmount()));
        vo.setShippedAmount(scaleAmount(vo.getShippedAmount()));
        vo.setUnshippedAmount(scaleAmount(vo.getUnshippedAmount()));
    }
    public static void normalizeProcurementBusinessSummary(ProcurementBusinessSummaryDto dto) {
        if (dto == null) {
            return;
        }
        dto.setPurchaseAmount(scaleAmount(dto.getPurchaseAmount()));
        dto.setAveragePrice(scaleAmount(dto.getAveragePrice()));
        dto.setReturnAmount(scaleAmount(dto.getReturnAmount()));
    }
    public static void normalizeProcurementBusinessSummaryList(List<ProcurementBusinessSummaryDto> list) {
        if (list == null) {
            return;
        }
        list.forEach(AmountUtils::normalizeProcurementBusinessSummary);
    }
}
src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -3,13 +3,17 @@
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
/**
 * æ—¶é—´å·¥å…·ç±»
 *
 *
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
@@ -25,13 +29,13 @@
    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    /**
     * èŽ·å–å½“å‰Date型日期
     *
     *
     * @return Date() å½“前日期
     */
    public static Date getNowDate()
@@ -41,15 +45,16 @@
    public static LocalDate toLocalDate(Date date){
        if(date == null) return LocalDate.now();
        // java.sql.Date.toInstant() ä¼šæŠ› UnsupportedOperationException,需先转为 java.util.Date
        Instant instant = new java.util.Date(date.getTime()).toInstant();
        return instant.atZone(ZoneId.of("Asia/Shanghai"))
        // 2. æŒ‡å®šå›ºå®šæ—¶åŒºè½¬æ¢ï¼ˆå¦‚东八区UTC+8、UTC零时区)
        return date.toInstant()
                .atZone(ZoneId.of("Asia/Shanghai")) // ä¸œå…«åŒºï¼ˆåŒ—京/上海时区)
                // .atZone(ZoneId.of("UTC")) // å¯é€‰ï¼šUTC零时区
                .toLocalDate();
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸ, é»˜è®¤æ ¼å¼ä¸ºyyyy-MM-dd
     *
     *
     * @return String
     */
    public static String getDate()
src/main/java/com/ruoyi/common/utils/OrderUtils.java
@@ -61,11 +61,8 @@
     * @param <T> å®žä½“类型
     * @return è®¢å•编号
     */
    public static <T> String countTodayByCreateTime(BaseMapper<T> mapper,String preFix,String code, LocalDateTime createTime) {
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        LocalDate today = createTime.toLocalDate();
    public static <T> String countTodayByCreateTime(BaseMapper<T> mapper,String preFix,String code) {
        LocalDate today = LocalDate.now();
        LocalDateTime todayStart = today.atStartOfDay();
        LocalDateTime tomorrowStart = today.plusDays(1).atStartOfDay();
        String dateStr = today.format(DateTimeFormatter.BASIC_ISO_DATE);
@@ -112,17 +109,13 @@
     * @param <T> å®žä½“类泛型
     * @return å½“天记录数量
     */
    public static <T> String countAfterServiceTodayByCreateTime(BaseMapper<T> mapper,String preFix, LocalDateTime createTime) {
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        LocalDate localDate = createTime.toLocalDate();
    public static <T> String countAfterServiceTodayByCreateTime(BaseMapper<T> mapper,String preFix) {
        LocalDateTime todayStart = LocalDateTime.of(
                localDate,
                LocalDateTime.now().toLocalDate(),
                LocalTime.MIN
        );
        LocalDateTime todayEnd = LocalDateTime.of(
                localDate,
                LocalDateTime.now().toLocalDate(),
                LocalTime.MAX
        );
@@ -134,6 +127,6 @@
                .lt("create_time", endDate);
        Long aLong = mapper.selectCount(queryWrapper);
        return preFix + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1));
        return preFix + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1));
    }
}
src/main/java/com/ruoyi/customervisits/service/impl/CustomerVisitsServiceImpl.java
@@ -37,7 +37,7 @@
                wrapper.like(CustomerVisits::getVisitingPeople, customerVisits.getVisitingPeople());
            }
        }
        wrapper.orderByDesc(CustomerVisits::getId);
        return customerVisitsMapper.selectPage(page, wrapper);
    }
src/main/java/com/ruoyi/device/controller/DeviceDefectRecordController.java
@@ -4,6 +4,8 @@
import com.ruoyi.device.dto.DeviceDefectRecordDto;
import com.ruoyi.device.pojo.DeviceDefectRecord;
import com.ruoyi.device.service.DeviceDefectRecordService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -29,16 +31,19 @@
        return AjaxResult.success(deviceDefectRecordService.listPage(new Page<>(1,-1),deviceDefectRecordDto));
    }
    @Log(title = "新增设备缺陷记录", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "添加设备缺陷记录")
    public AjaxResult add(@RequestBody DeviceDefectRecord deviceDefectRecord) {
        return AjaxResult.success(deviceDefectRecordService.add(deviceDefectRecord));
    }
    @Log(title = "修改设备缺陷记录", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改设备缺陷记录")
    public AjaxResult update(@RequestBody DeviceDefectRecord deviceDefectRecord) {
        return AjaxResult.success(deviceDefectRecordService.updateByDDR(deviceDefectRecord));
    }
    @Log(title = "删除设备缺陷记录", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除设备缺陷记录")
    public AjaxResult delete(@PathVariable Long id) {
src/main/java/com/ruoyi/device/controller/DeviceLedgerController.java
@@ -12,6 +12,8 @@
import com.ruoyi.device.pojo.DeviceMaintenance;
import com.ruoyi.device.service.IDeviceLedgerService;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -41,6 +43,7 @@
        return AjaxResult.success(deviceLedgerService.queryPage(page, deviceLedger));
    }
    @Log(title = "新增设备台账", businessType = BusinessType.INSERT)
    @PostMapping()
    @Operation(summary = "添加设备台账")
    public AjaxResult add(@RequestBody DeviceLedgerDto deviceLedgerDto) {
@@ -54,12 +57,14 @@
        return AjaxResult.success(deviceLedgerDto);
    }
    @Log(title = "修改设备台账", businessType = BusinessType.UPDATE)
    @PutMapping()
    @Operation(summary = "修改设备台账")
    public AjaxResult update(@RequestBody DeviceLedgerDto deviceLedgerDto) {
        return deviceLedgerService.updateDeviceLedger(deviceLedgerDto);
    }
    @Log(title = "删除设备台账", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    @Operation(summary = "删除设备台账")
    public AjaxResult delete(@PathVariable("ids") ArrayList<Long> ids) {
@@ -70,12 +75,14 @@
        return AjaxResult.success();
    }
    @Log(title = "导出设备台账", businessType = BusinessType.EXPORT)
    @PostMapping("export")
    @Operation(summary = "导出设备台账")
    public void export(HttpServletResponse response, Long[] ids) {
        deviceLedgerService.export(response, ids);
    }
    @Log(title = "下载设备台账模板", businessType = BusinessType.EXPORT)
    @Operation(summary = "下载模板")
    @PostMapping("/downloadTemplate")
    public void downloadTemplate(HttpServletResponse response) {
@@ -83,6 +90,7 @@
        util.importTemplateExcel(response, "设备导入模板");
    }
    @Log(title = "导入设备台账", businessType = BusinessType.IMPORT)
    @PostMapping("/import")
    @Operation(summary = "导入设备台账")
    public AjaxResult importData(MultipartFile file) throws IOException {
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
@@ -7,6 +7,8 @@
import com.ruoyi.device.pojo.DeviceMaintenance;
import com.ruoyi.device.service.IDeviceLedgerService;
import com.ruoyi.device.service.IDeviceMaintenanceService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -32,6 +34,7 @@
        return AjaxResult.success(deviceMaintenanceService.queryPage(page,deviceMaintenanceDto));
    }
    @Log(title = "新增设备保养", businessType = BusinessType.INSERT)
    @PostMapping()
    @Operation(summary = "添加设备保养")
    public AjaxResult add(@RequestBody DeviceMaintenanceDto deviceMaintenance) {
@@ -47,6 +50,7 @@
        return AjaxResult.success(deviceMaintenanceService.detailById(id));
    }
    @Log(title = "修改设备保养", businessType = BusinessType.UPDATE)
    @PutMapping ()
    @Operation(summary = "修改设备保养")
    public AjaxResult update(@RequestBody DeviceMaintenanceDto deviceMaintenance) {
@@ -56,6 +60,7 @@
        return deviceMaintenanceService.updateDeviceDeviceMaintenance(deviceMaintenance);
    }
    @Log(title = "执行设备保养", businessType = BusinessType.UPDATE)
    @PostMapping ("maintenance")
    @Operation(summary = "修改设备保养")
    public AjaxResult maintenance(@RequestBody DeviceMaintenanceDto deviceMaintenance) {
@@ -63,6 +68,7 @@
    }
    @Log(title = "删除设备保养", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    @Operation(summary = "删除设备保养")
    public AjaxResult delete(@PathVariable("ids") Long[] ids) {
@@ -73,6 +79,7 @@
        return AjaxResult.success();
    }
    @Log(title = "导出设备保养", businessType = BusinessType.EXPORT)
    @PostMapping("export")
    @Operation(summary = "导出设备保养")
    public void export(HttpServletResponse response, Long[] ids) {
src/main/java/com/ruoyi/device/controller/DeviceMaintenanceFileController.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.device.pojo.DeviceMaintenanceFile;
import com.ruoyi.device.service.DeviceMaintenanceFileService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.util.CollectionUtils;
@@ -34,6 +36,7 @@
     * @param deviceMaintenanceFile
     * @return
     */
    @Log(title = "新增设备保养附件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody DeviceMaintenanceFile deviceMaintenanceFile) {
        return AjaxResult.success(deviceMaintenanceFileService.save(deviceMaintenanceFile));
@@ -44,6 +47,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除设备保养附件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delQualityUnqualified(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/device/controller/DeviceRepairController.java
@@ -5,6 +5,8 @@
import com.ruoyi.device.dto.DeviceRepairDto;
import com.ruoyi.device.pojo.DeviceRepair;
import com.ruoyi.device.service.IDeviceRepairService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -28,6 +30,7 @@
        return AjaxResult.success(deviceRepairService.queryPage(page,deviceRepairDto));
    }
    @Log(title = "新增设备报修", businessType = BusinessType.INSERT)
    @PostMapping()
    @Operation(summary = "添加设备报修")
    public AjaxResult add( @RequestBody DeviceRepairDto deviceRepairDto) {
@@ -40,24 +43,28 @@
        return AjaxResult.success(deviceRepairService.detailById(id));
    }
    @Log(title = "修改设备报修", businessType = BusinessType.UPDATE)
    @PutMapping ()
    @Operation(summary = "修改设备报修")
    public AjaxResult update( @RequestBody DeviceRepairDto deviceRepairDto) {
        return deviceRepairService.updateDeviceRepair(deviceRepairDto);
    }
    @Log(title = "设备维修", businessType = BusinessType.UPDATE)
    @PostMapping ("/repair")
    @Operation(summary = "设备维修")
    public AjaxResult repair( @RequestBody DeviceRepairDto deviceRepairDto) {
        return deviceRepairService.confirmRepair(deviceRepairDto);
    }
    @Log(title = "设备报修验收", businessType = BusinessType.UPDATE)
    @PostMapping ("/acceptance")
    @Operation(summary = "设备报修验收审批")
    public AjaxResult acceptance(@RequestBody DeviceRepairDto deviceRepairDto) {
        return deviceRepairService.approveRepairAcceptance(deviceRepairDto);
    }
    @Log(title = "删除设备报修", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    @Operation(summary = "删除设备报修")
    public AjaxResult delete(@PathVariable("ids") Long[] ids) {
@@ -68,6 +75,7 @@
        return AjaxResult.success();
    }
    @Log(title = "导出设备报修", businessType = BusinessType.EXPORT)
    @PostMapping("export")
    @Operation(summary = "导出设备报修")
    public void export(HttpServletResponse response, Long[] ids) {
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -62,8 +62,8 @@
    @Schema(description = "最后执行时间")
    private LocalDateTime lastExecutionTime;
    @Schema(description = "是否激活")
    private boolean isActive;
    @Schema(description = "是否激活, 1=启用, 0=停用")
    private Integer isActive;
    @Schema(description = "备注")
    @Excel(name = "备注")
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
@@ -25,13 +25,25 @@
    private final Scheduler scheduler;
    /**
     * æ·»åŠ æ–°ä»»åŠ¡åˆ°è°ƒåº¦å™¨
     * æ·»åŠ æˆ–æ›´æ–°ä»»åŠ¡åˆ°è°ƒåº¦å™¨
     */
    public void scheduleMaintenanceTask(MaintenanceTask task){
        try {
            JobDetail jobDetail = buildJobDetail(task);
            Trigger trigger = buildJobTrigger(task, jobDetail);
            scheduler.scheduleJob(jobDetail, trigger);
            // æ£€æŸ¥è§¦å‘器是否已存在
            TriggerKey triggerKey = trigger.getKey();
            Trigger existingTrigger = scheduler.getTrigger(triggerKey);
            if (existingTrigger != null) {
                // è§¦å‘器已存在,更新它
                scheduler.rescheduleJob(triggerKey, trigger);
            } else {
                // è§¦å‘器不存在,先确保 Job å­˜åœ¨ï¼Œç„¶åŽè°ƒåº¦è§¦å‘器
                scheduler.addJob(jobDetail, true);
                scheduler.scheduleJob(trigger);
            }
        }catch (SchedulerException e){
            log.error("SchedulerException scheduleMaintenanceTask ERROR",e);
            throw new RuntimeException(e);
@@ -45,26 +57,41 @@
       try{
           TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
           // èŽ·å–çŽ°æœ‰è§¦å‘å™¨å¹¶è½¬æ¢ä¸º CronTrigger
           // èŽ·å–çŽ°æœ‰è§¦å‘å™¨
           Trigger oldTrigger = scheduler.getTrigger(triggerKey);
           // æž„建新的 JobDetail å’Œ Trigger
           JobDetail jobDetail = buildJobDetail(task);
           Trigger newTrigger = buildJobTrigger(task, jobDetail);
           if (oldTrigger == null) {
               // è§¦å‘器不存在,说明任务之前被删除过
               // å…ˆç¡®ä¿ Job å­˜åœ¨ï¼Œç„¶åŽè°ƒåº¦è§¦å‘器
               scheduler.addJob(jobDetail, true);
               scheduler.scheduleJob(newTrigger);
               return;
           }
           // è§¦å‘器存在,直接更新
           if (!(oldTrigger instanceof CronTrigger)) {
               throw new SchedulerException("Existing trigger is not a CronTrigger");
           }
           // 3. æž„建CronTrigger,确保持久化配置
           CronTrigger newTrigger = TriggerBuilder.newTrigger()
                   .withIdentity(triggerKey)                // å”¯ä¸€æ ‡è¯†ï¼Œç”¨äºŽæŒä¹…化存储
                   .withDescription(task.getTaskName() + "_TRIGGER") // è§¦å‘器描述
                   .forJob(oldTrigger.getJobKey())                       // å…³è”对应的Job
           // æž„建新的 CronTrigger
           CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                   .withIdentity(triggerKey)
                   .withDescription(task.getTaskName() + "_TRIGGER")
                   .forJob(oldTrigger.getJobKey())
                   .withSchedule(CronScheduleBuilder
                           .cronSchedule(convertToCronExpression(task)) // é”™è¿‡æ‰§è¡Œæ—¶çš„策略(根据业务调整)
                           .cronSchedule(convertToCronExpression(task))
                           .withMisfireHandlingInstructionDoNothing()
                   )
                   // 4. è®¾ç½®å¼€å§‹æ—¶é—´ï¼ˆè‹¥ä¸ºnull则立即生效)
                   .startAt(task.getNextExecutionTime() != null
                           ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
                           : new Date())
                   .build();
           scheduler.rescheduleJob(triggerKey, newTrigger);
           scheduler.rescheduleJob(triggerKey, cronTrigger);
       }catch (SchedulerException e){
           log.error("SchedulerException rescheduleMaintenanceTask ERROR",e);
           throw new RuntimeException(e);
@@ -93,6 +120,7 @@
    public void unscheduleMaintenanceTask(Long taskId){
        try {
            JobKey jobKey = new JobKey("MaintenanceTask_" + taskId);
            // åˆ é™¤ Job ä¼šè‡ªåŠ¨åˆ é™¤å…³è”çš„ Trigger
            scheduler.deleteJob(jobKey);
        }catch (SchedulerException e){
            log.error("SchedulerException unscheduleMaintenanceTask ERROR",e);
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
@@ -1,6 +1,6 @@
package com.ruoyi.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.bean.BeanUtils;
@@ -35,7 +35,9 @@
    @Override
    public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) {
        Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, new QueryWrapper<MaintenanceTask>().orderByDesc("create_time"));
       LambdaQueryWrapper<MaintenanceTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(MaintenanceTask::getId);
        Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, queryWrapper);
        // 2. å¦‚果没有数据,直接返回空分页
        if (taskPage.getRecords().isEmpty()) {
            return AjaxResult.success(taskPage);
@@ -68,7 +70,6 @@
    @Override
    public AjaxResult add(MaintenanceTask maintenanceTask) {
        maintenanceTask.setActive(true);
        // è®¡ç®—首次执行时间
        TimingTask task = new TimingTask();
        task.setFrequencyType(maintenanceTask.getFrequencyType());
@@ -77,7 +78,10 @@
        maintenanceTask.setNextExecutionTime(firstExecutionTime);
        int insert = maintenanceTaskMapper.insert(maintenanceTask);
        if (insert > 0) {
            maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
            // åªæœ‰å½“ isActive ä¸º 1 æ—¶æ‰æ·»åŠ åˆ°å®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨
            if (maintenanceTask.getIsActive() != null && maintenanceTask.getIsActive() == 1) {
                maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
            }
        }
        return AjaxResult.success("添加成功");
    }
@@ -88,22 +92,50 @@
        if (maintenanceTask1 == null) {
            return AjaxResult.warn("没有此数据");
        }
        // ä¿å­˜æ—§çš„ isActive çŠ¶æ€
        Integer oldIsActive = maintenanceTask1.getIsActive();
        Integer newIsActive = maintenanceTask.getIsActive();
        BeanUtils.copyProperties(maintenanceTask, maintenanceTask1);
        int update = maintenanceTaskMapper.updateById(maintenanceTask1);
        if (update > 0) {
            maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1);
            // å¤„理 isActive çŠ¶æ€å˜åŒ–
            if (newIsActive != null && newIsActive == 1) {
                // æ–°çŠ¶æ€ä¸ºå¯ç”¨ï¼šæ·»åŠ åˆ°å®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨
                if (oldIsActive == null || oldIsActive != 1) {
                    // ä»Žæœªå¯ç”¨å˜ä¸ºå¯ç”¨ï¼Œæ·»åŠ åˆ°è°ƒåº¦å™¨
                    maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask1);
                } else {
                    // å·²ç»å¯ç”¨ï¼Œæ›´æ–°è°ƒåº¦å™¨ä¸­çš„任务
                    maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1);
                }
            } else {
                // æ–°çŠ¶æ€ä¸ºåœç”¨ï¼šä»Žå®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨ä¸­ç§»é™¤
                if (oldIsActive != null && oldIsActive == 1) {
                    maintenanceTaskScheduler.unscheduleMaintenanceTask(maintenanceTask1.getId());
                }
            }
        }
        return AjaxResult.success("更新成功");
    }
    @Override
    public AjaxResult delete(List<Long> ids) {
        // å…ˆä»Žå®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨ä¸­ç§»é™¤æ‰€æœ‰å¾…åˆ é™¤çš„ä»»åŠ¡
        ids.forEach(id -> {
            try {
                maintenanceTaskScheduler.unscheduleMaintenanceTask(id);
            } catch (Exception e) {
                log.error("删除定时任务调度失败, id: {}", id, e);
            }
        });
        // å†ä»Žæ•°æ®åº“中删除记录
        int delete = maintenanceTaskMapper.deleteBatchIds(ids);
        if (delete > 0) {
            ids.forEach(id -> {
                maintenanceTaskScheduler.unscheduleMaintenanceTask(id);
            });
            return AjaxResult.success("删除成功");
        }
        return AjaxResult.success("删除成功");
        return AjaxResult.error("删除失败");
    }
}
src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
@@ -1,64 +1,30 @@
package com.ruoyi.framework.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
 * ç¨‹åºæ³¨è§£é…ç½®
 *
 * @author ruoyi
 */
@Configuration
// è¡¨ç¤ºé€šè¿‡aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// æŒ‡å®šè¦æ‰«æçš„Mapper类的包的路径
@MapperScan("com.ruoyi.**.mapper")
public class ApplicationConfig {
    /**
     * æ—¶åŒºé…ç½®
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
    }
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        // å…¨å±€å¿½ç•¥æœªçŸ¥å­—段
        mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule module = new JavaTimeModule();
        // LocalDateTime:支持 yyyy-MM-dd HH:mm:ss
        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        module.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(dateTimeFormatter));
        module.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(dateTimeFormatter));
        // LocalDate:支持 yyyy-MM-dd
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("yyyy-MM-dd");
        module.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(dateFormatter));
        module.addSerializer(LocalDate.class,
                new LocalDateSerializer(dateFormatter));
        mapper.registerModule(module);
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
    }
}
package com.ruoyi.framework.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
 * ç¨‹åºæ³¨è§£é…ç½®
 *
 * @author ruoyi
 */
@Configuration
// è¡¨ç¤ºé€šè¿‡aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// æŒ‡å®šè¦æ‰«æçš„Mapper类的包的路径
@MapperScan("com.ruoyi.**.mapper")
public class ApplicationConfig
{
    /**
     * æ—¶åŒºé…ç½®
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
    {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
    }
}
src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -61,8 +61,7 @@
    private static final Integer ORDER_STATUS_WAIT = 1;
    private static final Integer ORDER_STATUS_RUNNING = 2;
    private static final Integer ORDER_STATUS_COMPLETED = 3;
    //private static final Integer ORDER_STATUS_END = 4;  // å·²å–消
    private static final Integer ORDER_STATUS_END = 5;
    private static final Integer ORDER_STATUS_PAUSED = 4;
    /********************************************************基础类*****************************************************/
    @GetMapping("/todos")
@@ -163,8 +162,8 @@
    @GetMapping("/processOutputAnalysis")
    @Operation(summary = "工序产出分析")
    public R processOutputAnalysis(@DefaultType Integer type){
        List<MapDto> list = homeService.processOutputAnalysis(type);
    public R processOutputAnalysis(@DefaultType Integer dateType){
        List<MapDto> list = homeService.processOutputAnalysis(dateType);
        return R.ok(list);
    }
@@ -261,14 +260,13 @@
        }
        Integer statusFromParam = parseOrderStatus(status);
        if (!isBlank(status) && statusFromParam == null && !"all".equalsIgnoreCase(status.trim())) {
            return R.fail("status参数不合法,可选值:all/waiting/inProgress/completed/end æˆ– 1/2/3/5");
            return R.fail("status参数不合法,可选值:all/waiting/inProgress/completed/paused æˆ– 1/2/3/4");
        }
        Integer queryStatus = resolveOrderStatus(status, tab);
        long safePageNum = pageNum == null || pageNum < 1 ? 1 : pageNum;
        boolean queryAll = pageSize != null && pageSize == -1;
        long safePageSize = queryAll ? -1 : (pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 50));
        long offset = queryAll ? 0 : (safePageNum - 1) * safePageSize;
        long safePageSize = pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 50);
        long offset = (safePageNum - 1) * safePageSize;
        LocalDateTime startTime = queryDate == null ? null : queryDate.atStartOfDay();
        LocalDateTime endTime = queryDate == null ? null : queryDate.plusDays(1).atStartOfDay();
@@ -283,7 +281,7 @@
        long waitingCount = 0L;
        long inProgressCount = 0L;
        long completedCount = 0L;
        long endCount = 0L;
        long pausedCount = 0L;
        List<Map<String, Object>> statusCountRows = productionOrderMapper.countHomeOrderProgressByStatus(startTime, endTime);
        if (statusCountRows != null) {
            for (Map<String, Object> countRow : statusCountRows) {
@@ -295,8 +293,8 @@
                    inProgressCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_COMPLETED)) {
                    completedCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_END)) {
                    endCount = cnt;
                } else if (Objects.equals(statusKey, ORDER_STATUS_PAUSED)) {
                    pausedCount = cnt;
                }
            }
        }
@@ -305,13 +303,13 @@
        result.put("tab", mapOrderTab(queryStatus));
        result.put("status", mapOrderStatus(queryStatus));
        result.put("bizDate", queryDate == null ? null : queryDate.format(DATE_FORMATTER));
        result.put("total", toLong(productionOrderMapper.countHomeOrderProgress(null, startTime, endTime)));
        result.put("total", toLong(productionOrderMapper.countHomeOrderProgress(queryStatus, startTime, endTime)));
        result.put("pageNum", safePageNum);
        result.put("pageSize", safePageSize);
        result.put("waitingCount", waitingCount);
        result.put("inProgressCount", inProgressCount);
        result.put("completedCount", completedCount);
        result.put("endCount", endCount);
        result.put("pausedCount", pausedCount);
        result.put("records", records);
        return R.ok(result);
    }
@@ -531,8 +529,8 @@
        if ("3".equals(normalized) || "completed".equals(normalized)) {
            return ORDER_STATUS_COMPLETED;
        }
        if ("5".equals(normalized) || "end".equals(normalized)) {
            return ORDER_STATUS_END;
        if ("4".equals(normalized) || "paused".equals(normalized)) {
            return ORDER_STATUS_PAUSED;
        }
        return null;
    }
@@ -544,8 +542,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "completed";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "end";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "paused";
        }
        if (Objects.equals(status, ORDER_STATUS_WAIT)) {
            return "waiting";
@@ -563,8 +561,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "completed";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "end";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "paused";
        }
        return "all";
    }
@@ -579,8 +577,8 @@
        if (Objects.equals(status, ORDER_STATUS_COMPLETED)) {
            return "已完成";
        }
        if (Objects.equals(status, ORDER_STATUS_END)) {
            return "已结束";
        if (Objects.equals(status, ORDER_STATUS_PAUSED)) {
            return "已暂停";
        }
        return "未知";
    }
@@ -637,7 +635,7 @@
                .select(ProductionOrder::getQuantity, ProductionOrder::getCompleteQuantity)
                .ge(ProductionOrder::getCreateTime, day.atStartOfDay())
                .lt(ProductionOrder::getCreateTime, day.plusDays(1).atStartOfDay())
                .ne(ProductionOrder::getStatus, ORDER_STATUS_END));
                .ne(ProductionOrder::getStatus, ORDER_STATUS_PAUSED));
        BigDecimal totalQuantity = BigDecimal.ZERO;
        BigDecimal totalCompleteQuantity = BigDecimal.ZERO;
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -18,11 +18,7 @@
import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
import com.ruoyi.approve.mapper.ApprovalTaskMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalTask;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
@@ -32,7 +28,6 @@
import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
import com.ruoyi.collaborativeApproval.pojo.Notice;
import com.ruoyi.common.enums.ApproveTypeEnum;
import com.ruoyi.common.enums.TypeEnums;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mapper.DeviceRepairMapper;
@@ -66,6 +61,7 @@
import lombok.RequiredArgsConstructor;
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;
@@ -102,10 +98,6 @@
    private final QualityInspectMapper qualityStatisticsMapper;
    private final ApproveProcessMapper approveProcessMapper;
    private final ApprovalTaskMapper approvalTaskMapper;
    private final ApprovalInstanceMapper approvalInstanceMapper;
    private final SysDeptMapper sysDeptMapper;
@@ -393,58 +385,40 @@
    @Override
    public List<ApproveProcess> todos() throws ParseException {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<ApproveProcess> approveProcesses = new ArrayList<>();
        // æŸ¥è¯¢æ—§å®¡æ‰¹è¡¨ï¼ˆapprove_process)
        LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
        approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
                .ne(ApproveProcess::getApproveStatus, 2);
        List<ApproveProcess> oldList = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(oldList)) {
            oldList.forEach(a -> {
                if (a.getApproveType() != null) {
                    a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
                }
            });
            approveProcesses.addAll(oldList);
        // .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
        List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(approveProcesses)) {
            approveProcesses = new ArrayList<>();
        }
        // æŸ¥è¯¢æ–°å®¡æ‰¹è¡¨ï¼ˆapproval_task + approval_instance)
        List<ApprovalTask> pendingTasks = approvalTaskMapper.selectList(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, loginUser.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        if (!CollectionUtils.isEmpty(pendingTasks)) {
            List<Long> instanceIds = pendingTasks.stream()
                    .map(ApprovalTask::getInstanceId)
                    .distinct()
                    .collect(Collectors.toList());
            List<ApprovalInstance> instances = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>()
                    .in(ApprovalInstance::getId, instanceIds)
                    .eq(ApprovalInstance::getDeleted, 0));
            Map<Long, ApprovalInstance> instanceMap = instances.stream()
                    .collect(Collectors.toMap(ApprovalInstance::getId, i -> i, (a, b) -> a));
            for (ApprovalTask task : pendingTasks) {
                ApprovalInstance instance = instanceMap.get(task.getInstanceId());
                if (instance == null) {
                    continue;
                }
                ApproveProcess ap = new ApproveProcess();
                ap.setId(instance.getId());
                ap.setApproveId(instance.getInstanceNo());
                ap.setApproveReason(instance.getTitle());
                ap.setApproveTime(instance.getApplyTime() != null ? java.sql.Timestamp.valueOf(instance.getApplyTime()) : null);
                ap.setApproveUserName(instance.getApplicantName());
                ap.setApproveDeptName(instance.getTemplateName());
                if (instance.getBusinessType() != null) {
                    ap.setApproveType(instance.getBusinessType().intValue());
                    ap.setApproveTypeName(TypeEnums.getLabelByValue(instance.getBusinessType()));
                }
                approveProcesses.add(ap);
        approveProcesses.forEach(a -> {
            if (a.getApproveType() != null) {
                a.setApproveTypeName(ApproveTypeEnum.getNameByCode(a.getApproveType()));
            }
        }
        });
        // // æŸ¥è¯¢æœªé¢†ç”¨åŠ³ä¿è®°å½•
        // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //
        // LaborIssue laborIssue1 = new LaborIssue();
        // laborIssue1.setAdoptedDate(new Date());
        // laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
        // List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);
        // //staff_join_leave_record表被删除
        // if(!CollectionUtils.isEmpty(laborIssues)){
        // for (LaborIssue laborIssue : laborIssues) {
        // ApproveProcess approveProcess = new ApproveProcess();
        // approveProcess.setApproveId(laborIssue.getOrderNo());
        // approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
        // approveProcess.setApproveTime(laborIssue.getIssueDate());
        // approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" +
        // laborIssue.getDictName() + "超时未领取");
        // approveProcesses.add(approveProcess);
        // }
        // }
        return approveProcesses;
    }
@@ -497,23 +471,18 @@
    @Override
    public Map<String, Object> approveAndDeviceTodos() {
        // æ—§å®¡æ‰¹è¡¨å¾…办
        Long oldCount = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
        // å®¡æ‰¹ååŒå¾…办
        Long aLong = approveProcessMapper.selectCount(new LambdaQueryWrapper<ApproveProcess>()
                .eq(ApproveProcess::getApproveUserCurrentId, SecurityUtils.getUserId())
                .eq(ApproveProcess::getApproveDelete, 0)
                .in(ApproveProcess::getApproveStatus, 0, 1, 3));
        // æ–°å®¡æ‰¹è¡¨å¾…办
        Long newCount = approvalTaskMapper.selectCount(new LambdaQueryWrapper<ApprovalTask>()
                .eq(ApprovalTask::getApproverId, SecurityUtils.getUserId())
                .eq(ApprovalTask::getTaskStatus, "PENDING")
                .eq(ApprovalTask::getDeleted, 0));
        // è®¾å¤‡æŠ¥ä¿®å¾…办
        Long aLong1 = deviceRepairMapper.selectCount(new LambdaQueryWrapper<DeviceRepair>()
                .eq(DeviceRepair::getStatus, 0)
                .eq(DeviceRepair::getRepairName, SecurityUtils.getLoginUser().getNickName()));
        return new HashMap<String, Object>() {
            {
                put("approveTodo", oldCount + newCount);
                put("approveTodo", aLong);
                put("deviceRepairTodo", aLong1);
            }
        };
@@ -529,9 +498,20 @@
    @Override
    public ProductionProgressDto productionProgress() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfMonth = now
                .with(TemporalAdjusters.firstDayOfMonth())
                .with(LocalTime.MIN);
        LocalDateTime endOfMonth = now
                .with(TemporalAdjusters.lastDayOfMonth())
                .with(LocalTime.MAX);
        ProductionProgressDto productionProgressDto = new ProductionProgressDto();
        List<ProductionProgressOrderDto> orderDetails = productionOrderMapper.selectProgressOrders(
                LocalDateTime.now().minusMonths(1), LocalDateTime.now());
        List<ProductionProgressOrderDto> orderDetails = productionOrderMapper
                .selectProgressOrders(startOfMonth, endOfMonth);
        productionProgressDto.setCompletedOrderDetails(orderDetails);
        long totalCount = orderDetails.size();
        long count = orderDetails.stream().filter(
@@ -551,7 +531,7 @@
    public ProductionTurnoverDto workInProcessTurnover() {
        ProductionTurnoverDto productionTurnoverDto = new ProductionTurnoverDto();
        List<ProductionTaskStatisticsDto> taskList = productionOperationTaskMapper
                .selectTaskStatisticsByDate(LocalDate.now().minusMonths(1), LocalDate.now());
                .selectTaskStatisticsByDate(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()), LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()));
        long sum = taskList.stream()
                .filter(task -> defaultDecimal(task.getPlanQuantity()).compareTo(defaultDecimal(task.getCompleteQuantity())) > 0)
                .map(ProductionTaskStatisticsDto::getPlanQuantity)
@@ -1377,15 +1357,15 @@
        String endStr = endDate.atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//        List<Map<String, Object>> inputList = productionProductInputMapper.selectInputStats(startStr, endStr);
        List<Map<String, Object>> inputList = null;
        List<Map<String, Object>> inputList = productionProductInputMapper.selectInputStats(startStr, endStr);
        //List<Map<String, Object>> inputList = null;
        List<Map<String, Object>> outputList = productionProductOutputMapper.selectDailyOutputStats(startStr, endStr);
        Map<String, InputOutputAnalysisDto> dateMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(inputList)) {
            for (Map<String, Object> map : inputList) {
                String date = (String) map.get("date");
                String date = (String) map.get("statDate");
                if (date == null)
                    continue;
                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
@@ -1398,7 +1378,7 @@
        if (!CollectionUtils.isEmpty(outputList)) {
            for (Map<String, Object> map : outputList) {
                String date = (String) map.get("date");
                String date = (String) map.get("statDate");
                if (date == null)
                    continue;
                InputOutputAnalysisDto dto = dateMap.getOrDefault(date, new InputOutputAnalysisDto());
@@ -1657,16 +1637,16 @@
    @Override
    public List<MapDto> orderCount() {
        LocalDate now = LocalDate.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String currentStart = now.withDayOfMonth(1).atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String currentEnd = now.atTime(LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime startDateTime = now.with(TemporalAdjusters.firstDayOfMonth()).atStartOfDay();
        LocalDateTime endDateTime = now.with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX);
        LocalDate lastMonthDate = now.minusMonths(1);
        String lastStart = lastMonthDate.withDayOfMonth(1).atStartOfDay()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String lastEnd = lastMonthDate.withDayOfMonth(lastMonthDate.lengthOfMonth()).atTime(LocalTime.MAX)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String currentStart = startDateTime.format(dateTimeFormatter);
        String currentEnd = endDateTime.format(dateTimeFormatter);
        String lastStart = startDateTime.minusMonths(1).format(dateTimeFormatter);
        String lastEnd = endDateTime.format(dateTimeFormatter);
        int currentCreated = productionOrderMapper.countCreated(currentStart, currentEnd);
        int lastCreated = productionOrderMapper.countCreated(lastStart, lastEnd);
src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
@@ -3,6 +3,8 @@
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.R;
import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
@@ -43,8 +45,9 @@
    /**
     * å¯¼å‡º
     */
    @PostMapping("/export")
    @Log(title = "检验任务", businessType = BusinessType.EXPORT)
    @Operation(summary = "导出定时任务记录")
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        Page page = new Page<>(-1, -1);
        InspectionTaskDto timingTask = new InspectionTaskDto();
@@ -56,8 +59,9 @@
    /**
     * å·¡æ£€ä»»åŠ¡è¡¨æ–°å¢žä¿®æ”¹
     */
    @PostMapping("/addOrEditInspectionTask")
    @Log(title = "检验任务", businessType = BusinessType.INSERT)
    @Operation(summary = "巡检任务表新增修改")
    @PostMapping("/addOrEditInspectionTask")
    @Transactional(rollbackFor = Exception.class)
    public R addOrEditInspectionTask(@RequestBody InspectionTaskDto inspectionTaskDto) {
        return R.ok(inspectionTaskService.addOrEditInspectionTask(inspectionTaskDto));
@@ -66,8 +70,9 @@
    /**
     * å·¡æ£€ä»»åŠ¡è¡¨åˆ é™¤
     */
    @DeleteMapping("/delInspectionTask")
    @Log(title = "检验任务", businessType = BusinessType.DELETE)
    @Operation(summary = "巡检任务表删除")
    @DeleteMapping("/delInspectionTask")
    @Transactional(rollbackFor = Exception.class)
    public R remove(@RequestBody Long[] ids) {
        return R.ok(inspectionTaskService.delByIds(ids));
src/main/java/com/ruoyi/inspectiontask/controller/QrCodeController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.R;
import com.ruoyi.inspectiontask.dto.QrCodeDto;
@@ -37,8 +39,9 @@
    /**
     * äºŒç»´ç ç®¡ç†è¡¨æ–°å¢žä¿®æ”¹
     */
    @PostMapping("/addOrEditQrCode")
    @Log(title = "二维码", businessType = BusinessType.INSERT)
    @Operation(summary = "二维码管理表新增修改")
    @PostMapping("/addOrEditQrCode")
    public R<Long> addOrEditQrCode(@RequestBody QrCodeDto qrCodeDto) {
        return R.ok(qrCodeService.addOrEditQrCode(qrCodeDto));
    }
@@ -46,8 +49,9 @@
    /**
     * äºŒç»´ç ç®¡ç†è¡¨åˆ é™¤
     */
    @DeleteMapping("/delQrCode")
    @Log(title = "二维码", businessType = BusinessType.DELETE)
    @Operation(summary = "二维码管理表删除")
    @DeleteMapping("/delQrCode")
    public R remove(@RequestBody Long[] ids) {
        return R.ok(qrCodeService.delByIds(ids));
    }
src/main/java/com/ruoyi/inspectiontask/controller/QrCodeScanRecordController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.R;
import com.ruoyi.inspectiontask.dto.QrCodeScanRecordDto;
@@ -38,8 +40,9 @@
    /**
     * äºŒç»´ç æ‰«ç è®°å½•表新增修改
     */
    @PostMapping("/addOrEditQrCodeRecord")
    @Log(title = "二维码扫描记录", businessType = BusinessType.INSERT)
    @Operation(summary = "二维码扫码记录表新增修改")
    @PostMapping("/addOrEditQrCodeRecord")
    public R addOrEditQrCodeRecord(@RequestBody QrCodeScanRecordDto qrCodeScanRecordDto) {
        return R.ok(qrCodeScanRecordService.addOrEditQrCodeRecord(qrCodeScanRecordDto));
    }
@@ -47,8 +50,9 @@
    /**
     * äºŒç»´ç æ‰«ç è®°å½•表删除
     */
    @DeleteMapping("/delSalesRecord")
    @Log(title = "二维码扫描记录", businessType = BusinessType.DELETE)
    @Operation(summary = "二维码扫码记录表删除")
    @DeleteMapping("/delSalesRecord")
    public R remove(@RequestBody Long[] ids) {
        return R.ok(qrCodeScanRecordService.delByIds(ids));
    }
src/main/java/com/ruoyi/inspectiontask/controller/TimingTaskController.java
@@ -42,6 +42,7 @@
    /**
     * å¯¼å‡º
     */
    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @Operation(summary = "导出定时任务")
    public void export(HttpServletResponse response) {
@@ -55,9 +56,9 @@
    /**
     * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨æ–°å¢žä¿®æ”¹
     */
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @PostMapping("/addOrEditTimingTask")
    @Operation(summary = "新增修改定时任务")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    public R addOrEditTimingTask(@RequestBody TimingTaskDto timingTaskDto) throws SchedulerException {
        return R.ok(timingTaskService.addOrEditTimingTask(timingTaskDto));
    }
@@ -65,9 +66,9 @@
    /**
     * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨åˆ é™¤
     */
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @DeleteMapping("/delTimingTask")
    @Operation(summary = "删除定时任务")
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    public R remove(@RequestBody Long[] ids) {
        return R.ok(timingTaskService.delByIds(ids));
    }
src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -67,7 +67,6 @@
        if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionProject())) {
            queryWrapper.like(InspectionTask::getInspectionProject, inspectionTaskDto.getInspectionProject());
        }
        queryWrapper.orderByDesc(InspectionTask::getCreateTime);
        IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper);
        //  æ— æ•°æ®æå‰è¿”回
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -35,6 +35,17 @@
        // èŽ·å–çŽ°æœ‰è§¦å‘å™¨å¹¶è½¬æ¢ä¸º CronTrigger
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (oldTrigger == null) {
            JobKey jobKey = new JobKey("timingTask_" + task.getId());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail != null) {
                Trigger trigger = buildJobTrigger(task, jobDetail);
                scheduler.scheduleJob(trigger);
            } else {
                scheduleTimingTask(task);
            }
            return;
        }
        if (!(oldTrigger instanceof CronTrigger)) {
            throw new SchedulerException("Existing trigger is not a CronTrigger");
        }
@@ -144,18 +155,13 @@
        // ä½¿ç”¨switch确保条件互斥
        String frequencyType = task.getFrequencyType().toUpperCase(); // ç»Ÿä¸€è½¬ä¸ºå¤§å†™æ¯”较
        switch (frequencyType) {
            case "DAILY":
                return convertDailyToCron(task.getFrequencyDetail());
            case "WEEKLY":
                return convertWeeklyToCron(task.getFrequencyDetail());
            case "MONTHLY":
                return convertMonthlyToCron(task.getFrequencyDetail());
            case "QUARTERLY":
                return convertQuarterlyToCron(task.getFrequencyDetail());
            default:
                throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType());
        }
        return switch (frequencyType) {
            case "DAILY" -> convertDailyToCron(task.getFrequencyDetail());
            case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail());
            case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail());
            case "QUARTERLY" -> convertQuarterlyToCron(task.getFrequencyDetail());
            default -> throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType());
        };
    }
    // æ¯æ—¥ä»»åŠ¡è½¬æ¢
src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskServiceImpl.java
@@ -53,7 +53,8 @@
        if (timingTask.getIsEnabled() != null) {
            queryWrapper.eq(TimingTask::getIsEnabled, timingTask.getIsEnabled());
        }
        queryWrapper.orderByDesc(TimingTask::getCreateTime);
        // æŒ‰ ID å€’序排序
        queryWrapper.orderByDesc(TimingTask::getId);
        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, queryWrapper);
        // 2. å¦‚果没有数据,直接返回空分页
src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
@@ -29,6 +31,7 @@
    @GetMapping("/listPage")
    @Operation(summary = "备件分类-分页查询")
    @Log(title = "备件领用记录-分页查询", businessType = BusinessType.OTHER)
    public AjaxResult listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto){
        IPage<SparePartsRequisitionRecordDto> listPage = sparePartsRequisitionRecordService.listPage(page, sparePartsRequisitionRecordDto);
        return AjaxResult.success(listPage);
src/main/java/com/ruoyi/officesupplies/controller/OfficeSuppliesController.java
@@ -44,6 +44,7 @@
    @PostMapping("/add")
    @Operation(summary = "办公物资-添加")
    @Log(title = "办公物资-添加", businessType = BusinessType.INSERT)
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(@RequestBody OfficeSupplies officeSupplies) {
        // æŒ‰ç…§å½“前时间yyyyMMdd + å½“天新增数量 + 1生成编号
@@ -60,6 +61,7 @@
    @PostMapping("/update")
    @Operation(summary = "办公物资-修改")
    @Log(title = "办公物资-修改", businessType = BusinessType.UPDATE)
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult update(@RequestBody OfficeSupplies officeSupplies) {
        return officeSuppliesService.updateById(officeSupplies) ? success() : error();
@@ -67,6 +69,7 @@
    @DeleteMapping("/delete")
    @Operation(summary = "办公物资-删除")
    @Log(title = "办公物资-删除", businessType = BusinessType.DELETE)
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
src/main/java/com/ruoyi/other/controller/PdaVersionController.java
@@ -1,6 +1,8 @@
package com.ruoyi.other.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.other.dto.PdaVersionDTO;
import com.ruoyi.other.pojo.PdaVersion;
@@ -23,6 +25,7 @@
    }
    @Operation(summary = "添加版本")
    @Log(title = "PDA版本-添加", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R add(@RequestBody PdaVersionDTO pdaVersion) {
        return R.ok(pdaVersionService.add(pdaVersion));
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnManagementDto.java
@@ -27,18 +27,6 @@
    @Schema(description = "关联出库单号")
    private String shippingNo;
    @Schema(description = "发货类型")
    private Integer shippingType;
    @Schema(description = "发货车牌号")
    private String truckPlateNo;
    @Schema(description = "快递公司")
    private String expressCompany;
    @Schema(description = "快递单号")
    private String expressNo;
    @Schema(description = "项目名称")
    private String projectName;
@@ -47,8 +35,5 @@
    @Schema(description = "销售产品对象数组")
    private List<ReturnSaleProductDto> returnSaleProducts;
    @Schema(description = "销售退货入库审批状态")
    private Integer stockInApprovalStatus;
}
src/main/java/com/ruoyi/procurementrecord/bean/dto/ReturnSaleProductDto.java
@@ -35,7 +35,4 @@
    @Schema(description = "发货出库数量")
    private BigDecimal stockOutNum;
    @Schema(description = "待处理退货数量")
    private BigDecimal pendingReturnNum;
}
src/main/java/com/ruoyi/procurementrecord/bean/vo/ShippingProductVo.java
@@ -40,7 +40,4 @@
    @Schema(description = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
    @Schema(description = "待处理退货数量")
    private BigDecimal pendingReturnNum;
}
src/main/java/com/ruoyi/procurementrecord/controller/GasTankWarningController.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.procurementrecord.pojo.GasTankWarning;
import com.ruoyi.procurementrecord.service.GasTankWarningService;
@@ -23,23 +25,26 @@
        return AjaxResult.success(gasTankWarningService.listPage(page, gasTankWarning));
    }
    @Log(title = "新增气瓶预警", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody GasTankWarning gasTankWarning) {
        return AjaxResult.success(gasTankWarningService.save(gasTankWarning));
    }
    @Log(title = "修改气瓶预警", businessType = BusinessType.UPDATE)
    @PostMapping("update")
    public AjaxResult update(@RequestBody GasTankWarning gasTankWarning) {
        return AjaxResult.success(gasTankWarningService.updateById(gasTankWarning));
    }
    @Log(title = "删除气瓶预警", businessType = BusinessType.DELETE)
    @DeleteMapping("delete")
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
        return AjaxResult.success(gasTankWarningService.removeByIds(ids));
    }
    //导出
    @Log(title = "导出气瓶预警", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, @RequestParam(name = "ids", required = false) List<Long> ids) {
        gasTankWarningService.export(response, ids);
src/main/java/com/ruoyi/procurementrecord/controller/InboundManagementController.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.procurementrecord.pojo.InboundManagement;
@@ -35,6 +37,7 @@
        return AjaxResult.success(result);
    }
    @Log(title = "新增到货管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "到货管理-添加")
    @Transactional(rollbackFor = Exception.class)
@@ -44,6 +47,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "修改到货管理", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "到货管理-修改")
    @Transactional(rollbackFor = Exception.class)
@@ -52,6 +56,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "删除到货管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "到货管理-删除")
    @Transactional(rollbackFor = Exception.class)
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementExceptionRecordController.java
@@ -1,5 +1,7 @@
package com.ruoyi.procurementrecord.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.procurementrecord.mapper.ProcurementExceptionRecordMapper;
@@ -21,12 +23,14 @@
    private ProcurementExceptionRecordMapper procurementExceptionRecordMapper;
    @Log(title = "新增采购异常记录", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Transactional
    public AjaxResult add(@RequestBody ProcurementExceptionRecord procurementExceptionRecord) {
        return AjaxResult.success(procurementExceptionRecordMapper.insert(procurementExceptionRecord));
    }
    @Log(title = "修改采购异常记录", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Transactional
    public AjaxResult updatePro(@RequestBody ProcurementExceptionRecord procurementExceptionRecord) {
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementPlanController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.procurementrecord.pojo.ProcurementPlan;
@@ -33,6 +35,7 @@
        return AjaxResult.success(result);
    }
    @Log(title = "新增采购计划", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "采购计划-添加")
    public AjaxResult add(@RequestBody ProcurementPlan procurementPlan){
@@ -40,6 +43,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "修改采购计划", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "采购计划-修改")
    public AjaxResult update(@RequestBody ProcurementPlan procurementPlan){
@@ -47,6 +51,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "删除采购计划", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "采购计划-删除")
    public AjaxResult del(@RequestBody List<Long> ids){
@@ -54,10 +59,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    /**
     * å¯¼å‡º
     * @param response
     */
    @Log(title = "导出采购计划", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        procurementPlanService.export(response);
src/main/java/com/ruoyi/procurementrecord/controller/ProcurementPriceManagementController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.procurementrecord.pojo.ProcurementPriceManagement;
@@ -34,6 +36,7 @@
        return AjaxResult.success(result);
    }
    @Log(title = "新增采购价格管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "采购价格管理-添加")
    @Transactional(rollbackFor = Exception.class)
@@ -42,6 +45,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "修改采购价格管理", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "采购价格管理-修改")
    @Transactional(rollbackFor = Exception.class)
@@ -50,6 +54,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "删除采购价格管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "采购价格管理-删除")
    @Transactional(rollbackFor = Exception.class)
@@ -61,10 +66,7 @@
        return result ? AjaxResult.success() : AjaxResult.error();
    }
    /**
     * å¯¼å‡º
     * @param response
     */
    @Log(title = "导出采购价格管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        procurementPriceManagementService.export(response);
src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
@@ -8,6 +8,8 @@
import com.ruoyi.account.pojo.AccountStatementDetails;
import com.ruoyi.account.service.AccountStatementDetailsService;
import com.ruoyi.common.exception.ServiceException;
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.procurementrecord.bean.dto.ReturnManagementDto;
@@ -45,6 +47,7 @@
        return AjaxResult.success(result);
    }
    @Log(title = "新增销售退货", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "销售退货-添加")
    @Transactional(rollbackFor = Exception.class)
@@ -52,6 +55,7 @@
        return returnManagementService.addReturnManagementDto(returnManagementDto) ? success() : error();
    }
    @Log(title = "修改销售退货", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "销售退货-修改")
    @Transactional(rollbackFor = Exception.class)
@@ -68,6 +72,7 @@
    }
    @Log(title = "删除销售退货", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "销售退货-删除")
    @Transactional(rollbackFor = Exception.class)
src/main/java/com/ruoyi/procurementrecord/controller/ReturnSaleProductController.java
@@ -1,5 +1,7 @@
package com.ruoyi.procurementrecord.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
src/main/java/com/ruoyi/procurementrecord/pojo/CustomStorage.java
@@ -97,6 +97,7 @@
    /**
     * å…¥åº“æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDateTime createTime;
src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
@@ -34,18 +34,6 @@
    @Schema(description = "关联出库单号Id")
    private Long shippingId;
    @Schema(description = "发货类型 1货车 2快递")
    private Integer shippingType;
    @Schema(description = "发货车牌号")
    private String truckPlateNo;
    @Schema(description = "快递公司")
    private String expressCompany;
    @Schema(description = "快递单号")
    private String expressNo;
    @Schema(description = "项目id")
    private Long projectId;
@@ -75,6 +63,7 @@
    @Schema(description = "创建时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
src/main/java/com/ruoyi/procurementrecord/service/impl/ProcurementRecordServiceImpl.java
@@ -7,6 +7,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -535,7 +536,7 @@
            Long aLong = customStorageMapper.selectCount(null);
            item.setInboundBatches(aLong.equals(0L) ? "第1批次(自定义入库)" : "第"+ (aLong + 1) + "批次(自定义入库)" );
            item.setCreateBy(loginUser.getNickName());
            item.setCode(OrderUtils.countTodayByCreateTime(customStorageMapper, "", "code", item.getCreateTime() != null ? item.getCreateTime() : LocalDateTime.now()));
            item.setCode(OrderUtils.countTodayByCreateTime(customStorageMapper, "", "code"));
            customStorageMapper.insert(item);
        });
        return AjaxResult.success("自定义入库成功");
@@ -708,7 +709,9 @@
            dto.setInboundNum0(dto.getInboundNum().subtract(totalInboundNum));
            // åº“存价值
            if(dto.getTaxInclusiveUnitPrice() != null){
                dto.setTaxInclusiveTotalPrice(dto.getInboundNum0().multiply(dto.getTaxInclusiveUnitPrice()));
                dto.setTaxInclusiveTotalPrice(AmountUtils.scaleAmount(
                        dto.getInboundNum0().multiply(dto.getTaxInclusiveUnitPrice())
                ));
            }
        }
        pageList.setRecords(procurementPageDtoCopyList);
src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -8,8 +8,6 @@
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import java.time.LocalDateTime;
import com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto;
import com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto;
import com.ruoyi.procurementrecord.bean.vo.ShippingInfoVo;
@@ -20,6 +18,7 @@
import com.ruoyi.procurementrecord.service.ReturnManagementService;
import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.service.ShippingInfoService;
import lombok.RequiredArgsConstructor;
@@ -31,7 +30,6 @@
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author :yys
@@ -45,6 +43,7 @@
    private final ReturnManagementMapper returnManagementMapper;
    private final ReturnSaleProductService returnSaleProductService;
    private final ShippingInfoService shippingInfoService;
    private final SalesLedgerMapper salesLedgerMapper;
    private final SalesRefundAmountOrderService salesRefundAmountOrderService;
    private final StockUtils stockUtils;
@@ -57,7 +56,7 @@
    @Override
    public boolean addReturnManagementDto(ReturnManagementDto returnManagementDto) {
        if (ObjectUtils.isEmpty(returnManagementDto.getReturnNo())){
            String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT","return_no", returnManagementDto.getCreateTime() != null ? returnManagementDto.getCreateTime() : LocalDateTime.now());
            String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT","return_no");
            returnManagementDto.setReturnNo(rt);
        }
        save(returnManagementDto);
@@ -99,36 +98,31 @@
    public boolean handle(Long returnManagementId) {
        ReturnManagement byId = this.getById(returnManagementId);
        List<ReturnSaleProductDto> list = returnSaleProductService.listReturnSaleProduct(returnManagementId);
        // 1. è®¡ç®—正确的退款总额(金额=单价×数量)
        BigDecimal totalRefund = BigDecimal.ZERO;
        for (ReturnSaleProductDto returnSaleProduct : list) {
            BigDecimal itemAmount = returnSaleProduct.getAmount() != null
                    ? returnSaleProduct.getAmount()
                    : BigDecimal.ZERO;
            totalRefund = totalRefund.add(itemAmount);
            // æ˜¯å¦æœ‰è´¨é‡é—®é¢˜
            if (returnSaleProduct.getIsQuality() == 1) {
                stockUtils.addUnStockWithBatchNo(returnSaleProduct.getProductModelId(), returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(), returnSaleProduct.getId(), returnSaleProduct.getBatchNo());
            } else {
                stockUtils.addStockWithBatchNo(returnSaleProduct.getProductModelId(), returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(), returnSaleProduct.getId(), returnSaleProduct.getBatchNo());
            }
        }
        // 2. æ›´æ–°é€€è´§å•状态和退款总额
        byId.setStatus(1);
        byId.setSettler(SecurityUtils.getLoginUser().getNickName());
        byId.setRefundAmount(totalRefund);
        updateById(byId);
        //更新退货产品列表的状态
        List<ReturnSaleProduct> updateList = list.stream().map(m -> {
            ReturnSaleProduct returnSaleProduct = new ReturnSaleProduct();
            returnSaleProduct.setId(m.getId());
            returnSaleProduct.setStatus(1);
            return returnSaleProduct;
        }).collect(Collectors.toList());
        returnSaleProductService.updateBatchById(updateList);
        SalesRefundAmountOrderDto salesRefundAmountOrder = new SalesRefundAmountOrderDto();
        salesRefundAmountOrder.setReturnManagementId(returnManagementId);
        salesRefundAmountOrder.setStatus(0);
        salesRefundAmountOrder.setCreateTime(byId.getCreateTime());
        salesRefundAmountOrder.setCreateUserId(SecurityUtils.getUserId());
        BigDecimal bigDecimal = new BigDecimal(0);
        for (ReturnSaleProductDto returnSaleProduct : list) {
            bigDecimal = bigDecimal.add(returnSaleProduct.getPrice());
            salesRefundAmountOrder.setRefundedAmount(new BigDecimal(0));
            // æ˜¯å¦æœ‰è´¨é‡é—®é¢˜
            if (returnSaleProduct.getIsQuality() == 1) {
                // æœ‰è´¨é‡é—®é¢˜ï¼Œå…¥ä¸åˆæ ¼åº“(带批次)
                stockUtils.addUnStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
            }else{
                // æ— è´¨é‡é—®é¢˜ï¼Œå…¥åˆæ ¼åº“(带批次)
                stockUtils.addStockWithBatchNo(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId(),returnSaleProduct.getBatchNo());
            }
        }
        salesRefundAmountOrder.setRefundAmount(bigDecimal);
        salesRefundAmountOrder.setNotRefundedAmount(salesRefundAmountOrder.getRefundedAmount());
        // åˆ†æ‰¹é€€æ¬¾
//        salesRefundAmountOrderService.addSalesRefundAmountOrderDto(salesRefundAmountOrder);
        return true;
    }
src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -2,11 +2,16 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.enums.ReviewStatusEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockOutRecord;
@@ -18,7 +23,6 @@
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@@ -90,21 +94,11 @@
     * @param recordId
     */
    public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) {
        addStock(productModelId, quantity, recordType, recordId, null);
    }
    /**
     * åˆæ ¼å…¥åº“
     * @param recordType
     * @param recordId
     */
    public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId, LocalDateTime createTime) {
        StockInventoryDto stockInventoryDto = new StockInventoryDto();
        stockInventoryDto.setRecordId(recordId);
        stockInventoryDto.setRecordType(String.valueOf(recordType));
        stockInventoryDto.setQualitity(quantity);
        stockInventoryDto.setProductModelId(productModelId);
        stockInventoryDto.setCreateTime(createTime);
        stockInventoryService.addStockInRecordOnly(stockInventoryDto);
    }
@@ -116,22 +110,12 @@
     * @param recordId
     */
    public void addStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo) {
        addStockWithBatchNo(productModelId, quantity, recordType, recordId, batchNo, null);
    }
    /**
     * åˆæ ¼å…¥åº“带批次号
     * @param recordType
     * @param recordId
     */
    public void addStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo, LocalDateTime createTime) {
        StockInventoryDto stockInventoryDto = new StockInventoryDto();
        stockInventoryDto.setRecordId(recordId);
        stockInventoryDto.setRecordType(String.valueOf(recordType));
        stockInventoryDto.setQualitity(quantity);
        stockInventoryDto.setProductModelId(productModelId);
        stockInventoryDto.setBatchNo(batchNo);
        stockInventoryDto.setCreateTime(createTime);
        stockInventoryService.addStockInRecordOnly(stockInventoryDto);
    }
@@ -150,7 +134,8 @@
        stockInventoryDto.setQualitity(quantity);
        stockInventoryDto.setProductModelId(productModelId);
        stockInventoryDto.setBatchNo(batchNo);
        stockInventoryService.addStockOutRecordOnly(stockInventoryDto);
        StockOutRecordDto stockOutRecordDto = stockInventoryService.addStockOutRecordOnly(stockInventoryDto);
        stockOutRecordService.batchApprove(Collections.singletonList(stockOutRecordDto.getId()),1);
    }
    /**
@@ -184,25 +169,17 @@
    //删除出库记录
    public void deleteStockOutRecord(Long recordId, String recordType) {
        List<StockOutRecord> records = stockOutRecordService.list(new QueryWrapper<StockOutRecord>()
                .lambda().eq(StockOutRecord::getRecordId, recordId)
        List<StockOutRecord> list = stockOutRecordService.list(Wrappers.<StockOutRecord>lambdaQuery()
                .eq(StockOutRecord::getRecordId, recordId)
                .eq(StockOutRecord::getRecordType, recordType));
        if (ObjectUtils.isEmpty(records)) {
            return;
        }
        List<Long> approvedIds = records.stream()
                .filter(one -> ReviewStatusEnum.APPROVED.getCode().equals(one.getApprovalStatus()))
                .map(StockOutRecord::getId)
                .toList();
        List<Long> pendingIds = records.stream()
                .filter(one -> !ReviewStatusEnum.APPROVED.getCode().equals(one.getApprovalStatus()))
                .map(StockOutRecord::getId)
                .toList();
        if (!approvedIds.isEmpty()) {
            stockOutRecordService.batchDelete(approvedIds);
        }
        if (!pendingIds.isEmpty()) {
            stockOutRecordService.removeByIds(pendingIds);
        if (CollectionUtils.isNotEmpty(list)) {
            for (StockOutRecord one : list) {
                if (ReviewStatusEnum.APPROVED.getCode().equals(one.getApprovalStatus())) {
                    stockOutRecordService.batchDelete(Collections.singletonList(one.getId()));
                } else {
                    stockOutRecordService.removeById(one.getId());
                }
            }
        }
    }
src/main/java/com/ruoyi/production/bean/dto/ProductionPlanImportDto.java
@@ -36,6 +36,10 @@
    @Excel(name = "需求数量")
    private BigDecimal qtyRequired;
    @Schema(description = "来源 é”€å”®/内部")
    @Excel(name = "来源 é”€å”®/内部")
    private String source;
    @Schema(description = "承诺日期")
    @Excel(name = "承诺日期")
    private LocalDate promisedDeliveryDate;
src/main/java/com/ruoyi/production/bean/vo/ProcessRouteStatusVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.production.bean.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Schema(name = "ProcessRouteStatusVo", description = "工序生产进度项")
public class ProcessRouteStatusVo {
    @Schema(description = "工序名称")
    private String name;
    @Schema(description = "完成进度百分比")
    private BigDecimal percentage;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderProcessTaskVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.bean.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Schema(name = "ProductionOrderProcessTaskVo", description = "生产订单工序任务进度")
public class ProductionOrderProcessTaskVo {
    @Schema(description = "生产订单ID")
    private Long productionOrderId;
    @Schema(description = "工序名称")
    private String operationName;
    @Schema(description = "完成进度")
    private BigDecimal completionStatus;
}
src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
@@ -44,4 +44,7 @@
    @Schema(description = "是否已退料")
    private Boolean returned;
    @Schema(description = "工序生产进度")
    private List<ProcessRouteStatusVo> processRouteStatus;
}
src/main/java/com/ruoyi/production/controller/ProductionBomStructureController.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.bean.dto.ProductionBomStructureDto;
import com.ruoyi.production.bean.vo.ProductionBomStructureVo;
@@ -31,6 +33,7 @@
        return R.ok(productionBomStructureService.listByBomId(bomId));
    }
    @Log(title = "新增或修改BOM结构", businessType = BusinessType.UPDATE)
    @PostMapping("/addOrUpdateBomStructs")
    @Operation(summary = "新增或修改BOM结构")
    public R addProductionBomStructure(@RequestBody ProductionBomStructureDto productionBomStructureDto) {
src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.bean.dto.ProductionOperationTaskDto;
@@ -40,36 +42,42 @@
        return R.ok(productionOperationTaskService.getProductionOperationTaskInfo(id));
    }
    @Log(title = "新增工单", businessType = BusinessType.INSERT)
    @PostMapping
    @Operation(summary = "新增工单")
    public R<Boolean> add(@RequestBody ProductionOperationTask productionOperationTask) {
        return R.ok(productionOperationTaskService.saveProductionOperationTask(productionOperationTask));
    }
    @Log(title = "修改工单", businessType = BusinessType.UPDATE)
    @PutMapping
    @Operation(summary = "修改工单")
    public R<Boolean> edit(@RequestBody ProductionOperationTask productionOperationTask) {
        return R.ok(productionOperationTaskService.saveProductionOperationTask(productionOperationTask));
    }
    @Log(title = "删除工单", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除工单")
    public R<Boolean> remove(@RequestBody List<Long> ids) {
        return R.ok(productionOperationTaskService.removeProductionOperationTask(ids));
    }
    @Log(title = "产品工单更新", businessType = BusinessType.UPDATE)
    @Operation(summary = "产品工单更新")
    @PostMapping("/updateProductWorkOrder")
    public R updateProductWorkOrder(@RequestBody ProductionOperationTaskDto dto) {
        return R.ok(productionOperationTaskService.updateProductWorkOrder(dto));
    }
    @Log(title = "指派报工人", businessType = BusinessType.UPDATE)
    @Operation(summary = "指派报工人")
    @PostMapping("/assign")
    public R<Boolean> assign(@RequestBody ProductionOperationTaskDto dto) {
        return R.ok(productionOperationTaskService.assign(dto));
    }
    @Log(title = "工单流转卡下载", businessType = BusinessType.EXPORT)
    @PostMapping("/down")
    @Operation(summary = "工单流转卡下载")
    public void down(HttpServletResponse response, @RequestBody ProductionOperationTaskDto dto) {
src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
@@ -53,6 +53,7 @@
        return R.ok(productionOrderService.getProductionOrderInfo(id));
    }
    @Log(title = "生产工单", businessType = BusinessType.INSERT)
    @PostMapping("/addOrder")
    @Operation(summary = "新增生产订单", description = "新增下单只支持1种方式:生产计划生成,传 productionPlanIds,系统自动汇总计划得到产品规格和数量;"
                    + "technologyRoutingId ä¸ºç©ºæ—¶ä¼šè‡ªåŠ¨åŒ¹é…è¯¥äº§å“è§„æ ¼æœ€æ–°å·¥è‰ºè·¯çº¿ï¼Œquantity æœ€ç»ˆå¿…须大于 0。")
@@ -62,18 +63,21 @@
        return R.ok(productionOrderService.saveProductionOrder(productionOrder));
    }
    @Log(title = "生产工单", businessType = BusinessType.UPDATE)
    @Operation(summary = "绑定工艺路线")
    @PostMapping("/bindingRoute")
    public R bindingRoute(@RequestBody ProductionOrderDto productionOrderDto) {
        return R.ok(productionOrderService.bindingRoute(productionOrderDto));
    }
    @Log(title = "生产工单", businessType = BusinessType.UPDATE)
    @PostMapping("/syncSnapshot/{id}")
    @Operation(summary = "同步生产订单工艺/BOM快照")
    public R<Integer> syncSnapshot(@PathVariable Long id) {
        return R.ok(productionOrderService.syncProductionOrderSnapshot(id));
    }
    @Log(title = "生产工单", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除生产订单")
    public R<Boolean> remove(@RequestBody List<Long> ids) {
@@ -98,6 +102,7 @@
        return R.ok(productionOrderService.getWorkOrderReportInspectDetail(productionOrderDto));
    }
    @Log(title = "生产工单", businessType = BusinessType.UPDATE)
    @Operation(summary = "更新订单状态")
    @PostMapping("/updateOrder")
    public R updateOrder(@RequestBody ProductionOrderDto productionOrderDto) {
src/main/java/com/ruoyi/production/controller/ProductionOrderPickController.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.bean.dto.ProductionOrderPickDto;
import com.ruoyi.production.bean.vo.ProductionOrderPickVo;
@@ -27,12 +29,14 @@
    private final ProductionOrderPickService productionOrderPickService;
    @Log(title = "领料保存到线边仓", businessType = BusinessType.INSERT)
    @PostMapping("/savePick")
    @Operation(summary = "领料保存到线边仓")
    public R<Boolean> savePick(@RequestBody ProductionOrderPickDto dto) {
        return R.ok(productionOrderPickService.savePick(dto));
    }
    @Log(title = "变更领料", businessType = BusinessType.UPDATE)
    @PostMapping("/updatePick")
    @Operation(summary = "变更领料")
    public R<Boolean> updatePick(@RequestBody ProductionOrderPickDto dto) {
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
@@ -1,5 +1,7 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.service.ProductionOrderRoutingOperationService;
@@ -30,24 +32,28 @@
        return R.ok(productionOrderRoutingService.listMain(orderId));
    }
    @Log(title = "生产工序路由", businessType = BusinessType.INSERT)
    @PostMapping("/addRouteItem")
    @Operation(summary = "新增生产订单的工艺路线详情")
    public R addRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
        return productionOrderRoutingOperationService.addRouteItem(productionOrderRoutingOperation);
    }
    @Log(title = "生产工序路由", businessType = BusinessType.UPDATE)
    @PostMapping("/updateRouteItem")
    @Operation(summary = "修改生产订单的工艺路线详情")
    public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
        return productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation);
        return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation));
    }
    @Log(title = "生产工序路由", businessType = BusinessType.DELETE)
    @DeleteMapping("/deleteRouteItem/{id}")
    @Operation(summary = "删除生产工艺路线")
    public R deleteRouteItem(@PathVariable("id") Long id) {
        return productionOrderRoutingOperationService.deleteRouteItem(id);
    }
    @Log(title = "生产工序路由", businessType = BusinessType.UPDATE)
    @PostMapping("/sortRouteItem")
    @Operation(summary = "排序")
    public R sortRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingOperationParamController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.production.bean.dto.ProductionOrderRoutingOperationParamDto;
import com.ruoyi.production.bean.vo.ProductionOrderRoutingOperationParamVo;
@@ -41,18 +43,21 @@
        return R.ok(productionOrderRoutingOperationParamService.getProductionOrderRoutingOperationParamInfo(id));
    }
    @Log(title = "生产工序参数", businessType = BusinessType.INSERT)
    @PostMapping
    @Operation(summary = "新增生产订单工艺路线工序参数")
    public R<Boolean> add(@RequestBody ProductionOrderRoutingOperationParam item) {
        return R.ok(productionOrderRoutingOperationParamService.saveProductionOrderRoutingOperationParam(item));
    }
    @Log(title = "生产工序参数", businessType = BusinessType.UPDATE)
    @PutMapping
    @Operation(summary = "修改生产订单工艺路线工序参数")
    public R<Boolean> edit(@RequestBody ProductionOrderRoutingOperationParam item) {
        return R.ok(productionOrderRoutingOperationParamService.updateById(item));
    }
    @Log(title = "生产工序参数", businessType = BusinessType.DELETE)
    @DeleteMapping("/{id}")
    @Operation(summary = "删除生产订单工艺路线工序参数")
    public R<Boolean> remove(@PathVariable("id") Long id) {
src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
@@ -2,6 +2,8 @@
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.domain.R;
import com.ruoyi.production.bean.dto.ProductionProductMainDto;
import com.ruoyi.production.service.ProductionProductMainService;
@@ -50,30 +52,35 @@
     * @param productionProductMainDto
     * @return
     */
    @Log(title = "生产产品", businessType = BusinessType.INSERT)
    @PostMapping("/addProductMain")
    @PreAuthorize("@ss.hasPermi('productionProductMain:add')")
    public R addProductMain(@RequestBody ProductionProductMainDto productionProductMainDto) {
        return R.ok(productionProductMainService.addProductMain(productionProductMainDto));
    }
    @Log(title = "生产产品", businessType = BusinessType.INSERT)
    @PostMapping
    @Operation(summary = "新增生产报工")
    public R add(@RequestBody ProductionProductMainDto productionProductMainDto) {
        return R.ok(productionProductMainService.saveProductionProductMain(productionProductMainDto));
    }
    @Log(title = "生产产品", businessType = BusinessType.UPDATE)
    @PutMapping
    @Operation(summary = "修改生产报工")
    public R edit(@RequestBody ProductionProductMainDto productionProductMainDto) {
        return R.ok(productionProductMainService.saveProductionProductMain(productionProductMainDto));
    }
    @Log(title = "生产产品", businessType = BusinessType.DELETE)
    @Operation(summary = "删除报工")
    @DeleteMapping("/delete")
    public R delete(@RequestBody ProductionProductMainDto productionProductMainDto) {
        return R.ok(productionProductMainService.removeProductMain(productionProductMainDto.getId()));
    }
    @Log(title = "生产产品", businessType = BusinessType.DELETE)
    @DeleteMapping("/{id}")
    @Operation(summary = "删除生产报工")
    public R remove(@PathVariable("id") Long id) {
@@ -84,6 +91,7 @@
    /**
     * å¯¼å‡º
     */
    @Log(title = "生产产品", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, ProductionProductMainDto productionProductMainDto) {
        List<ProductionProductMainDto> list;
src/main/java/com/ruoyi/production/enums/ProductOrderStatusEnum.java
@@ -17,8 +17,7 @@
    WAIT(1, "待开始"),
    RUNNING(2, "进行中"),
    FINISHED(3, "已完成"),
    //CANCEL(4, "已取消"),
    END(5, "已结束");
    CANCEL(4, "已取消");
    private final Integer code;
    private final String desc;
@@ -44,10 +43,10 @@
    }
    /**
     * åˆ¤æ–­æ˜¯å¦å…è®¸åˆ é™¤ï¼ˆ1)
     * åˆ¤æ–­æ˜¯å¦å…è®¸åˆ é™¤ï¼ˆ4)
     */
    public static boolean canDelete(Integer code) {
        return WAIT.getCode().equals(code);
        return WAIT.getCode().equals(code) || CANCEL.getCode().equals(code);
    }
    /**
@@ -58,9 +57,9 @@
    }
    /**
     * åˆ¤æ–­æ˜¯å¦å·²å¼€å§‹ç”Ÿäº§ï¼ˆ2、3、5)
     * åˆ¤æ–­æ˜¯å¦å·²å¼€å§‹ç”Ÿäº§ï¼ˆ2、3)
     */
    public static boolean isStarted(Integer code) {
        return RUNNING.getCode().equals(code) || FINISHED.getCode().equals(code) || END.getCode().equals(code);
        return RUNNING.getCode().equals(code) || FINISHED.getCode().equals(code);
    }
}
src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java
@@ -7,6 +7,7 @@
import com.ruoyi.home.dto.processDataProductionStatisticsDto;
import com.ruoyi.production.bean.dto.ProductionOperationTaskDto;
import com.ruoyi.production.bean.vo.ProductionOperationTaskVo;
import com.ruoyi.production.bean.vo.ProductionOrderProcessTaskVo;
import com.ruoyi.production.pojo.ProductionOperationTask;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -43,4 +44,6 @@
    ProductionOperationTaskDto getProductWorkOrderFlowCard(@Param("id") Long id);
    List<ProductionOperationTaskVo> getOperation(@Param("c") ProductionOperationTaskDto dto);
    List<ProductionOrderProcessTaskVo> listProcessStatusByOrderIds(@Param("orderIds") List<Long> orderIds);
}
src/main/java/com/ruoyi/production/pojo/ProductionOrder.java
@@ -41,6 +41,7 @@
    private String npsNo;
    @Schema(description = "录入时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -13,5 +13,4 @@
    R deleteRouteItem(Long id);
    int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
}
src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java
@@ -161,21 +161,11 @@
        // æ£€æŸ¥æ•°æ®æ˜¯å¦é‡å¤ï¼Œé¿å…é‡å¤ä¿å­˜
        boolean duplicate = productionOrderRoutingOperationParamMapper.selectCount(
                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                        .eq(ProductionOrderRoutingOperationParam::getProductionOrderId, item.getProductionOrderId())
                        .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId)
                        .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, item.getProductionOrderRoutingOperationId())
                        .eq(item.getTechnologyRoutingOperationParamId() != null,
                                ProductionOrderRoutingOperationParam::getTechnologyRoutingOperationParamId, item.getTechnologyRoutingOperationParamId())
                        .eq(item.getTechnologyRoutingOperationParamId() == null && item.getTechnologyOperationParamId() != null,
                                ProductionOrderRoutingOperationParam::getTechnologyOperationParamId, item.getTechnologyOperationParamId())
                        .eq(item.getTechnologyRoutingOperationParamId() == null
                                        && item.getTechnologyOperationParamId() == null
                                        && item.getParamId() != null,
                                ProductionOrderRoutingOperationParam::getParamId, item.getParamId())
                        .eq(item.getTechnologyRoutingOperationParamId() == null
                                        && item.getTechnologyOperationParamId() == null
                                        && item.getParamId() == null
                                        && item.getParamCode() != null,
                        .eq(item.getTechnologyRoutingOperationParamId() == null && item.getParamCode() != null,
                                ProductionOrderRoutingOperationParam::getParamCode, item.getParamCode())
                        .ne(item.getId() != null, ProductionOrderRoutingOperationParam::getId, item.getId())
        ) > 0;
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -22,6 +22,8 @@
import com.ruoyi.production.bean.vo.ProductionOrderVo;
import com.ruoyi.production.bean.vo.ProductionPlanVo;
import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo;
import com.ruoyi.production.bean.vo.ProcessRouteStatusVo;
import com.ruoyi.production.bean.vo.ProductionOrderProcessTaskVo;
import com.ruoyi.production.enums.ProductOrderStatusEnum;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.*;
@@ -44,7 +46,6 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@@ -87,6 +88,7 @@
        // åˆ†é¡µæŸ¥è¯¢ç”Ÿäº§è®¢å•
        Page<ProductionOrderVo> result = (Page<ProductionOrderVo>) baseMapper.pageProductionOrder(page, dto);
        fillProductImages(result.getRecords());
        fillProcessRouteStatus(result.getRecords());
        return result;
    }
@@ -95,6 +97,7 @@
        // æŸ¥è¯¢ç”Ÿäº§è®¢å•列表
        List<ProductionOrderVo> records = baseMapper.listProductionOrder(dto);
        fillProductImages(records);
        fillProcessRouteStatus(records);
        return records;
    }
@@ -116,7 +119,7 @@
        // ä¸‹å•入口统一补齐来源单据、计划和工艺信息,避免前端分别传多套字段。
        validateAndFillOrder(productionOrder, oldOrder);
        if (productionOrder.getNpsNo() == null || productionOrder.getNpsNo().trim().isEmpty()) {
            productionOrder.setNpsNo(generateNextOrderNo(productionOrder.getCreateTime() != null ? productionOrder.getCreateTime() : LocalDateTime.now()));
            productionOrder.setNpsNo(generateNextOrderNo());
        }
        if (productionOrder.getCompleteQuantity() == null) {
            productionOrder.setCompleteQuantity(BigDecimal.ZERO);
@@ -134,7 +137,7 @@
                || !Objects.equals(oldOrder.getProductModelId(), productionOrder.getProductModelId())
                || compareDecimal(oldOrder.getQuantity(), productionOrder.getQuantity()) != 0
                || productionOrderRoutingMapper.selectCount(Wrappers.<ProductionOrderRouting>lambdaQuery()
                        .eq(ProductionOrderRouting::getProductionOrderId, productionOrder.getId())) == 0);
                .eq(ProductionOrderRouting::getProductionOrderId, productionOrder.getId())) == 0);
        if (needSync) {
            syncProductionOrderSnapshot(productionOrder.getId());
        }
@@ -260,7 +263,7 @@
        Map<String, BigDecimal> operationDemandedQuantityMap =
                buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
        Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds(
        // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                        // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                        routingOperations.stream()
                                .map(TechnologyRoutingOperation::getTechnologyOperationId)
                                .filter(Objects::nonNull)
@@ -461,7 +464,7 @@
    private void clearProductionSnapshot(Long productionOrderId) {
        // æ¸…理订单已生成的工艺与BOM快照数据
        boolean hasPickRecord = productionOrderPickRecordMapper.selectCount(
        // æŸ¥è¯¢å¹¶å‡†å¤‡ä¸šåŠ¡æ•°æ®
                // æŸ¥è¯¢å¹¶å‡†å¤‡ä¸šåŠ¡æ•°æ®
                Wrappers.<ProductionOrderPickRecord>lambdaQuery()
                        .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)) > 0;
        // å‚数与前置条件校验
@@ -471,7 +474,7 @@
        List<Long> taskIds = productionOperationTaskMapper.selectList(
                        Wrappers.<ProductionOperationTask>lambdaQuery()
                                .eq(ProductionOperationTask::getProductionOrderId, productionOrderId))
        // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                .stream().map(ProductionOperationTask::getId).collect(Collectors.toList());
        if (!taskIds.isEmpty()) {
            // å·²æœ‰æŠ¥å·¥è®°å½•说明订单已开工,此时不允许再重建快照。
@@ -509,10 +512,9 @@
                .orderByDesc(ProductionOrder::getId);
    }
    private String generateNextOrderNo(LocalDateTime createTime) {
    private String generateNextOrderNo() {
        // ç”Ÿæˆä¸‹ä¸€ä¸ªç”Ÿäº§è®¢å•号
        LocalDate localDate = createTime.toLocalDate();
        String datePrefix = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String prefix = "SC" + datePrefix;
        ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery()
                .likeRight(ProductionOrder::getNpsNo, prefix)
@@ -814,6 +816,46 @@
        }
    }
    private void fillProcessRouteStatus(List<ProductionOrderVo> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        List<Long> orderIds = records.stream()
                .map(ProductionOrderVo::getId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (orderIds.isEmpty()) {
            return;
        }
        List<ProductionOrderProcessTaskVo> tasks = productionOperationTaskMapper.listProcessStatusByOrderIds(orderIds);
        Map<Long, List<ProcessRouteStatusVo>> statusMap = new LinkedHashMap<>();
        if (tasks != null) {
            for (ProductionOrderProcessTaskVo task : tasks) {
                if (task == null || task.getProductionOrderId() == null) {
                    continue;
                }
                ProcessRouteStatusVo status = new ProcessRouteStatusVo();
                status.setName(task.getOperationName() != null && !task.getOperationName().isBlank()
                        ? task.getOperationName()
                        : "未知工序");
                BigDecimal percentage = task.getCompletionStatus() == null
                        ? BigDecimal.ZERO
                        : task.getCompletionStatus();
                if (percentage.compareTo(new BigDecimal("100")) > 0) {
                    percentage = new BigDecimal("100");
                }
                status.setPercentage(percentage);
                statusMap.computeIfAbsent(task.getProductionOrderId(), key -> new ArrayList<>()).add(status);
            }
        }
        for (ProductionOrderVo record : records) {
            record.setProcessRouteStatus(statusMap.getOrDefault(record.getId(), Collections.emptyList()));
        }
    }
    private StorageBlobVO toStorageBlobVO(StorageBlob blob) {
        // å°†å­˜å‚¨æ–‡ä»¶å¯¹è±¡è½¬æ¢ä¸ºVO
        StorageBlobVO vo = BeanUtil.copyProperties(blob, StorageBlobVO.class);
@@ -841,7 +883,7 @@
                new Page<ProductionOperationTaskVo>(1, -1), taskQuery);
        List<ProductionOperationTaskVo> workOrderList = workOrderPage == null || workOrderPage.getRecords() == null
                ? Collections.emptyList()
        // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
                : workOrderPage.getRecords().stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(ProductionOperationTaskVo::getId, Comparator.nullsLast(Comparator.naturalOrder())))
@@ -857,7 +899,7 @@
                .collect(Collectors.toList());
        List<ProductionProductMain> reportMainList = workOrderIdList.isEmpty()
                ? Collections.emptyList()
        // æŸ¥è¯¢å¹¶å‡†å¤‡ä¸šåŠ¡æ•°æ®
                // æŸ¥è¯¢å¹¶å‡†å¤‡ä¸šåŠ¡æ•°æ®
                : productionProductMainMapper.selectList(
                Wrappers.<ProductionProductMain>lambdaQuery()
                        .in(ProductionProductMain::getProductionOperationTaskId, workOrderIdList)
@@ -1065,20 +1107,30 @@
            }
        }
        // è¿‡æ»¤å‡ºéžæ ¹èŠ‚ç‚¹ï¼ˆå®žé™…é¢†æ–™é¡¹ï¼‰
        // æŽ’除投入品与产出品相同且比例为1的情况(自身加工,不需要领料)
        // æ”¶é›†è®¢å•成品规格ID:包含生产订单成品规格与BOM根节点规格,用于排除“自己”这一物料
        Set<Long> finishedProductModelIds = new HashSet<>();
        if (productionOrder != null && productionOrder.getProductModelId() != null) {
            finishedProductModelIds.add(productionOrder.getProductModelId());
        }
        bomStructureList.stream()
                .filter(s -> s != null && (s.getParentId() == null || s.getParentId() == 0))
                .map(ProductionBomStructureVo::getProductModelId)
                .filter(Objects::nonNull)
                .forEach(finishedProductModelIds::add);
        // æ”¶é›†æ‰€æœ‰è¢«å¼•用为父级的节点ID:BOM从最后一道工序往里配置前一道工序,
        // ä¸­é—´èŠ‚ç‚¹æ˜¯ä¸Šä¸€é“å·¥åºçš„äº§å‡ºï¼ˆè‡ªåˆ¶ï¼‰ï¼Œåªæœ‰æ¯æ¡åˆ†æ”¯æœ€é‡Œå±‚çš„å¶å­èŠ‚ç‚¹æ‰æ˜¯çœŸæ­£éœ€è¦é¢†ç”¨çš„ç‰©æ–™ã€‚
        Set<Long> parentNodeIds = bomStructureList.stream()
                .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0)
                .map(ProductionBomStructureVo::getParentId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        // è¿‡æ»¤å‡ºå¶å­èŠ‚ç‚¹ï¼ˆå®žé™…é¢†æ–™é¡¹ï¼‰ï¼šéžæ ¹èŠ‚ç‚¹ã€éžä¸­é—´èŠ‚ç‚¹ï¼Œå¹¶æŽ’é™¤ä¸Žè®¢å•æˆå“åŒè§„æ ¼çš„ç‰©æ–™ï¼ˆè‡ªåˆ¶æˆå“æ— éœ€é¢†æ–™ï¼‰
        List<ProductionBomStructureVo> childStructureList = bomStructureList.stream()
                .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0)
                .filter(s -> {
                    ProductionBomStructureVo parent = structureByIdMap.get(s.getParentId());
                    if (parent == null) {
                        return true;
                    }
                    // æŠ•入品与产出品相同且比例为1时,不需要领料
                    boolean sameProduct = Objects.equals(s.getProductModelId(), parent.getProductModelId());
                    boolean unitRatio = BigDecimal.ONE.compareTo(defaultDecimal(s.getUnitQuantity())) == 0;
                    return !(sameProduct && unitRatio);
                })
                .filter(s -> s.getId() == null || !parentNodeIds.contains(s.getId()))
                .filter(s -> s.getProductModelId() == null || !finishedProductModelIds.contains(s.getProductModelId()))
                .collect(Collectors.toList());
        // éåŽ†å¤„ç†æ•°æ®å¹¶ç»„è£…ç»“æžœ
@@ -1092,7 +1144,8 @@
        if (!productModelIds.isEmpty()) {
            List<StockInventory> stockList = stockInventoryMapper.selectList(
                    Wrappers.<StockInventory>lambdaQuery()
                            .in(StockInventory::getProductModelId, productModelIds));
                            .in(StockInventory::getProductModelId, productModelIds)
                            .gt(StockInventory::getQualitity, BigDecimal.ZERO));
            for (StockInventory stockItem : stockList) {
                if (stockItem == null || stockItem.getProductModelId() == null) {
                    continue;
@@ -1106,35 +1159,40 @@
            }
        }
        List<ProductionOrderPickVo> pickList = new ArrayList<>();
        Map<String, ProductionOrderPickVo> mergedPickMap = new LinkedHashMap<>();
        for (ProductionBomStructureVo structure : childStructureList) {
            if (structure == null || structure.getProductModelId() == null) {
                continue;
            }
            Long productModelId = structure.getProductModelId();
            ProductionOrderPickVo vo = new ProductionOrderPickVo();
            vo.setProductModelId(productModelId);
            vo.setOperationName(structure.getOperationName());
            vo.setTechnologyOperationId(structure.getTechnologyOperationId());
            vo.setProductName(structure.getProductName());
            vo.setModel(structure.getModel());
            vo.setDemandedQuantity(defaultDecimal(structure.getDemandedQuantity()));
            vo.setUnit(structure.getUnit());
            List<String> batchNoList = stockBatchNoMap.get(productModelId) == null
                    ? Collections.emptyList()
                    : new ArrayList<>(stockBatchNoMap.get(productModelId));
            vo.setBatchNoList(batchNoList);
            vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO));
            vo.setBom(true);
            pickList.add(vo);
            String mergeKey = String.valueOf(structure.getTechnologyOperationId()) + "#" + productModelId;
            ProductionOrderPickVo vo = mergedPickMap.get(mergeKey);
            if (vo == null) {
                vo = new ProductionOrderPickVo();
                vo.setProductModelId(productModelId);
                vo.setOperationName(structure.getOperationName());
                vo.setTechnologyOperationId(structure.getTechnologyOperationId());
                vo.setProductName(structure.getProductName());
                vo.setModel(structure.getModel());
                vo.setDemandedQuantity(BigDecimal.ZERO);
                vo.setUnit(structure.getUnit());
                List<String> batchNoList = stockBatchNoMap.get(productModelId) == null
                        ? Collections.emptyList()
                        : new ArrayList<>(stockBatchNoMap.get(productModelId));
                vo.setBatchNoList(batchNoList);
                vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO));
                vo.setBom(true);
                mergedPickMap.put(mergeKey, vo);
            }
            vo.setDemandedQuantity(defaultDecimal(vo.getDemandedQuantity()).add(defaultDecimal(structure.getDemandedQuantity())));
        }
        return pickList;
        return new ArrayList<>(mergedPickMap.values());
    }
    @Override
    public int updateOrder(ProductionOrderDto productionOrderDto) {
        // æ›´æ–°ç”Ÿäº§è®¢å•主数据
        productionOrderDto.setStatus(ProductOrderStatusEnum.END.getCode());
        productionOrderDto.setStatus(5);
        return baseMapper.updateById(productionOrderDto);
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionPlanServiceImpl.java
@@ -311,7 +311,7 @@
            BeanUtils.copyProperties(dto, entity);
            entity.setProductModelId(resolveProductModelId(dto, i + 2, allModels, productNameById));
            entity.setStatus(PLAN_STATUS_WAIT);
            entity.setSource("内部");
            entity.setSource(StringUtils.isNotEmpty(dto.getSource()) ? StringUtils.trim(dto.getSource()) : "内部");
            entity.setQuantityIssued(BigDecimal.ZERO);
            entity.setCreateTime(now);
            entity.setUpdateTime(now);
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -27,6 +27,7 @@
import com.ruoyi.quality.mapper.*;
import com.ruoyi.quality.pojo.*;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.service.StockInventoryService;
@@ -42,15 +43,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
@Service
@@ -360,6 +353,12 @@
                        StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
                        productModel.getId(),
                        "0");
                //StockInRecord stockInRecord = stockInRecordService.getOne(Wrappers.<StockInRecord>lambdaQuery()
                //        .eq(StockInRecord::getRecordId, productionProductMain.getId())
                //        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode())
                //);
                //stockInRecordService.batchApprove(Collections.singletonList(stockInRecord.getId()),1);
            }
            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty));
@@ -378,10 +377,8 @@
            if (ObjectUtils.isNull(productionOrder.getStartTime())) {
                productionOrder.setStartTime(LocalDateTime.now());
            }
            // ä»…待开始状态才推进到进行中,避免非末道工序报工时覆盖已完成状态。
            if (ProductOrderStatusEnum.WAIT.getCode().equals(productionOrder.getStatus())) {
                productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            }
            // è®¢å•状态由最后一道工序的合格产出推动,避免中间工序提前完工。
            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            if (isLastOperation) {
                productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty));
                if (productionOrder.getQuantity() != null
src/main/java/com/ruoyi/project/common/CommonController.java
@@ -3,6 +3,8 @@
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -36,6 +38,7 @@
    private final StorageBlobService storageBlobService;
    private final FileUtil fileUtil;
    @Log(title = "通用文件上传", businessType = BusinessType.INSERT)
    @PostMapping({"/upload"})
    @Operation(summary = "文件上传")
    public R upload(@RequestParam("files") List<MultipartFile> files) {
@@ -46,6 +49,7 @@
     * å…¬å…±æ–‡ä»¶ä¸Šä¼ 
     * æ­¤æŽ¥å£ä¸Šä¼ çš„æ–‡ä»¶æ°¸ä¹…有效,慎用
     */
    @Log(title = "公共文件上传", businessType = BusinessType.INSERT)
    @PostMapping({"/public/upload"})
    @Operation(summary = "文件上传")
    public R publicUpload(@RequestParam("files") List<MultipartFile> files) {
src/main/java/com/ruoyi/project/system/domain/SysUser.java
@@ -1,8 +1,8 @@
package com.ruoyi.project.system.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.xss.Xss;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.xss.Xss;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type;
@@ -60,13 +60,13 @@
    @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    private String delFlag;
    /** æ˜¯å¦å¼€é€šAI功能(0否 1是) */
    @JsonIgnore
    private Integer aiEnabled;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    private String delFlag;
    /** æ˜¯å¦å¼€é€šAI功能(0否 1是) */
    @JsonIgnore
    private Integer aiEnabled;
    /** æœ€åŽç™»å½•IP */
    @Excel(name = "最后登录IP", type = Type.EXPORT)
    private String loginIp;
@@ -128,12 +128,6 @@
     */
    @TableField(exist = false)
    private String deptNames;
    /**
     * è§’色名称
     */
    @TableField(exist = false)
    private String roleNames;
    public Long getCurrentDeptId() {
        return currentDeptId;
@@ -261,25 +255,25 @@
        this.status = status;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public String getDelFlag()
    {
        return delFlag;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public Integer getAiEnabled()
    {
        return aiEnabled;
    }
    public void setAiEnabled(Integer aiEnabled)
    {
        this.aiEnabled = aiEnabled;
    }
    public void setDelFlag(String delFlag)
    {
        this.delFlag = delFlag;
    }
    public Integer getAiEnabled()
    {
        return aiEnabled;
    }
    public void setAiEnabled(Integer aiEnabled)
    {
        this.aiEnabled = aiEnabled;
    }
    public String getLoginIp()
    {
@@ -375,14 +369,6 @@
        this.deptNames = deptNames;
    }
    public String getRoleNames() {
        return roleNames;
    }
    public void setRoleNames(String roleNames) {
        this.roleNames = roleNames;
    }
    public Long getDeptId() {
        return deptId;
    }
@@ -402,10 +388,10 @@
            .append("sex", getSex())
            .append("avatar", getAvatar())
            .append("password", getPassword())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("aiEnabled", getAiEnabled())
            .append("loginIp", getLoginIp())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("aiEnabled", getAiEnabled())
            .append("loginIp", getLoginIp())
            .append("loginDate", getLoginDate())
            .append("createBy", getCreateBy())
            .append("createTime", getCreateTime())
src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java
@@ -3,7 +3,6 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.project.system.domain.SysUserDept;
import com.ruoyi.project.system.domain.vo.SysUserDeptVo;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -16,28 +15,4 @@
    List<SysUserDeptVo> userLoginFacotryList(@Param("userDeptVo") SysUserDeptVo userDeptVo);
    List<Map<String, Object>> setSchemeApplicableStaffUserInfo(@Param("ids") List<Long> ids);
    @Select("<script>" +
            "select count(distinct sud.user_id) " +
            "from sys_user_dept sud " +
            "inner join sys_user su on su.user_id = sud.user_id " +
            "where su.del_flag = '0' " +
            "and sud.dept_id in " +
            "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" +
            "#{deptId}" +
            "</foreach>" +
            "</script>")
    Long countDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds);
    @Select("<script>" +
            "select distinct sud.user_id " +
            "from sys_user_dept sud " +
            "inner join sys_user su on su.user_id = sud.user_id " +
            "where su.del_flag = '0' " +
            "and sud.dept_id in " +
            "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" +
            "#{deptId}" +
            "</foreach>" +
            "</script>")
    List<Long> selectDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds);
}
src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java
@@ -155,10 +155,5 @@
    List<Long> getUserByRole(@Param("role") String role);
    /**
     * æŸ¥è¯¢æ‰€æœ‰æ´»è·ƒç”¨æˆ·ï¼ˆå«éƒ¨é—¨åç§°å’Œè§’色名称)
     */
    List<SysUser> selectUserListWithDetail();
    List<Long> getUserByPerms(@Param("perms") List<String> perms);
}
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
@@ -4,8 +4,6 @@
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.collaborativeApproval.mapper.EnterpriseNewsMapper;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysNotice;
@@ -20,12 +18,8 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * å…¬å‘Š æœåŠ¡å±‚å®žçŽ°
@@ -36,14 +30,11 @@
@RequiredArgsConstructor
public class SysNoticeServiceImpl  extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
    private static final Pattern ENTERPRISE_NEWS_ID_PATTERN = Pattern.compile("[?&]id=(\\d+)");
    private final SysNoticeMapper noticeMapper;
    private final SysUserMapper userMapper;
    private final SysDeptMapper deptMapper;
    private final SysUserDeptMapper userDeptMapper;
    private final UnipushService unipushService;
    private final EnterpriseNewsMapper enterpriseNewsMapper;
    /**
     * æŸ¥è¯¢å…¬å‘Šä¿¡æ¯
@@ -87,24 +78,9 @@
     * @return ç»“æžœ
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateNotice(SysNotice notice)
    {
        if (notice == null || notice.getNoticeId() == null) {
            return 0;
        }
        SysNotice dbNotice = noticeMapper.selectNoticeById(notice.getNoticeId());
        if (dbNotice == null) {
            return 0;
        }
        boolean needSyncNewsReadCount = isEnterpriseNewsNotice(dbNotice)
                && notice.getStatus() != null
                && !notice.getStatus().equals(dbNotice.getStatus());
        int rows = noticeMapper.updateNotice(notice);
        if (rows > 0 && needSyncNewsReadCount) {
            syncEnterpriseNewsReadCount(dbNotice.getJumpPath());
        }
        return rows;
        return noticeMapper.updateNotice(notice);
    }
    /**
@@ -141,17 +117,10 @@
    @Override
    public int readAll() {
        Long userId = SecurityUtils.getUserId();
        List<SysNotice> unreadNotices = noticeMapper.selectList(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0"));
        int rows = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
        return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getConsigneeId, userId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1"));
        if (rows > 0) {
            syncEnterpriseNewsReadCount(unreadNotices);
        }
        return rows;
    }
    @Override
@@ -251,63 +220,9 @@
            return false;
        }
        sysNotice.setStatus("1");
        boolean updated = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
        return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
                .eq(SysNotice::getNoticeId, noticeId)
                .eq(SysNotice::getStatus, "0")
                .set(SysNotice::getStatus, "1")) > 0;
        if (updated) {
            syncEnterpriseNewsReadCount(sysNotice.getJumpPath());
        }
        return updated;
    }
    private boolean isEnterpriseNewsNotice(SysNotice sysNotice) {
        return sysNotice != null
                && sysNotice.getJumpPath() != null
                && sysNotice.getJumpPath().contains("/enterpriseNews?id=");
    }
    private void syncEnterpriseNewsReadCount(List<SysNotice> notices) {
        if (notices == null || notices.isEmpty()) {
            return;
        }
        Set<String> jumpPaths = new HashSet<>();
        for (SysNotice notice : notices) {
            if (isEnterpriseNewsNotice(notice)) {
                jumpPaths.add(notice.getJumpPath());
            }
        }
        for (String jumpPath : jumpPaths) {
            syncEnterpriseNewsReadCount(jumpPath);
        }
    }
    private void syncEnterpriseNewsReadCount(String jumpPath) {
        Long newsId = parseEnterpriseNewsId(jumpPath);
        if (newsId == null) {
            return;
        }
        long readCount = noticeMapper.selectCount(Wrappers.<SysNotice>lambdaQuery()
                .eq(SysNotice::getStatus, "1")
                .eq(SysNotice::getJumpPath, jumpPath));
        EnterpriseNews enterpriseNews = new EnterpriseNews();
        enterpriseNews.setId(newsId);
        enterpriseNews.setReadCount((int) readCount);
        enterpriseNewsMapper.updateById(enterpriseNews);
    }
    private Long parseEnterpriseNewsId(String jumpPath) {
        if (jumpPath == null || !jumpPath.startsWith("/enterpriseNews")) {
            return null;
        }
        Matcher matcher = ENTERPRISE_NEWS_ID_PATTERN.matcher(jumpPath);
        if (!matcher.find()) {
            return null;
        }
        try {
            return Long.parseLong(matcher.group(1));
        } catch (NumberFormatException e) {
            return null;
        }
    }
}
src/main/java/com/ruoyi/project/tool/swagger/TestController.java
@@ -12,14 +12,16 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.ruoyi.common.utils.StringUtils;
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.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
 * swagger ç”¨æˆ·æµ‹è¯•方法
@@ -46,9 +48,9 @@
    }
    @Operation(summary = "获取用户详细")
    @Parameter(name = "userId", description = "用户ID", required = true)
    @GetMapping("/{userId}")
    public R<UserEntity> getUser(@PathVariable Integer userId)
    @Parameter(name = "userId", description = "用户ID", required = true)
    @GetMapping("/{userId}")
    public R<UserEntity> getUser(@PathVariable Integer userId)
    {
        if (!users.isEmpty() && users.containsKey(userId))
        {
@@ -60,15 +62,16 @@
        }
    }
    @Log(title = "测试接口新增用户", businessType = BusinessType.INSERT)
    @Operation(summary = "新增用户")
    @Parameters({
        @Parameter(name = "userId", description = "用户id"),
        @Parameter(name = "username", description = "用户名称"),
        @Parameter(name = "password", description = "用户密码"),
        @Parameter(name = "mobile", description = "用户手机")
    })
    @PostMapping("/save")
    public R<String> save(UserEntity user)
    @Parameters({
        @Parameter(name = "userId", description = "用户id"),
        @Parameter(name = "username", description = "用户名称"),
        @Parameter(name = "password", description = "用户密码"),
        @Parameter(name = "mobile", description = "用户手机")
    })
    @PostMapping("/save")
    public R<String> save(UserEntity user)
    {
        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
        {
@@ -78,6 +81,7 @@
        return R.ok();
    }
    @Log(title = "测试接口更新用户", businessType = BusinessType.UPDATE)
    @Operation(summary = "更新用户")
    @PutMapping("/update")
    public R<String> update(@RequestBody UserEntity user)
@@ -95,10 +99,11 @@
        return R.ok();
    }
    @Log(title = "测试接口删除用户", businessType = BusinessType.DELETE)
    @Operation(summary = "删除用户信息")
    @Parameter(name = "userId", description = "用户ID", required = true)
    @DeleteMapping("/{userId}")
    public R<String> delete(@PathVariable Integer userId)
    @Parameter(name = "userId", description = "用户ID", required = true)
    @DeleteMapping("/{userId}")
    public R<String> delete(@PathVariable Integer userId)
    {
        if (!users.isEmpty() && users.containsKey(userId))
        {
src/main/java/com/ruoyi/projectManagement/controller/InfoController.java
@@ -1,5 +1,7 @@
package com.ruoyi.projectManagement.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.projectManagement.dto.InfoStageDto;
import com.ruoyi.projectManagement.dto.UpdateStateInfo;
@@ -31,6 +33,7 @@
    @PostMapping("/save")
    @Operation(summary = "保存")
    @Log(title = "项目信息-保存", businessType = BusinessType.INSERT)
    public AjaxResult save(@RequestBody @Valid SaveInfoVo saveInfoVo) {
        infoService.save(saveInfoVo);
        return AjaxResult.success();
@@ -38,6 +41,7 @@
    @PostMapping("/updateStatus")
    @Operation(summary = "修改状态")
    @Log(title = "项目信息-修改状态", businessType = BusinessType.UPDATE)
    public AjaxResult updateStatus(@RequestBody @Valid UpdateStateInfo updateStateInfo){
        infoService.updateStatus(updateStateInfo);
        return AjaxResult.success();
@@ -45,6 +49,7 @@
    @PostMapping("/delete/{id}")
    @Operation(summary = "删除")
    @Log(title = "项目信息-删除", businessType = BusinessType.DELETE)
    public AjaxResult delete(@PathVariable Long id) {
        infoService.deleteInfo(id);
        return AjaxResult.success();
@@ -65,6 +70,7 @@
    @PostMapping("/saveStage")
    @Operation(summary = "保存阶段")
    @Log(title = "项目信息-保存阶段", businessType = BusinessType.INSERT)
    public AjaxResult saveStage(@RequestBody @Valid SaveInfoStageVo dto) {
        infoStageHandleService.save(dto);
        return AjaxResult.success();
@@ -78,6 +84,7 @@
    @PostMapping("/deleteStage/{id}")
    @Operation(summary = "删除阶段")
    @Log(title = "项目信息-删除阶段", businessType = BusinessType.DELETE)
    public AjaxResult deleteStage(@PathVariable Long id) {
        infoStageHandleService.deleteById(id);
        return AjaxResult.success();
src/main/java/com/ruoyi/projectManagement/controller/PlanController.java
@@ -1,5 +1,7 @@
package com.ruoyi.projectManagement.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.projectManagement.service.PlanService;
import com.ruoyi.projectManagement.vo.SavePlanNodeVo;
@@ -28,6 +30,7 @@
    @PostMapping("/save")
    @Operation(summary = "保存")
    @Log(title = "项目计划-保存", businessType = BusinessType.INSERT)
    public AjaxResult save(@RequestBody @Valid SavePlanVo savePlanVo) {
        planService.savePlan(savePlanVo);
        return AjaxResult.success();
@@ -35,6 +38,7 @@
    @PostMapping("/delete/{id}")
    @Operation(summary = "删除")
    @Log(title = "项目计划-删除", businessType = BusinessType.DELETE)
    public AjaxResult delete(@PathVariable Long id) {
        planService.deletePlan(id);
        return AjaxResult.success();
src/main/java/com/ruoyi/projectManagement/controller/RolesController.java
@@ -4,9 +4,9 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import java.time.LocalDateTime;
import com.ruoyi.projectManagement.dto.RoleDto;
import com.ruoyi.projectManagement.mapper.RolesMapper;
import com.ruoyi.projectManagement.pojo.Roles;
@@ -35,21 +35,24 @@
    @PostMapping("/add")
    @Operation(summary = "新增")
    @Log(title = "项目角色-新增", businessType = BusinessType.INSERT)
    public AjaxResult add(@RequestBody RoleDto roleDto) {
        if (roleDto.getIsDefaultNo()) {
            roleDto.setNo(OrderUtils.countTodayByCreateTime(rolesMapper, "XMJS","no", roleDto.getCreateTime() != null ? roleDto.getCreateTime() : LocalDateTime.now()));
            roleDto.setNo(OrderUtils.countTodayByCreateTime(rolesMapper, "XMJS","no"));
        }
        return AjaxResult.success(rolesservice.save(roleDto));
    }
    @PostMapping("/update")
    @Operation(summary = "修改")
    @Log(title = "项目角色-修改", businessType = BusinessType.UPDATE)
    public AjaxResult update(@RequestBody Roles roles) {
        return AjaxResult.success(rolesservice.updateById(roles));
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除")
    @Log(title = "项目角色-删除", businessType = BusinessType.DELETE)
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) return AjaxResult.error("请传入要删除的ID");
        return AjaxResult.success(rolesservice.removeBatchByIds(ids));
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/purchase/controller/PurchaseLedgerController.java
@@ -7,7 +7,6 @@
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.framework.web.domain.R;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.mapper.PurchaseLedgerTemplateMapper;
@@ -32,8 +31,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * é‡‡è´­å°è´¦Controller
@@ -118,16 +117,6 @@
    }
    /**
     * æŸ¥è¯¢é‡‡è´­é€€è´§å¯é€‰é‡‡è´­å°è´¦
     */
    @GetMapping("/returnableList")
    public TableDataInfo returnableList(PurchaseLedger purchaseLedger) {
        startPage();
        List<PurchaseLedger> list = purchaseLedgerService.selectReturnablePurchaseLedgerList(purchaseLedger);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºé‡‡è´­å°è´¦åˆ—表
     */
    @Log(title = "采购台账", businessType = BusinessType.EXPORT)
@@ -146,13 +135,6 @@
    @PostMapping("/addOrEditPurchase")
    public AjaxResult addOrEditPurchase(@RequestBody PurchaseLedgerDto purchaseLedgerDto) throws Exception {
        return toAjax(purchaseLedgerService.addOrEditPurchase(purchaseLedgerDto));
    }
    @Operation(summary = "批量推进采购台账到入库")
    @Log(title = "批量推进采购台账到入库", businessType = BusinessType.OTHER)
    @PostMapping("/batchInsertPurchaseSteps")
    public R batchInsertPurchaseSteps(@RequestBody PurchaseLedgerDto purchaseLedgerDto) {
        return purchaseLedgerService.batchInsertPurchaseSteps(purchaseLedgerDto == null ? null : purchaseLedgerDto.getIds());
    }
    /**
@@ -176,6 +158,7 @@
    /**
     * ä¿®æ”¹é‡‡è´­å°è´¦å®¡æ‰¹çŠ¶æ€
     */
    @Log(title = "采购台账", businessType = BusinessType.UPDATE)
    @PostMapping("/updateApprovalStatus")
    public AjaxResult addOrEditPurchase(@RequestBody PurchaseLedger purchaseLedger){
        return toAjax(purchaseLedgerService.updateById(purchaseLedger));
@@ -265,7 +248,17 @@
    @Operation(summary = "生成采购序列号")
    @GetMapping("/createPurchaseNo")
    @Log(title = "生成采购序列号", businessType = BusinessType.OTHER)
    public AjaxResult createPurchaseNo(@RequestParam Date entryDate) {
        return AjaxResult.success("生成成功",purchaseLedgerService.getPurchaseNo(entryDate));
    public AjaxResult createPurchaseNo() {
        return AjaxResult.success("生成成功",purchaseLedgerService.getPurchaseNo());
    }
    @Operation(summary = "获取采购单打印数据")
    @GetMapping("/printData/{id}")
    public AjaxResult printData(@PathVariable Long id) {
        Map<String, Object> data = purchaseLedgerService.getPrintData(id);
        if (data == null) {
            return AjaxResult.error("采购单不存在");
        }
        return AjaxResult.success(data);
    }
}
src/main/java/com/ruoyi/purchase/controller/PurchaseReturnOrdersController.java
@@ -13,10 +13,6 @@
import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import com.ruoyi.stock.pojo.StockOutRecord;
import java.time.LocalDateTime;
import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
import com.ruoyi.purchase.vo.PurchaseStockInProductVo;
import io.swagger.v3.oas.annotations.Operation;
@@ -42,7 +38,6 @@
    private PurchaseReturnOrdersService purchaseReturnOrdersService;
    private PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
    private AccountStatementDetailsService accountStatementDetailsService;
    private StockOutRecordMapper stockOutRecordMapper;
    @GetMapping("/listPage")
@@ -55,7 +50,7 @@
    @PostMapping("/add")
    public AjaxResult add(@RequestBody PurchaseReturnOrderDto purchaseReturnOrderDto) throws Exception {
        if (purchaseReturnOrderDto.getIsDefaultNo()) {
            purchaseReturnOrderDto.setNo(OrderUtils.countTodayByCreateTime(purchaseReturnOrdersMapper, "CGTL", "no", purchaseReturnOrderDto.getCreateTime() != null ? purchaseReturnOrderDto.getCreateTime() : LocalDateTime.now()));
            purchaseReturnOrderDto.setNo(OrderUtils.countTodayByCreateTime(purchaseReturnOrdersMapper, "CGTL", "no"));
        }
        return AjaxResult.success(purchaseReturnOrdersService.add(purchaseReturnOrderDto));
    }
@@ -66,6 +61,7 @@
        return AjaxResult.success(purchaseReturnOrdersService.getPurchaseReturnOrderDtoById(id));
    }
    @Log(title = "采购退货单", businessType = BusinessType.DELETE)
    @PostMapping("/deleteById/{id}")
    public AjaxResult deleteById(@PathVariable Long id) {
        //如果该采购退货已经生成对账单则无法删除
@@ -74,13 +70,6 @@
                .eq(AccountStatementDetails::getReceiptNumber, purchaseReturnOrders.getNo()));
        if (CollectionUtils.isNotEmpty(accountStatementDetails)){
            throw new ServiceException("该采购退货单已经生成对账单,无法删除");
        }
        List<StockOutRecord> approvedStockOutRecords = stockOutRecordMapper.selectList(Wrappers.<StockOutRecord>lambdaQuery()
                .eq(StockOutRecord::getRecordType, "9")
                .eq(StockOutRecord::getApprovalStatus, 1)
                .inSql(StockOutRecord::getRecordId, "select id from purchase_return_order_products where purchase_return_order_id = " + id));
        if (CollectionUtils.isNotEmpty(approvedStockOutRecords)) {
            throw new ServiceException("该采购退货单对应出库已审核通过,无法删除");
        }
        purchaseReturnOrdersService.deleteById(id);
        return AjaxResult.success();
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -33,11 +33,6 @@
    private Long id;
    /**
     * å®¡æ‰¹å®žä¾‹ID
     */
    private Long approvalInstanceId;
    /**
     * é‡‡è´­åˆåŒå·
     */
    @Excel(name = "采购合同号")
@@ -93,7 +88,7 @@
    /**
     * å½•入日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date entryDate;
@@ -138,9 +133,6 @@
    private List<SalesLedgerProduct> productData;
    @Schema(description = "批量处理采购台账ID列表")
    private List<Long> ids;
    private List<String> tempFileIds;
    private List<CommonFile> SalesLedgerFiles;
@@ -179,12 +171,6 @@
     */
    @Excel(name = "合同金额")
    private BigDecimal contractAmount = BigDecimal.ZERO;
    /**
     * å®žé™…合同金额(扣减退货后)
     */
    @Schema(description = "实际合同金额")
    private BigDecimal netContractAmount = BigDecimal.ZERO;
    @Schema(description = "文件类型  å– 4")
src/main/java/com/ruoyi/purchase/dto/PurchaseReturnOrderHasAllInfoDto.java
@@ -15,13 +15,6 @@
    // é‡‡è´­åˆåŒå·
    private String purchaseContractNumber;
    private String shippingTypeText;
    /**
     * å¯¹åº”出库是否已审核通过
     */
    private Boolean stockOutApproved;
    //退料人姓名
    private String returnUserName;
src/main/java/com/ruoyi/purchase/mapper/PurchaseLedgerMapper.java
@@ -23,8 +23,6 @@
    IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, @Param("c") PurchaseLedgerDto purchaseLedger);
    List<PurchaseLedger> selectReturnablePurchaseLedgerList(@Param("c") PurchaseLedger purchaseLedger);
    List<IncomeExpenseAnalysisDto> selectPurchaseStats(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("dateFormat") String dateFormat);
    BigDecimal selectTotalPurchaseAmount(@Param("startDate") String startDate, @Param("endDate") String endDate);
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
@@ -69,7 +69,7 @@
    /**
     * å½•入日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date entryDate;
@@ -119,12 +119,6 @@
     * åˆåŒé‡‘额(产品含税总价)
     */
    private BigDecimal contractAmount;
    /**
     * å®žé™…合同金额(扣减退货后)
     */
    @Schema(description = "实际合同金额")
    private BigDecimal netContractAmount;
//
//    /**
//     * ä¸šåŠ¡å‘˜
@@ -163,4 +157,5 @@
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/purchase/pojo/PurchaseReturnOrders.java
@@ -44,18 +44,6 @@
    @Schema(description = "供应商id")
    private Long supplierId;
    @Schema(description = "发货类型 1货车 2快递")
    private Integer shippingType;
    @Schema(description = "发货车牌号")
    private String truckPlateNo;
    @Schema(description = "快递公司")
    private String expressCompany;
    @Schema(description = "快递单号")
    private String expressNo;
    @Schema(description = "项目id")
    private Long projectId;
@@ -94,6 +82,7 @@
    private BigDecimal totalAmount;
    @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;
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java
@@ -4,14 +4,13 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import org.springframework.web.multipart.MultipartFile;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * é‡‡è´­å°è´¦Service接口
@@ -23,11 +22,7 @@
    List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger);
    List<PurchaseLedger> selectReturnablePurchaseLedgerList(PurchaseLedger purchaseLedger);
    int addOrEditPurchase(PurchaseLedgerDto purchaseLedgerDto) throws Exception;
    R batchInsertPurchaseSteps(List<Long> ids);
    void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct);
@@ -45,9 +40,11 @@
    IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger);
    String getPurchaseNo(Date entryDate);
    String getPurchaseNo();
    AjaxResult importData(MultipartFile file);
    PurchaseLedgerDto getPurchaseByCode(PurchaseLedgerDto purchaseLedgerDto);
    Map<String, Object> getPrintData(Long id);
}
src/main/java/com/ruoyi/purchase/service/impl/ProcurementBusinessSummaryServiceImpl.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.purchase.dto.ProcurementBusinessSummaryDto;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import lombok.RequiredArgsConstructor;
@@ -20,6 +21,8 @@
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    public IPage<ProcurementBusinessSummaryDto> listPage(Page page, ProcurementBusinessSummaryDto procurementBusinessSummaryDto) {
        return salesLedgerProductMapper.procurementBusinessSummaryListPage(page, procurementBusinessSummaryDto);
        IPage<ProcurementBusinessSummaryDto> result = salesLedgerProductMapper.procurementBusinessSummaryListPage(page, procurementBusinessSummaryDto);
        AmountUtils.normalizeProcurementBusinessSummaryList(result.getRecords());
        return result;
    }
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -6,13 +6,8 @@
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalTemplate;
import com.ruoyi.approve.bean.vo.ApproveProcessVO;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.service.ApprovalInstanceService;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
@@ -23,23 +18,20 @@
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.SupplierManage;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.ApprovalStatusEnum;
import com.ruoyi.common.enums.ReviewStatusEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.TypeEnums;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseLedgerImportDto;
@@ -55,15 +47,13 @@
import com.ruoyi.quality.pojo.QualityInspectParam;
import com.ruoyi.quality.pojo.QualityTestStandard;
import com.ruoyi.quality.pojo.QualityTestStandardParam;
import com.ruoyi.quality.service.IQualityInspectService;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.service.StockInRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
@@ -74,7 +64,6 @@
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -100,6 +89,7 @@
    private final SysUserMapper userMapper;
    private final TempFileMapper tempFileMapper;
    private final CommonFileMapper commonFileMapper;
    private final SysDeptMapper sysDeptMapper;
    private final SupplierManageMapper supplierManageMapper;
    private final ProductMapper productMapper;
    private final ProductModelMapper productModelMapper;
@@ -113,11 +103,6 @@
    private final ApproveProcessServiceImpl approveProcessService;
    private final ProcurementRecordMapper procurementRecordStorageMapper;
    private final FileUtil fileUtil;
    private final ApprovalInstanceService approvalInstanceService;
    private final IQualityInspectService qualityInspectService;
    private final StockInRecordService stockInRecordService;
    private final StockUtils stockUtils;
    private final ApprovalTemplateMapper approvalTemplateMapper;
    @Override
    public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
@@ -131,15 +116,7 @@
        if (purchaseLedger.getApprovalStatus() != null) {
            queryWrapper.eq(PurchaseLedger::getApprovalStatus, purchaseLedger.getApprovalStatus());
        }
        // åªæŸ¥è¯¢å…¥åº“已审批通过的采购台账
        queryWrapper.inSql(PurchaseLedger::getId,
                "SELECT DISTINCT record_id FROM stock_in_record WHERE approval_status = 1");
        return purchaseLedgerMapper.selectList(queryWrapper);
    }
    @Override
    public List<PurchaseLedger> selectReturnablePurchaseLedgerList(PurchaseLedger purchaseLedger) {
        return purchaseLedgerMapper.selectReturnablePurchaseLedgerList(purchaseLedger);
    }
    @Override
@@ -148,6 +125,7 @@
        PurchaseLedger purchaseLedger = new PurchaseLedger();
        // DTO转Entity
        BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger);
        AmountUtils.normalizeContractAmount(purchaseLedger);
        SalesLedger salesLedger = salesLedgerMapper.selectById(purchaseLedgerDto.getSalesLedgerId());
        //录入人
        SysUser sysUser = userMapper.selectUserById(purchaseLedgerDto.getRecorderId());
@@ -172,13 +150,21 @@
        purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId());
        purchaseLedger.setApprovalStatus(1);
        // å®žé™…合同金额始终等于合同金额
        purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount());
        // 3. æ–°å¢žæˆ–更新主表
        if (purchaseLedger.getId() == null && !StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
            purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
        if (purchaseLedger.getId() == null) {
            purchaseLedgerMapper.insert(purchaseLedger);
        } else {
            // åˆ é™¤é‡‡è´­å®¡æ ¸ï¼Œé‡æ–°æäº¤
            ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                    .eq(ApproveProcess::getApproveType, 5)
                    .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber())
                    .eq(ApproveProcess::getApproveDelete, 0)
                    .last("limit 1"));
            if (one != null) {
                approveProcessService.delByIds(Collections.singletonList(one.getId()));
            }
            purchaseLedgerMapper.updateById(purchaseLedger);
        }
        this.saveOrUpdate(purchaseLedger);
        // 4. å¤„理子表数据
        List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData();
@@ -186,202 +172,10 @@
            handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType());
        }
        // 6.采购审核新增;审批管理未配置采购审批人时,审批服务会自动置为审批通过。
        //查询当前采购合同号是否已添加审批,如果有且不是驳回状态,则不重复添加
        ApprovalInstance oldApprovalInstance = approvalInstanceService.getOne(Wrappers.<ApprovalInstance>lambdaQuery()
                .eq(ApprovalInstance::getDeleted, 0)
                .eq(ApprovalInstance::getBusinessType, TypeEnums.PURCHASE_APPROVAL.getCode())
                .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                .ne(ApprovalInstance::getStatus,"REJECTED")
                .last("limit 1")
        );
        if(Objects.isNull(oldApprovalInstance)){
            addApproveByPurchase(loginUser, purchaseLedger, productList);
        }
        addApproveByPurchase(loginUser, purchaseLedger);
        // 5. è¿ç§»ä¸´æ—¶æ–‡ä»¶åˆ°æ­£å¼ç›®å½•
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS());
        return 1;
    }
    @Override
    public R batchInsertPurchaseSteps(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return R.fail("请选择采购台账");
        }
        List<Long> distinctIds = ids.stream()
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(distinctIds)) {
            return R.fail("请选择采购台账");
        }
        PurchaseLedgerDto queryDto = new PurchaseLedgerDto();
        queryDto.setIds(distinctIds);
        IPage<PurchaseLedgerDto> pageResult = this.selectPurchaseLedgerListPage(
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, distinctIds.size()),
                queryDto
        );
        List<PurchaseLedgerDto> ledgerDtos = pageResult == null || pageResult.getRecords() == null
                ? Collections.emptyList()
                : pageResult.getRecords();
        Map<Long, PurchaseLedgerDto> ledgerDtoMap = ledgerDtos.stream()
                .filter(item -> item.getId() != null)
                .collect(Collectors.toMap(PurchaseLedgerDto::getId, item -> item, (left, right) -> left, LinkedHashMap::new));
        List<Map<String, Object>> details = new ArrayList<>();
        int successCount = 0;
        int skipCount = 0;
        int failCount = 0;
        int autoApprovedCount = 0;
        for (Long id : distinctIds) {
            Map<String, Object> detail = new LinkedHashMap<>();
            detail.put("purchaseLedgerId", id);
            PurchaseLedgerDto purchaseLedgerDto = ledgerDtoMap.get(id);
            if (purchaseLedgerDto == null) {
                failCount++;
                detail.put("status", "FAIL");
                detail.put("message", "采购台账不存在或未查询到");
                details.add(detail);
                continue;
            }
            detail.put("purchaseContractNumber", purchaseLedgerDto.getPurchaseContractNumber());
            detail.put("approvalStatus", purchaseLedgerDto.getApprovalStatus());
            detail.put("stockInStatus", purchaseLedgerDto.getStockInStatus());
            if (ApprovalStatusEnum.REJECTED.getCode().equals(purchaseLedgerDto.getApprovalStatus())) {
                skipCount++;
                detail.put("status", "SKIP");
                detail.put("message", "采购单已驳回");
                details.add(detail);
                continue;
            }
            if ("完全入库".equals(purchaseLedgerDto.getStockInStatus())) {
                skipCount++;
                detail.put("status", "SKIP");
                detail.put("message", "采购单已完全入库");
                details.add(detail);
                continue;
            }
            try {
                PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
                if (purchaseLedger == null) {
                    failCount++;
                    detail.put("status", "FAIL");
                    detail.put("message", "采购台账不存在");
                    details.add(detail);
                    continue;
                }
                if (!ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
                    ApprovalInstance approvalInstance = approvalInstanceService.getOne(
                            Wrappers.<ApprovalInstance>lambdaQuery()
                                    .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                                    .eq(ApprovalInstance::getBusinessType, 5L)
                                    .eq(ApprovalInstance::getDeleted, 0)
                                    .orderByDesc(ApprovalInstance::getId)
                                    .last("limit 1")
                    );
                    if (approvalInstance == null) {
                        failCount++;
                        detail.put("status", "FAIL");
                        detail.put("message", "未找到对应的采购审批实例");
                        details.add(detail);
                        continue;
                    }
                    if ("APPROVED".equals(approvalInstance.getStatus())
                            && !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
                        purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode());
                        purchaseLedgerMapper.updateById(purchaseLedger);
                    } else if (!"APPROVED".equals(approvalInstance.getStatus())) {
                        R autoApproveResult = approvalInstanceService.autoApprove(approvalInstance.getId());
                        if (autoApproveResult == null || !R.isSuccess(autoApproveResult)) {
                            failCount++;
                            detail.put("status", "FAIL");
                            detail.put("message", autoApproveResult == null ? "采购审批自动通过失败" : autoApproveResult.getMsg());
                            details.add(detail);
                            continue;
                        }
                        autoApprovedCount++;
                    }
                    purchaseLedger = purchaseLedgerMapper.selectById(id);
                    if (purchaseLedger == null || !ApprovalStatusEnum.APPROVED.getCode().equals(purchaseLedger.getApprovalStatus())) {
                        failCount++;
                        detail.put("status", "FAIL");
                        detail.put("message", "采购单审批状态未更新为已通过");
                        details.add(detail);
                        continue;
                    }
                }
                List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(
                        Wrappers.<SalesLedgerProduct>lambdaQuery()
                                .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                                .eq(SalesLedgerProduct::getType, 2)
                                .orderByAsc(SalesLedgerProduct::getId)
                );
                if (CollectionUtils.isEmpty(products)) {
                    skipCount++;
                    detail.put("status", "SKIP");
                    detail.put("message", "采购单没有产品明细");
                    details.add(detail);
                    continue;
                }
                int processedProductCount = 0;
                int skippedProductCount = 0;
                int failedProductCount = 0;
                for (SalesLedgerProduct product : products) {
                    try {
                        boolean processed;
                        if (Boolean.TRUE.equals(product.getIsChecked())) {
                            processed = processPurchaseQualityProduct(purchaseLedger, product);
                        } else {
                            processed = processPurchaseDirectProduct(purchaseLedger, product);
                        }
                        if (processed) {
                            processedProductCount++;
                        } else {
                            skippedProductCount++;
                        }
                    } catch (Exception ex) {
                        failedProductCount++;
                        log.error("批量推进采购台账失败, purchaseLedgerId={}, productId={}, productModelId={}",
                                purchaseLedger.getId(), product.getId(), product.getProductModelId(), ex);
                    }
                }
                successCount++;
                detail.put("status", failedProductCount > 0 ? "PARTIAL" : "SUCCESS");
                detail.put("processedProductCount", processedProductCount);
                detail.put("skippedProductCount", skippedProductCount);
                detail.put("failedProductCount", failedProductCount);
                detail.put("message", failedProductCount > 0 ? "部分产品处理失败" : "处理完成");
                details.add(detail);
            } catch (Exception ex) {
                failCount++;
                detail.put("status", "FAIL");
                detail.put("message", ex.getMessage());
                details.add(detail);
                log.error("批量推进采购台账失败, purchaseLedgerId={}", id, ex);
            }
        }
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("totalCount", distinctIds.size());
        summary.put("successCount", successCount);
        summary.put("skipCount", skipCount);
        summary.put("failCount", failCount);
        summary.put("autoApprovedCount", autoApprovedCount);
        summary.put("details", details);
        return R.ok(summary, "批量推进采购步骤完成");
    }
@@ -409,219 +203,7 @@
                        param.setId(null);
                        param.setInspectId(qualityInspect.getId());
                        qualityInspectParamMapper.insert(param);
            });
        }
    }
    private boolean processPurchaseQualityProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) {
        if (purchaseLedger == null || product == null || product.getProductModelId() == null) {
            return false;
        }
        QualityInspect qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId());
        if (qualityInspect == null) {
            addQualityInspect(purchaseLedger, product);
            qualityInspect = findLatestPurchaseQualityInspect(purchaseLedger.getId(), product.getProductModelId());
        }
        if (qualityInspect == null) {
            return false;
        }
        LocalDateTime purchaseInspectTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1);
        if (purchaseInspectTime != null && (qualityInspect.getCheckTime() == null
                || !DateUtils.toLocalDate(qualityInspect.getCheckTime()).equals(purchaseInspectTime.toLocalDate()))) {
            qualityInspect.setCheckTime(DateUtils.toDate(purchaseInspectTime.toLocalDate()));
            qualityInspectMapper.updateById(qualityInspect);
        }
        List<StockInRecord> stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
        if (hasApprovedStockRecord(stockRecords)) {
            return true;
        }
        if (!Integer.valueOf(1).equals(qualityInspect.getInspectState())) {
            R autoSubmitResult = qualityInspectService.autoSubmit(qualityInspect.getId());
            if (autoSubmitResult == null || !R.isSuccess(autoSubmitResult)) {
                return false;
            }
            qualityInspect = qualityInspectMapper.selectById(qualityInspect.getId());
        }
        stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
        if (CollectionUtils.isEmpty(stockRecords)
                && qualityInspect.getQualifiedQuantity() != null
                && qualityInspect.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) {
            stockUtils.addStockWithBatchNo(
                    product.getProductModelId(),
                    qualityInspect.getQualifiedQuantity(),
                    StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode(),
                    qualityInspect.getId(),
                    null,
                    purchaseInspectTime == null ? null : purchaseInspectTime.plusDays(1)
            );
            stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
        }
        StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords);
        if (targetStockRecord == null) {
            return false;
        }
        LocalDateTime qualityStockCreateTime = resolveQualityStockCreateTime(qualityInspect);
        if (qualityStockCreateTime != null && (targetStockRecord.getCreateTime() == null
                || !qualityStockCreateTime.equals(targetStockRecord.getCreateTime()))) {
            targetStockRecord.setCreateTime(qualityStockCreateTime);
            stockInRecordService.updateById(targetStockRecord);
        }
        approveStockRecords(Collections.singletonList(targetStockRecord));
        stockRecords = findQualityStockRecords(qualityInspect.getId(), product.getProductModelId());
        return hasApprovedStockRecord(stockRecords);
    }
    private boolean processPurchaseDirectProduct(PurchaseLedger purchaseLedger, SalesLedgerProduct product) {
        if (purchaseLedger == null || product == null || product.getProductModelId() == null) {
            return false;
        }
        if (product.getQuantity() == null) {
            return false;
        }
        if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
            return false;
        }
        LocalDateTime stockCreateTime = toStartOfDayPlusDays(purchaseLedger.getEntryDate(), 1);
        List<StockInRecord> stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
        if (hasApprovedStockRecord(stockRecords)) {
            return true;
        }
        if (CollectionUtils.isEmpty(stockRecords)) {
            stockUtils.addStockWithBatchNo(
                    product.getProductModelId(),
                    product.getQuantity(),
                    StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
                    purchaseLedger.getId(),
                    purchaseLedger.getPurchaseContractNumber() + "-" + product.getId(),
                    stockCreateTime
            );
            stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
        }
        if (CollectionUtils.isEmpty(stockRecords)) {
            return false;
        }
        StockInRecord targetStockRecord = findLatestUnapprovedStockRecord(stockRecords);
        if (targetStockRecord == null) {
            return false;
        }
        if (stockCreateTime != null && (targetStockRecord.getCreateTime() == null
                || !stockCreateTime.equals(targetStockRecord.getCreateTime()))) {
            targetStockRecord.setCreateTime(stockCreateTime);
            stockInRecordService.updateById(targetStockRecord);
        }
        approveStockRecords(Collections.singletonList(targetStockRecord));
        stockRecords = findDirectStockRecords(purchaseLedger.getId(), purchaseLedger.getPurchaseContractNumber(), product.getProductModelId(), product.getId());
        return hasApprovedStockRecord(stockRecords);
    }
    private LocalDateTime toStartOfDayPlusDays(Date date, int days) {
        if (date == null) {
            return null;
        }
        return DateUtils.toLocalDate(date).plusDays(days).atStartOfDay();
    }
    private LocalDateTime resolveQualityStockCreateTime(QualityInspect qualityInspect) {
        if (qualityInspect == null || qualityInspect.getCheckTime() == null) {
            return null;
        }
        return DateUtils.toLocalDate(qualityInspect.getCheckTime()).plusDays(1).atStartOfDay();
    }
    private QualityInspect findLatestPurchaseQualityInspect(Long purchaseLedgerId, Long productModelId) {
        if (purchaseLedgerId == null || productModelId == null) {
            return null;
        }
        return qualityInspectMapper.selectOne(
                Wrappers.<QualityInspect>lambdaQuery()
                        .eq(QualityInspect::getInspectType, 0)
                        .eq(QualityInspect::getPurchaseLedgerId, purchaseLedgerId)
                        .eq(QualityInspect::getProductModelId, productModelId)
                        .orderByDesc(QualityInspect::getId)
                        .last("limit 1")
        );
    }
    private List<StockInRecord> findQualityStockRecords(Long qualityInspectId, Long productModelId) {
        if (qualityInspectId == null || productModelId == null) {
            return Collections.emptyList();
        }
        return stockInRecordService.list(
                Wrappers.<StockInRecord>lambdaQuery()
                        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode())
                        .eq(StockInRecord::getRecordId, qualityInspectId)
                        .eq(StockInRecord::getProductModelId, productModelId)
                        .orderByDesc(StockInRecord::getId)
        );
    }
    private List<StockInRecord> findDirectStockRecords(Long purchaseLedgerId, String purchaseContractNumber, Long productModelId, Long purchaseProductId) {
        if (purchaseLedgerId == null || productModelId == null || purchaseProductId == null || !StringUtils.hasText(purchaseContractNumber)) {
            return Collections.emptyList();
        }
        String batchNo = purchaseContractNumber + "-" + purchaseProductId;
        return stockInRecordService.list(
                Wrappers.<StockInRecord>lambdaQuery()
                        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode())
                        .eq(StockInRecord::getRecordId, purchaseLedgerId)
                        .eq(StockInRecord::getProductModelId, productModelId)
                        .eq(StockInRecord::getBatchNo, batchNo)
                        .orderByDesc(StockInRecord::getId)
        );
    }
    private boolean hasApprovedStockRecord(List<StockInRecord> stockRecords) {
        return stockRecords != null && stockRecords.stream()
                .anyMatch(item -> ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus()));
    }
    private StockInRecord findLatestUnapprovedStockRecord(List<StockInRecord> stockRecords) {
        if (CollectionUtils.isEmpty(stockRecords)) {
            return null;
        }
        return stockRecords.stream()
                .filter(item -> !ReviewStatusEnum.APPROVED.getCode().equals(item.getApprovalStatus()))
                .findFirst()
                .orElse(null);
    }
    private void approveStockRecords(List<StockInRecord> stockRecords) {
        if (CollectionUtils.isEmpty(stockRecords)) {
            return;
        }
        List<Long> rejectedIds = stockRecords.stream()
                .filter(item -> ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus()))
                .map(StockInRecord::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!rejectedIds.isEmpty()) {
            stockInRecordService.batchReAudit(rejectedIds);
        }
        List<Long> pendingIds = stockRecords.stream()
                .filter(item -> item.getApprovalStatus() == null
                        || ReviewStatusEnum.PENDING_REVIEW.getCode().equals(item.getApprovalStatus())
                        || ReviewStatusEnum.REJECTED.getCode().equals(item.getApprovalStatus()))
                .map(StockInRecord::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!pendingIds.isEmpty()) {
            stockInRecordService.batchApprove(pendingIds, ReviewStatusEnum.APPROVED.getCode());
                    });
        }
    }
@@ -657,6 +239,7 @@
        // è®¾ç½®å­—段
        for (SalesLedgerProduct product : products) {
            AmountUtils.normalizeSalesLedgerProduct(product);
            product.setSalesLedgerId(salesLedgerId);
            Long productId = product.getProductId();
@@ -698,10 +281,10 @@
        }
        // è®¡ç®—总含税金额
        BigDecimal totalTaxInclusiveAmount = products.stream()
        BigDecimal totalTaxInclusiveAmount = AmountUtils.scaleAmount(products.stream()
                .map(SalesLedgerProduct::getTaxInclusiveTotalPrice)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        // æ›´æ–°ä¸»è¡¨çš„æ€»é‡‘额字段
        if (salesLedgerId != null) {
@@ -763,30 +346,13 @@
        for (Long id : ids) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
            if(purchaseLedger != null){
                List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>()
                        .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                        .eq(ApprovalInstance::getBusinessType, 5L)
                        .eq(ApprovalInstance::getDeleted, 0));
                if (CollectionUtils.isNotEmpty(approvalInstances)) {
                    approvalInstanceService.delete(
                            approvalInstances.stream()
                                    .map(ApprovalInstance::getId)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList())
                    );
                }
                List<ApproveProcess> approveProcesses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
                ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                        .eq(ApproveProcess::getApproveType, 5)
                        .eq(ApproveProcess::getApproveDelete, 0)
                        .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber()));
                if (CollectionUtils.isNotEmpty(approveProcesses)) {
                    approveProcessService.delByIds(
                            approveProcesses.stream()
                                    .map(ApproveProcess::getId)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList())
                    );
                        .eq(ApproveProcess::getApproveReason, purchaseLedger.getPurchaseContractNumber())
                        .last("limit 1"));
                if (one != null) {
                    approveProcessService.delByIds(Collections.singletonList(one.getId()));
                }
            }
        }
@@ -816,7 +382,9 @@
        // 4. è½¬æ¢ DTO
        PurchaseLedgerDto resultDto = new PurchaseLedgerDto();
        BeanUtils.copyProperties(purchaseLedger, resultDto);
        AmountUtils.normalizeContractAmount(resultDto);
        if (!products.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(products);
            resultDto.setHasChildren(true);
            resultDto.setProductData(products);
            resultDto.setStorageBlobVOS(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId()));
@@ -859,6 +427,7 @@
        queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(queryWrapper);
        AmountUtils.normalizeSalesLedgerProducts(productList);
        resultDto.setProductData(productList);
        return resultDto;
    }
@@ -890,17 +459,23 @@
    @Override
    public IPage<PurchaseLedgerDto> selectPurchaseLedgerListPage(IPage ipage, PurchaseLedgerDto purchaseLedger) {
        return purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
        IPage<PurchaseLedgerDto> page = purchaseLedgerMapper.selectPurchaseLedgerListPage(ipage, purchaseLedger);
        if (!CollectionUtils.isEmpty(page.getRecords())) {
            page.getRecords().forEach(record -> {
                AmountUtils.normalizeContractAmount(record);
                AmountUtils.normalizeSalesLedgerProducts(record.getProductData());
            });
        }
        return page;
    }
    @Override
    public String getPurchaseNo(Date entryDate) {
        LocalDate localDate = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    public String getPurchaseNo() {
        // ç”Ÿæˆæ—¥æœŸå‰ç¼€ï¼ˆä¾‹å¦‚:CG20250405)
        String purchaseNo = "CG" + localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String purchaseNo = "CG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        // æž„建 Redis Key(按天分隔)
        String redisKey = "purchase_no:" + localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
        String redisKey = "purchase_no:" + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
        // èŽ·å–å½“å‰åºå·å¹¶é€’å¢žï¼ˆåŽŸå­æ“ä½œï¼‰
        Long sequence = redisTemplate.opsForValue().increment(redisKey);
@@ -967,7 +542,6 @@
                salesLedger.setContractAmount(salesLedgerProductImportDtos.stream()
                        .map(PurchaseLedgerProductImportDto::getTaxInclusiveTotalPrice)
                        .reduce(BigDecimal.ZERO,BigDecimal::add));
                salesLedger.setNetContractAmount(salesLedger.getContractAmount());
                // é€šè¿‡é”€å”®å•号绑定销售
                SalesLedger salesLedger1 = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>()
                        .eq(SalesLedger::getSalesContractNo, salesLedger.getSalesContractNo())
@@ -997,8 +571,14 @@
                    BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(2);
                    AmountUtils.normalizeSalesLedgerProduct(salesLedgerProduct);
                    // è®¡ç®—不含税总价
                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
                    salesLedgerProduct.setTaxExclusiveTotalPrice(
                            AmountUtils.calcTaxExclusiveTotalPrice(
                                    salesLedgerProduct.getTaxInclusiveTotalPrice(),
                                    salesLedgerProduct.getTaxRate()
                            )
                    );
                    list.stream()
                            .filter(map -> map.get("productName").equals(salesLedgerProduct.getProductCategory()) && map.get("model").equals(salesLedgerProduct.getSpecificationModel()))
                            .findFirst()
@@ -1017,12 +597,7 @@
                    salesLedgerProductMapper.insert(salesLedgerProduct);
                }
                // é‡‡è´­å®¡æ ¸
                List<SalesLedgerProduct> snapshotProducts = salesLedgerProductMapper.selectList(
                        new LambdaQueryWrapper<SalesLedgerProduct>()
                                .eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId())
                                .eq(SalesLedgerProduct::getType, 2)
                );
                addApproveByPurchase(loginUser, salesLedger, snapshotProducts);
                addApproveByPurchase(loginUser,salesLedger);
            }
            return AjaxResult.success("导入成功");
@@ -1034,131 +609,45 @@
    @Override
    public PurchaseLedgerDto getPurchaseByCode(PurchaseLedgerDto purchaseLedgerDto) {
        PurchaseLedger purchaseLedger = null;
        ApprovalInstance approvalSnapshotInstance = null;
        // 1. ä¼˜å…ˆæŒ‰å½“前审批实例查询快照
        if (purchaseLedgerDto.getApprovalInstanceId() != null) {
            approvalSnapshotInstance = approvalInstanceService.getById(purchaseLedgerDto.getApprovalInstanceId());
            if (approvalSnapshotInstance != null && approvalSnapshotInstance.getBusinessId() != null) {
                purchaseLedger = purchaseLedgerMapper.selectById(approvalSnapshotInstance.getBusinessId());
            }
        }
        // 2. æ²¡ä¼ å®¡æ‰¹å®žä¾‹ID时,退回到按采购合同号查询
        // 1. æŸ¥è¯¢ä¸»è¡¨
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                .eq(PurchaseLedger::getPurchaseContractNumber, purchaseLedgerDto.getPurchaseContractNumber())
                .last("LIMIT 1"));
        if (purchaseLedger == null) {
            purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>()
                    .eq(PurchaseLedger::getPurchaseContractNumber, purchaseLedgerDto.getPurchaseContractNumber())
                    .last("LIMIT 1"));
            if (purchaseLedger == null) {
                throw new BaseException("采购台账不存在");
            }
            throw new BaseException("采购台账不存在");
        }
        // 3. æŸ¥è¯¢å½“前采购单对应的审批快照
        if (approvalSnapshotInstance == null) {
            approvalSnapshotInstance = approvalInstanceService.getOne(new LambdaQueryWrapper<ApprovalInstance>()
                    .eq(ApprovalInstance::getBusinessId, purchaseLedger.getId())
                    .eq(ApprovalInstance::getBusinessType, 5L)
                    .eq(ApprovalInstance::getDeleted, 0)
                    .orderByDesc(ApprovalInstance::getId)
                    .last("LIMIT 1"));
        }
        if (approvalSnapshotInstance != null && StringUtils.hasText(approvalSnapshotInstance.getFormConfig())) {
            try {
                PurchaseLedgerDto snapshot = JSON.parseObject(approvalSnapshotInstance.getFormConfig(), PurchaseLedgerDto.class);
                if (snapshot != null) {
                    snapshot.setId(purchaseLedger.getId());
                    if (snapshot.getPurchaseContractNumber() == null) {
                        snapshot.setPurchaseContractNumber(purchaseLedger.getPurchaseContractNumber());
                    }
                    if (snapshot.getSupplierId() == null) {
                        snapshot.setSupplierId(purchaseLedger.getSupplierId());
                    }
                    if (snapshot.getSupplierName() == null) {
                        snapshot.setSupplierName(purchaseLedger.getSupplierName());
                    }
                    if (snapshot.getSalesContractNo() == null) {
                        snapshot.setSalesContractNo(purchaseLedger.getSalesContractNo());
                    }
                    if (snapshot.getSalesContractNoId() == null) {
                        snapshot.setSalesContractNoId(purchaseLedger.getSalesLedgerId());
                    }
                    if (snapshot.getProjectName() == null) {
                        snapshot.setProjectName(purchaseLedger.getProjectName());
                    }
                    if (snapshot.getEntryDate() == null) {
                        snapshot.setEntryDate(purchaseLedger.getEntryDate());
                    }
                    if (snapshot.getExecutionDate() == null) {
                        snapshot.setExecutionDate(purchaseLedger.getExecutionDate());
                    }
                    if (snapshot.getRemarks() == null) {
                        snapshot.setRemarks(purchaseLedger.getRemarks());
                    }
                    if (snapshot.getRecorderId() == null) {
                        snapshot.setRecorderId(purchaseLedger.getRecorderId());
                    }
                    if (snapshot.getRecorderName() == null) {
                        snapshot.setRecorderName(purchaseLedger.getRecorderName());
                    }
                    snapshot.setProductData(snapshot.getProductData() == null ? Collections.emptyList() : snapshot.getProductData());
                    snapshot.setHasChildren(CollectionUtils.isNotEmpty(snapshot.getProductData()));
                    return snapshot;
                }
            } catch (Exception e) {
                log.warn("解析采购审批快照失败: {}", e.getMessage());
            }
        }
        // 4. æŸ¥è¯¢å­è¡¨
        // 2. æŸ¥è¯¢å­è¡¨
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        // 5. è½¬æ¢ DTO
        // 4. è½¬æ¢ DTO
        PurchaseLedgerDto resultDto = new PurchaseLedgerDto();
        BeanUtils.copyProperties(purchaseLedger, resultDto);
        AmountUtils.normalizeContractAmount(resultDto);
        if (!products.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(products);
            resultDto.setHasChildren(true);
            resultDto.setProductData(products);
        }
        return resultDto;
    }
    public void addApproveByPurchase(LoginUser loginUser, PurchaseLedger purchaseLedger, List<SalesLedgerProduct> productList) {
    public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) throws Exception {
        if (loginUser == null) {
            return;
        }
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
                new LambdaQueryWrapper<ApprovalTemplate>()
                        .eq(ApprovalTemplate::getBusinessType, 5L)
                        .eq(ApprovalTemplate::getDeleted, 0)
                        .orderByDesc(ApprovalTemplate::getId)
                        .last("LIMIT 1")
        );
        if (approvalTemplate == null) {
            throw new BaseException("请先配置采购审批模板");
        }
        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
        approvalInstance.setTemplateId(approvalTemplate.getId());
        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
        approvalInstance.setBusinessId(purchaseLedger.getId());
        approvalInstance.setBusinessType(5L);
        approvalInstance.setCurrentLevel(1);
        approvalInstance.setApplicantId(loginUser.getUserId());
        approvalInstance.setTitle(purchaseLedger.getPurchaseContractNumber()+"审批");
        approvalInstance.setApplicantName(loginUser.getNickName());
        approvalInstance.setApplyTime(LocalDateTime.now());
        PurchaseLedgerDto snapshot = new PurchaseLedgerDto();
        BeanUtils.copyProperties(purchaseLedger, snapshot);
        snapshot.setProductData(productList == null ? Collections.emptyList() : new ArrayList<>(productList));
        approvalInstance.setFormConfig(JSON.toJSONString(snapshot));
        approvalInstanceService.add(approvalInstance);
        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
        approveProcessVO.setApproveType(5);
        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
        approveProcessVO.setApproveReason(purchaseLedger.getPurchaseContractNumber());
        approveProcessVO.setApproveUserIds(purchaseLedger.getApproveUserIds());
        approveProcessVO.setApproveUser(loginUser.getUserId());
        approveProcessVO.setApproveTime(LocalDate.now().toString());
        approveProcessVO.setPrice(AmountUtils.scaleAmount(purchaseLedger.getContractAmount()));
        approveProcessService.addApprove(approveProcessVO);
    }
    /**
@@ -1183,81 +672,22 @@
        return sb.toString();
    }
    private static final String PURCHASE_LOCK_PREFIX = "purchase_contract_no:";
    private static final long PURCHASE_LOCK_WAIT_TIMEOUT = 10;
    private static final long PURCHASE_LOCK_EXPIRE_TIME = 30;
    private String generatePurchaseContractNo(Date entryDate) {
        LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now();
        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
        String lockKey = PURCHASE_LOCK_PREFIX + datePart;
        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime();
        try {
            long startWaitTime = System.currentTimeMillis();
            while (System.currentTimeMillis() - startWaitTime < PURCHASE_LOCK_WAIT_TIMEOUT * 1000) {
                Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, PURCHASE_LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
                if (Boolean.TRUE.equals(locked)) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取锁时被中断", e);
                }
            }
            if (Boolean.FALSE.equals(redisTemplate.hasKey(lockKey))) {
                throw new RuntimeException("获取采购合同编号生成锁失败:超时");
            }
            String prefix = "CG-" + datePart + "-";
            List<PurchaseLedger> existingList = purchaseLedgerMapper.selectList(
                    new LambdaQueryWrapper<PurchaseLedger>()
                            .likeRight(PurchaseLedger::getPurchaseContractNumber, prefix)
                            .select(PurchaseLedger::getPurchaseContractNumber));
            List<Integer> existingSequences = existingList.stream()
                    .map(PurchaseLedger::getPurchaseContractNumber)
                    .filter(Objects::nonNull)
                    .map(no -> {
                        int lastDash = no.lastIndexOf('-');
                        if (lastDash >= 0) {
                            try {
                                return Integer.parseInt(no.substring(lastDash + 1));
                            } catch (NumberFormatException e) {
                                return 0;
                            }
                        }
                        return 0;
                    })
                    .collect(Collectors.toList());
            int nextSequence = findFirstMissingSequence(existingSequences);
            return prefix + String.format("%03d", nextSequence);
        } finally {
            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
            redisTemplate.execute(
                    new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
                    Collections.singletonList(lockKey),
                    lockValue
            );
    @Override
    public Map<String, Object> getPrintData(Long id) {
        PurchaseLedger purchaseLedger = getById(id);
        if (purchaseLedger == null) {
            return null;
        }
    }
    private int findFirstMissingSequence(List<Integer> sequences) {
        if (sequences.isEmpty()) {
            return 1;
        }
        sequences.sort(Integer::compareTo);
        int next = 1;
        for (int seq : sequences) {
            if (seq == next) {
                next++;
            } else if (seq > next) {
                break;
            }
        }
        return next;
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(
            new LambdaQueryWrapper<SalesLedgerProduct>()
                .eq(SalesLedgerProduct::getSalesLedgerId, id)
                .eq(SalesLedgerProduct::getType, 2)
        );
        SysDept dept = sysDeptMapper.selectDeptById(100L);
        Map<String, Object> result = new HashMap<>();
        result.put("companyName", dept != null ? dept.getDeptName() : "");
        result.put("ledger", purchaseLedger);
        result.put("products", products);
        return result;
    }
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseReturnOrdersServiceImpl.java
@@ -14,7 +14,6 @@
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.PurchaseReturnOrderProducts;
import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
@@ -29,7 +28,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -77,18 +75,6 @@
            }
        }else {
            throw new RuntimeException("请选择退货商品");
        }
        // æ›´æ–°é‡‡è´­å°è´¦çš„实际合同金额
        if (purchaseReturnOrderDto.getPurchaseLedgerId() != null && purchaseReturnOrderDto.getTotalAmount() != null) {
            PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseReturnOrderDto.getPurchaseLedgerId());
            if (purchaseLedger != null) {
                BigDecimal baseAmount = purchaseLedger.getNetContractAmount() != null
                        ? purchaseLedger.getNetContractAmount()
                        : purchaseLedger.getContractAmount();
                purchaseLedger.setNetContractAmount(baseAmount.subtract(purchaseReturnOrderDto.getTotalAmount()));
                purchaseLedgerMapper.updateById(purchaseLedger);
            }
        }
        return true;
    }
src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderProductsDetailVo.java
@@ -47,9 +47,6 @@
    @Schema(description = "已退货数量")
    private BigDecimal totalReturnNum;
    @Schema(description = "销售发货数量")
    private BigDecimal saleOutQuantity;
    @Schema(description = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
src/main/java/com/ruoyi/purchase/vo/PurchaseStockInProductVo.java
@@ -42,9 +42,6 @@
    @Schema(description = "退货总数")
    private BigDecimal totalReturnNum;
    @Schema(description = "销售发货数量")
    private BigDecimal saleOutQuantity;
    @Schema(description = "含税单价")
    private BigDecimal taxInclusiveUnitPrice;
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java
@@ -24,13 +24,10 @@
    @Schema(description = "合同金额")
    private BigDecimal contractAmount;
    @Schema(description = "付款金额")
    private BigDecimal paymentAmount;
    @Schema(description = "已入库金额")
    private BigDecimal shippedAmount;
    @Schema(description = "应付金额")
    private BigDecimal payableAmount;
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "未入库金额")
    private BigDecimal unshippedAmount;
}
src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java
@@ -16,18 +16,12 @@
    private String supplierName;
    @Schema(description = "合同总金额")
    //该供应商采购合同累计金额
    private BigDecimal contractAmounts;
    @Schema(description = "付款金额")
    //该供应商采购付款累计金额
    private BigDecimal paymentAmount;
    @Schema(description = "已入库金额")
    private BigDecimal shippedAmount;
    @Schema(description = "应付金额")
    //该供应商采购应付累计金额=初始金额-付款-退款
    private BigDecimal payableAmount;
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "未入库金额")
    private BigDecimal unshippedAmount;
}
src/main/java/com/ruoyi/quality/controller/QualityInspectFileController.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityInspectFile;
import com.ruoyi.quality.pojo.QualityInspectParam;
@@ -33,6 +35,7 @@
     * @param qualityInspectFile
     * @return
     */
    @Log(title = "上传质检文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody QualityInspectFile qualityInspectFile) {
        return AjaxResult.success(qualityInspectFileService.save(qualityInspectFile));
@@ -43,6 +46,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除质检文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delQualityUnqualified(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/java/com/ruoyi/quality/controller/QualityTestStandardParamController.java
@@ -80,9 +80,7 @@
    @Operation(summary = "检测指标维护查询")
    @Log(title = "检测指标维护查询", businessType = BusinessType.OTHER)
    public R<?> list(Long testStandardId) {
        return R.ok(qualityTestStandardParamService.list(Wrappers.<QualityTestStandardParam>lambdaQuery()
                .eq(QualityTestStandardParam::getTestStandardId, testStandardId)
                .orderByDesc(QualityTestStandardParam::getCreateTime)));
        return R.ok(qualityTestStandardParamService.list(Wrappers.<QualityTestStandardParam>lambdaQuery().eq(QualityTestStandardParam::getTestStandardId,testStandardId)));
    }
}
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -38,7 +38,7 @@
    /**
     * æ£€æµ‹æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "检测日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date checkTime;
@@ -107,6 +107,20 @@
    private BigDecimal unqualifiedQuantity;
    /**
     * æ˜¯å¦æŠ½æ£€(0:否;1:是)
     */
    @Excel(name = "是否抽检", readConverterExp = "0=否,1=是")
    @TableField("is_sampling")
    private Integer isSampling;
    /**
     * æŠ½æ£€æ•°é‡
     */
    @Excel(name = "抽检数量")
    @TableField("sampling_quantity")
    private BigDecimal samplingQuantity;
    /**
     * æ£€æµ‹å•位
     */
    @Excel(name = "检测单位")
@@ -159,6 +173,13 @@
    @Schema(description = "关联检测标准主表id")
    private Long testStandardId;
    /**
     * åˆæ ¼çއ
     */
    @TableField(exist = false)
    @Excel(name = "合格率(%)")
    private BigDecimal passRate;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
src/main/java/com/ruoyi/quality/pojo/QualityUnqualified.java
@@ -47,7 +47,7 @@
    /**
     * æ£€æµ‹æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "检测日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date checkTime;
@@ -107,7 +107,7 @@
    /**
     * å¤„理日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "处理日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date dealTime;
src/main/java/com/ruoyi/quality/service/IQualityInspectService.java
@@ -3,7 +3,6 @@
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.framework.web.domain.R;
import com.ruoyi.quality.dto.QualityInspectDto;
import com.ruoyi.quality.pojo.QualityInspect;
@@ -23,8 +22,6 @@
    QualityInspectDto getDetailById(Integer id);
    int submit(QualityInspect qualityInspect);
    R autoSubmit(Long id);
    void down(HttpServletResponse response, QualityInspect qualityInspect);
}
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -11,7 +11,6 @@
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.HackLoopTableRenderPolicy;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.R;
@@ -41,9 +40,8 @@
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
@@ -96,11 +94,7 @@
    public int submit(QualityInspect inspect) {
        QualityInspect qualityInspect = qualityInspectMapper.selectById(inspect.getId());
        //提交前必须判断是否合格
        if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
            throw new ServiceException("请先判断是否合格");
        }
        //提交前必须判断是否合格(通过合格数量和不合格数量来判断)
        if (ObjectUtils.isNull(qualityInspect.getQualifiedQuantity())) {
            throw new ServiceException("合格数量不能为空");
        }
@@ -131,15 +125,16 @@
            stockInventoryDto.setRecordId(qualityInspect.getId());
            stockInventoryDto.setProductModelId(qualityInspect.getProductModelId());
            stockInventoryDto.setQualitity(qualityInspect.getQualifiedQuantity());
            if (qualityInspect.getCheckTime() != null) {
                LocalDate stockCreateDate = DateUtils.toLocalDate(qualityInspect.getCheckTime()).plusDays(1);
                stockInventoryDto.setCreateTime(LocalDateTime.of(stockCreateDate, java.time.LocalTime.MIDNIGHT));
            }
            stockInventoryDto.setBatchNo(resolveProductionBatchNo(
                    qualityInspect.getProductMainId(),
                    qualityInspect.getId(),
                    qualityInspect.getProductModelId()));
            stockInventoryService.addStockInRecordOnly(stockInventoryDto);
            //StockInRecord stockInRecord = stockInRecordService.getOne(Wrappers.<StockInRecord>lambdaQuery()
            //        .eq(StockInRecord::getRecordId, qualityInspect.getId())
            //        .eq(StockInRecord::getRecordType, stockInventoryDto.getRecordType())
            //);
            //stockInRecordService.batchApprove(Collections.singletonList(stockInRecord.getId()),1);
        }
        // ä¸åˆæ ¼å¤„理
        if(qualityInspect.getUnqualifiedQuantity().compareTo(BigDecimal.ZERO) > 0){
@@ -157,33 +152,6 @@
        qualityInspect.setInspectState(1);//已提交
        return qualityInspectMapper.updateById(qualityInspect);
    }
    @Override
    public R autoSubmit(Long id) {
        if (id == null) {
            return R.fail("检验单ID不能为空");
        }
        QualityInspect qualityInspect = qualityInspectMapper.selectById(id);
        if (qualityInspect == null) {
            return R.fail("检验单不存在");
        }
        if (Integer.valueOf(1).equals(qualityInspect.getInspectState())) {
            return R.ok("检验单已提交");
        }
        if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
            qualityInspect.setCheckResult("合格");
        }
        if (ObjectUtils.isNull(qualityInspect.getQualifiedQuantity())) {
            qualityInspect.setQualifiedQuantity(qualityInspect.getQuantity() == null ? BigDecimal.ZERO : qualityInspect.getQuantity());
        }
        if (ObjectUtils.isNull(qualityInspect.getUnqualifiedQuantity())) {
            qualityInspect.setUnqualifiedQuantity(BigDecimal.ZERO);
        }
        qualityInspectMapper.updateById(qualityInspect);
        int rows = submit(qualityInspect);
        return rows > 0 ? R.ok("检验单提交成功") : R.fail("检验单提交失败");
    }
    private String resolveProductionBatchNo(Long productionProductMainId,
src/main/java/com/ruoyi/quality/service/impl/QualityTestStandardBindingServiceImpl.java
@@ -9,6 +9,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
@@ -32,16 +33,18 @@
    @Override
    public int add(List<QualityTestStandardBinding> qualityTestStandardBindings) {
        ArrayList<QualityTestStandardBinding> qualityTestStandardBindings1 = new ArrayList<>(qualityTestStandardBindings.size());
        //判断是否已经有绑定过的数据
        for (QualityTestStandardBinding qualityTestStandardBinding : qualityTestStandardBindings) {
            List<QualityTestStandardBinding> testStandardBindings = qualityTestStandardBindingMapper.selectList(Wrappers.<QualityTestStandardBinding>lambdaQuery()
                    .eq(QualityTestStandardBinding::getTestStandardId, qualityTestStandardBinding.getTestStandardId())
                    .eq(QualityTestStandardBinding::getProductId, qualityTestStandardBinding.getProductId()));
            if (testStandardBindings.size() > 0){
                throw new RuntimeException("该产品已经绑定过,请检查");
                continue;
            }
            qualityTestStandardBindings1.add(qualityTestStandardBinding);
        }
        saveBatch(qualityTestStandardBindings);
        saveBatch(qualityTestStandardBindings1);
        return 0;
    }
}
src/main/java/com/ruoyi/safe/controller/SafeAccidentController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeAccident;
import com.ruoyi.safe.service.SafeAccidentService;
@@ -34,18 +36,21 @@
    }
    @Operation(summary = "新增事故上报记录")
    @Log(title = "安全事故", businessType = BusinessType.INSERT)
    @PostMapping()
    public R add(@RequestBody SafeAccident safeAccident) {
        return R.ok(safeAccidentService.save(safeAccident));
    }
    @Operation(summary = "修改事故上报记录")
    @Log(title = "安全事故", businessType = BusinessType.UPDATE)
    @PutMapping ()
    public R update(@RequestBody  SafeAccident safeAccident) {
        return R.ok(safeAccidentService.updateById(safeAccident));
    }
    @Operation(summary = "删除事故上报记录")
    @Log(title = "安全事故", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R delSafeAccident(@RequestBody List<Integer> ids) {
        return R.ok(safeAccidentService.removeBatchByIds(ids));
src/main/java/com/ruoyi/safe/controller/SafeCertificationController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeCertification;
import com.ruoyi.safe.service.SafeCertificationService;
@@ -35,18 +37,21 @@
    }
    @Operation(summary = "新增安全规程与资质管理")
    @Log(title = "安全证书", businessType = BusinessType.INSERT)
    @PostMapping()
    public R add(@RequestBody SafeCertification safeCertification) {
        return R.ok(safeCertificationService.save(safeCertification));
    }
    @Operation(summary = "修改安全规程与资质管理")
    @Log(title = "安全证书", businessType = BusinessType.UPDATE)
    @PutMapping ()
    public R update(@RequestBody  SafeCertification safeCertification) {
        return R.ok(safeCertificationService.updateById(safeCertification));
    }
    @Operation(summary = "删除安全规程与资质管理")
    @Log(title = "安全证书", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R delSafeCertification(@RequestBody List<Integer> ids) {
        return R.ok(safeCertificationService.removeBatchByIds(ids));
src/main/java/com/ruoyi/safe/controller/SafeCertificationFileController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeCertificationFile;
import com.ruoyi.safe.service.SafeCertificationFileService;
@@ -35,6 +37,7 @@
     * @param safeCertificationFile
     * @return
     */
    @Log(title = "新增安全证书文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public R add(@RequestBody SafeCertificationFile safeCertificationFile) {
@@ -46,6 +49,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除安全证书文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "删除")
    public R delSafeCertificationFile(@RequestBody List<Integer> ids) {
src/main/java/com/ruoyi/safe/controller/SafeContingencyPlanController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeContingencyPlan;
import com.ruoyi.safe.service.SafeContingencyPlanService;
@@ -33,18 +35,21 @@
        return R.ok(safeContingencyPlanService.pageSafeContingencyPlan(page, safeContingencyPlan));
    }
    @Log(title = "新增应急预案", businessType = BusinessType.INSERT)
    @Operation(summary = "新增应急预案查阅")
    @PostMapping()
    public R add(@RequestBody SafeContingencyPlan safeContingencyPlan) {
        return R.ok(safeContingencyPlanService.save(safeContingencyPlan));
    }
    @Log(title = "修改应急预案", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改应急预案查阅")
    @PutMapping ()
    public R update(@RequestBody  SafeContingencyPlan safeContingencyPlan) {
        return R.ok(safeContingencyPlanService.updateById(safeContingencyPlan));
    }
    @Log(title = "删除应急预案", businessType = BusinessType.DELETE)
    @Operation(summary = "删除应急预案查阅")
    @DeleteMapping("/{ids}")
    public R delSafeCertification(@RequestBody List<Integer> ids) {
src/main/java/com/ruoyi/safe/controller/SafeHazardController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeHazard;
import com.ruoyi.safe.service.SafeHazardService;
@@ -33,18 +35,21 @@
        return R.ok(safeHazardService.pageSafeHazard(page, safeHazard));
    }
    @Log(title = "新增危险源", businessType = BusinessType.INSERT)
    @Operation(summary = "新增危险源台账")
    @PostMapping()
    public R add(@RequestBody SafeHazard safeHazard) {
        return R.ok(safeHazardService.save(safeHazard));
    }
    @Log(title = "修改危险源", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改危险源台账")
    @PutMapping ()
    public R update(@RequestBody  SafeHazard safeHazard) {
        return R.ok(safeHazardService.updateById(safeHazard));
    }
    @Log(title = "删除危险源", businessType = BusinessType.DELETE)
    @Operation(summary = "删除危险源台账")
    @DeleteMapping("/{ids}")
    public R delSafeHazard(@RequestBody List<Integer> ids) {
src/main/java/com/ruoyi/safe/controller/SafeHazardRecordController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.dto.SafeHazardRecordDto;
import com.ruoyi.safe.pojo.SafeHazardRecord;
@@ -35,18 +37,21 @@
    }
    @Operation(summary = "领用")
    @Log(title = "危险源记录", businessType = BusinessType.INSERT)
    @PostMapping("/borrow")
    public R borrow(@RequestBody SafeHazardRecord safeHazardRecord) {
        return R.ok(safeHazardRecordService.borrow(safeHazardRecord));
    }
    @Operation(summary = "归还")
    @Log(title = "危险源记录", businessType = BusinessType.UPDATE)
    @PutMapping("/return")
    public R returnSafeHazardRecord(@RequestBody  SafeHazardRecord safeHazardRecord) {
        return R.ok(safeHazardRecordService.returnSafeHazardRecord(safeHazardRecord));
    }
    @Operation(summary = "删除")
    @Log(title = "危险源记录", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R delSafeHazardRecord(@RequestBody List<Integer> ids) {
        return R.ok(safeHazardRecordService.delSafeHazardRecord(ids));
src/main/java/com/ruoyi/safe/controller/SafeHiddenController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.dto.SafeHiddenDto;
import com.ruoyi.safe.pojo.SafeHidden;
@@ -35,18 +37,21 @@
    }
    @Operation(summary = "新增隐患排查")
    @Log(title = "安全隐患", businessType = BusinessType.INSERT)
    @PostMapping()
    public R add(@RequestBody SafeHidden safeHidden) {
        return R.ok(safeHiddenService.add(safeHidden));
    }
    @Operation(summary = "修改隐患排查(整改/验收)")
    @Log(title = "安全隐患", businessType = BusinessType.UPDATE)
    @PutMapping ()
    public R update(@RequestBody SafeHidden safeHidden) {
        return R.ok(safeHiddenService.updateById(safeHidden));
    }
    @Operation(summary = "删除隐患排查")
    @Log(title = "安全隐患", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R delSafeHidden(@RequestBody List<Integer> ids) {
        return R.ok(safeHiddenService.delSafeHidden(ids));
src/main/java/com/ruoyi/safe/controller/SafeHiddenFileController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeHiddenFile;
import com.ruoyi.safe.service.SafeHiddenFileService;
@@ -35,6 +37,7 @@
     * @param safeHiddenFile
     * @return
     */
    @Log(title = "新增隐患文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public R add(@RequestBody SafeHiddenFile safeHiddenFile) {
@@ -46,6 +49,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除隐患文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "删除")
    public R delSafeHiddenFile(@RequestBody List<Integer> ids) {
src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.dto.SafeTrainingDto;
import com.ruoyi.safe.pojo.SafeTraining;
@@ -39,12 +41,14 @@
    }
    @Operation(summary = "新增/编辑安全培训考核")
    @Log(title = "安全培训", businessType = BusinessType.INSERT)
    @PostMapping()
    public R addOrUpdate(@RequestBody SafeTraining safeTraining) {
        return R.ok(safeTrainingService.addOrUpdate(safeTraining));
    }
    @Operation(summary = "签到")
    @Log(title = "安全培训", businessType = BusinessType.INSERT)
    @PostMapping ("/sign")
    public R sign(@RequestBody SafeTrainingDetails safeTrainingDetails) {
        return R.ok(safeTrainingDetailsService.save(safeTrainingDetails));
@@ -57,18 +61,21 @@
    }
    @Operation(summary = "结果明细保存")
    @Log(title = "安全培训", businessType = BusinessType.UPDATE)
    @PostMapping ("/saveSafeTraining")
    public R saveSafeTraining(@RequestBody SafeTrainingDto safeTrainingDto) {
        return R.ok(safeTrainingService.saveSafeTraining(safeTrainingDto));
    }
    @Operation(summary = "删除安全培训考核")
    @Log(title = "安全培训", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R delSafeTraining(@RequestBody List<Integer> ids) {
        return R.ok(safeTrainingService.delSafeTraining(ids));
    }
    @Operation(summary = "导出")
    @Log(title = "安全培训", businessType = BusinessType.EXPORT)
    @PostMapping ("/export")
    public void export(HttpServletResponse response, @RequestBody SafeTraining safeTraining) {
        safeTrainingService.export(response,safeTraining.getId());
src/main/java/com/ruoyi/safe/controller/SafeTrainingDetailsController.java
@@ -1,6 +1,8 @@
package com.ruoyi.safe.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeTrainingDetails;
import com.ruoyi.safe.service.SafeTrainingDetailsService;
@@ -33,6 +35,7 @@
        return R.ok(safeTrainingDetailsService.pageDetails(page, safeTrainingDetails));
    }
    @Log(title = "导出培训详情", businessType = BusinessType.EXPORT)
    @Operation(summary = "导出")
    @PostMapping("/export")
    public void export(HttpServletResponse response, @RequestBody SafeTrainingDetails safeTrainingDetails) {
src/main/java/com/ruoyi/safe/controller/SafeTrainingFileController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.safe.pojo.SafeHiddenFile;
import com.ruoyi.safe.pojo.SafeTrainingFile;
@@ -36,6 +38,7 @@
     * @param safeHiddenFile
     * @return
     */
    @Log(title = "新增培训文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "新增")
    public R add(@RequestBody SafeTrainingFile safeHiddenFile) {
@@ -47,6 +50,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除培训文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    @Operation(summary = "删除")
    public R delSafeHiddenFile(@RequestBody List<Integer> ids) {
src/main/java/com/ruoyi/sales/controller/PaymentShippingController.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.OrderUtils;
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.mapper.PaymentShippingMapper;
@@ -15,7 +17,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -38,16 +39,18 @@
        return AjaxResult.success(listPage);
    }
    @Log(title = "付款发货", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "添加支付与发货信息")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(@RequestBody PaymentShipping paymentShipping) {
        String ord = OrderUtils.countTodayByCreateTime(paymentShippingMapper, "ORD","order_no", paymentShipping.getCreateTime() != null ? paymentShipping.getCreateTime() : LocalDateTime.now());
        String ord = OrderUtils.countTodayByCreateTime(paymentShippingMapper, "ORD","order_no");
        paymentShipping.setOrderNo(ord);
        boolean save = paymentShippingService.save(paymentShipping);
        return save ? success() : error();
    }
    @Log(title = "付款发货", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改支付与发货信息")
    @Transactional(rollbackFor = Exception.class)
@@ -56,6 +59,7 @@
        return update ? success() : error();
    }
    @Log(title = "付款发货", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除支付与发货信息")
    @Transactional(rollbackFor = Exception.class)
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -262,7 +262,7 @@
            //  å¦‚果已经有过开票或回款操作,则不允许编辑
            boolean hasReceiptOperation = receiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) > 0;
            salesLedgerVo.setIsEdit(!hasReceiptOperation);
            salesLedgerVo.setIsEdit(hasReceiptOperation);
            salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, ledgerId));
        }
@@ -281,4 +281,14 @@
    public R getSalesLedgerWithProductsLoss(Long salesLedgerId) {
        return R.ok(salesLedgerService.getSalesLedgerWithProductsLoss(salesLedgerId));
    }
    @Operation(summary = "获取销售单打印数据")
    @GetMapping("/printData/{id}")
    public AjaxResult printData(@PathVariable Long id) {
        Map<String, Object> data = salesLedgerService.getPrintData(id);
        if (data == null) {
            return AjaxResult.error("销售单不存在");
        }
        return AjaxResult.success(data);
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLedgerProductController.java
@@ -7,13 +7,20 @@
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * äº§å“ä¿¡æ¯Controller
@@ -27,6 +34,9 @@
public class SalesLedgerProductController extends BaseController
{
    private final ISalesLedgerProductService salesLedgerProductService;
    private final ProcurementRecordService procurementRecordService;
    private final StockUtils stockUtils;
    private final PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper;
    /**
@@ -35,32 +45,26 @@
    @GetMapping("/list")
    public AjaxResult list(SalesLedgerProduct salesLedgerProduct) {
        List<SalesLedgerProduct> list = salesLedgerProductService.selectSalesLedgerProductList(salesLedgerProduct);
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(item -> {
                if (item.getApproveStatus() != 2) {
                    if (item.getHasSufficientStock() == 0) {
                        item.setApproveStatus(0);
                    } else {
                        item.setApproveStatus(1);
                    }
                }
                java.math.BigDecimal returnQuantity = item.getReturnQuantity() == null
                        ? java.math.BigDecimal.ZERO
                        : item.getReturnQuantity();
                item.setReturnQuantity(returnQuantity);
                java.math.BigDecimal availableQuality = item.getQuantity() == null
                        ? java.math.BigDecimal.ZERO
                        : item.getQuantity();
                if (item.getType() != null && item.getType() == 2) {
                    availableQuality = item.getApprovedStockInNum() == null
                            ? java.math.BigDecimal.ZERO
                            : item.getApprovedStockInNum();
                    availableQuality = availableQuality
                            .subtract(item.getShippedQuantity() == null ? java.math.BigDecimal.ZERO : item.getShippedQuantity());
                }
                item.setAvailableQuality(availableQuality.subtract(returnQuantity));
            });
        if (CollUtil.isEmpty(list)) {
            return AjaxResult.success(list);
        }
        List<Long> productIds = list.stream().map(SalesLedgerProduct::getProductModelId).collect(Collectors.toList());
        List<SimpleReturnOrderGroupDto> groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds);
        Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, item -> item.getSumReturnQuantity()));
        list.forEach(item -> {
            if (item.getApproveStatus() != 2) {
                if (item.getHasSufficientStock() == 0) {
                    item.setApproveStatus(0);
                } else {
                    item.setApproveStatus(1);
                }
            }
            // ç»Ÿè®¡é€€è´§æ•°é‡
            BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
            item.setReturnQuality(returnQuality);
            item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
        });
        return AjaxResult.success(list);
    }
src/main/java/com/ruoyi/sales/controller/SalesQuotationController.java
@@ -3,6 +3,8 @@
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.domain.AjaxResult;
import com.ruoyi.sales.dto.SalesQuotationDto;
import com.ruoyi.sales.service.SalesQuotationService;
@@ -21,6 +23,7 @@
    }
    @Log(title = "销售报价", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response) {
        Page page = new Page(-1,-1);
@@ -31,14 +34,17 @@
    }
    @Log(title = "销售报价", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody SalesQuotationDto salesQuotationDto) {
        return AjaxResult.success(salesQuotationService.add(salesQuotationDto));
    }
    @Log(title = "销售报价", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody SalesQuotationDto salesQuotationDto) {
        return AjaxResult.success(salesQuotationService.edit(salesQuotationDto));
    }
    @Log(title = "销售报价", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestBody Long id) {
        return AjaxResult.success(salesQuotationService.delete(id));
src/main/java/com/ruoyi/sales/controller/SalespersonManagementController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.pojo.SalespersonManagement;
@@ -33,6 +35,7 @@
        return AjaxResult.success(listPage);
    }
    @Log(title = "业务员管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @Operation(summary = "添加业务员信息")
    @Transactional(rollbackFor = Exception.class)
@@ -41,6 +44,7 @@
        return save ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "业务员管理", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    @Operation(summary = "修改业务员信息")
    @Transactional(rollbackFor = Exception.class)
@@ -49,6 +53,7 @@
        return update ? AjaxResult.success() : AjaxResult.error();
    }
    @Log(title = "业务员管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete")
    @Operation(summary = "删除业务员信息")
    @Transactional(rollbackFor = Exception.class)
src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -2,10 +2,14 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.approve.bean.vo.ApproveProcessVO;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
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.security.LoginUser;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
@@ -21,6 +25,7 @@
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
/**
@@ -50,7 +55,22 @@
    @Transactional(rollbackFor = Exception.class)
    @Log(title = "发货信息管理", businessType = BusinessType.INSERT)
    public AjaxResult add(@RequestBody ShippingInfoDto req) throws Exception {
        return AjaxResult.success(shippingInfoService.addReq(req) ? "添加成功" : "添加失败");
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no");
        // å‘货审批
        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
        approveProcessVO.setApproveType(7);
        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
        approveProcessVO.setApproveReason(sh);//发货编号
        approveProcessVO.setApproveUserIds(req.getApproveUserIds());
        approveProcessVO.setApproveUser(loginUser.getUserId());
        approveProcessVO.setApproveTime(LocalDate.now().toString());
        approveProcessService.addApprove(approveProcessVO);
        // æ·»åŠ å‘è´§æ¶ˆæ¯
        req.setShippingNo(sh);
        req.setStatus("待审核");
        boolean save = shippingInfoService.add(req);
        return save ? AjaxResult.success() : AjaxResult.error();
    }
    @Operation(summary = "发货扣库存")
@@ -86,6 +106,7 @@
    /**
     * å¯¼å‡ºå‘货信息管理
     */
    @Log(title = "发货信息", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @Operation(summary = "导出发货信息")
    public void export(HttpServletResponse response) {
src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -19,7 +19,7 @@
    private String salesContractNo;
    private String customerContractNo;
    private String projectName;
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date entryDate;
    private String entryDateStart;
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -17,6 +17,4 @@
     */
    // å®¡æ‰¹äºº
    private String approveUserIds;
    private Long templateId;
}
src/main/java/com/ruoyi/sales/dto/SalesTrendDto.java
@@ -14,6 +14,7 @@
    private String month; // æœˆä»½ï¼ˆå¦‚2024-12)
    private Integer orderCount; // è®¢å•æ•°
    private BigDecimal salesAmount; // é”€å”®é¢
    private BigDecimal productQuantity; // äº§å“é”€å”®æ•°é‡
    private BigDecimal shipRate;
}
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -47,9 +47,5 @@
    //发货数量
    private BigDecimal totalQuantity;
    private Long templateId;
    private String templateName;
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
@@ -66,6 +66,7 @@
            "DATE_FORMAT(sl.entry_date, '%Y-%m') AS month, " +
            "COUNT(DISTINCT sl.id) AS order_count, " +  // æ€»è®¢å•æ•°
            "SUM(slp.tax_inclusive_total_price) AS sales_amount, " +  // é”€å”®é¢
            "SUM(IFNULL(slp.quantity, 0)) AS product_quantity, " +  // äº§å“é”€å”®æ•°é‡
            // å‘货率 = å‘货订单数 * 100 / æ€»è®¢å•数(保留2位小数,避免除0报错)
            "ROUND(IF(COUNT(DISTINCT sl.id) = 0, 0, " +
            "SUM(CASE WHEN slp.approve_status = 2 THEN 1 ELSE 0 END) / COUNT(DISTINCT sl.id) * 100), 2) AS ship_rate " +
src/main/java/com/ruoyi/sales/pojo/PaymentShipping.java
@@ -60,6 +60,7 @@
    private String remark;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "创建用户")
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -50,7 +50,7 @@
    /**
     * å½•入日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date entryDate;
@@ -108,9 +108,6 @@
    @Schema(description = "付款方式")
    private String paymentMethod;
    @Schema(description = "实际合同金额")
    private BigDecimal netContractAmount;
    @TableField(exist = false)
    @Schema(description = "生产状态")
src/main/java/com/ruoyi/sales/pojo/SalesLedgerProduct.java
@@ -164,7 +164,11 @@
    @TableField(exist = false)
    private Integer hasSufficientStock;
    // å¯ç”¨æ•°é‡  quantity - returnQuantity
    // é€€è´§æ•°é‡
    @TableField(exist = false)
    private BigDecimal returnQuality;
    // å¯ç”¨æ•°é‡  quantity - returnQuality
    @TableField(exist = false)
    private BigDecimal availableQuality;
    @Schema(description = "创建用户")
@@ -181,20 +185,4 @@
    @TableField(exist = false)
    @Schema(description = "待发货数量")
    private BigDecimal noQuantity;
    @TableField(exist = false)
    @Schema(description = "退货数量")
    private BigDecimal returnQuantity;
    @TableField(exist = false)
    @Schema(description = "销售发货数量")
    private BigDecimal shippedQuantity;
    @TableField(exist = false)
    @Schema(description = "已审核入库数量")
    private BigDecimal approvedStockInNum;
    @TableField(exist = false)
    @Schema(description = "审批中数量")
    private BigDecimal pendingApprovalQuantity;
}
src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java
@@ -52,6 +52,7 @@
    @ApiModelProperty(value = "备注")
    private String remark;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改时间")
src/main/java/com/ruoyi/sales/pojo/SalesQuotationProduct.java
@@ -4,6 +4,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@@ -27,11 +28,11 @@
    @Schema(description = "单位")
    private String unit;
    @Schema(description = "单价")
    private Double unitPrice;
    private BigDecimal unitPrice;
    @Schema(description = "数量")
    private Integer quantity;
    @Schema(description = "金额")
    private Double amount;
    private BigDecimal amount;
    @Schema(description = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -18,6 +18,7 @@
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
 * é”€å”®å°è´¦Service接口
@@ -55,4 +56,6 @@
    List<LossProductModelDto> getSalesLedgerWithProductsLoss(Long salesLedgerId);
    IPage<SalesLedgerDto> listSalesLedger(SalesLedgerDto salesLedgerDto, Page page);
    Map<String, Object> getPrintData(Long id);
}
src/main/java/com/ruoyi/sales/service/ShippingInfoService.java
@@ -31,6 +31,4 @@
    List<ShippingProductDetailDto> getDetail(Long id);
    ShippingApproveDto getDateilByShippingNo(String shippingNo);
    boolean addReq(ShippingInfoDto req);
}
src/main/java/com/ruoyi/sales/service/impl/MetricStatisticsServiceImpl.java
@@ -34,22 +34,57 @@
    public AjaxResult total() {
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(null);
        if(CollectionUtils.isEmpty(salesLedgers)) return AjaxResult.success(salesLedgers);
        if (CollectionUtils.isEmpty(salesLedgers)) {
            return AjaxResult.success(salesLedgers);
        }
        Map<String, Object> map = new HashMap<>();
        // é”€å”®é¢
        map.put("contractAmountTotal", salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        // è®¢å•数量
        map.put("total", new BigDecimal(salesLedgers.size()));
        // å‘货率
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
        //  é”€å”®é¢
        BigDecimal contractAmountTotal = salesLedgers.stream()
                .map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        map.put("contractAmountTotal", contractAmountTotal);
        //  è®¢å•数量
        int totalOrders = salesLedgers.size();
        map.put("total", new BigDecimal(totalOrders));
        map.put("shipRate", "0.00%");
        //  äº§å“é”€å”®æ•°é‡
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
                .eq(SalesLedgerProduct::getType, 1));
        map.put("shipRate", "0%");
        if(CollectionUtils.isEmpty(salesLedgerProducts)) return AjaxResult.success(map);
        // å‘货数量
        long count = shippingInfoMapper.selectCount(new LambdaQueryWrapper<ShippingInfo>()
                .in(ShippingInfo::getSalesLedgerProductId, salesLedgerProducts.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList()))
                .eq(ShippingInfo::getStatus,"已发货"));
        map.put("shipRate", String.format("%.2f", count * 100.0 / salesLedgerProducts.size()) + "%");
        BigDecimal productQuantityTotal = CollectionUtils.isEmpty(products) ? BigDecimal.ZERO :
                products.stream()
                        .map(SalesLedgerProduct::getQuantity)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
        map.put("productQuantityTotal", productQuantityTotal);
        if (CollectionUtils.isEmpty(products)) {
            return AjaxResult.success(map);
        }
        Map<Long, Long> productIdToLedgerIdMap = products.stream()
                .filter(p -> p.getId() != null && p.getSalesLedgerId() != null)
                .collect(Collectors.toMap(SalesLedgerProduct::getId, SalesLedgerProduct::getSalesLedgerId, (k1, k2) -> k1));
        List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                .eq(ShippingInfo::getStatus, "审核通过"));
        long shippedOrderCount = shippingInfos.stream()
                .filter(info -> productIdToLedgerIdMap.containsKey(info.getSalesLedgerProductId()))
                .map(info -> {
                    if (info.getSalesLedgerId() != null) {
                        return info.getSalesLedgerId();
                    }
                    return productIdToLedgerIdMap.get(info.getSalesLedgerProductId());
                })
                .filter(Objects::nonNull)
                .distinct()
                .count();
        map.put("shipRate", String.format("%.2f", shippedOrderCount * 100.0 / totalOrders) + "%");
        return AjaxResult.success(map);
    }
@@ -88,6 +123,7 @@
        List<String> dateList = new ArrayList<>();
        List<BigDecimal> orderCountList = new ArrayList<>();
        List<BigDecimal> salesAmountList = new ArrayList<>();
        List<BigDecimal> productQuantityList = new ArrayList<>();
        List<BigDecimal> shippingRateList = new ArrayList<>();
        Calendar tempCalendar = Calendar.getInstance();
@@ -108,10 +144,12 @@
            if (dto != null) {
                orderCountList.add(new BigDecimal(dto.getOrderCount()));
                salesAmountList.add(dto.getSalesAmount() != null ? dto.getSalesAmount() : BigDecimal.ZERO);
                productQuantityList.add(dto.getProductQuantity() != null ? dto.getProductQuantity() : BigDecimal.ZERO);
                shippingRateList.add(new BigDecimal(String.valueOf(dto.getShipRate())));
            } else {
                orderCountList.add(BigDecimal.ZERO);
                salesAmountList.add(BigDecimal.ZERO);
                productQuantityList.add(BigDecimal.ZERO);
                shippingRateList.add(BigDecimal.ZERO);
            }
@@ -122,6 +160,7 @@
        map.put("dateList", dateList);
        map.put("orderCountList", orderCountList);
        map.put("salesAmountList", salesAmountList);
        map.put("productQuantityList", productQuantityList);
        map.put("shippingRateList", shippingRateList);
        return AjaxResult.success(map);
    }
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.*;
@@ -25,7 +26,6 @@
import com.ruoyi.technology.mapper.TechnologyBomStructureMapper;
import com.ruoyi.technology.mapper.TechnologyRoutingMapper;
import com.ruoyi.technology.pojo.TechnologyRouting;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -34,6 +34,7 @@
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
@@ -89,7 +90,9 @@
    @Override
    public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
        return salesLedgerProductMapper.selectById(id);
        SalesLedgerProduct product = salesLedgerProductMapper.selectById(id);
        AmountUtils.normalizeSalesLedgerProduct(product);
        return product;
    }
    @Override
@@ -97,29 +100,17 @@
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct);
        if(!CollectionUtils.isEmpty(salesLedgerProducts)){
            salesLedgerProducts.forEach(item -> {
                // å‘货信息(取最新一条,只查询审核通过或已发货的记录)
                ShippingInfo latestShippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                AmountUtils.normalizeSalesLedgerProduct(item);
                // å‘货信息
                ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
                        .in(ShippingInfo::getStatus, "审核通过", "已发货")
                        .orderByDesc(ShippingInfo::getCreateTime)
                        .last("limit 1"));
                if(latestShippingInfo != null){
                    item.setShippingDate(latestShippingInfo.getShippingDate());
                    item.setExpressCompany(latestShippingInfo.getExpressCompany());
                    item.setExpressNumber(latestShippingInfo.getExpressNumber());
                }
                // è½¦ç‰Œå·ï¼šå–所有审核通过或已发货记录的车牌号,逗号拼接
                List<ShippingInfo> allShippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, item.getId())
                        .in(ShippingInfo::getStatus, "审核通过", "已发货")
                        .orderByAsc(ShippingInfo::getCreateTime));
                if(!CollectionUtils.isEmpty(allShippingInfos)){
                    String carNumbers = allShippingInfos.stream()
                            .map(ShippingInfo::getShippingCarNumber)
                            .filter(carNo -> carNo != null && !carNo.trim().isEmpty())
                            .distinct()
                            .collect(Collectors.joining(","));
                    item.setShippingCarNumber(carNumbers);
                if(shippingInfo != null){
                    item.setShippingDate(shippingInfo.getShippingDate());
                    item.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                    item.setExpressCompany(shippingInfo.getExpressCompany());
                    item.setExpressNumber(shippingInfo.getExpressNumber());
                }
            });
        }
@@ -177,6 +168,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addOrUpdateSalesLedgerProduct(SalesLedgerProduct salesLedgerProduct) {
        AmountUtils.normalizeSalesLedgerProduct(salesLedgerProduct);
        int result;
        Long salesLedgerId = salesLedgerProduct.getSalesLedgerId();
@@ -228,22 +220,20 @@
     */
    public void addProductionData(SalesLedgerProduct salesLedgerProduct) {
        //先判断该产品是否需要生产
        if (!BooleanUtils.isTrue(salesLedgerProduct.getIsProduction())) {
            return;
        if (Boolean.TRUE.equals(salesLedgerProduct.getIsProduction())) {
            SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerProduct.getSalesLedgerId());
            ProductionPlan productionPlan = new ProductionPlan();
            productionPlan.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
            productionPlan.setSalesLedgerProductId(salesLedgerProduct.getId());
            productionPlan.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
            productionPlan.setProductModelId(salesLedgerProduct.getProductModelId());
            productionPlan.setQtyRequired(salesLedgerProduct.getQuantity());
            productionPlan.setSource("销售");
            productionPlan.setStatus(0);
            productionPlan.setRequiredDate(salesLedger.getDeliveryDate());//需求日期=交货日期
            productionPlan.setPromisedDeliveryDate(salesLedger.getDeliveryDate());//承诺日期=交货日期
            productionPlanMapper.insert(productionPlan);
        }
        SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerProduct.getSalesLedgerId());
        ProductionPlan productionPlan = new ProductionPlan();
        productionPlan.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
        productionPlan.setSalesLedgerProductId(salesLedgerProduct.getId());
        productionPlan.setMpsNo(generateNextPlanNo(com.ruoyi.common.utils.DateUtils.toLocalDate(salesLedger.getEntryDate())
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        productionPlan.setProductModelId(salesLedgerProduct.getProductModelId());
        productionPlan.setQtyRequired(salesLedgerProduct.getQuantity());
        productionPlan.setSource("销售");
        productionPlan.setStatus(0);
        productionPlan.setRequiredDate(salesLedger.getDeliveryDate());//需求日期=交货日期
        productionPlan.setPromisedDeliveryDate(salesLedger.getDeliveryDate());//承诺日期=交货日期
        productionPlanMapper.insert(productionPlan);
    }
@@ -295,7 +285,7 @@
            Field amountField = mainEntityClass.getDeclaredField("contractAmount");
            amountField.setAccessible(true);
            amountField.set(entity, totalAmount);
            amountField.set(entity, AmountUtils.scaleAmount(totalAmount));
            mainMapper.updateById(entity);
        } catch (Exception e) {
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -22,6 +22,7 @@
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.SecurityUtils;
@@ -38,6 +39,8 @@
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto;
import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.sales.dto.*;
import com.ruoyi.sales.mapper.CommonFileMapper;
@@ -61,7 +64,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.io.InputStream;
import java.lang.reflect.Field;
@@ -119,6 +121,9 @@
    @Autowired
    private ProductionProductMainService productionProductMainService;
    @Autowired
    private PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper;
    ;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
@@ -143,11 +148,17 @@
        productWrapper.eq(SalesLedgerProduct::getType, type.getCode());
        List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(productWrapper);
        if (type.equals(SaleEnum.PURCHASE)) {
            // æŸ¥è¯¢é€€è´§ä¿¡æ¯
            List<Long> productIds = salesLedgerProducts.stream().map(SalesLedgerProduct::getProductModelId).collect(Collectors.toList());
            List<SimpleReturnOrderGroupDto> groupListByProductIds = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(productIds)) {
                groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds);
            }
            Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, SimpleReturnOrderGroupDto::getSumReturnQuantity));
            salesLedgerProducts.forEach(item -> {
                BigDecimal returnQuantity = item.getReturnQuantity() == null ? BigDecimal.ZERO : item.getReturnQuantity();
                item.setReturnQuantity(returnQuantity);
                BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity();
                item.setAvailableQuality(quantity.subtract(returnQuantity));
                BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO);
                item.setReturnQuality(returnQuality);
                item.setAvailableQuality(item.getQuantity().subtract(returnQuality));
            });
        }
        return salesLedgerProducts;
@@ -166,6 +177,7 @@
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId());
        productWrapper.eq(SalesLedgerProduct::getType, 1);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        AmountUtils.normalizeSalesLedgerProducts(products);
        for (SalesLedgerProduct product : products) {
            product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName());
            product.setRegisterDate(LocalDateTime.now());
@@ -175,7 +187,11 @@
                    .orderByDesc(ShippingInfo::getCreateTime)
                    .last("limit 1"));
            if (shippingInfo != null) {
                product.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                product.setShippingDate(shippingInfo.getShippingDate());
                product.setShippingStatus(shippingInfo.getStatus());
                product.setExpressCompany(shippingInfo.getExpressCompany());
                product.setExpressNumber(shippingInfo.getExpressNumber());
            }
        }
@@ -188,6 +204,7 @@
        // 4. è½¬æ¢ DTO
        SalesLedgerDto resultDto = new SalesLedgerDto();
        BeanUtils.copyProperties(salesLedger, resultDto);
        AmountUtils.normalizeContractAmount(resultDto);
        if (!products.isEmpty()) {
            resultDto.setHasChildren(true);
            resultDto.setProductData(products);
@@ -287,7 +304,7 @@
            dto.setReceiptAmount(totalIncome);
            //开票金额
            List<AccountInvoiceApplication> accountInvoiceApplications = accountInvoiceApplicationMapper.selectList(new LambdaQueryWrapper<AccountInvoiceApplication>()
                            .eq(AccountInvoiceApplication::getStatus,1)
                    .eq(AccountInvoiceApplication::getStatus, 1)
                    .between(AccountInvoiceApplication::getApplyDate, startTime, endTime));
            BigDecimal totalInvoiceAmount = Optional.of(
                    accountInvoiceApplications.stream()
@@ -303,7 +320,11 @@
    @Override
    public IPage<SalesLedgerVo> selectSalesLedgerListPage(Page page, SalesLedgerDto salesLedgerDto) {
        return salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto);
        IPage<SalesLedgerVo> result = salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto);
        if (!CollectionUtils.isEmpty(result.getRecords())) {
            result.getRecords().forEach(AmountUtils::normalizeContractAmount);
        }
        return result;
    }
    @Override
@@ -320,17 +341,9 @@
            if (CollectionUtils.isEmpty(salesLedgerImportDtoList)) return AjaxResult.error("销售台账数据为空!");
            List<SalesLedgerImportDto> salesLedgerProductImportDtoList = stringListMap.get("销售产品数据");
            if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return AjaxResult.error("销售产品数据为空!");
            // å®¢æˆ·æ•°æ® - å‚考 listPage æŸ¥è¯¢ç§æµ·å®¢æˆ·ï¼ˆtype = 0)
            // type = 0(私海客户)或者 type = 1(公海客户)且已被分配,并且是自己领用、自己创建或者共享给自己的客户
            Long loginUserId = loginUser.getUser().getUserId();
            List<Customer> customers = customerMapper.selectList(new QueryWrapper<Customer>()
                    .in("customer_name", salesLedgerImportDtoList.stream()
                            .map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList()))
                    .and(wrapper -> wrapper.eq("type", 0)
                            .or(wrapper2 -> wrapper2.eq("type", 1).eq("is_assigned", 1)))
                    .and(wrapper -> wrapper.eq("usage_user", loginUserId)
                            .or(wrapper2 -> wrapper2.eq("create_user", loginUserId)
                                    .or(wrapper3 -> wrapper3.exists("select 1 from customer_user cu where cu.customer_id = customer.id and cu.user_id = " + loginUserId)))));
            // å®¢æˆ·æ•°æ®
            List<Customer> customers = customerMapper.selectList(new LambdaQueryWrapper<Customer>().in(Customer::getCustomerName,
                    salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList())));
//            // è§„格型号数据
//            List<ProductModel> productModels = productModelMapper.selectList(new LambdaQueryWrapper<ProductModel>().in(ProductModel::getModel,
//                    salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getSpecificationModel).collect(Collectors.toList())));
@@ -348,26 +361,21 @@
                if (salesLedger1 != null) {
                    continue;
                }
                // åˆ¤æ–­ä¸šåŠ¡å‘˜æ˜¯å¦å­˜åœ¨
                SysUser salesman = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getNickName, salesLedgerImportDto.getSalesman()));
                if (salesman == null) {
                    throw new RuntimeException("业务员:" + salesLedgerImportDto.getSalesman() + "不存在!");
                }
                SalesLedger salesLedger = new SalesLedger();
                BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
                salesLedger.setExecutionDate(DateUtils.toLocalDate(salesLedgerImportDto.getExecutionDate()));
                salesLedger.setDeliveryDate(DateUtils.toLocalDate(salesLedgerImportDto.getDeliveryDate()));
                // é€šè¿‡å®¢æˆ·åç§°æŸ¥è¯¢å®¢æˆ·ID,客户合同号
                Optional<Customer> customerOptional = customers.stream()
                salesLedger.setCustomerId(customers.stream()
                        .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName()))
                        .findFirst();
                if (customerOptional.isEmpty()) {
                    throw new RuntimeException("客户:" + salesLedger.getCustomerName() + "不存在!或者非私海用户");
                }
                salesLedger.setCustomerId(customerOptional.get().getId());
                salesLedger.setCustomerContractNo(customerOptional.get().getTaxpayerIdentificationNumber());
                        .findFirst()
                        .map(Customer::getId)
                        .orElse(null));
                salesLedger.setCustomerContractNo(customers.stream()
                        .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName()))
                        .findFirst()
                        .map(Customer::getTaxpayerIdentificationNumber)
                        .orElse(null));
                Long aLong = sysUsers.stream()
                        .filter(sysUser -> sysUser.getNickName().equals(salesLedger.getEntryPerson()))
                        .findFirst()
@@ -385,7 +393,6 @@
                salesLedger.setContractAmount(salesLedgerProductImportDtos.stream()
                        .map(SalesLedgerProductImportDto::getTaxInclusiveTotalPrice)
                        .reduce(BigDecimal.ZERO, BigDecimal::add));
                salesLedger.setNetContractAmount(salesLedger.getContractAmount());
                salesLedgerMapper.insert(salesLedger);
@@ -394,18 +401,21 @@
                    BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct);
                    salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
                    salesLedgerProduct.setType(1);
                    AmountUtils.normalizeSalesLedgerProduct(salesLedgerProduct);
                    // è®¡ç®—不含税总价
                    salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
                    // æ ¡éªŒäº§å“è§„格是否存在
                    Optional<Map<String, Object>> productModelOptional = list.stream()
                    salesLedgerProduct.setTaxExclusiveTotalPrice(
                            AmountUtils.calcTaxExclusiveTotalPrice(
                                    salesLedgerProduct.getTaxInclusiveTotalPrice(),
                                    salesLedgerProduct.getTaxRate()
                            )
                    );
                    list.stream()
                            .filter(map -> Objects.equals(map.get("productName"), salesLedgerProduct.getProductCategory()) && Objects.equals(map.get("model"), salesLedgerProduct.getSpecificationModel()))
                            .findFirst();
                    if (productModelOptional.isEmpty()) {
                        throw new RuntimeException("产品大类:" + salesLedgerProduct.getProductCategory() + ",规格型号:" + salesLedgerProduct.getSpecificationModel() + "不存在!");
                    }
                    Map<String, Object> productModelMap = productModelOptional.get();
                    salesLedgerProduct.setProductModelId(Long.parseLong(productModelMap.get("modelId").toString()));
                    salesLedgerProduct.setProductId(Long.parseLong(productModelMap.get("id").toString()));
                            .findFirst()
                            .ifPresent(map -> {
                                salesLedgerProduct.setProductModelId(Long.parseLong(map.get("modelId").toString()));
                                salesLedgerProduct.setProductId(Long.parseLong(map.get("id").toString()));
                            });
//                    salesLedgerProduct.setProductId(productList.stream()
//                            .filter(product -> product.getProductName().equals(salesLedgerProduct.getProductCategory()))
//                            .findFirst()
@@ -452,21 +462,23 @@
            productWrapper.eq(SalesLedgerProduct::getType, 1);
            List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
            for (SalesLedgerProduct product : products) {
               product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName());
                product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName());
                product.setRegisterDate(LocalDateTime.now());
                // å‘货信息
                ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>()
                        .eq(ShippingInfo::getSalesLedgerProductId, product.getId())
                        .orderByDesc(ShippingInfo::getCreateTime)
                        .last("limit 1"));
                product.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                product.setShippingDate(shippingInfo.getShippingDate());
                if (shippingInfo != null) {
                    product.setShippingCarNumber(shippingInfo.getShippingCarNumber());
                    product.setShippingDate(shippingInfo.getShippingDate());
                    product.setShippingStatus(shippingInfo.getStatus());
                    product.setExpressCompany(shippingInfo.getExpressCompany());
                    product.setExpressNumber(shippingInfo.getExpressNumber());
                }
            }
            // è¿‡æ»¤åªä¿ç•™å‘货记录
            products = products.stream().filter(product -> "已发货".equals(product.getShippingStatus())).collect(Collectors.toList());
            products = products.stream().filter(product -> "审核通过".equals(product.getShippingStatus())).collect(Collectors.toList());
            if (!products.isEmpty()) {
                salesLedger.setHasChildren(true);
                salesLedger.setProductData(products);
@@ -511,6 +523,7 @@
        // åˆ é™¤é”€å”®ç®¡ç†æ•°æ®
        LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, idList)
                .eq(SalesLedgerProduct::getType, 1)
                .select(SalesLedgerProduct::getId);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(queryWrapper);
@@ -559,17 +572,14 @@
        // 2. DTO转Entity
        SalesLedger salesLedger = convertToEntity(salesLedgerDto);
        AmountUtils.normalizeContractAmount(salesLedger);
        salesLedger.setCustomerName(customer.getCustomerName());
        salesLedger.setTenantId(customer.getTenantId());
        // å½•入人:如果未传则使用当前登录用户
        if (StringUtils.isEmpty(salesLedger.getEntryPerson())) {
            salesLedger.setEntryPerson(String.valueOf(SecurityUtils.getUserId()));
        }
        // 3. æ–°å¢žæˆ–更新主表
        if (salesLedger.getId() == null) {
            String contractNo = salesLedger.getSalesContractNo();
            if (StringUtils.isEmpty(contractNo)) {
                contractNo = generateSalesContractNo(salesLedgerDto.getEntryDate());
                contractNo = generateSalesContractNo();
            }
            salesLedger.setSalesContractNo(contractNo);
            salesLedgerMapper.insert(salesLedger);
@@ -581,6 +591,7 @@
        // 4. å¤„理子表数据
        List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
        if (productList != null && !productList.isEmpty()) {
            AmountUtils.normalizeSalesLedgerProducts(productList);
            handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class, salesLedgerDto.getType()));
            updateMainContractAmount(
                    salesLedger.getId(),
@@ -591,18 +602,7 @@
            );
        }
        // 5. å…œåº•:确保实际合同金额有初始值等于合同金额
        if (salesLedger.getNetContractAmount() == null) {
            SalesLedger dbLedger = salesLedgerMapper.selectById(salesLedger.getId());
            if (dbLedger != null && dbLedger.getNetContractAmount() == null && dbLedger.getContractAmount() != null) {
                SalesLedger updateEntity = new SalesLedger();
                updateEntity.setId(salesLedger.getId());
                updateEntity.setNetContractAmount(dbLedger.getContractAmount());
                salesLedgerMapper.updateById(updateEntity);
            }
        }
        // 6. ä¿å­˜é”€å”®å°è´¦é™„ä»¶
        // 5. ä¿å­˜é”€å”®å°è´¦é™„ä»¶
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs());
        return 1;
@@ -621,6 +621,7 @@
        // æ‰§è¡Œæ›´æ–°æ“ä½œ
        if (!updateList.isEmpty()) {
            for (SalesLedgerProduct product : updateList) {
                AmountUtils.normalizeSalesLedgerProduct(product);
                product.setType(type.getCode());
                salesLedgerProductMapper.updateById(product);
            }
@@ -628,6 +629,7 @@
        // æ‰§è¡Œæ’入操作
        if (!insertList.isEmpty()) {
            for (SalesLedgerProduct salesLedgerProduct : insertList) {
                AmountUtils.normalizeSalesLedgerProduct(salesLedgerProduct);
                salesLedgerProduct.setType(type.getCode());
                salesLedgerProductMapper.insert(salesLedgerProduct);
                // æ·»åŠ ç”Ÿäº§æ•°æ®
@@ -643,8 +645,8 @@
    }
    @Transactional(readOnly = true)
    public String generateSalesContractNo(Date entryDate) {
        LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now();
    public String generateSalesContractNo() {
        LocalDate currentDate = LocalDate.now();
        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
        String lockKey = LOCK_PREFIX + datePart;
        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime(); // å”¯ä¸€æ ‡è¯†é”æŒæœ‰è€…
@@ -672,14 +674,15 @@
            }
            // 2. æŸ¥è¯¢å½“天/公司已存在的序列号(与原逻辑一致)
            Long tenantId = SecurityUtils.getLoginUser().getTenantId();
            if (null != tenantId) {
                //获取公司编号
                SysDept sysDept = sysDeptMapper.selectDeptById(tenantId.longValue());
                if (!ObjectUtils.isEmpty(sysDept)) {
                    datePart = (StringUtils.isEmpty(sysDept.getDeptNick()) ? "" : sysDept.getDeptNick()) + datePart;
                }
            }
//            Long tenantId = SecurityUtils.getLoginUser().getTenantId();
//            if (null != tenantId) {
            //获取公司编号
//                SysDept sysDept = sysDeptMapper.selectDeptById(tenantId.longValue());
//                if (!ObjectUtils.isEmpty(sysDept)) {
//                    datePart = (StringUtils.isEmpty(sysDept.getDeptNick()) ? "" : sysDept.getDeptNick()) + datePart;
//                }
//            }
            datePart = "D" + datePart;
            List<Integer> existingSequences = salesLedgerMapper.selectSequencesByDate(datePart);
            int nextSequence = findFirstMissingSequence(existingSequences);
@@ -738,16 +741,7 @@
            Field amountField = mainEntityClass.getDeclaredField("contractAmount");
            amountField.setAccessible(true);
            amountField.set(entity, totalAmount);
            // å®žé™…合同金额初始值等于合同金额
            try {
                Field netAmountField = mainEntityClass.getDeclaredField("netContractAmount");
                netAmountField.setAccessible(true);
                netAmountField.set(entity, totalAmount);
            } catch (NoSuchFieldException ignored) {
                // å­ç±»å¯èƒ½æ²¡æœ‰è¯¥å­—段,忽略
            }
            amountField.set(entity, AmountUtils.scaleAmount(totalAmount));
            mainMapper.updateById(entity);
        } catch (Exception e) {
@@ -784,4 +778,19 @@
            return totalAmount;
        }
    }
    @Override
    public Map<String, Object> getPrintData(Long id) {
        SalesLedgerDto dto = new SalesLedgerDto();
        dto.setId(id);
        SalesLedgerDto ledger = getSalesLedgerWithProducts(dto);
        if (ledger == null) {
            return null;
        }
        SysDept dept = sysDeptMapper.selectDeptById(100L);
        Map<String, Object> result = new HashMap<>();
        result.put("companyName", dept != null ? dept.getDeptName() : "");
        result.put("ledger", ledger);
        return result;
    }
}
src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
@@ -7,17 +7,14 @@
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo;
import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
import com.ruoyi.approve.pojo.ApprovalInstance;
import com.ruoyi.approve.pojo.ApprovalTemplate;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.service.ApprovalInstanceService;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo;
import com.ruoyi.approve.bean.vo.ApproveProcessVO;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.common.enums.IsDeleteEnum;
import com.ruoyi.common.utils.AmountUtils;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
@@ -33,8 +30,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
@@ -47,8 +45,6 @@
    private final ApproveProcessServiceImpl approveProcessService;
    private final CustomerMapper customerMapper;
    private final ApprovalTemplateMapper approvalTemplateMapper;
    private final ApprovalInstanceService approvalInstanceService;
    @Override
    public IPage<SalesQuotationDto> listPage(Page page, SalesQuotationDto salesQuotationDto) {
@@ -56,31 +52,34 @@
        if(CollectionUtils.isEmpty(salesQuotationDtoIPage.getRecords())){
            return salesQuotationDtoIPage;
        }
        // æ‰¹é‡æŸ¥è¯¢äº§å“ï¼Œé¿å… N+1 é—®é¢˜
        List<Long> quotationIds = salesQuotationDtoIPage.getRecords().stream()
                .map(SalesQuotationDto::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!quotationIds.isEmpty()) {
            List<SalesQuotationProduct> allProducts = salesQuotationProductMapper.selectList(
                    new LambdaQueryWrapper<SalesQuotationProduct>()
                            .in(SalesQuotationProduct::getSalesQuotationId, quotationIds)
            );
            Map<Long, List<SalesQuotationProduct>> productMap = allProducts.stream()
                    .collect(Collectors.groupingBy(SalesQuotationProduct::getSalesQuotationId));
            salesQuotationDtoIPage.getRecords().forEach(record ->
                    record.setProducts(productMap.getOrDefault(record.getId(), new ArrayList<>()))
            );
        }
        salesQuotationDtoIPage.getRecords().forEach(record -> {
            record.setTotalAmount(AmountUtils.scaleAmount(record.getTotalAmount()));
            List<SalesQuotationProduct> products = salesQuotationProductMapper.selectBySalesQuotationId(record.getId());
            if (!CollectionUtils.isEmpty(products)) {
                products.forEach(product -> {
                    product.setUnitPrice(AmountUtils.scaleAmount(product.getUnitPrice()));
                    product.setAmount(AmountUtils.scaleAmount(product.getAmount()));
                });
            }
            record.setProducts(products);
        });
        return salesQuotationDtoIPage;
    }
    private void normalizeAmounts(SalesQuotationDto salesQuotationDto) {
        salesQuotationDto.setTotalAmount(AmountUtils.scaleAmount(salesQuotationDto.getTotalAmount()));
        if (CollectionUtils.isEmpty(salesQuotationDto.getProducts())) {
            return;
        }
        salesQuotationDto.getProducts().forEach(product -> {
            product.setUnitPrice(AmountUtils.scaleAmount(product.getUnitPrice()));
            product.setAmount(AmountUtils.scaleAmount(product.getAmount()));
        });
    }
    @Override
    public boolean add(SalesQuotationDto salesQuotationDto) {
        normalizeAmounts(salesQuotationDto);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SalesQuotation salesQuotation = new SalesQuotation();
        BeanUtils.copyProperties(salesQuotationDto, salesQuotation);
@@ -89,7 +88,7 @@
        if (ObjectUtils.isNotEmpty(customer))  {
            salesQuotation.setCustomer(customer.getCustomerName());
        }
        String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no", salesQuotationDto.getCreateTime() != null ? salesQuotationDto.getCreateTime() : LocalDateTime.now());
        String quotationNo = OrderUtils.countTodayByCreateTime(salesQuotationMapper, "QT","quotation_no");
        salesQuotation.setQuotationNo(quotationNo);
        salesQuotation.setStatus("待审批");
        salesQuotationMapper.insert(salesQuotation);
@@ -104,36 +103,25 @@
        }).collect(Collectors.toList());
        salesQuotationProductService.saveBatch(products);
        // æŠ¥ä»·å®¡æ‰¹
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
                new LambdaQueryWrapper<ApprovalTemplate>()
                        .eq(ApprovalTemplate::getBusinessType, 6L)
                        .eq(ApprovalTemplate::getDeleted, 0)
                        .orderByDesc(ApprovalTemplate::getId)
                        .last("LIMIT 1")
        );
        if (approvalTemplate == null) {
            throw new RuntimeException("请先配置报价审批模板");
        }
        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
        approvalInstance.setTemplateId(approvalTemplate.getId());
        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
        approvalInstance.setBusinessId(salesQuotation.getId());
        approvalInstance.setBusinessType(6L);
        approvalInstance.setCurrentLevel(1);
        approvalInstance.setTitle(quotationNo+"审批");
        approvalInstance.setApplicantId(loginUser.getUserId());
        approvalInstance.setApplicantName(loginUser.getNickName());
        approvalInstance.setApplyTime(LocalDateTime.now());
        ApproveProcessVO approveProcessVO = new ApproveProcessVO();
        approveProcessVO.setApproveType(6);
        approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId());
        approveProcessVO.setApproveReason(quotationNo);
        approveProcessVO.setApproveUserIds(salesQuotationDto.getApproveUserIds());
        approveProcessVO.setApproveUser(loginUser.getUserId());
        approveProcessVO.setApproveTime(LocalDate.now().toString());
        approveProcessVO.setPrice(salesQuotationDto.getTotalAmount());
        try {
            approvalInstanceService.add(approvalInstance);
        } catch (Exception e) {
            log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e);
            throw new RuntimeException("审批失败: " + e.getMessage(), e);
            approveProcessService.addApprove(approveProcessVO);
        }catch (Exception e){
            log.error("SalesQuotationServiceImpl error:{}", e);
            throw                                new RuntimeException("审批失败");
        }
        return true;
    }
    @Override
    public boolean edit(SalesQuotationDto salesQuotationDto) {
        normalizeAmounts(salesQuotationDto);
        SalesQuotation salesQuotation = new SalesQuotation();
        BeanUtils.copyProperties(salesQuotationDto, salesQuotation);
        ApproveGetAndUpdateVo vo = new ApproveGetAndUpdateVo();
@@ -157,35 +145,10 @@
        salesQuotationProductService.saveBatch(products);
        // ä¿®æ”¹æŠ¥ä»·å®¡æ‰¹
        // å…ˆç»“束之前未结束的报价审批
        approvalInstanceService.lambdaUpdate().set(ApprovalInstance::getStatus,"REJECTED").eq(ApprovalInstance::getBusinessId,salesQuotation.getId()).eq(ApprovalInstance::getBusinessType,6L).update();
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
                new LambdaQueryWrapper<ApprovalTemplate>()
                        .eq(ApprovalTemplate::getBusinessType, 6L)
                        .eq(ApprovalTemplate::getDeleted, 0)
                        .orderByDesc(ApprovalTemplate::getId)
                        .last("LIMIT 1")
        );
        if (approvalTemplate == null) {
            throw new RuntimeException("请先配置报价审批模板");
        }
        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
        approvalInstance.setTemplateId(approvalTemplate.getId());
        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
        approvalInstance.setBusinessId(salesQuotation.getId());
        approvalInstance.setBusinessType(6L);
        approvalInstance.setCurrentLevel(1);
        approvalInstance.setTitle(salesQuotation.getQuotationNo()+"审批");
        approvalInstance.setApplicantId(SecurityUtils.getUserId());
        approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName());
        approvalInstance.setApplyTime(LocalDateTime.now());
        try {
            approvalInstanceService.add(approvalInstance);
        } catch (Exception e) {
            log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e);
            throw new RuntimeException("审批失败: " + e.getMessage(), e);
        }
        vo.setApproveUserIds(salesQuotationDto.getApproveUserIds());
        vo.setApproveType(6);
        vo.setApproveReason(salesQuotationDto.getQuotationNo());
        approveProcessService.updateApproveUser(vo);
        return true;
    }
    @Override
@@ -194,31 +157,13 @@
        if(salesQuotation==null) return false;
        salesQuotationMapper.deleteById(id);
        salesQuotationProductMapper.delete(new LambdaQueryWrapper<SalesQuotationProduct>().eq(SalesQuotationProduct::getSalesQuotationId, id));
        // åˆ é™¤è¯¥æŠ¥ä»·å¯¹åº”的全部审批实例(可能存在多次提交/重审)
        List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>()
                .eq(ApprovalInstance::getBusinessId, id)
                .eq(ApprovalInstance::getBusinessType, 6L)
                .eq(ApprovalInstance::getDeleted, 0));
        if(!approvalInstances.isEmpty()){
            approvalInstanceService.delete(
                    approvalInstances.stream()
                            .map(ApprovalInstance::getId)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList())
            );
        }
        // åˆ é™¤è¯¥æŠ¥ä»·å¯¹åº”的全部审批流程(同一报价可能反复发起)
        List<ApproveProcess> approveProcesses = approveProcessService.list(new LambdaQueryWrapper<ApproveProcess>()
        // åˆ é™¤æŠ¥ä»·å®¡æ‰¹
        ApproveProcess one = approveProcessService.getOne(new LambdaQueryWrapper<ApproveProcess>()
                .eq(ApproveProcess::getApproveType, 6)
                .eq(ApproveProcess::getApproveDelete, IsDeleteEnum.NOT_DELETED)
                .eq(ApproveProcess::getApproveReason, salesQuotation.getQuotationNo()));
        if(!approveProcesses.isEmpty()){
            approveProcessService.delByIds(
                    approveProcesses.stream()
                            .map(ApproveProcess::getId)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList())
            );
        if(one != null){
            approveProcessService.delByIds(Collections.singletonList(one.getId()));
        }
        return true;
    }
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -2,22 +2,17 @@
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.approve.bean.dto.ApprovalInstanceDto;
import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
import com.ruoyi.approve.pojo.ApprovalTemplate;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.approve.service.ApprovalInstanceService;
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.dto.ShippingApproveDto;
@@ -29,13 +24,16 @@
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.pojo.ShippingProductDetail;
import com.ruoyi.sales.service.ShippingInfoService;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.pojo.StockOutRecord;
import com.ruoyi.stock.service.StockOutRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
/**
@@ -60,8 +58,7 @@
    private final ApproveProcessServiceImpl approveProcessService;
    private final FileUtil fileUtil;
    private final ShippingProductDetailMapper shippingProductDetailMapper;
    private final ApprovalTemplateMapper approvalTemplateMapper;
    private final ApprovalInstanceService approvalInstanceService;
    private final StockOutRecordService stockOutRecordService;
    @Override
    public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) {
@@ -147,6 +144,11 @@
        shippingProductDetailMapper.insert(req.getBatchNoDetailList());
        for (ShippingProductDetail shippingProductDetail : req.getBatchNoDetailList()) {
            stockUtils.substractStock(shippingProductDetail.getProductModelId(), shippingProductDetail.getQuantity(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), req.getId(), shippingProductDetail.getBatchNo());
            //StockOutRecord stockOutRecord = stockOutRecordService.getOne(Wrappers.<StockOutRecord>lambdaQuery()
            //        .eq(StockOutRecord::getRecordId, req.getId())
            //        .eq(StockOutRecord::getRecordType, StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode())
            //);
            //stockOutRecordService.batchApprove(Collections.singletonList(stockOutRecord.getId()),1);
        }
        // ä¿å­˜æ–‡ä»¶
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, req.getId(), req.getStorageBlobDTOs());
@@ -167,53 +169,5 @@
        List<ShippingProductDetailDto> dateilByShippingNo = shippingProductDetailMapper.getDateilByShippingNo(shippingNo);
        shippingApproveDto.setShippingProductDetailDtoList(dateilByShippingNo);
        return shippingApproveDto;
    }
    @Override
    public boolean addReq(ShippingInfoDto req) {
        // æ ¡éªŒè¯¥äº§å“æ˜¯å¦å·²æœ‰å¾…审核/审核中的发货记录,防止重复提交
        Long salesLedgerProductId = req.getSalesLedgerProductId();
        if (salesLedgerProductId != null) {
            long pendingCount = this.count(new LambdaQueryWrapper<ShippingInfo>()
                    .eq(ShippingInfo::getSalesLedgerProductId, salesLedgerProductId)
                    .in(ShippingInfo::getStatus, "待审核", "审核中"));
            if (pendingCount > 0) {
                throw new RuntimeException("该产品已有待审核的发货记录,请勿重复提交");
            }
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no",req.getCreateTime());
        // å…ˆä¿å­˜å‘货单,再发起审批;无审核人自动通过时需要按发货编号回写发货状态。
        req.setShippingNo(sh);
        req.setStatus("待审核");
        boolean save = this.add(req);
        // å‘货审批
        ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
                new LambdaQueryWrapper<ApprovalTemplate>()
                        .eq(ApprovalTemplate::getBusinessType, 7L)
                        .eq(ApprovalTemplate::getDeleted, 0)
                        .orderByDesc(ApprovalTemplate::getId)
                        .last("LIMIT 1")
        );
        if (approvalTemplate == null) {
            throw new RuntimeException("请先配置发货审批模板");
        }
        ApprovalInstanceDto approvalInstance = new ApprovalInstanceDto();
        approvalInstance.setTemplateId(approvalTemplate.getId());
        approvalInstance.setTemplateName(approvalTemplate.getTemplateName());
        approvalInstance.setBusinessId(req.getId());
        approvalInstance.setBusinessType(7L);
        approvalInstance.setCurrentLevel(1);
        approvalInstance.setTitle(sh);
        approvalInstance.setApplicantId(loginUser.getUserId());
        approvalInstance.setApplicantName(loginUser.getNickName());
        approvalInstance.setApplyTime(LocalDateTime.now());
        approvalInstanceService.add(approvalInstance);
        // åˆ›å»ºå®¡æ‰¹å®žä¾‹åŽï¼Œæ›´æ–°å‘货状态为"审核中",使前端正确显示审批状态
        req.setStatus("审核中");
        this.updateById(req);
        return true;
    }
}
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
@@ -24,13 +24,10 @@
    @Schema(description = "合同金额")
    private BigDecimal contractAmount;
    @Schema(description = "收款金额")
    private BigDecimal receiptPaymentAmount;
    @Schema(description = "已出库金额")
    private BigDecimal shippedAmount;
    @Schema(description = "应收金额")
    private BigDecimal receiptableAmount;
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "未出库金额")
    private BigDecimal unshippedAmount;
}
src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
@@ -16,18 +16,12 @@
    private String customerName;
    @Schema(description = "合同总金额")
    //该客户销售合同累计金额
    private BigDecimal contractAmounts;
    @Schema(description = "收款金额")
    //该客户销售收款累计金额
    private BigDecimal receiptPaymentAmount;
    @Schema(description = "已出库金额")
    private BigDecimal shippedAmount;
    @Schema(description = "应收金额")
    //该客户销售应收累计金额=初始金额-回款-退款
    private BigDecimal receiptableAmount;
    @Schema(description = "退货金额")
    private BigDecimal returnAmount;
    @Schema(description = "未出库金额")
    private BigDecimal unshippedAmount;
}
src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java
@@ -1,6 +1,8 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.staff.pojo.HolidayApplication;
import com.ruoyi.staff.service.HolidayApplicationService;
@@ -23,6 +25,7 @@
    /**
     * æ–°å¢žè¯·å‡ç”³è¯·
     */
    @Log(title = "新增请假申请", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody HolidayApplication holidayApplication){
        return AjaxResult.success(holidayApplicationService.save(holidayApplication));
@@ -30,6 +33,7 @@
    /**
     * ä¿®æ”¹è¯·å‡ç”³è¯·
     */
    @Log(title = "修改请假申请", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public AjaxResult update(@RequestBody HolidayApplication holidayApplication){
        return AjaxResult.success(holidayApplicationService.updateById(holidayApplication));
@@ -37,6 +41,7 @@
    /**
     * åˆ é™¤è¯·å‡ç”³è¯·
     */
    @Log(title = "删除请假申请", businessType = BusinessType.DELETE)
    @DeleteMapping("/delete/{id}")
    public AjaxResult delete(@PathVariable("id") Long id){
        return AjaxResult.success(holidayApplicationService.removeById(id));
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java
@@ -1,9 +1,8 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
@@ -31,12 +30,11 @@
    private final PersonalAttendanceLocationConfigService personalAttendanceLocationConfigService;
    @Operation(summary = "新增/修改人员打卡规则配置")
    @Log(title = "新增/修改人员打卡规则配置", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R add(@RequestBody PersonalAttendanceLocationConfig personalAttendanceLocationConfig){
        if (personalAttendanceLocationConfig.getId() != null
                && !personalAttendanceLocationConfigService.canEdit(personalAttendanceLocationConfig.getId())) {
            return R.fail("该班次配置已被排班并产生打卡记录,不能编辑");
        }
        // ç­æ¬¡éœ€è¦ä¸èƒ½å¤Ÿé‡å¤
        if (personalAttendanceLocationConfig.getId() == null && personalAttendanceLocationConfigService.lambdaQuery().eq(PersonalAttendanceLocationConfig::getShift, personalAttendanceLocationConfig.getShift()).one() != null) {
            return R.fail("班次不能重复");
@@ -46,35 +44,15 @@
    @Operation(summary = "分页查询人员打卡规则配置")
    @GetMapping("/listPage")
    public R listPage(Page page, PersonalAttendanceLocationConfig query){
        page.addOrder(OrderItem.desc("id"));
        LambdaQueryWrapper<PersonalAttendanceLocationConfig> wrapper = new LambdaQueryWrapper<PersonalAttendanceLocationConfig>()
                .ne(PersonalAttendanceLocationConfig::getId, 999);
        if (query.getSysDeptId() != null) {
            wrapper.eq(PersonalAttendanceLocationConfig::getSysDeptId, query.getSysDeptId());
        }
        if (query.getShift() != null && !query.getShift().isEmpty()) {
            wrapper.eq(PersonalAttendanceLocationConfig::getShift, query.getShift());
        }
        IPage<PersonalAttendanceLocationConfig> result = personalAttendanceLocationConfigService.page(page, wrapper);
        result.getRecords().forEach(item -> {
            boolean locked = personalAttendanceLocationConfigService.hasAttendanceUsage(item.getId());
            item.setUsageLocked(locked);
            item.setCanEdit(!locked);
            item.setCanDelete(!locked);
        });
        return R.ok(result);
    public R listPage(Page page){
        return R.ok(personalAttendanceLocationConfigService.page(page));
    }
    @Operation(summary = "删除人员打卡规则配置")
    @Log(title = "删除人员打卡规则配置", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public R del(@RequestBody List<Integer> ids) {
        for (Integer id : ids) {
            if (!personalAttendanceLocationConfigService.canDelete(id)) {
                return R.fail("存在已排班且已有打卡记录的班次配置,不能删除");
            }
        }
        return R.ok(personalAttendanceLocationConfigService.removeBatchByIds(ids));
    }
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java
@@ -1,6 +1,8 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
@@ -27,6 +29,7 @@
    @Resource
    private PersonalAttendanceRecordsService personalAttendanceRecordsService;
    @Log(title = "个人考勤记录", businessType = BusinessType.INSERT)
    @Operation(summary = "新增打卡签到")
    @PostMapping("")
    public AjaxResult add(@RequestBody PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
@@ -45,6 +48,7 @@
        return AjaxResult.success(personalAttendanceRecordsService.todayInfo(personalAttendanceRecordsDto));
    }
    @Log(title = "个人考勤记录", businessType = BusinessType.EXPORT)
    @Operation(summary = "导出打卡签到")
    @PostMapping("/export")
    public void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
src/main/java/com/ruoyi/staff/controller/PersonalShiftController.java
@@ -4,6 +4,8 @@
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.staff.dto.PerformanceShiftAddDto;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
@@ -42,6 +44,7 @@
    private PersonalShiftService personalShiftService;
    @Operation(summary = "人员排班")
    @Log(title = "新增人员排班", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R add(@RequestBody PerformanceShiftAddDto performanceShiftAddDto){
        return R.ok(personalShiftService.performanceShiftAdd(performanceShiftAddDto));
@@ -60,13 +63,15 @@
    }
    @Operation(summary = "班次状态修改")
    @PostMapping("/update")
    @Log(title = "修改班次状态", businessType = BusinessType.UPDATE)
    @PostMapping("update")
    public R performanceShiftUpdate(@RequestBody PersonalShift personalShift) {
        personalShiftService.performanceShiftUpdate(personalShift);
        return R.ok();
    }
    @Operation(summary = "导出")
    @Log(title = "导出人员排班", businessType = BusinessType.EXPORT)
    @GetMapping("export")
    public void exportToExcel(@NotNull(message = "时间不能为空!") String time, String userName, Integer sysDeptId, Boolean isMonth, HttpServletResponse response) throws Exception {
        ServletOutputStream out = response.getOutputStream();
src/main/java/com/ruoyi/staff/controller/StaffLeaveController.java
@@ -1,6 +1,8 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.staff.dto.StaffLeaveDto;
import com.ruoyi.staff.service.StaffLeaveService;
@@ -37,6 +39,7 @@
     * @param staffLeaveDto
     * @return
     */
    @Log(title = "新增离职记录", businessType = BusinessType.INSERT)
    @PostMapping("")
    public AjaxResult add(@RequestBody StaffLeaveDto staffLeaveDto) {
        return AjaxResult.success(staffLeaveService.add(staffLeaveDto));
@@ -48,6 +51,7 @@
     * @param staffLeaveDto
     * @return
     */
    @Log(title = "修改离职记录", businessType = BusinessType.UPDATE)
    @PutMapping("/{id}")
    public AjaxResult update(@PathVariable("id") Long id, @RequestBody StaffLeaveDto staffLeaveDto) {
        return AjaxResult.success(staffLeaveService.update(id, staffLeaveDto));
@@ -58,6 +62,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除离职记录", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult del(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
@@ -70,6 +75,7 @@
     * ç¦»èŒå¯¼å‡º
     * @param staffLeaveDto
     */
    @Log(title = "导出离职记录", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, StaffLeaveDto staffLeaveDto) {
        staffLeaveService.export(response, staffLeaveDto);
src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
@@ -39,7 +39,7 @@
     * @return
     */
    @GetMapping("/listPage")
    public AjaxResult staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) {
    public AjaxResult staffOnJobListPage(Page page, StaffOnJob staffOnJob) {
        return AjaxResult.success(staffOnJobService.staffOnJobListPage(page, staffOnJob));
    }
@@ -57,6 +57,7 @@
     * @param staffOnJob
     * @return
     */
    @Log(title = "在职员工", businessType = BusinessType.INSERT)
    @PostMapping("")
    public AjaxResult add(@RequestBody StaffOnJobDto staffOnJob) {
        return AjaxResult.success(staffOnJobService.add(staffOnJob));
@@ -67,6 +68,7 @@
     * @param staffOnJobDto
     * @return
     */
    @Log(title = "在职员工", businessType = BusinessType.UPDATE)
    @PutMapping("/{id}")
    public AjaxResult update(@PathVariable("id") Long id, @RequestBody StaffOnJobDto staffOnJobDto) {
        return AjaxResult.success(staffOnJobService.update(id, staffOnJobDto));
@@ -77,6 +79,7 @@
     * @param ids
     * @return
     */
    @Log(title = "在职员工", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delStaffOnJobs(@RequestBody List<Integer> ids) {
        if(CollectionUtils.isEmpty(ids)){
@@ -101,12 +104,14 @@
     * @param staffContract
     * @return
     */
    @Log(title = "在职员工", businessType = BusinessType.UPDATE)
    @PostMapping("/renewContract/{id}")
    public AjaxResult renewContract(@PathVariable Long id, @RequestBody StaffContract staffContract) {
        return AjaxResult.success(staffOnJobService.renewContract(id, staffContract));
    }
    @Operation(summary = "下载模板")
    @Log(title = "在职员工", businessType = BusinessType.EXPORT)
    @PostMapping("/downloadTemplate")
    public void downloadTemplate(HttpServletResponse response) {
        ExcelUtil<StaffOnJobExcelDto> util = new ExcelUtil<>(StaffOnJobExcelDto.class);
@@ -131,6 +136,7 @@
     * @param response
     * @param staffOnJob
     */
    @Log(title = "在职员工", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void staffOnJobExport(HttpServletResponse response,StaffOnJob staffOnJob) {
        staffOnJobService.staffOnJobExport(response, staffOnJob);
@@ -141,6 +147,7 @@
     * @param response
     * @param staffOnJob
     */
    @Log(title = "在职员工", businessType = BusinessType.EXPORT)
    @PostMapping("/exportCopy")
    @Operation(summary = "word模板合同在职员工导出")
    public AjaxResult exportCopy(HttpServletResponse response,@RequestBody StaffOnJob staffOnJob) throws Exception{
src/main/java/com/ruoyi/staff/controller/StaffSalaryMainController.java
@@ -5,12 +5,10 @@
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.staff.dto.CalculateSalaryDto;
import com.ruoyi.staff.dto.StaffSalaryCalculateDto;
import com.ruoyi.staff.pojo.StaffSalaryMain;
import com.ruoyi.staff.service.StaffSalaryMainService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@@ -45,12 +43,6 @@
        return staffSalaryMainService.calculateSalary(calculateSalaryDto);
    }
    @Operation(summary = "按员工重算工资明细")
    @PostMapping("/calculateByEmployeeId")
    public AjaxResult calculateByEmployeeId(@RequestBody StaffSalaryCalculateDto staffSalaryCalculateDto) {
        return staffSalaryMainService.calculateSalaryByEmployee(staffSalaryCalculateDto);
    }
    @PostMapping("/add")
    @Operation(summary = "新建工资表")
    @Log(title = "新建工资表", businessType = BusinessType.INSERT)
@@ -75,16 +67,6 @@
        return staffSalaryMainService.delete(ids);
    }
    @PostMapping("/export")
    @Operation(summary = "导出员工薪资明细")
    public void export(HttpServletResponse response, String salaryMonth) {
        staffSalaryMainService.exportSalaryDetail(response, salaryMonth);
    }
    @GetMapping("/availableDepts")
    @Operation(summary = "获取可选部门(排除已发放工资表的部门)")
    public AjaxResult getAvailableDepts(String salaryMonth) {
        return staffSalaryMainService.getAvailableDepts(salaryMonth);
    }
}
src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java
@@ -35,18 +35,21 @@
       return AjaxResult.success(staffSchedulingService.listPage(vo));
    }
    @Log(title = "员工排班", businessType = BusinessType.INSERT)
    @PostMapping("/save")
    public AjaxResult save(@RequestBody @Validated SaveStaffSchedulingDto saveStaffSchedulingDto){
        staffSchedulingService.saveStaffScheduling(saveStaffSchedulingDto);
        return AjaxResult.success();
    }
    @Log(title = "员工排班", businessType = BusinessType.DELETE)
    @DeleteMapping("/delByIds")
    public AjaxResult delByIds(@RequestBody List<Integer> ids){
        staffSchedulingService.removeByIds(ids);
        return AjaxResult.success();
    }
    @Log(title = "员工排班", businessType = BusinessType.DELETE)
    @DeleteMapping("/del/{id}")
    public AjaxResult del(@PathVariable("id") Integer id){
        staffSchedulingService.removeById(id);
src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java
@@ -36,6 +36,7 @@
    /**
     * å®¶åº­ä½å€
     */
    @Excel(name = "家庭住址", sort = 6)
    private String adress;
    /**
src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java
@@ -25,6 +25,4 @@
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date contractEndTime;
    private Long sysDeptId;
}
src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java
@@ -31,6 +31,4 @@
    List<Map<String, Object>> performanceShiftYearList(@Param("time") String time, @Param("userName") String userName, @Param("sysDeptId") Integer sysDeptId);
    List<PerformanceShiftMapDto> performanceShiftList(@Param("time") String time, @Param("userName") String userName, @Param("sysDeptId") Integer sysDeptId);
    boolean existsAttendanceUsedByConfig(@Param("configId") Integer configId);
}
src/main/java/com/ruoyi/staff/mapper/SchemeApplicableStaffMapper.java
@@ -26,13 +26,6 @@
    List<SchemeApplicableStaff> selectSchemeByStaffId(@Param("staffId") Long staffId);
    /**
     * æ ¹æ®éƒ¨é—¨ID查询最新社保方案
     * @param deptId éƒ¨é—¨ID
     * @return æœ€æ–°ç¤¾ä¿æ–¹æ¡ˆ
     */
    SchemeApplicableStaff selectLatestSchemeByDeptId(@Param("deptId") Long deptId);
    /**
     * æ ¹æ®ç¤¾ä¿æ–¹æ¡ˆID查询对应的明细列表
     * @param schemeId ç¤¾ä¿æ–¹æ¡ˆID
     * @return æ–¹æ¡ˆæ˜Žç»†åˆ—表
src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
@@ -14,7 +14,7 @@
@Mapper
public interface StaffOnJobMapper extends BaseMapper<StaffOnJob> {
    IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJobDto staffOnJob);
    IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJob staffOnJob);
    List<StaffOnJobDto> staffOnJobList(@Param("staffOnJob") StaffOnJob staffOnJob);
@@ -42,4 +42,4 @@
     * @return å‘˜å·¥æ•°æ®
     */
    StaffOnJob selectStaffByNickName(String staffName);
}
}
src/main/java/com/ruoyi/staff/mapper/StaffSalaryMainMapper.java
@@ -4,8 +4,6 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * <p>
 * å‘˜å·¥å·¥èµ„主表 Mapper æŽ¥å£
@@ -16,7 +14,5 @@
 */
@Mapper
public interface StaffSalaryMainMapper extends BaseMapper<StaffSalaryMain> {
    List<String> selectIssuedDeptIds(String salaryMonth);
}
src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java
@@ -61,28 +61,6 @@
    @Schema(description = "班次")
    private String shift;
    /**
     * æ˜¯å¦å·²è¢«æŽ’班且已有打卡记录使用
     */
    @Schema(description = "是否已被排班且已有打卡记录使用")
    @TableField(exist = false)
    private Boolean usageLocked;
    /**
     * æ˜¯å¦å¯ç¼–辑
     */
    @Schema(description = "是否可编辑")
    @TableField(exist = false)
    private Boolean canEdit;
    /**
     * æ˜¯å¦å¯åˆ é™¤
     */
    @Schema(description = "是否可删除")
    @TableField(exist = false)
    private Boolean canDelete;
    @Schema(description = "创建用户")
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java
@@ -77,6 +77,7 @@
    /**
     * å®¶åº­ä½å€
     */
    @Excel(name = "家庭住址", sort = 6)
    private String adress;
    /**
src/main/java/com/ruoyi/staff/pojo/StaffSalaryDetail.java
@@ -68,9 +68,6 @@
    @Schema(description = "其他支出")
    private BigDecimal otherDeduct;
    @Schema(description = "社保补缴")
    private BigDecimal socialSupplementAmount;
    @Schema(description = "工资个税")
    private BigDecimal salaryTax;
src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java
@@ -14,7 +14,7 @@
public interface IStaffOnJobService extends IService<StaffOnJob> {
    IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob);
    IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob);
     StaffOnJobDto staffOnJobDetail(Long id);
src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java
@@ -13,19 +13,4 @@
 */
public interface PersonalAttendanceLocationConfigService extends IService<PersonalAttendanceLocationConfig> {
    /**
     * åˆ¤æ–­ç­æ¬¡é…ç½®æ˜¯å¦å·²è¢«æŽ’班且已有打卡记录使用。
     */
    boolean hasAttendanceUsage(Integer configId);
    /**
     * åˆ¤æ–­ç­æ¬¡é…ç½®æ˜¯å¦å…è®¸ç¼–辑。
     */
    boolean canEdit(Integer configId);
    /**
     * åˆ¤æ–­ç­æ¬¡é…ç½®æ˜¯å¦å…è®¸åˆ é™¤ã€‚
     */
    boolean canDelete(Integer configId);
}
src/main/java/com/ruoyi/staff/service/StaffSalaryMainService.java
@@ -3,9 +3,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.staff.dto.CalculateSalaryDto;
import com.ruoyi.staff.dto.StaffSalaryCalculateDto;
import com.ruoyi.staff.pojo.StaffSalaryMain;
import jakarta.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -29,10 +27,4 @@
    AjaxResult delete(List<Long> ids);
    AjaxResult calculateSalary(CalculateSalaryDto calculateSalaryDto);
    AjaxResult calculateSalaryByEmployee(StaffSalaryCalculateDto staffSalaryCalculateDto);
    void exportSalaryDetail(HttpServletResponse response, String salaryMonth);
    AjaxResult getAvailableDepts(String salaryMonth);
}
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java
@@ -1,12 +1,15 @@
package com.ruoyi.staff.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
import com.ruoyi.staff.mapper.PersonalShiftMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,26 +20,6 @@
 * @since 2026-02-11 09:41:34
 */
@Service
@RequiredArgsConstructor
public class PersonalAttendanceLocationConfigServiceImpl extends ServiceImpl<PersonalAttendanceLocationConfigMapper, PersonalAttendanceLocationConfig> implements PersonalAttendanceLocationConfigService {
    private final PersonalShiftMapper personalShiftMapper;
    @Override
    public boolean hasAttendanceUsage(Integer configId) {
        if (configId == null) {
            return false;
        }
        return personalShiftMapper.existsAttendanceUsedByConfig(configId);
    }
    @Override
    public boolean canEdit(Integer configId) {
        return !hasAttendanceUsage(configId);
    }
    @Override
    public boolean canDelete(Integer configId) {
        return !hasAttendanceUsage(configId);
    }
}
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
@@ -72,8 +72,7 @@
        List<PersonalShift> personalShifts = personalShiftMapper.selectList(Wrappers.<PersonalShift>lambdaQuery()
                .eq(PersonalShift::getStaffOnJobId, staffOnJob.getId())
                .eq(PersonalShift::getWorkTime, currentDate.atStartOfDay())
                .isNotNull(PersonalShift::getPersonalAttendanceLocationConfigId)
                .ne(PersonalShift::getPersonalAttendanceLocationConfigId, 999));
                .isNotNull(PersonalShift::getPersonalAttendanceLocationConfigId));
        if (CollectionUtil.isEmpty(personalShifts)){
            throw new BaseException("当前用户当天没有排班数据");
        }
@@ -231,15 +230,12 @@
        List<PersonalShift> personalShifts = personalShiftMapper.selectList(Wrappers.<PersonalShift>lambdaQuery()
                .eq(PersonalShift::getStaffOnJobId, staffOnJob.getId())
                .eq(PersonalShift::getWorkTime, currentDate.atStartOfDay())
                .isNotNull(PersonalShift::getPersonalAttendanceLocationConfigId)
                .ne(PersonalShift::getPersonalAttendanceLocationConfigId, 999));
                .isNotNull(PersonalShift::getPersonalAttendanceLocationConfigId));
        if (CollectionUtil.isNotEmpty(personalShifts)){
            PersonalAttendanceLocationConfig personalAttendanceLocationConfig = personalAttendanceLocationConfigMapper.selectById(personalShifts.get(0).getPersonalAttendanceLocationConfigId());
            if (personalAttendanceLocationConfig != null) {
                resultDto.setStartAt(personalAttendanceLocationConfig.getStartAt());
                resultDto.setEndAt(personalAttendanceLocationConfig.getEndAt());
                resultDto.setShift(personalAttendanceLocationConfig.getShift());
            }
            resultDto.setStartAt(personalAttendanceLocationConfig.getStartAt());
            resultDto.setEndAt(personalAttendanceLocationConfig.getEndAt());
            resultDto.setShift(personalAttendanceLocationConfig.getShift());
        }
        return resultDto;
    }
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
@@ -25,7 +25,6 @@
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -60,7 +59,6 @@
                schemeApplicableStaffLambdaQueryWrapper.like(SchemeApplicableStaff::getTitle, schemeApplicableStaff.getTitle());
            }
        }
        schemeApplicableStaffLambdaQueryWrapper.orderByDesc(SchemeApplicableStaff::getId);
        Page<SchemeApplicableStaff> page1 = schemeApplicableStaffMapper.selectPage(page, schemeApplicableStaffLambdaQueryWrapper);
        List<Long> collect = page1.getRecords().stream().map(SchemeApplicableStaff::getId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(collect)){
@@ -154,85 +152,100 @@
     * @param id
     */
    public void calculateByEmployeeId(Integer id,Map<String, Object> map,String date) {
        if (id == null || map == null) {
            return;
        // 1. å…¥å‚校验
        if (id == null) {
            return; // æˆ–返回空列表,根据业务需求调整
        }
        Long staffId = id.longValue();
        // ç¤¾ä¿é‡‘额
        BigDecimal socialPersonal = new BigDecimal("0.00");
        // å…¬ç§¯é‡‘金额
        BigDecimal fundPersonal = new BigDecimal("0.00");
        // åŸºæœ¬å·¥èµ„
        BigDecimal basicSalary = new BigDecimal("0.00");
        map.put("fundPersonal", fundPersonal); // å…¬ç§¯é‡‘
        map.put("socialPersonal", socialPersonal); // ç¤¾ä¿é‡‘额
        map.put("basicSalary", basicSalary); // åŸºæœ¬å·¥èµ„
        // ä¸ªç¨Žé‡‘额
        BigDecimal salaryTax = new BigDecimal("0.00");
        map.put("salaryTax", salaryTax);
        // è®¡ä»¶å·¥èµ„
        BigDecimal pieceSalary = new BigDecimal("0.00");
        map.put("pieceSalary", pieceSalary);
        // è®¡æ—¶å·¥èµ„
        BigDecimal hourlySalary = new BigDecimal("0.00");
        map.put("hourlySalary", hourlySalary);
        // å…¶ä»–æ”¶å…¥
        BigDecimal otherIncome = new BigDecimal("0.00");
        map.put("otherIncome", otherIncome);
        // å…¶ä»–支出
        BigDecimal otherDeduct = new BigDecimal("0.00");
        map.put("otherDeduct", otherDeduct);
        // åº”发工资
        BigDecimal grossSalary = new BigDecimal("0.00");
        map.put("grossSalary", grossSalary);
        // åº”扣工资
        BigDecimal deductSalary = new BigDecimal("0.00");
        map.put("deductSalary", deductSalary);
        // å®žå‘工资
        BigDecimal netSalary = new BigDecimal("0.00");
        map.put("netSalary", netSalary);
        // è°ƒç”¨åŸºæœ¬å·¥èµ„
        StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId);
        if(staffOnJobDto == null){
            return;
        }
        BigDecimal basicSalary = hasValue(map, "basicSalary")
                ? normalizeMoney(getBigDecimal(map, "basicSalary"))
                : normalizeMoney(staffOnJobDto.getBasicSalary());
        BigDecimal otherIncome = normalizeMoney(getBigDecimal(map, "otherIncome"));
        BigDecimal otherDeduct = normalizeMoney(getBigDecimal(map, "otherDeduct"));
        BigDecimal socialSupplementAmount = hasValue(map, "socialSupplementAmount")
                ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount"))
                : calculateLatestDeptSocialSupplementAmount(staffOnJobDto);
        BigDecimal pieceSalary = normalizeMoney(getBigDecimal(map, "pieceSalary"));
        BigDecimal hourlySalary = normalizeMoney(getBigDecimal(map, "hourlySalary"));
        BigDecimal socialPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        BigDecimal fundPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        basicSalary = staffOnJobDto.getBasicSalary();
        map.put("basicSalary", basicSalary);
        // åº”发工资
        map.put("grossSalary", basicSalary);
        // ä¸ªç¨Žé‡‘额(无社保版)
        BigDecimal bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
        map.put("salaryTax", bigDecimal);
        // è®¡æ—¶å·¥èµ„ è®¡ä»¶å·¥èµ„
        UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto();
        userProductionAccountingDto.setUserId(getUidByStaffId(staffId));
        userProductionAccountingDto.setDate(date);
        UserAccountDto byUserId = salesLedgerProductionAccountingService.getByUserId(userProductionAccountingDto);
        if(byUserId != null){
            pieceSalary = normalizeMoney(byUserId.getAccountBalance());
            hourlySalary = normalizeMoney(byUserId.getAccount());
            map.put("pieceSalary", byUserId.getAccountBalance());
            map.put("hourlySalary", byUserId.getAccount());
            // åº”发 å®žå‘增加
            grossSalary = grossSalary.add(byUserId.getAccountBalance()).add(byUserId.getAccount());
            map.put("grossSalary", grossSalary);
            netSalary = netSalary.add(byUserId.getAccountBalance()).add(byUserId.getAccount());
            map.put("netSalary", netSalary);
        }
        // æŸ¥è¯¢è¯¥äººå‘˜å¯¹åº”的社保方案
        // 2. æŸ¥è¯¢è¯¥äººå‘˜å¯¹åº”的社保方案
        List<SchemeApplicableStaff> schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId);
        if (!CollectionUtils.isEmpty(schemeList)) {
            for (SchemeApplicableStaff scheme : schemeList) {
                List<SchemeInsuranceDetail> detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(scheme.getId());
                if(CollectionUtils.isEmpty(detailList)){
                    continue;
                }
                for (SchemeInsuranceDetail detail : detailList) {
                    if("公积金".equals(detail.getInsuranceType())){
                        fundPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, basicSalary, staffOnJobDto, detail);
                    }else{
                        socialPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, basicSalary, staffOnJobDto, detail);
                    }
        if (CollectionUtils.isEmpty(schemeList)) {
            return; // æ— åŒ¹é…æ–¹æ¡ˆï¼Œè¿”回空列表
        }
        // 3. ä¸ºæ¯ä¸ªæ–¹æ¡ˆå…³è”明细列表
        for (SchemeApplicableStaff scheme : schemeList) {
            List<SchemeInsuranceDetail> detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(scheme.getId());
            // æ ¹æ®æ˜Žç»†åˆ—表计算社保缴费金额
            if(CollectionUtils.isEmpty(detailList)){
                continue;
            }
            for (SchemeInsuranceDetail detail : detailList) {
                if("公积金".equals(detail.getInsuranceType())){
                    fundPersonal = fundPersonal.add(calculateByEmployeeIdType(detail.getInsuranceType(),fundPersonal, staffOnJobDto, detail));
                }else{
                    socialPersonal = socialPersonal.add(calculateByEmployeeIdType(detail.getInsuranceType(),socialPersonal, staffOnJobDto, detail));
                }
            }
        }
        BigDecimal grossSalary = basicSalary.add(pieceSalary).add(hourlySalary).add(otherIncome);
        BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax(
                grossSalary,
                socialPersonal.add(socialSupplementAmount),
                fundPersonal
        );
        BigDecimal deductSalary = salaryTax
                .add(fundPersonal)
                .add(socialPersonal)
                .add(socialSupplementAmount)
                .add(otherDeduct);
        BigDecimal netSalary = grossSalary.subtract(deductSalary);
        if (!hasText(map, "staffName")) {
            map.put("staffName", staffOnJobDto.getStaffName());
        }
        if (!hasText(map, "nation")) {
            map.put("nation", staffOnJobDto.getNation());
        }
        map.put("basicSalary", basicSalary);
        map.put("pieceSalary", pieceSalary);
        map.put("hourlySalary", hourlySalary);
        map.put("otherIncome", otherIncome);
        map.put("socialPersonal", socialPersonal);
        map.put("fundPersonal", fundPersonal);
        map.put("otherDeduct", otherDeduct);
        map.put("socialSupplementAmount", socialSupplementAmount);
        map.put("grossSalary", grossSalary);
        map.put("salaryTax", salaryTax);
        map.put("deductSalary", deductSalary);
        map.put("netSalary", netSalary);
        // ä¸ªç¨Žé‡‘额(社保版)
        bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
        map.put("salaryTax", bigDecimal);
        // åº”扣工资
        map.put("deductSalary", bigDecimal.add(fundPersonal).add(socialPersonal));
        // å®žå‘工资
        map.put("netSalary", basicSalary.subtract(bigDecimal).subtract(fundPersonal).subtract(socialPersonal));
    }
    /**
@@ -263,81 +276,17 @@
     * @param detail
     * @return
     */
    public BigDecimal calculateByEmployeeIdType(String type,BigDecimal bigDecimal, BigDecimal basicSalary, StaffOnJob staffOnJobDto,SchemeInsuranceDetail detail) {
    public BigDecimal calculateByEmployeeIdType(String type,BigDecimal bigDecimal, StaffOnJob staffOnJobDto,SchemeInsuranceDetail detail) {
        // åˆ¤æ–­æ˜¯å¦è°ƒç”¨åŸºæœ¬å·¥èµ„
        if (detail.getUseBasicSalary() == 1) {
            BigDecimal divide = detail.getPaymentBase().multiply(detail.getPersonalRatio()).divide(new BigDecimal("100"), 2);
            bigDecimal = bigDecimal.add(divide);
        }else{
            // è°ƒç”¨å½“前工资表中的基本工资
            BigDecimal multiply = basicSalary.multiply(detail.getPersonalRatio().divide(new BigDecimal("100"), 2));
            // è°ƒç”¨åŸºæœ¬å·¥èµ„
            BigDecimal multiply = staffOnJobDto.getBasicSalary().multiply(detail.getPersonalRatio().divide(new BigDecimal("100"), 2));
            bigDecimal = bigDecimal.add(multiply);
        }
        bigDecimal = bigDecimal.add(detail.getPersonalFixed());
        return bigDecimal;
    }
    private BigDecimal calculateLatestDeptSocialSupplementAmount(StaffOnJob staffOnJobDto) {
        if (staffOnJobDto == null || staffOnJobDto.getSysDeptId() == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        SchemeApplicableStaff latestScheme = schemeApplicableStaffMapper.selectLatestSchemeByDeptId(staffOnJobDto.getSysDeptId());
        if (latestScheme == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        List<SchemeInsuranceDetail> detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(latestScheme.getId());
        if (CollectionUtils.isEmpty(detailList)) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        BigDecimal socialSupplementAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        for (SchemeInsuranceDetail detail : detailList) {
            if (!"公积金".equals(detail.getInsuranceType())) {
                socialSupplementAmount = calculateByEmployeeIdType(
                        detail.getInsuranceType(),
                        socialSupplementAmount,
                        normalizeMoney(staffOnJobDto.getBasicSalary()),
                        staffOnJobDto,
                        detail
                );
            }
        }
        return socialSupplementAmount.setScale(2, RoundingMode.HALF_UP);
    }
    private boolean hasValue(Map<String, Object> map, String key) {
        return map != null && map.get(key) != null;
    }
    private boolean hasText(Map<String, Object> map, String key) {
        if (map == null) {
            return false;
        }
        Object value = map.get(key);
        return value != null && !String.valueOf(value).trim().isEmpty();
    }
    private BigDecimal getBigDecimal(Map<String, Object> map, String key) {
        if (map == null) {
            return null;
        }
        Object value = map.get(key);
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        if (value instanceof Number) {
            return new BigDecimal(String.valueOf(value));
        }
        String text = String.valueOf(value).trim();
        if (text.isEmpty()) {
            return null;
        }
        return new BigDecimal(text);
    }
    private BigDecimal normalizeMoney(BigDecimal value) {
        return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : value.setScale(2, RoundingMode.HALF_UP);
    }
}
src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
@@ -59,7 +59,6 @@
        StaffLeave staffLeave = new StaffLeave();
        staffLeave.setStaffOnJobId(staffLeaveDto.getStaffOnJobId());
        staffLeave.setReason(staffLeaveDto.getReason());
        staffLeave.setLeaveDate(staffLeaveDto.getLeaveDate());
        String reason = staffLeaveDto.getReason();
        if (StaffLeaveReasonOther.getCode().equals(reason)){
            staffLeave.setRemark(staffLeaveDto.getRemark());
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -7,7 +7,6 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.dto.WordDateDto;
import com.ruoyi.project.system.domain.SysDept;
@@ -68,9 +67,8 @@
    //在职员工台账分页查询
    @Override
    public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) {
        IPage<StaffOnJobDto> staffOnJobDtoIPage = staffOnJobMapper.staffOnJobListPage(page, staffOnJob);
        return staffOnJobDtoIPage;
    public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob) {
        return staffOnJobMapper.staffOnJobListPage(page,staffOnJob);
    }
    //新增入职
@@ -85,7 +83,6 @@
        }
        // åˆ›å»ºå…¥èŒæ•°æ®
        syncStudyInfoFromEducation(staffOnJobPrams);
        staffOnJobPrams.setContractExpireTime(staffOnJobPrams.getContractEndTime());
        staffOnJobPrams.setStaffState(1);
        staffOnJobMapper.insert(staffOnJobPrams);
@@ -144,7 +141,6 @@
        // ç»‘定子表数据
        bingingStaffOnJobExtra(id,staffOnJobParams);
        // æ›´æ–°å‘˜å·¥æ•°æ®
        syncStudyInfoFromEducation(staffOnJobParams);
        staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
        return staffOnJobMapper.updateById(staffOnJobParams);
    }
@@ -162,7 +158,6 @@
                    .forEach(staff -> staff.setStaffOnJobId(id)); // èµ‹å€¼
            staffEducationService.saveBatch(staffOnJobPrams.getStaffEducationList());
        }
        // æ–°å¢žå·¥ä½œç»åކ
        if(CollectionUtils.isNotEmpty(staffOnJobPrams.getStaffWorkExperienceList())){
            staffOnJobPrams.getStaffWorkExperienceList().stream()
@@ -177,28 +172,6 @@
                    .forEach(staff -> staff.setStaffOnJobId(id)); // èµ‹å€¼
            staffEmergencyContactServiceImpl.saveBatch(staffOnJobPrams.getStaffEmergencyContactList());
        }
    }
    private void syncStudyInfoFromEducation(StaffOnJob staffOnJobPrams) {
        if (staffOnJobPrams == null || CollectionUtils.isEmpty(staffOnJobPrams.getStaffEducationList())) {
            if (staffOnJobPrams != null) {
                staffOnJobPrams.setFirstStudy("/");
                staffOnJobPrams.setProfession("/");
            }
            return;
        }
        Optional<StaffEducation> matchedEducation = staffOnJobPrams.getStaffEducationList().stream()
                .filter(Objects::nonNull)
                .filter(education -> StringUtils.isNotEmpty(education.getMajor()))
                .findFirst();
        if (matchedEducation.isPresent()) {
            StaffEducation education = matchedEducation.get();
            staffOnJobPrams.setFirstStudy(education.getEducation());
            staffOnJobPrams.setProfession(education.getMajor());
            return;
        }
        staffOnJobPrams.setFirstStudy("/");
        staffOnJobPrams.setProfession("/");
    }
src/main/java/com/ruoyi/staff/service/impl/StaffSalaryMainServiceImpl.java
@@ -4,15 +4,9 @@
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.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserDeptMapper;
import com.ruoyi.staff.excel.StaffSalaryExportData;
import jakarta.servlet.http.HttpServletResponse;
import com.ruoyi.staff.dto.CalculateSalaryDto;
import com.ruoyi.staff.dto.StaffSalaryCalculateDto;
import com.ruoyi.staff.mapper.StaffLeaveMapper;
import com.ruoyi.staff.mapper.StaffSalaryDetailMapper;
import com.ruoyi.staff.mapper.StaffSalaryMainMapper;
@@ -26,12 +20,8 @@
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * <p>
@@ -51,7 +41,6 @@
    private final SchemeApplicableStaffServiceImpl schemeApplicableStaffService;
    private final SysUserDeptMapper sysUserDeptMapper;
    private final StaffLeaveMapper staffLeaveMapper;
    private final SysDeptMapper sysDeptMapper;
    @Override
    public AjaxResult listPage(Page page, StaffSalaryMain staffSalaryMain) {
@@ -67,7 +56,6 @@
                staffSalaryMainLambdaQueryWrapper.eq(StaffSalaryMain::getStatus, staffSalaryMain.getStatus());
            }
        }
        staffSalaryMainLambdaQueryWrapper.orderByDesc(StaffSalaryMain::getId);
        Page<StaffSalaryMain> page1 = staffSalaryMainMapper.selectPage(page, staffSalaryMainLambdaQueryWrapper);
        page1.getRecords().forEach(main -> {
            List<StaffSalaryDetail> staffSalaryDetailList = staffSalaryDetailMapper.selectList(new LambdaQueryWrapper<StaffSalaryDetail>().eq(StaffSalaryDetail::getMainId, main.getId()));
@@ -78,26 +66,6 @@
    @Override
    public AjaxResult add(StaffSalaryMain staffSalaryMain) {
        if (staffSalaryMain == null || StringUtils.isEmpty(staffSalaryMain.getSalaryMonth())) {
            return AjaxResult.error("参数错误");
        }
        Integer status = staffSalaryMain.getStatus();
        if (status != null && status == 5) {
            String salaryMonth = staffSalaryMain.getSalaryMonth();
            String deptIds = staffSalaryMain.getDeptIds();
            List<StaffSalaryMain> issuedList = staffSalaryMainMapper.selectList(new LambdaQueryWrapper<StaffSalaryMain>()
                    .eq(StaffSalaryMain::getSalaryMonth, salaryMonth)
                    .eq(StaffSalaryMain::getStatus, 5));
            for (StaffSalaryMain issued : issuedList) {
                if (hasOverlappingDept(deptIds, issued.getDeptIds())) {
                    return AjaxResult.error("同一月份同一部门工资不可以发放多次");
                }
            }
        }
        staffSalaryMainMapper.insert(staffSalaryMain);
        staffSalaryMain.getStaffSalaryDetailList().forEach(detail -> {
            detail.setMainId(staffSalaryMain.getId());
@@ -115,30 +83,6 @@
        if(staffSalaryMain1 == null){
            return AjaxResult.error("参数错误");
        }
        Integer newStatus = staffSalaryMain.getStatus();
        if (newStatus != null && newStatus == 5) {
            String salaryMonth = staffSalaryMain.getSalaryMonth();
            if (StringUtils.isEmpty(salaryMonth)) {
                salaryMonth = staffSalaryMain1.getSalaryMonth();
            }
            String deptIds = staffSalaryMain.getDeptIds();
            if (StringUtils.isEmpty(deptIds)) {
                deptIds = staffSalaryMain1.getDeptIds();
            }
            List<StaffSalaryMain> issuedList = staffSalaryMainMapper.selectList(new LambdaQueryWrapper<StaffSalaryMain>()
                    .eq(StaffSalaryMain::getSalaryMonth, salaryMonth)
                    .eq(StaffSalaryMain::getStatus, 5)
                    .ne(StaffSalaryMain::getId, staffSalaryMain.getId()));
            for (StaffSalaryMain issued : issuedList) {
                if (hasOverlappingDept(deptIds, issued.getDeptIds())) {
                    return AjaxResult.error("同一月份同一部门工资不可以发放多次");
                }
            }
        }
        // å¾…审核不可编辑
//        if(staffSalaryMain1.getStatus() > 3){
//            return AjaxResult.error("待审核不可编辑");
@@ -190,138 +134,12 @@
        return AjaxResult.success(mapList);
    }
    @Override
    public AjaxResult calculateSalaryByEmployee(StaffSalaryCalculateDto staffSalaryCalculateDto) {
        if (staffSalaryCalculateDto == null
                || staffSalaryCalculateDto.getStaffOnJobId() == null
                || StringUtils.isEmpty(staffSalaryCalculateDto.getSalaryMonth())) {
            return AjaxResult.error("参数错误");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("id", staffSalaryCalculateDto.getStaffOnJobId());
        map.put("staffOnJobId", staffSalaryCalculateDto.getStaffOnJobId());
        map.put("salaryMonth", staffSalaryCalculateDto.getSalaryMonth());
        map.put("staffName", staffSalaryCalculateDto.getStaffName());
        map.put("postName", staffSalaryCalculateDto.getPostName());
        map.put("deptName", staffSalaryCalculateDto.getDeptName());
        if (staffSalaryCalculateDto.getBasicSalary() != null) {
            map.put("basicSalary", staffSalaryCalculateDto.getBasicSalary());
        }
        map.put("pieceSalary", staffSalaryCalculateDto.getPieceSalary());
        map.put("hourlySalary", staffSalaryCalculateDto.getHourlySalary());
        map.put("otherIncome", staffSalaryCalculateDto.getOtherIncome());
        map.put("socialPersonal", staffSalaryCalculateDto.getSocialPersonal());
        map.put("fundPersonal", staffSalaryCalculateDto.getFundPersonal());
        map.put("otherDeduct", staffSalaryCalculateDto.getOtherDeduct());
        map.put("socialSupplementAmount", staffSalaryCalculateDto.getSocialSupplementAmount());
        map.put("salaryTax", staffSalaryCalculateDto.getSalaryTax());
        map.put("grossSalary", staffSalaryCalculateDto.getGrossSalary());
        map.put("deductSalary", staffSalaryCalculateDto.getDeductSalary());
        map.put("netSalary", staffSalaryCalculateDto.getNetSalary());
        map.put("remark", staffSalaryCalculateDto.getRemark());
        schemeApplicableStaffService.calculateByEmployeeId(
                staffSalaryCalculateDto.getStaffOnJobId().intValue(),
                map,
                staffSalaryCalculateDto.getSalaryMonth()
        );
        return AjaxResult.success(map);
    }
    @Override
    public void exportSalaryDetail(HttpServletResponse response, String salaryMonth) {
        LambdaQueryWrapper<StaffSalaryMain> mainWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(salaryMonth)) {
            mainWrapper.eq(StaffSalaryMain::getSalaryMonth, salaryMonth);
        }
        List<StaffSalaryMain> mainList = staffSalaryMainMapper.selectList(mainWrapper);
        List<StaffSalaryExportData> exportDataList = new ArrayList<>();
        for (StaffSalaryMain main : mainList) {
            List<StaffSalaryDetail> detailList = staffSalaryDetailMapper.selectList(
                    new LambdaQueryWrapper<StaffSalaryDetail>().eq(StaffSalaryDetail::getMainId, main.getId())
            );
            for (StaffSalaryDetail detail : detailList) {
                StaffSalaryExportData exportData = new StaffSalaryExportData();
                exportData.setSalaryMonth(main.getSalaryMonth());
                exportData.setSalaryTitle(main.getSalaryTitle());
                exportData.setStaffName(detail.getStaffName());
                exportData.setDeptName(detail.getDeptName());
                exportData.setBasicSalary(detail.getBasicSalary());
                exportData.setPieceSalary(detail.getPieceSalary());
                exportData.setHourlySalary(detail.getHourlySalary());
                exportData.setOtherIncome(detail.getOtherIncome());
                exportData.setSocialPersonal(detail.getSocialPersonal());
                exportData.setFundPersonal(detail.getFundPersonal());
                exportData.setOtherDeduct(detail.getOtherDeduct());
                exportData.setSocialSupplementAmount(detail.getSocialSupplementAmount());
                exportData.setSalaryTax(detail.getSalaryTax());
                exportData.setGrossSalary(detail.getGrossSalary());
                exportData.setDeductSalary(detail.getDeductSalary());
                exportData.setNetSalary(detail.getNetSalary());
                exportData.setRemark(detail.getRemark());
                exportDataList.add(exportData);
            }
        }
        ExcelUtil<StaffSalaryExportData> util = new ExcelUtil<>(StaffSalaryExportData.class);
        util.exportExcel(response, exportDataList, "员工薪资明细");
    }
    public List<Map<String, Object>> setSchemeApplicableStaffUserInfo(List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return new ArrayList<>();
        }
        // é€šè¿‡éƒ¨é—¨èŽ·å–äººå‘˜id
        return sysUserDeptMapper.setSchemeApplicableStaffUserInfo(ids);
    }
    @Override
    public AjaxResult getAvailableDepts(String salaryMonth) {
        if (StringUtils.isEmpty(salaryMonth)) {
            return AjaxResult.error("工资月份不能为空");
        }
        List<String> issuedDeptIdStrings = staffSalaryMainMapper.selectIssuedDeptIds(salaryMonth);
        Set<Long> issuedDeptIds = new HashSet<>();
        for (String deptIds : issuedDeptIdStrings) {
            if (StringUtils.isNotEmpty(deptIds)) {
                String[] ids = deptIds.split(",");
                for (String id : ids) {
                    issuedDeptIds.add(Long.parseLong(id.trim()));
                }
            }
        }
        List<SysDept> allDepts = sysDeptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getStatus, "0")
                .eq(SysDept::getDelFlag, "0"));
        List<SysDept> availableDepts = new ArrayList<>();
        for (SysDept dept : allDepts) {
            if (!issuedDeptIds.contains(dept.getDeptId())) {
                availableDepts.add(dept);
            }
        }
        return AjaxResult.success(availableDepts);
    }
    private boolean hasOverlappingDept(String deptIds1, String deptIds2) {
        if (StringUtils.isEmpty(deptIds1) || StringUtils.isEmpty(deptIds2)) {
            return false;
        }
        Set<String> deptSet1 = new HashSet<>(Arrays.asList(deptIds1.split(",")));
        String[] deptArray2 = deptIds2.split(",");
        for (String deptId : deptArray2) {
            if (deptSet1.contains(deptId.trim())) {
                return true;
            }
        }
        return false;
    }
}
src/main/java/com/ruoyi/stock/controller/StockOutRecordController.java
@@ -87,15 +87,4 @@
        return AjaxResult.success();
    }
    @PostMapping("/reAudit")
    @Log(title = "出库管理-反审出库", businessType = BusinessType.UPDATE)
    @Operation(summary = "批量反审出库记录")
    public AjaxResult reAudit(@RequestBody StockOutRecordDto approveDto) {
        if(CollectionUtils.isEmpty(approveDto.getIds())){
            return AjaxResult.error("请选择至少一条数据");
        }
        stockOutRecordService.batchReAudit(approveDto.getIds());
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/stock/controller/StockUninventoryController.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
@@ -36,6 +38,7 @@
    @PostMapping("/addstockUninventory")
    @Operation(summary = "新增库存")
    @Log(title = "新增不合格库存", businessType = BusinessType.INSERT)
    public R addstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()));
        stockUninventoryDto.setRecordId(0L);
@@ -45,6 +48,7 @@
    @PostMapping("/subtractstockUninventory")
    @Operation(summary = "扣减库存")
    @Log(title = "扣减不合格库存", businessType = BusinessType.UPDATE)
    public R subtractstockUninventory(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockOutQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode()));
        stockUninventoryDto.setRecordId(0L);
@@ -53,6 +57,7 @@
    @PostMapping("/addStockInRecordOnly")
    @Operation(summary = "新增入库记录(仅创建记录,不调整库存)")
    @Log(title = "新增不合格库存入库记录", businessType = BusinessType.INSERT)
    public R addStockInRecordOnly(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()));
        stockUninventoryDto.setRecordId(0L);
@@ -61,6 +66,7 @@
    @PostMapping("/addStockOutRecordOnly")
    @Operation(summary = "新增出库记录(仅创建记录,不调整库存)")
    @Log(title = "新增不合格库存出库记录", businessType = BusinessType.INSERT)
    public R addStockOutRecordOnly(@RequestBody StockUninventoryDto stockUninventoryDto) {
        stockUninventoryDto.setRecordType(String.valueOf(StockOutQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_OUT.getCode()));
        stockUninventoryDto.setRecordId(0L);
@@ -69,6 +75,7 @@
    @PostMapping("/exportStockUninventory")
    @Operation(summary = "导出库存")
    @Log(title = "导出不合格库存", businessType = BusinessType.EXPORT)
    public void exportStockUninventory(HttpServletResponse response, StockUninventoryDto stockUninventoryDto) {
        stockUninventoryService.exportStockUninventory(response,stockUninventoryDto);
    }
@@ -76,12 +83,14 @@
    @PostMapping("/frozenStock")
    @Operation(summary = "冻结库存")
    @Log(title = "冻结不合格库存", businessType = BusinessType.UPDATE)
    public R frozenStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockUninventoryService.frozenStock(stockInventoryDto));
    }
    @PostMapping("/thawStock")
    @Operation(summary = "解冻库存")
    @Log(title = "解冻不合格库存", businessType = BusinessType.UPDATE)
    public R thawStock(@RequestBody StockInventoryDto stockInventoryDto) {
        return R.ok(stockUninventoryService.thawStock(stockInventoryDto));
    }
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -7,14 +7,17 @@
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("stock_in_record")
@Schema(name = "入库管理")
public class StockInRecord {
public class StockInRecord implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * åºå·
@@ -44,12 +47,14 @@
    private String remark;
    @Schema(description = "预警数量")
    @TableField(exist = false)
    private BigDecimal warnNum;
    @Schema(description = "类型  0合格入库 1不合格入库")
    private String type;
    @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;
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
@@ -53,6 +53,7 @@
    private String remark;
    @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;
src/main/java/com/ruoyi/stock/service/StockInRecordService.java
@@ -12,7 +12,7 @@
public interface StockInRecordService extends IService<StockInRecord> {
    IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto);
    int add(StockInRecordDto stockInRecordDto);
    StockInRecord add(StockInRecordDto stockInRecordDto);
    int update(Long id, StockInRecordDto stockInRecordDto);
src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -6,6 +6,7 @@
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.pojo.StockInventory;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
@@ -32,7 +33,7 @@
    Boolean addStockInRecordOnly(StockInventoryDto stockInventoryDto);
    Boolean addStockOutRecordOnly(StockInventoryDto stockInventoryDto);
    StockOutRecordDto addStockOutRecordOnly(StockInventoryDto stockInventoryDto);
    R importStockInventory(MultipartFile file);
src/main/java/com/ruoyi/stock/service/StockOutRecordService.java
@@ -20,7 +20,7 @@
public interface StockOutRecordService extends IService<StockOutRecord> {
    IPage<StockOutRecordDto> listPage(Page page, StockOutRecordDto stockOutRecordDto);
    int add(StockOutRecordDto stockOutRecordDto);
    StockOutRecordDto add(StockOutRecordDto stockOutRecordDto);
    int update(Long id, StockOutRecordDto stockOutRecordDto);
@@ -31,6 +31,4 @@
    void exportStockOutRecord(HttpServletResponse response, StockOutRecordDto stockOutRecordDto);
    int batchApprove(List<Long> ids, Integer approvalStatus);
    int batchReAudit(List<Long> ids);
}
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -13,22 +13,12 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import java.time.LocalDateTime;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockInRecordExportData;
import com.ruoyi.production.mapper.ProductionOrderPickMapper;
import com.ruoyi.production.pojo.ProductionOrderPick;
import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
import com.ruoyi.procurementrecord.mapper.ReturnSaleProductMapper;
import com.ruoyi.procurementrecord.pojo.ReturnManagement;
import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
@@ -53,10 +43,6 @@
    private StockInventoryMapper stockInventoryMapper;
    private StockUninventoryMapper stockUninventoryMapper;
    private ProductionOrderPickMapper productionOrderPickMapper;
    private ReturnSaleProductMapper returnSaleProductMapper;
    private ReturnManagementMapper returnManagementMapper;
    private ShippingInfoMapper shippingInfoMapper;
    private SalesLedgerMapper salesLedgerMapper;
    @Override
    public IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto) {
@@ -66,17 +52,13 @@
    // æ–°å¢žå…¥åº“
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(StockInRecordDto stockInRecordDto) {
        LocalDateTime createTime = stockInRecordDto.getCreateTime();
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK","inbound_batches", createTime);
    public StockInRecord add(StockInRecordDto stockInRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK","inbound_batches");
        stockInRecordDto.setInboundBatches(no);
        stockInRecordDto.setCreateTime(createTime);
        StockInRecord stockInRecord = new StockInRecord();
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord);
        return stockInRecordMapper.insert(stockInRecord);
        stockInRecordMapper.insert(stockInRecord);
        return stockInRecord;
    }
    @Override
@@ -88,9 +70,56 @@
            throw new BaseException("该入库记录不存在,无法更新!!!");
        }
        // è®°å½•修改前的 batch_no
        String oldBatchNo = stockInRecord.getBatchNo();
        String newBatchNo = stockInRecordDto.getBatchNo();
        String[] ignoreProperties = {"id", "inbound_batches"};//排除id属性
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord, ignoreProperties);
        return stockInRecordMapper.updateById(stockInRecord);
        int result = stockInRecordMapper.updateById(stockInRecord);
        // å¦‚æžœ batch_no å‘生变化,需要同步更新关联表
        if (newBatchNo != null && !newBatchNo.equals(oldBatchNo)) {
            updateRelatedBatchNo(stockInRecord, oldBatchNo, newBatchNo);
        }
        return result;
    }
    /**
     * åŒæ­¥æ›´æ–°å…³è”表的 batch_no
     * @param stockInRecord å…¥åº“记录
     * @param oldBatchNo ä¿®æ”¹å‰çš„æ‰¹å·
     * @param newBatchNo ä¿®æ”¹åŽçš„æ‰¹å·
     */
    private void updateRelatedBatchNo(StockInRecord stockInRecord, String oldBatchNo, String newBatchNo) {
        // 1. æ›´æ–° stock_inventory è¡¨ï¼ˆåˆæ ¼åº“存)
        LambdaQueryWrapper<StockInventory> inventoryEq = new LambdaQueryWrapper<StockInventory>()
                .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId());
        if (StringUtils.isEmpty(oldBatchNo)) {
            inventoryEq.isNull(StockInventory::getBatchNo);
        } else {
            inventoryEq.eq(StockInventory::getBatchNo, oldBatchNo);
        }
        StockInventory stockInventory = stockInventoryMapper.selectOne(inventoryEq);
        if (stockInventory != null) {
            stockInventory.setBatchNo(newBatchNo);
            stockInventoryMapper.updateById(stockInventory);
        }
        // 2. æ›´æ–° stock_uninventory è¡¨ï¼ˆä¸åˆæ ¼åº“存)
        LambdaQueryWrapper<StockUninventory> uninventoryEq = new LambdaQueryWrapper<StockUninventory>()
                .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
        if (StringUtils.isEmpty(oldBatchNo)) {
            uninventoryEq.isNull(StockUninventory::getBatchNo);
        } else {
            uninventoryEq.eq(StockUninventory::getBatchNo, oldBatchNo);
        }
        StockUninventory stockUninventory = stockUninventoryMapper.selectOne(uninventoryEq);
        if (stockUninventory != null) {
            stockUninventory.setBatchNo(newBatchNo);
            stockUninventoryMapper.updateById(stockUninventory);
        }
    }
    @Override
@@ -235,9 +264,9 @@
            if (stockInRecord == null) {
                throw new BaseException("入库记录不存在,无法审批!!!");
            }
            if (stockInRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockInRecord.getApprovalStatus())) {
            /*if (stockInRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有待审批状态的记录才能审批,入库批次:" + stockInRecord.getInboundBatches());
            }
            }*/
            stockInRecord.setApprovalStatus(approvalStatus);
            stockInRecordMapper.updateById(stockInRecord);
@@ -257,19 +286,16 @@
                    stockInventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockInventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryDto.setRemark(stockInRecord.getRemark());
                    stockInventoryDto.setWarnNum(stockInRecord.getWarnNum());
                    if (stockInventory == null) {
                        try {
                            stockInventoryMapper.insert(new StockInventory() {{
                                setProductModelId(stockInRecord.getProductModelId());
                                setQualitity(stockInRecord.getStockInNum());
                                setBatchNo(stockInRecord.getBatchNo());
                                setRemark(stockInRecord.getRemark());
                                setWarnNum(stockInRecord.getWarnNum());
                                setVersion(1);
                            }});
                        } catch (org.springframework.dao.DuplicateKeyException e) {
                            stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
                        }
                        stockInventoryMapper.insert(new StockInventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
                            setQualitity(stockInRecord.getStockInNum());
                            setWarnNum(stockInRecord.getWarnNum());
                            setBatchNo(stockInRecord.getBatchNo());
                            setRemark(stockInRecord.getRemark());
                            setVersion(1);
                        }});
                    } else {
                        stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
                    }
@@ -282,23 +308,17 @@
                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockUninventoryDto.setRemark(stockInRecord.getRemark());
                    if (stockUninventory == null) {
                        try {
                            stockUninventoryMapper.insert(new StockUninventory() {{
                                setProductModelId(stockInRecord.getProductModelId());
                                setQualitity(stockInRecord.getStockInNum());
                                setBatchNo(stockInRecord.getBatchNo());
                                setRemark(stockInRecord.getRemark());
                                setVersion(1);
                            }});
                        } catch (org.springframework.dao.DuplicateKeyException e) {
                            stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                        }
                        stockUninventoryMapper.insert(new StockUninventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
                            setQualitity(stockInRecord.getStockInNum());
                            setBatchNo(stockInRecord.getBatchNo());
                            setRemark(stockInRecord.getRemark());
                            setVersion(1);
                        }});
                    } else {
                        stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                    }
                }
                // é”€å”®é€€è´§å…¥åº“:扣减销售台账实际合同金额(退货生效)
                handleSalesReturnRefund(stockInRecord);
            }
        }
        return ids.size();
@@ -348,48 +368,5 @@
        }
        return ids.size();
    }
    /**
     * é”€å”®é€€è´§å…¥åº“审批通过时,扣减对应销售台账的实际合同金额。
     * å…³è”链:stock_in_record.record_id = return_sale_product.id
     *        â†’ return_management.shipping_id = shipping_info.id
     *        â†’ shipping_info.sales_ledger_id = sales_ledger.id
     * åªå¤„理 record_type ä¸º 14(合格)或 15(不合格)的销售退货入库。
     */
    private void handleSalesReturnRefund(StockInRecord stockInRecord) {
        String recordType = stockInRecord.getRecordType();
        if (!StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode().equals(recordType)
                && !StockInQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode().equals(recordType)) {
            return;
        }
        if (stockInRecord.getRecordId() == null) {
            return;
        }
        ReturnSaleProduct rsp = returnSaleProductMapper.selectById(stockInRecord.getRecordId());
        if (rsp == null || rsp.getReturnManagementId() == null) {
            return;
        }
        ReturnManagement rm = returnManagementMapper.selectById(rsp.getReturnManagementId());
        if (rm == null || rm.getShippingId() == null) {
            return;
        }
        ShippingInfo shippingInfo = shippingInfoMapper.selectById(rm.getShippingId());
        if (shippingInfo == null || shippingInfo.getSalesLedgerId() == null) {
            return;
        }
        SalesLedger salesLedger = salesLedgerMapper.selectById(shippingInfo.getSalesLedgerId());
        if (salesLedger == null || salesLedger.getContractAmount() == null) {
            return;
        }
        BigDecimal refund = rsp.getAmount() != null ? rsp.getAmount() : BigDecimal.ZERO;
        if (refund.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        BigDecimal baseAmount = salesLedger.getNetContractAmount() != null
                ? salesLedger.getNetContractAmount()
                : salesLedger.getContractAmount();
        salesLedger.setNetContractAmount(baseAmount.subtract(refund));
        salesLedgerMapper.updateById(salesLedger);
    }
}
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -37,10 +37,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
 * <p>
@@ -105,11 +102,7 @@
            newStockInventory.setBatchNo(stockInventoryDto.getBatchNo());
            newStockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
            newStockInventory.setWarnNum(stockInventoryDto.getWarnNum());
            try {
                stockInventoryMapper.insert(newStockInventory);
            } catch (org.springframework.dao.DuplicateKeyException e) {
                stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
            }
            stockInventoryMapper.insert(newStockInventory);
        } else {
            stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
        }
@@ -170,10 +163,13 @@
        stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
        stockInRecordDto.setBatchNo(batchNo);
        stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
        stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
        stockInRecordDto.setType("0");
        stockInRecordDto.setRemark(stockInventoryDto.getRemark());
        stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
        stockInRecordService.add(stockInRecordDto);
        StockInRecord stockInRecord = stockInRecordService.add(stockInRecordDto);
        stockInRecordService.batchApprove(Collections.singletonList(stockInRecord.getId()),1);
        return true;
    }
@@ -255,7 +251,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addStockOutRecordOnly(StockInventoryDto stockInventoryDto) {
    public StockOutRecordDto addStockOutRecordOnly(StockInventoryDto stockInventoryDto) {
        LambdaQueryWrapper<StockInventory> eq = new LambdaQueryWrapper<>();
        eq.eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId());
        if (StringUtils.isEmpty(stockInventoryDto.getBatchNo())) {
@@ -291,8 +287,8 @@
        stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
        stockOutRecordDto.setType("0");
        stockOutRecordDto.setRemark(stockInventoryDto.getRemark());
        stockOutRecordService.add(stockOutRecordDto);
        return true;
        StockOutRecordDto add = stockOutRecordService.add(stockOutRecordDto);
        return add;
    }
    @Override
src/main/java/com/ruoyi/stock/service/impl/StockOutRecordServiceImpl.java
@@ -15,8 +15,6 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import java.time.LocalDateTime;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
@@ -59,18 +57,14 @@
    }
    @Override
    public int add(StockOutRecordDto stockOutRecordDto) {
        LocalDateTime createTime = stockOutRecordDto.getCreateTime();
        if (createTime == null) {
            createTime = LocalDateTime.now();
        }
        String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches", createTime);
    public StockOutRecordDto add(StockOutRecordDto stockOutRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockOutRecordMapper, "CK","outbound_batches");
        stockOutRecordDto.setOutboundBatches(no);
        stockOutRecordDto.setCreateTime(createTime);
        if (StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode().equals(stockOutRecordDto.getRecordType())){
            stockOutRecordDto.setApprovalStatus(3);
        }
        return stockOutRecordMapper.insert(stockOutRecordDto);
        stockOutRecordMapper.insert(stockOutRecordDto);
        return stockOutRecordDto;
    }
    @Override
@@ -180,9 +174,9 @@
            if (stockOutRecord == null) {
                throw new BaseException("出库记录不存在,无法审批!!!");
            }
            if (stockOutRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockOutRecord.getApprovalStatus())) {
                throw new BaseException("只有待审批状态的记录才能审批,出库批次:" + stockOutRecord.getOutboundBatches());
            }
            //if (stockOutRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockOutRecord.getApprovalStatus())) {
            //    throw new BaseException("只有待审批状态的记录才能审批,出库批次:" + stockOutRecord.getOutboundBatches());
            //}
            stockOutRecord.setApprovalStatus(approvalStatus);
            stockOutRecordMapper.updateById(stockOutRecord);
            // å®¡æ‰¹é€šè¿‡æ—¶ï¼Œæ‰£å‡åº“å­˜
@@ -211,26 +205,6 @@
                    stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
                }
            }
        }
        return ids.size();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchReAudit(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BaseException("请选择至少一条数据");
        }
        for (Long id : ids) {
            StockOutRecord stockOutRecord = stockOutRecordMapper.selectById(id);
            if (stockOutRecord == null) {
                throw new BaseException("出库记录不存在,无法重新审核!!!");
            }
            if (!ReviewStatusEnum.REJECTED.getCode().equals(stockOutRecord.getApprovalStatus())) {
                throw new BaseException("只有驳回状态的记录才能重新审核,出库批次:" + stockOutRecord.getOutboundBatches());
            }
            stockOutRecord.setApprovalStatus(ReviewStatusEnum.PENDING_REVIEW.getCode());
            stockOutRecordMapper.updateById(stockOutRecord);
        }
        return ids.size();
    }
src/main/java/com/ruoyi/technology/controller/TechnologyBomStructureController.java
@@ -1,5 +1,7 @@
package com.ruoyi.technology.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.technology.bean.dto.TechnologyBomStructureDto;
import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo;
@@ -21,6 +23,7 @@
    @PostMapping
    @Operation(summary = "新增或修改BOM结构")
    @Log(title = "新增或修改BOM结构", businessType = BusinessType.INSERT)
    public R addOrUpdate(@RequestBody TechnologyBomStructureDto technologyBomStructureDto) {
        return R.ok(technologyBomStructureService.addTechnologyBomStructure(technologyBomStructureDto));
    }
src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java
@@ -31,21 +31,21 @@
    }
    @PostMapping("/add")
    @Log(title = "工序新增", businessType = BusinessType.INSERT)
    @Log(title = "新增工序", businessType = BusinessType.INSERT)
    @Operation(summary = "新增工序")
    public R add(@RequestBody TechnologyOperationDto technologyOperationDto) {
        return technologyOperationService.add(technologyOperationDto);
    }
    @PutMapping("/update")
    @Log(title = "工序更新", businessType = BusinessType.UPDATE)
    @Log(title = "修改工序", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改工序")
    public R update(@RequestBody com.ruoyi.technology.pojo.TechnologyOperation technologyOperation) {
        return technologyOperationService.update(technologyOperation);
        return R.ok(technologyOperationService.updateById(technologyOperation));
    }
    @DeleteMapping("/batchDelete")
    @Log(title = "删除工序", businessType = BusinessType.DELETE)
    @Log(title = "批量删除工序", businessType = BusinessType.DELETE)
    @Operation(summary = "批量删除工序")
    public R batchDelete(@RequestBody List<Long> ids) {
        return R.ok(technologyOperationService.batchDelete(ids));
src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java
@@ -36,7 +36,7 @@
    }
    @DeleteMapping("/batchDelete/{id}")
    @Log(title = "删除工序参数", businessType = BusinessType.DELETE)
    @Log(title = "Delete technology operation param", businessType = BusinessType.DELETE)
    @Operation(summary = "删除工序参数")
    public AjaxResult batchDelete(@PathVariable("id") Long id) {
        return AjaxResult.success(technologyOperationParamService.batchDelete(id));
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.technology.bean.dto.TechnologyRoutingDto;
import com.ruoyi.technology.bean.vo.TechnologyRoutingVo;
@@ -43,8 +45,9 @@
    /**
     * æ–°å¢žå·¥è‰ºè·¯çº¿ã€‚
     */
    @PostMapping("/addTechRoute")
    @Log(title = "工艺路线", businessType = BusinessType.INSERT)
    @Operation(summary = "新增工艺路线")
    @PostMapping("/addTechRoute")
    public R add(@RequestBody TechnologyRouting technologyRouting) {
        return R.ok(technologyRoutingService.saveTechnologyRouting(technologyRouting));
    }
@@ -52,8 +55,9 @@
    /**
     * ä¿®æ”¹å·¥è‰ºè·¯çº¿ã€‚
     */
    @PutMapping("/editTechRoute")
    @Log(title = "工艺路线", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改工艺路线")
    @PutMapping("/editTechRoute")
    public R edit(@RequestBody TechnologyRouting technologyRouting) {
        return R.ok(technologyRoutingService.updateTechnologyRouting(technologyRouting));
    }
@@ -61,8 +65,9 @@
    /**
     * æ‰¹é‡åˆ é™¤å·¥è‰ºè·¯çº¿ã€‚
     */
    @DeleteMapping("/delete")
    @Log(title = "工艺路线", businessType = BusinessType.DELETE)
    @Operation(summary = "删除工艺路线")
    @DeleteMapping("/delete")
    public R remove(@RequestBody List<Long> ids) {
        return R.ok(technologyRoutingService.removeTechnologyRouting(ids));
    }
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingOperationController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.technology.bean.dto.TechnologyRoutingOperationDto;
import com.ruoyi.technology.bean.vo.TechnologyRoutingOperationVo;
@@ -41,26 +43,30 @@
        return R.ok(technologyRoutingOperationService.getTechnologyRoutingOperationInfo(id));
    }
    @PostMapping("/add")
    @Log(title = "工艺路线工序", businessType = BusinessType.INSERT)
    @Operation(summary = "新增工艺路线工序")
    @PostMapping("/add")
    public R add(@RequestBody TechnologyRoutingOperation technologyRoutingOperation) {
        return R.ok(technologyRoutingOperationService.saveTechnologyRoutingOperation(technologyRoutingOperation));
    }
    @PutMapping
    @Log(title = "工艺路线工序", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改工艺路线工序")
    @PutMapping
    public R edit(@RequestBody TechnologyRoutingOperation technologyRoutingOperation) {
        return R.ok(technologyRoutingOperationService.updateById(technologyRoutingOperation));
    }
    @DeleteMapping("/{id}")
    @Log(title = "工艺路线工序", businessType = BusinessType.DELETE)
    @Operation(summary = "删除工艺路线工序")
    @DeleteMapping("/{id}")
    public R remove(@PathVariable("id") Long id) {
        return R.ok(technologyRoutingOperationService.removeTechnologyRoutingOperation(id));
    }
    @PostMapping ("/sort")
    @Log(title = "工艺路线工序", businessType = BusinessType.UPDATE)
    @Operation(summary = "排序工艺路线工序")
    @PostMapping ("/sort")
    public R sort(@RequestBody TechnologyRoutingOperation technologyRoutingOperation) {
        return R.ok(technologyRoutingOperationService.sort(technologyRoutingOperation));
    }
src/main/java/com/ruoyi/technology/controller/TechnologyRoutingOperationParamController.java
@@ -2,6 +2,8 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.technology.bean.dto.TechnologyRoutingOperationParamDto;
import com.ruoyi.technology.bean.dto.TechnologyRoutingOperationParamSyncDto;
@@ -44,24 +46,28 @@
    @PostMapping("/add")
    @Operation(summary = "新增生产订单工艺路线工序参数")
    @Log(title = "新增工艺路线工序参数", businessType = BusinessType.INSERT)
    public R<Boolean> add(@RequestBody TechnologyRoutingOperationParam technologyRoutingOperationParam) {
        return R.ok(technologyRoutingOperationParamService.saveTechnologyRoutingOperationParam(technologyRoutingOperationParam));
    }
    @PutMapping
    @Operation(summary = "修改生产订单工艺路线工序参数")
    @Log(title = "修改工艺路线工序参数", businessType = BusinessType.UPDATE)
    public R<Boolean> edit(@RequestBody TechnologyRoutingOperationParam technologyRoutingOperationParam) {
        return R.ok(technologyRoutingOperationParamService.saveTechnologyRoutingOperationParam(technologyRoutingOperationParam));
    }
    @DeleteMapping("/{id}")
    @Operation(summary = "删除生产订单工艺路线工序参数")
    @Log(title = "删除工艺路线工序参数", businessType = BusinessType.DELETE)
    public R<Boolean> remove(@PathVariable("id") Long id) {
        return R.ok(technologyRoutingOperationParamService.removeTechnologyRoutingOperationParam(id));
    }
    @PostMapping("/sync")
    @Operation(summary = "按工艺路线工序同步工序参数")
    @Log(title = "同步工艺路线工序参数", businessType = BusinessType.UPDATE)
    public R<Integer> sync(@RequestBody TechnologyRoutingOperationParamSyncDto syncDto) {
        return R.ok(technologyRoutingOperationParamService.syncTechnologyRoutingOperationParam(syncDto));
    }
src/main/java/com/ruoyi/technology/pojo/TechnologyParam.java
@@ -1,10 +1,8 @@
package com.ruoyi.technology.pojo;
import com.baomidou.mybatisplus.annotation.*;
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.io.Serializable;
import java.time.LocalDateTime;
@@ -55,8 +53,6 @@
    @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 = "修改人")
@@ -65,8 +61,6 @@
    @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 = "部门ID")
src/main/java/com/ruoyi/technology/pojo/TechnologyRouting.java
@@ -32,6 +32,7 @@
    private String description;
    @Schema(description = "录入时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
src/main/java/com/ruoyi/technology/service/TechnologyOperationService.java
@@ -16,8 +16,6 @@
    R add(TechnologyOperationDto technologyOperationDto);
    R update(TechnologyOperation technologyOperation);
    String batchDelete(List<Long> ids);
    List<TechnologyOperationVo> listVo();
src/main/java/com/ruoyi/technology/service/impl/TechnologyOperationServiceImpl.java
@@ -57,27 +57,6 @@
        return R.ok();
    }
    @Override
    public R update(TechnologyOperation technologyOperation) {
        if (technologyOperation.getId() == null) {
            return R.fail("工序ID不能为空");
        }
        boolean updated = technologyOperationMapper.update(
                null,
                Wrappers.<TechnologyOperation>lambdaUpdate()
                        .eq(TechnologyOperation::getId, technologyOperation.getId())
                        .set(TechnologyOperation::getName, technologyOperation.getName())
                        .set(TechnologyOperation::getNo, technologyOperation.getNo())
                        .set(TechnologyOperation::getRemark, technologyOperation.getRemark())
                        .set(TechnologyOperation::getSalaryQuota, technologyOperation.getSalaryQuota())
                        .set(TechnologyOperation::getIsQuality, technologyOperation.getIsQuality())
                        .set(TechnologyOperation::getIsProduction, technologyOperation.getIsProduction())
                        .set(TechnologyOperation::getType, technologyOperation.getType())
                        .set(TechnologyOperation::getDeviceLedgerId, technologyOperation.getDeviceLedgerId())
        ) > 0;
        return updated ? R.ok() : R.fail("修改失败");
    }
    /**
     * åˆ é™¤å·¥åºå‰æ ¡éªŒæ˜¯å¦å·²è¢«BOM结构或工艺路线引用。
     */
src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
@@ -7,8 +7,6 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.OrderUtils;
import java.time.LocalDateTime;
import com.ruoyi.technology.bean.dto.TechnologyRoutingDto;
import com.ruoyi.technology.bean.vo.TechnologyRoutingVo;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
@@ -62,7 +60,7 @@
    @Override
    public Long saveTechnologyRouting(TechnologyRouting technologyRouting) {
        String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code", technologyRouting.getCreateTime() != null ? technologyRouting.getCreateTime() : LocalDateTime.now());
        String code = OrderUtils.countTodayByCreateTime(technologyRoutingMapper, "GYLX", "process_route_code");
        technologyRouting.setProcessRouteCode(code);
        technologyRoutingMapper.insert(technologyRouting);
        // å¸¦å…¥bom产品结构
src/main/java/com/ruoyi/warehouse/controller/DocumentationFileController.java
@@ -1,5 +1,7 @@
package com.ruoyi.warehouse.controller;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.warehouse.pojo.DocumentationFile;
import com.ruoyi.warehouse.service.DocumentationFileService;
@@ -25,6 +27,7 @@
     * @param documentationFile
     * @return
     */
    @Log(title = "新增文档文件", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public AjaxResult add(@RequestBody DocumentationFile documentationFile) {
        return AjaxResult.success(documentationFileService.save(documentationFile));
@@ -35,6 +38,7 @@
     * @param ids
     * @return
     */
    @Log(title = "删除文档文件", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public AjaxResult delDocumentationFile(@RequestBody List<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
src/main/resources/application-btyxpro.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:3306/product-inventory-management-btyx-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 15
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://1.15.17.182:9056 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-ddgw.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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-ddgw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 200381
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      host: localhost
      #      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.137.101.88:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-dev.yml
@@ -7,14 +7,14 @@
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: D:/ruoyi/uploadPath
  profile: /javaWork/product-inventory-management/file
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: DEV
  approvalNumberPrefix: NEW
  # ä¸ªæŽ¨ Unipush é…ç½®
  getui:
@@ -28,7 +28,7 @@
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 7005
  port: 9003
  servlet:
    # åº”用的访问路径
    context-path: /
@@ -46,7 +46,6 @@
# æ—¥å¿—配置
logging:
  level:
    org.quartz: DEBUG
    com.ruoyi: warn
    org.springframework: warn
@@ -57,7 +56,7 @@
  accessKey: admin
  secretKey: 12345678
  preview-expiry: 24 # é¢„览地址默认24小时
  default-bucket: uploadPath
  default-bucket: jxc
# ç”¨æˆ·é…ç½®
user:
  password:
@@ -74,9 +73,9 @@
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/product-inventory-management-bdpro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        url: jdbc:mysql://localhost:3306/product-inventory-management-hnhb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
        password: 200381
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
@@ -143,18 +142,17 @@
    restart:
      # çƒ­éƒ¨ç½²å¼€å…³
      enabled: false
  # redis é…ç½®
  data:
    mongodb:
      uri: mongodb://114.132.189.42:9028/chat_memory_db
    # redis é…ç½®
    redis:
      # åœ°å€
      host: 127.0.0.1
      #    host: 172.17.0.1
      #      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      database: 15
      # å¯†ç 
      #    password: root2022!
      password:
@@ -171,6 +169,7 @@
          max-active: 8
          # #连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
  # Quartz定时任务配置(新增部分)
  quartz:
    job-store-type: jdbc  # ä½¿ç”¨æ•°æ®åº“存储
@@ -196,7 +195,6 @@
            threadPriority: 5
            makeThreadsDaemons: true
          updateCheck: false  # å…³é—­ç‰ˆæœ¬æ£€æŸ¥
# token配置
token:
  # ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
@@ -256,15 +254,13 @@
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: D:/ruoyi/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: D:/ruoyi/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: D:/ruoyi/prod/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /common # é“¾æŽ¥å‰ç¼€
  domain: http://127.0.0.1:7006 # åŸŸåå‰ç¼€
  temp-dir: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://1.15.17.182:9056 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-dtzg.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://36.213.180.217:11101/product-inventory-management-dtzg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.213.180.217:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-dyzn.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,267 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://36.213.180.217:11101/product-inventory-management-dyzn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 36.213.180.217
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
#  domain: http://192.168.1.32:9001 # åŸŸåå‰ç¼€
  domain: http://36.213.180.217:9518 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-hyjc.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:9002/product-inventory-management-hyjc-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: hyjc@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.138.94.178:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-hylq.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:9002/product-inventory-management-hylq-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: hylq@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.139.202.111:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-jhjm.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://172.17.0.1:9000/product-inventory-management-jhjm?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: jhjm@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #    host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.133.41.166:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-jhsn.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:9002/product-inventory-management-jhsn-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: jhsn@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.139.201.20:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-lmjn.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:3306/product-inventory-management-lmjn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
#        password: lmjn@123456..
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
#  domain: http://192.168.1.32:9001 # åŸŸåå‰ç¼€
  domain: http://1.15.17.182:9105 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-qxy.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /home/ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: DEV
  # ä¸ªæŽ¨ 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: 7003
  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:
    org.quartz: DEBUG
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://36.134.154.10:9002/product-inventory-management-qxy?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: qxy@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #    host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.134.154.10:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-sdks.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://36.213.180.217:11101/product-inventory-management-sdks?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: localhost
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.137.101.88:9520 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-sdtx.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,272 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /home/ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: DEV
  # ä¸ªæŽ¨ 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: 7003
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# æ—¥å¿—配置
logging:
  level:
    org.quartz: DEBUG
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://172.17.0.1:9002/product-inventory-management-sdtx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: sdtx@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #    host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.213.156.184:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-skjs.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,267 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://36.213.180.217:11101/product-inventory-management-skjs?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 36.213.180.217
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
#  domain: http://192.168.1.32:9001 # åŸŸåå‰ç¼€
  domain: http://36.213.180.217:9514 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-tddq.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://172.17.0.1:9000/product-inventory-management-tddq?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: tddq@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      host: 172.17.0.1
      #      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.137.101.88:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-xcdq.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,272 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /home/ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: false
  # éªŒè¯ç ç±»åž‹ math æ•°å­—计算 char å­—符验证
  captchaType: math
  # ååŒå®¡æ‰¹ç¼–号前缀(配置文件后缀命名)
  approvalNumberPrefix: DEV
  # ä¸ªæŽ¨ 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: 9002
  servlet:
    # åº”用的访问路径
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # è¿žæŽ¥æ•°æ»¡åŽçš„æŽ’队数,默认为100
    accept-count: 1000
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100
# æ—¥å¿—配置
logging:
  level:
    org.quartz: DEBUG
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://36.133.45.183:9000/product-inventory-management-xcdq?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xcdq@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #    host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.133.45.183:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-xhzg.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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-xhzg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 200381
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      host: localhost
      #      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.137.101.88:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-xlzn.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://36.213.180.217:11101/product-inventory-management-xlzn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.213.180.217:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-xswh.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://172.17.0.1:9000/product-inventory-management-xswh?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xswh@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.138.177.225:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-yjyy.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,267 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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: 9002
  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://36.213.180.217:11101/product-inventory-management-yjyy?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: xd@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 36.213.180.217
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 11102
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
#  domain: http://192.168.1.32:9001 # åŸŸåå‰ç¼€
  domain: http://36.213.180.217:9516 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-ytjz.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:9002/product-inventory-management-ytjz-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: ytjz@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
#      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.139.201.181:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application-zqsy.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,269 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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
    max-swallow-size: -1          # -1 è¡¨ç¤ºä¸é™åˆ¶
    max-http-form-post-size: -1   # POST è¯·æ±‚体不限制
    connection-timeout: 60000     # è¿žæŽ¥è¶…æ—¶æ—¶é—´(毫秒)
    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: uploadPath
# ç”¨æˆ·é…ç½®
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://172.17.0.1:9002/product-inventory-management-zqsy-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: zqsy@123456..
      # ä»Žåº“数据源
      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
  data:
    # redis é…ç½®
    redis:
      # åœ°å€
      #    host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 0
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: /javaWork/product-inventory-management/file/path/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://36.213.128.159:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
knowledge:
  one: D:\新疆大罗素企业产品体系说明文档.md
src/main/resources/application-zxzn.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
# é¡¹ç›®ç›¸å…³é…ç½®
ruoyi:
  # åç§°
  name: RuoYi
  # ç‰ˆæœ¬
  version: 3.8.9
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2025
  # æ–‡ä»¶è·¯å¾„ ç¤ºä¾‹ï¼ˆ Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
  profile: /javaWork/product-inventory-management/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://172.17.0.1:3306/product-inventory-management-zxzn?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: zxzn@123456..
      # ä»Žåº“数据源
      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:
    # redis é…ç½®
    redis:
      # åœ°å€
      #      host: 127.0.0.1
      host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      # æ•°æ®åº“索引
      database: 15
      # å¯†ç 
      #    password: root2022!
      password:
      # è¿žæŽ¥è¶…æ—¶æ—¶é—´
      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: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  domain: http://192.168.0.200:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
  needCompressSize: 10MB # åŽ‹ç¼©é˜ˆå€¼
  compressQuality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
src/main/resources/application.yml
@@ -1,19 +1,9 @@
# Spring配置
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  main:
    allow-circular-references: true
  profiles:
    active: dev
# Pinecone å‘量数据库配置
pinecone:
  api-key: pcsk_4SJLnh_tNB3wSLJU8tc4E5P28PcXX8eCLdURqZpVhg1FMV8CRYxjneWdzqRdB5Ftqooi9
  index: xiaozhi-index
  namespace: knowledge-base
langchain4j:
  mcp:
    # MCP æœåŠ¡ç«¯åœ°å€ï¼ˆæ ¹æ®å®žé™…éƒ¨ç½²çš„ MCP æœåŠ¡è°ƒæ•´ï¼‰
src/main/resources/financial-agent-prompt.txt
@@ -2,10 +2,10 @@
当前日期:{{currentDate}}(中国时区)。
工作规则:
1. ç”¨æˆ·æå‡ºâ€œæŸ¥ã€é—®ã€ç»Ÿè®¡ã€åˆ†æžã€é¢„警、建议、报告”类需求时,优先调用工具返回结构化 JSON,不编造业务数据。
2. å‘½ä¸­æˆæœ¬ã€åˆ©æ¶¦ã€åº“存资金、现金流、预警、驾驶舱、日报、周报等场景时,优先调用对应工具。
1. ç”¨æˆ·æå‡ºâ€œæŸ¥ã€é—®ã€ç»Ÿè®¡ã€åˆ†æžã€é¢„警、建议、报告”需求时,优先调用工具返回结构化 JSON,不编造业务数据。
2. å‘½ä¸­æˆæœ¬ã€åˆ©æ¶¦ã€åº“存资金、现金流、预警、驾驶舱、日报周报场景时,优先调用对应工具。
3. å·¥å…·è¿”回 JSON æ—¶ï¼Œç›´æŽ¥è¾“出原始 JSON å­—符串,不要额外包裹 Markdown,也不要在前后追加解释文本。
4. ç”¨æˆ·æ²¡æœ‰æ˜Žç¡®ç»™å‡ºæ—¶é—´ã€å®¢æˆ·ã€ä¾›åº”商、产品、订单、数量等筛选条件时,不要自行补充条件;工具参数保持为空,由工具按当前业务口径查询。
5. ç”¨æˆ·æåˆ°â€œæœ¬å‘¨ã€æœ¬æœˆã€ä»Šå¹´ã€ä»Šå¤©ã€æœ€è¿‘、近30天、上月、去年”等相对时间时,必须严格基于“当前日期”换算,禁止自行假设年份。
6. ç”¨æˆ·é—®â€œä¸ºä»€ä¹ˆåˆ©æ¶¦ä¸‹é™â€â€œå“ªä¸ªè®¢å•亏损”“哪个客户最赚钱”“哪个客户利润贡献最高”“哪个工序成本最高”等问题时,优先基于订单利润与工序成本分析工具作答。
7. å›žç­”必须使用中文;若数据不足以得出结论,明确指出缺少哪些关键字段或筛选条件。
4. å½“用户问题缺少时间范围时,默认使用工具内置口径(如近30天、本月、近90天),并在后续可提醒用户补充范围。
5. ç”¨æˆ·é—®â€œä¸ºä»€ä¹ˆåˆ©æ¶¦ä¸‹é™â€â€œå“ªä¸ªè®¢å•亏损”“哪个客户最赚钱”“哪个客户利润贡献最高”“哪个车间/工序成本最高”等问题时,优先基于订单利润与工序成本分析工具作答。
6. å›žç­”必须使用中文;若数据不足以得出结论,明确指出缺少哪些关键字段或筛选条件。
7. ç”¨æˆ·æåˆ°â€œä»Šå¹´/本月/今天/最近/上月/去年”等相对时间时,必须严格基于“当前日期”换算,禁止自行假设年份。
src/main/resources/mapper/account/purchase/AccountPaymentApplicationMapper.xml
@@ -44,7 +44,6 @@
                AND A.apply_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by A.id desc
    </select>
    <select id="getInboundBatchesBySupplier"
@@ -88,14 +87,12 @@
    </select>
    <select id="existsByStockInRecordId" resultType="java.lang.Boolean">
        SELECT COUNT(*) > 0
         SELECT COUNT(*) > 0
        FROM account_payment_application
        WHERE status != 2
        AND (
            <foreach collection="stockInRecordIds" item="id" separator=" OR ">
                FIND_IN_SET(#{id}, stock_in_record_ids) > 0
            </foreach>
        )
        WHERE  status != 2
        <foreach collection="stockInRecordIds" item="id" open="(" separator=" OR " close=")">
            FIND_IN_SET(#{id}, stock_in_record_ids)
        </foreach>
    </select>
</mapper>
src/main/resources/mapper/account/purchase/AccountPurchaseInvoiceMapper.xml
@@ -42,7 +42,6 @@
                AND api.issue_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by api.id desc
    </select>
    <select id="getInboundBatchesBySupplier"
            resultType="com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo">
src/main/resources/mapper/account/purchase/AccountPurchasePaymentMapper.xml
@@ -44,7 +44,6 @@
                AND app.payment_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by app.id desc
    </select>
    <select id="selectPayment" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
        SELECT DATE_FORMAT(payment_date, #{dateFormat}) AS dateStr,
src/main/resources/mapper/account/sales/AccountInvoiceApplicationMapper.xml
@@ -25,7 +25,6 @@
                AND A.apply_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by A.id desc
    </select>
    <select id="getOutboundBatchesByCustomer"
            resultType="com.ruoyi.account.bean.vo.sales.SalesOutboundVo">
@@ -65,33 +64,10 @@
    <select id="existsByStockOutRecordId" resultType="java.lang.Boolean">
        SELECT COUNT(*) > 0
        FROM account_invoice_application
        WHERE status != 2
        AND (
            <foreach collection="stockOutRecordIds" item="id" separator=" OR ">
                FIND_IN_SET(#{id}, stock_out_record_ids) > 0
            </foreach>
        )
    </select>
    <select id="selectAccountInvoiceApplicationList"
            resultType="com.ruoyi.account.bean.vo.sales.AccountInvoiceApplicationVo">
        select
            *
        from (
            select aia.*,
            asi.id AS sales_invoice_id,
            asi.status AS sales_invoice_status,
            c.customer_name,
            GROUP_CONCAT(sour.outbound_batches SEPARATOR ',') AS outboundBatches
            from account_invoice_application aia
            left join customer c on aia.customer_id = c.id
            left join stock_out_record sour on FIND_IN_SET(sour.id, aia.stock_out_record_ids) > 0
            left join account_sales_invoice asi on aia.id = asi.account_invoice_application_id
            GROUP BY aia.id
        )A
        WHERE A.customer_id = #{customerId}
        AND (COALESCE(A.sales_invoice_id,'') = '' OR A.sales_invoice_status = 1)
        AND A.status = 1
        order by A.id desc
        WHERE status!=2
        <foreach collection="stockOutRecordIds" item="id" open="(" separator=" OR " close=")">
            FIND_IN_SET(#{id}, stock_out_record_ids)
        </foreach>
    </select>
</mapper>
src/main/resources/mapper/account/sales/AccountSalesCollectionMapper.xml
@@ -45,7 +45,7 @@
                AND A.collection_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by A.id desc
    </select>
    <select id="existsByStockOutRecordId" resultType="java.lang.Boolean">
        SELECT COUNT(*) > 0
@@ -58,51 +58,36 @@
    <select id="getOutboundBatchesByCustomer"
            resultType="com.ruoyi.account.bean.vo.sales.SalesOutboundVo">
        SELECT
            id,
            outbound_batches,
            customer_name,
            shipping_date,
            product_name,
            specification_model,
            tax_rate,
            outbound_amount,
            shipping_no,
            sales_contract_no,
            amount_received
        FROM
            (
                SELECT
                    sor.id,
                    sor.outbound_batches,
                    sl.customer_name,
                    sor.create_time AS shipping_date,
                    p.product_name,
                    pm.model AS specification_model,
                    slp.tax_rate,
                    sor.stock_out_num * slp.tax_inclusive_unit_price AS outbound_amount,
                    s.shipping_no,
                    sl.sales_contract_no,
                    COALESCE ( SUM( acsc.collection_amount ), 0 ) AS amount_received
                FROM
                    stock_out_record sor
                        LEFT JOIN shipping_info s ON sor.record_id = s.id
                        LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
                        LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id
                        AND slp.type = 1
                        LEFT JOIN product_model pm ON slp.product_model_id = pm.id
                        LEFT JOIN product p ON pm.product_id = p.id
                        LEFT JOIN account_sales_collection acsc ON acsc.stock_out_record_ids = sor.id
                WHERE
                    sor.record_type = '13'
                  AND sor.approval_status = 1
                  AND sl.customer_id = #{customerId}
                GROUP BY
                    sor.outbound_batches
                ORDER BY
                    sor.id
            ) tempA
        WHERE
            amount_received &lt; outbound_amount
        sor.id,
        sor.outbound_batches,
        sl.customer_name,
        sor.create_time as shippingDate,
        p.product_name,
        pm.model as specification_model,
        slp.tax_rate,
        sor.stock_out_num * slp.tax_inclusive_unit_price as outboundAmount,
        s.shipping_no,
        sl.sales_contract_no
        FROM stock_out_record sor
        left join shipping_info s on sor.record_id = s.id
        LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
        LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id and slp.type = 1
        left join product_model pm on slp.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        WHERE sor.record_type='13' and sor.approval_status=1
        and sl.customer_id=#{customerId}
        and sor.id NOT IN (
        SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(a.stock_out_record_ids, ',', n.n), ',', -1)
        FROM account_sales_collection a
        CROSS JOIN (
            SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL
            SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
        ) n
        WHERE n.n &lt;= CHAR_LENGTH(a.stock_out_record_ids) - CHAR_LENGTH(REPLACE(a.stock_out_record_ids, ',', '')) + 1
        )
        order by sor.id DESC
    </select>
    <select id="selectIncomeStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
         SELECT DATE_FORMAT(collection_date, #{dateFormat}) AS dateStr,
src/main/resources/mapper/account/sales/AccountSalesInvoiceMapper.xml
@@ -42,7 +42,6 @@
                AND asi.issue_date BETWEEN #{req.startDate} AND #{req.endDate}
            </if>
        </where>
        order by asi.id desc
    </select>
</mapper>
src/main/resources/mapper/aftersalesservice/AfterSalesNearExpiryMapper.xml
@@ -25,18 +25,17 @@
    <select id="listPage" resultType="com.ruoyi.aftersalesservice.pojo.AfterSalesNearExpiry">
        select * from after_sales_near_expiry
        <where>
            <if test="req.expireDate != null">
                AND expire_date = #{req.expireDate}
            </if>
            <if test="req.disDate != null">
                AND dis_date = #{req.disDate}
            </if>
            <if test="req.status != null">
                AND status = #{req.status}
            </if>
        </where>
        where 1 = 1
        <if test="req.expireDate != null">
            AND expire_date = #{req.expireDate}
        </if>
        <if test="req.disDate != null">
            AND dis_date = #{req.disDate}
        </if>
        <if test="req.status != null">
            AND status = #{req.status}
        </if>
        order by create_time desc
    </select>
</mapper>
</mapper>
src/main/resources/mapper/aftersalesservice/AfterSalesServiceMapper.xml
@@ -32,7 +32,7 @@
        <if test="req.serviceType != null">
            and service_type = #{req.serviceType}
        </if>
        order by create_time desc
        order by update_time desc
    </select>
    <select id="countAfterSalesService" resultType="com.ruoyi.aftersalesservice.dto.CountDto">
        select
@@ -42,4 +42,4 @@
        group by status
    </select>
</mapper>
</mapper>
src/main/resources/mapper/approve/ApproveProcessMapper.xml
@@ -29,17 +29,19 @@
        approve_user_names,approve_reason,approve_time,approve_over_time,approve_status,
        approve_delete,tenant_id,approve_type,approve_remark,start_date_time,end_date_time
    </sql>
    <select id="listPage" resultType="com.ruoyi.approve.vo.ApproveProcessVo">
        select * from approve_process where approve_delete = 0
        <if test="req.approveId != null and req.approveId != ''">
            and approve_id like concat('%',#{req.approveId},'%')
        </if>
        <if test="req.approveStatus != null or req.approveStatus == 0">
        <if test="req.approveStatus != null">
            and approve_status = #{req.approveStatus}
        </if>
        <if test="req.approveType != null ">
            and approve_type = #{req.approveType}
        </if>
        order by id desc
        order by approve_time desc
    </select>
</mapper>
src/main/resources/mapper/approve/KnowledgeBaseMapper.xml
@@ -16,6 +16,6 @@
                and type = #{knowledgeBase.type}
            </if>
        </where>
        order by id desc
    </select>
</mapper>
</mapper>
src/main/resources/mapper/basic/CustomerMapper.xml
@@ -9,9 +9,9 @@
    <select id="listPage" resultType="com.ruoyi.basic.vo.CustomerVo">
        select
        c.*,
        u.user_name usage_user_name,
        u.nick_name as usage_user_name,
        (
        select group_concat(u2.user_name separator ', ')
        select group_concat(u2.nick_name separator ', ')
        from customer_user cu
        left join sys_user u2 on cu.user_id = u2.user_id
        where cu.customer_id = c.id
@@ -22,14 +22,7 @@
        from customer_user cu
        where cu.customer_id = c.id
        and cu.user_id != c.usage_user
        ) as user_ids_str,
        CASE
            WHEN EXISTS (SELECT 1 FROM sales_ledger sl WHERE sl.customer_id = c.id)
              OR EXISTS (SELECT 1 FROM sales_quotation sq WHERE sq.customer_id = c.id)
              OR EXISTS (SELECT 1 FROM return_management rm WHERE rm.customer_id = c.id)
            THEN 1
            ELSE 0
        END AS sales_reference_flag
        ) as user_ids_str
        from customer c
        left join sys_user u on c.usage_user = u.user_id
        <where>
@@ -40,11 +33,11 @@
                and customer_type = #{c.customerType}
            </if>
            <!-- å…¬æµ·æŸ¥è¯¢ï¼štype = 1(公海客户)-->
            <if test="c.type != null and c.type == 1">
            <!--<if test="c.type != null and c.type == 1">
                and type = #{c.type}
            </if>
            </if>-->
            <!-- ç§æµ·æŸ¥è¯¢ï¼štype = 0(私海客户)或者 type = 1(公海客户)且已被分配,并且是自己领用、自己创建或者共享给自己的客户 -->
            <if test="c.type != null and c.type == 0">
            <!--<if test="c.type != null and c.type == 0">
                and (
                    (type = #{c.type} or (type = 1 and is_assigned = 1))
                    and (
@@ -57,9 +50,9 @@
                        )
                    )
                )
            </if>
            </if>-->
        </where>
        order by c.id desc
        order by maintenance_time desc
    </select>
    <select id="list" resultType="com.ruoyi.basic.vo.CustomerVo">
@@ -78,14 +71,7 @@
        from customer_user cu
        where cu.customer_id = c.id
        and cu.user_id != c.usage_user
        ) as user_ids_str,
        CASE
            WHEN EXISTS (SELECT 1 FROM sales_ledger sl WHERE sl.customer_id = c.id)
              OR EXISTS (SELECT 1 FROM sales_quotation sq WHERE sq.customer_id = c.id)
              OR EXISTS (SELECT 1 FROM return_management rm WHERE rm.customer_id = c.id)
            THEN 1
            ELSE 0
        END AS sales_reference_flag
        ) as user_ids_str
        from customer c
        left join sys_user u on c.usage_user = u.user_id
        <where>
@@ -102,11 +88,11 @@
                and customer_type = #{c.customerType}
            </if>
            <!-- å…¬æµ·æŸ¥è¯¢ï¼štype = 1(公海客户)-->
            <if test="c.type != null and c.type == 1">
            <!--<if test="c.type != null and c.type == 1">
                and type = #{c.type}
            </if>
            </if>-->
            <!-- ç§æµ·æŸ¥è¯¢ï¼štype = 0(私海客户)或者 type = 1(公海客户)且已被分配,并且是自己领用、自己创建或者共享给自己的客户 -->
            <if test="c.type != null and c.type == 0">
            <!--<if test="c.type != null and c.type == 0">
                and (
                    (type = #{c.type} or (type = 1 and is_assigned = 1))
                    and (
@@ -119,69 +105,67 @@
                        )
                    )
                )
            </if>
            </if>-->
        </where>
        order by c.id desc
    </select>
    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
        select T1.customer_id,
               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
               T1.contractAmounts
        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
        left join (
            select
                sl.customer_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
        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>
        </where>
        order by T1.customer_id desc
    </select>
    <select id="customewTransactionsDetails"
            resultType="com.ruoyi.sales.vo.CustomerTransactionsDetailsVo">
        select sl.id salesLedgerId,
               sl.sales_contract_no,
               sl.execution_date,
               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
               sl.contract_amount
        from sales_ledger sl
        left join (
            select
                sl.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.id
        )T1 on T1.id = sl.id
        left join (
            select sl.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.id
        )T3 on T3.id = sl.id
        where sl.customer_id = #{customerId}
        order by sl.id desc
        order by sl.execution_date
    </select>
    <select id="getShippedAmountByCustomerIds" resultType="java.util.Map">
        SELECT
            sl.customer_id as customerId,
            sum(sor.stock_out_num * slp.tax_inclusive_unit_price) as outboundAmount
        FROM stock_out_record sor
        LEFT join shipping_info s on sor.record_id = s.id
        LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
        LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id
        WHERE sor.record_type='13'
            and sor.approval_status=1
            and slp.type = 1
            and sl.customer_id IN
            <foreach collection="customerIds" item="customerId" open="(" separator="," close=")">
                #{customerId}
            </foreach>
        group by sl.customer_id
    </select>
    <select id="getShippedAmountBySalesLedgerIds" resultType="java.util.Map">
        SELECT
            sl.id as salesLedgerId,
            sum(sor.stock_out_num * slp.tax_inclusive_unit_price) as outboundAmount
        FROM stock_out_record sor
        LEFT join shipping_info s on sor.record_id = s.id
        LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
        LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id
        WHERE sor.record_type='13'
            and sor.approval_status=1
            and slp.type = 1
            and sl.id IN
            <foreach collection="salesLedgerIds" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        group by sl.id
    </select>
</mapper>
src/main/resources/mapper/basic/ProductModelMapper.xml
@@ -16,6 +16,7 @@
    <resultMap id="ProductModelVoResultMap" type="com.ruoyi.basic.vo.ProductModelVo" extends="BaseResultMap">
        <result column="create_time" property="createTime" />
        <result column="top_product_name" property="topProductName" />
        <collection property="batchNoList"
                    ofType="java.lang.String"
                    column="{productModelId=id}"
@@ -23,9 +24,20 @@
    </resultMap>
    <select id="listPageProductModel" resultMap="ProductModelVoResultMap">
        select pm.*,p.product_name
        WITH RECURSIVE product_root AS (
            SELECT id, parent_id, product_name
            FROM product
            WHERE parent_id IS NULL
            UNION ALL
            SELECT p.id, p.parent_id, pr.product_name
            FROM product p
            INNER JOIN product_root pr ON p.parent_id = pr.id
        )
        select pm.*, p.product_name,
               pr.product_name as top_product_name
        from product_model pm
        left join product p on pm.product_id = p.id
        left join product_root pr on p.id = pr.id
        <where>
            <if test="c.model != null and c.model != ''">
                and pm.model  like  concat('%',#{c.model},'%')
src/main/resources/mapper/basic/SupplierManageMapper.xml
@@ -35,7 +35,7 @@
                AND T1.is_white = #{supplierManageDto.isWhite}
            </if>
        </where>
        order by T1.id desc
        order by T1.maintain_time desc
    </select>
    <select id="supplierExportList" resultType="com.ruoyi.basic.excel.SupplierManageExcelDto">
@@ -69,65 +69,29 @@
            </if>
        </where>
    </select>
    <select id="supplierTransactions" resultType="com.ruoyi.purchase.vo.SupplierTransactionsVo">
        select T1.supplier_id,
        SELECT T1.supplier_id,
               sm.supplier_name,
               T1.contractAmounts,
               IFNULL(T2.paymentAmount, 0) AS paymentAmount,
               T1.contractAmounts - IFNULL(T2.paymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS payableAmount,
               IFNULL(T4.returnAmount, 0) AS returnAmount
        from (select supplier_id, sum(contract_amount) as contractAmounts from purchase_ledger group by supplier_id) T1
        left join (select supplier_id, sum(payment_amount) as paymentAmount from account_purchase_payment group by supplier_id) T2 on T1.supplier_id = T2.supplier_id
        left join (
            select
                supplier_id,
                sum(total_amount) as returnAmount
            from purchase_return_orders pro
            group by supplier_id
        ) T4 on T4.supplier_id=T1.supplier_id
        left join supplier_manage sm on T1.supplier_id = sm.id
               T1.contractAmounts
        FROM (SELECT supplier_id, SUM(contract_amount) AS contractAmounts FROM purchase_ledger GROUP BY supplier_id) T1
        LEFT JOIN supplier_manage sm ON T1.supplier_id = sm.id
        <where>
            <if test="supplierName!=null and supplierName!=''">
                AND sm.supplier_name LIKE CONCAT('%',#{supplierName},'%')
            </if>
        </where>
        order by T1.supplier_id desc
    </select>
    <select id="supplierTransactionsDetails"
            resultType="com.ruoyi.purchase.vo.SupplierTransactionsDetailsVo">
       select pl.id  purchaseLedgerId,
       SELECT pl.id purchaseLedgerId,
              pl.purchase_contract_number,
              pl.execution_date,
              pl.contract_amount AS contract_amount,
              IFNULL(T1.paymentAmount, 0) AS paymentAmount,
              pl.contract_amount - IFNULL(T1.paymentAmount, 0) - IFNULL(T3.returnAmount, 0) AS payableAmount,
              IFNULL(T3.returnAmount, 0) AS returnAmount
       from purchase_ledger pl
       left join (
           select
               pl.id,
               sum(app.payment_amount) as paymentAmount
           from account_purchase_payment app
           left join account_payment_application apa on app.account_payment_application_id = apa.id
           left join stock_in_record sir on FIND_IN_SET(sir.id, apa.stock_in_record_ids) > 0
               -- 10 ç±»åž‹æ‰å…³è”质检表
           LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
               -- åŠ¨æ€å…³è”é‡‡è´­ï¼ˆè‡ªåŠ¨é€‚é… 7 å’Œ 10)
           LEFT JOIN purchase_ledger pl
                     ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
           WHERE sir.approval_status = 1
             AND sir.record_type IN ('7','10')
           group by pl.id
       )T1 on T1.id = pl.id
       left join (
           select pl.id,
                  sum(pro.total_amount) as returnAmount
           from purchase_return_orders pro
                    left join purchase_ledger pl on pro.purchase_ledger_id = pl.id
           group by pl.id
       )T3 on T3.id = pl.id
       where pl.supplier_id = #{supplierId}
       order by pl.id desc
              pl.contract_amount
       FROM purchase_ledger pl
       WHERE pl.supplier_id = #{supplierId}
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/NoticeMapper.xml
@@ -3,7 +3,7 @@
<mapper namespace="com.ruoyi.collaborativeApproval.mapper.NoticeMapper">
    <select id="listPage" resultType="com.ruoyi.collaborativeApproval.dto.NoticeDTO">
        select n.*, su.user_name as create_user_name
        select n.*, su.nick_name as create_user_name
        from notice n
        left join sys_user su on n.create_user = su.user_id
        <where>
@@ -17,6 +17,5 @@
                and n.status = #{ew.status}
            </if>
        </where>
        order by n.create_time desc
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/RulesRegulationsManagementMapper.xml
@@ -13,7 +13,7 @@
    </resultMap>
    <select id="listPage" resultMap="RulesRegulationsManagementDTOMap">
        select rrm.*, su.user_name as create_user_name
        select rrm.*, su.nick_name as create_user_name
        from rules_regulations_management rrm
        left join sys_user su on rrm.create_user = su.user_id
        <where>
@@ -27,6 +27,5 @@
                and rrm.category = #{ew.category}
            </if>
        </where>
        order by rrm.id desc
    </select>
</mapper>
src/main/resources/mapper/collaborativeApproval/SealApplicationManagementMapper.xml
@@ -37,6 +37,5 @@
            </if>
        </where>
        GROUP BY sam.id
        order by sam.id desc
    </select>
</mapper>
src/main/resources/mapper/device/DeviceMaintenanceMapper.xml
@@ -26,31 +26,41 @@
        left join device_ledger dl on dm.device_ledger_id = dl.id
        left join sys_user su on dm.create_user = su.user_id
        <where>
            <if test="deviceMaintenanceDto.deviceName != null">
                and dl.device_name like concat('%',#{deviceMaintenanceDto.deviceName},'%')
            <if test="deviceMaintenanceDto.deviceName != null and deviceMaintenanceDto.deviceName != ''">
                and dl.device_name like concat('%', #{deviceMaintenanceDto.deviceName}, '%')
            </if>
            <if test="deviceMaintenanceDto.deviceModel != null">
                and dl.device_model like concat('%',#{deviceMaintenanceDto.deviceModel},'%')
            <if test="deviceMaintenanceDto.deviceModel != null and deviceMaintenanceDto.deviceModel != ''">
                and dl.device_model like concat('%', #{deviceMaintenanceDto.deviceModel}, '%')
            </if>
            <if test="deviceMaintenanceDto.status != null">
                and dm.status = #{deviceMaintenanceDto.status}
            </if>
            <if test="deviceMaintenanceDto.maintenanceActuallyName != null">
                and dm.maintenance_actually_name like concat('%',#{deviceMaintenanceDto.maintenanceActuallyName},'%')
            <if test="deviceMaintenanceDto.maintenanceActuallyName != null and deviceMaintenanceDto.maintenanceActuallyName != ''">
                and dm.maintenance_actually_name like concat('%', #{deviceMaintenanceDto.maintenanceActuallyName}, '%')
            </if>
            <if test="deviceMaintenanceDto.maintenancePlanTime != null">
                and dm.maintenance_plan_time like concat('%',#{deviceMaintenanceDto.maintenancePlanTime},'%')
                and dm.maintenance_plan_time = #{deviceMaintenanceDto.maintenancePlanTime}
            </if>
            <if test="deviceMaintenanceDto.maintenanceActuallyTime != null">
                and dm.maintenance_actually_time like concat('%',#{deviceMaintenanceDto.maintenanceActuallyTime},'%')
            </if>
            <if test="deviceMaintenanceDto.maintenanceActuallyTime != null">
                and dm.maintenance_actually_time >= str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d')
                and dm.maintenance_actually_time &lt; date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day)
                and dm.maintenance_actually_time &gt;= str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime},
                '%Y-%m-%d')
                and dm.maintenance_actually_time &lt;
                date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day)
            </if>
        </where>
        order by dm.create_time desc
        order by
        <!--    å¾…保养(0)优先排在上面,已完结(1)在下面 -->
        dm.status asc,
        case
        <!-- å½“状态是 0(待保养)时,按计划时间升序,即时间最远的单子在最上面 -->
        when dm.status = 0 then dm.maintenance_plan_time
        end asc,
        case
        <!-- å½“状态是 1(已完结)时,按实际保养时间降序,最近刚保养完的单子在已完结里排最前 -->
        when dm.status = 1 then dm.maintenance_actually_time
        end desc
    </select>
    <select id="detailById" resultType="com.ruoyi.device.vo.DeviceMaintenanceVo">
        select dm.id,
               dm.device_ledger_id,
src/main/resources/mapper/device/DeviceRepairMapper.xml
@@ -7,28 +7,29 @@
    <select id="queryPage" resultType="com.ruoyi.device.vo.DeviceRepairVo">
        select dr.id,
               dr.device_ledger_id,
                dr.repair_time,
                dr.repair_name,
                dr.remark,
                 dr.maintenance_name,
                 dr.maintenance_time,
                 dr.maintenance_result,
                 dr.acceptance_name,
                 dr.acceptance_time,
                 dr.acceptance_remark,
                 dr.status,
                dr.create_time,
                dr.update_time,
                dr.create_user,
                dr.update_user,
               dr.tenant_id,
               dl.device_name,
               dl.device_model,
               dr.machinery_category
        dr.device_ledger_id,
        dr.repair_time,
        dr.repair_name,
        dr.remark,
        dr.maintenance_name,
        dr.maintenance_time,
        dr.maintenance_result,
        dr.acceptance_name,
        dr.acceptance_time,
        dr.acceptance_remark,
        dr.status,
        dr.create_time,
        dr.update_time,
        dr.create_user,
        dr.update_user,
        dr.tenant_id,
        dl.device_name,
        dl.device_model,
        dr.machinery_category
        from device_repair dr
        left join device_ledger dl on dr.device_ledger_id = dl.id
        <where>
            1 = 1
            <if test="deviceRepairDto.deviceName != null">
                and dl.device_name like concat('%',#{deviceRepairDto.deviceName},'%')
            </if>
@@ -52,8 +53,9 @@
                and dr.maintenance_time like concat('%',#{deviceRepairDto.maintenanceTimeStr},'%')
            </if>
        </where>
        order by dr.create_time desc
        order by dr.id desc
    </select>
    <select id="detailById" resultType="com.ruoyi.device.vo.DeviceRepairVo">
        select dr.id,
               dr.device_ledger_id,
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerMapper.xml
@@ -62,6 +62,6 @@
                AND record_date = DATE_FORMAT(#{req.recordDate},'%Y-%m-%d')
            </if>
        </where>
        ORDER BY create_time DESC
        ORDER BY update_time DESC
    </select>
</mapper>
src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
@@ -32,7 +32,7 @@
                AND t1.record_date = DATE_FORMAT(#{req.recordDate},'%Y-%m-%d')
            </if>
        </where>
        ORDER BY t1.create_time DESC
        ORDER BY t1.update_time DESC
    </select>
    <select id="list" resultType="com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedgerRecord">
        SELECT
@@ -65,4 +65,4 @@
        </where>
        ORDER BY t1.update_time DESC
    </select>
</mapper>
</mapper>
src/main/resources/mapper/measuringinstrumentledger/SparePartsMapper.xml
@@ -10,6 +10,5 @@
                and sp.name like concat('%',#{spareParts.name},'%')
            </if>
        </where>
        order by sp.create_time desc
    </select>
</mapper>
</mapper>
src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
@@ -6,30 +6,13 @@
        select rm.*,
               c.customer_name,
               si.shipping_no,
               rm.shipping_type,
               rm.truck_plate_no,
               rm.express_company,
               rm.express_no,
               sl.project_name,
               sl.sales_contract_no,
               sl.salesman,
                CASE
                    WHEN tempA.total_approval = 0 OR tempA.total_approval IS NULL THEN 0
                    WHEN tempA.approval_sum = tempA.total_approval THEN 1
                    ELSE 2
                END AS stock_in_approval_status
               sl.salesman
        from return_management rm
                 left join shipping_info si on rm.shipping_id = si.id
                 left join customer c on rm.customer_id = c.id
                 left join sales_ledger sl on si.sales_ledger_id = sl.id
                LEFT JOIN (
                SELECT
                rsp.return_management_id,
                COUNT(sir.id) AS total_approval,
                SUM(CASE WHEN sir.approval_status = 1 THEN 1 ELSE 0 END) AS approval_sum
                FROM return_sale_product rsp LEFT JOIN stock_in_record sir ON rsp.id = sir.record_id
                GROUP BY rsp.return_management_id
                ) tempA ON tempA.return_management_id = rm.id
        <where>
            <if test="req.returnNo != null and req.returnNo != ''">
                and rm.return_no like concat('%',#{req.returnNo},'%')
@@ -56,16 +39,11 @@
                and sl.sales_contract_no like concat('%',#{req.salesContractNo},'%')
            </if>
        </where>
        order by rm.id desc
    </select>
    <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnManagementDto">
     select rm.*,
               c.customer_name,
               si.shipping_no,
               rm.shipping_type,
               rm.truck_plate_no,
               rm.express_company,
               rm.express_no,
               sl.project_name,
               sl.sales_contract_no,
               sl.salesman
@@ -80,10 +58,6 @@
                rm.return_no,
                c.customer_name,
                si.shipping_no,
                rm.shipping_type,
                rm.truck_plate_no,
                rm.express_company,
                rm.express_no,
                rm.make_time,
                rm.refund_amount,
                rm.return_reason,
src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
@@ -19,9 +19,8 @@
            sor.outbound_batches,
            sor.stock_out_num,
            sor.batch_no,
            GREATEST( sor.stock_out_num - COALESCE ( rs.total_return_num, 0 ), 0 ) AS un_quantity,
            COALESCE ( rs.pending_return_num, 0 ) AS pending_return_num ,
            COALESCE ( rs.return_num, 0 ) AS total_return_num
               GREATEST(sor.stock_out_num - COALESCE(rs1.total_return_num1, 0), 0) AS un_quantity,
               COALESCE(rs.total_return_num, 0)                             AS total_return_num
        FROM return_sale_product rsp
                 LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
                 LEFT JOIN shipping_info si ON si.id = rm.shipping_id
@@ -31,11 +30,15 @@
                left join product_model pm on slp.product_model_id = pm.id
                  LEFT JOIN product p on pm.product_id = p.id
                 LEFT JOIN (SELECT stock_out_record_id,
                                   SUM(num) AS total_return_num,
                                   SUM( CASE WHEN return_management_id =#{returnManagementId} AND STATUS = 0 THEN num ELSE 0 END ) AS pending_return_num ,
                                   SUM( CASE WHEN STATUS = 1 THEN num ELSE 0 END ) AS return_num
                                   SUM(num) AS total_return_num
                            FROM return_sale_product
                            WHERE 1 = 1 and return_management_id = #{returnManagementId}
                            GROUP BY stock_out_record_id) rs ON rs.stock_out_record_id = sor.id
                 LEFT JOIN (SELECT stock_out_record_id,
                                   SUM(num) AS total_return_num1
                            FROM return_sale_product
                            WHERE 1 = 1
                            GROUP BY stock_out_record_id) rs1 ON rs1.stock_out_record_id = sor.id
        where rm.id =#{returnManagementId}
    </select>
    <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.bean.dto.ReturnSaleProductDto">
src/main/resources/mapper/production/ProductionAccountMapper.xml
@@ -124,8 +124,10 @@
        select date_format(scheduling_date, '%m-%d') as dateStr,
               cast(ifnull(sum(finished_num), 0) as decimal(18,2)) as numberOfCompleted,
               cast(ifnull(sum(work_hours), 0) as decimal(18,2)) as amount,
               cast(0 as decimal(18,2)) as passRate
               cast(sum(ppo.quantity) / sum(ppo.quantity + ppo.scrap_qty) * 100.00 as decimal(18,2)) as passRate
        from production_account
        left join production_product_main ppm on ppm.id = production_account.production_product_main_id
        left join production_product_output ppo on ppo.production_product_main_id = ppm.id
        where scheduling_date &gt;= #{startDate}
          and scheduling_date &lt;= #{endDate}
        group by date_format(scheduling_date, '%m-%d')
src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -71,7 +71,7 @@
                and pot.work_order_no like concat('%', #{c.workOrderNo}, '%')
            </if>
        </where>
        order by pot.production_order_id desc, poro.drag_sort
        order by pot.work_order_no desc
    </select>
    <select id="selectTaskStatisticsByDate" resultType="com.ruoyi.home.dto.ProductionTaskStatisticsDto">
@@ -123,9 +123,9 @@
            <if test="endDateTime != null">
                and ppo.create_time &lt;= #{endDateTime}
            </if>
            <if test="userId != null">
                and ppm.create_user = #{userId}
            </if>
<!--            <if test="userId != null">-->
<!--                and ppm.create_user = #{userId}-->
<!--            </if>-->
            <if test="processIds != null and processIds.size() > 0">
                and poro.technology_operation_id in
                <foreach collection="processIds" item="id" open="(" separator="," close=")">
@@ -215,4 +215,18 @@
        order by min(poro.drag_sort), poro.operation_name
    </select>
    <select id="listProcessStatusByOrderIds" resultType="com.ruoyi.production.bean.vo.ProductionOrderProcessTaskVo">
        select pot.production_order_id as productionOrderId,
               poro.operation_name as operationName,
               ROUND(IFNULL(pot.complete_quantity, 0) / NULLIF(pot.plan_quantity, 0) * 100, 2) AS completionStatus
        from production_operation_task pot
                 left join production_order_routing_operation poro
                           on pot.production_order_routing_operation_id = poro.id
        where pot.production_order_id in
        <foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
            #{orderId}
        </foreach>
        order by pot.production_order_id, poro.drag_sort asc, pot.id asc
    </select>
</mapper>
src/main/resources/mapper/production/ProductionOrderMapper.xml
@@ -134,7 +134,7 @@
        <include refid="ProductionOrderVoColumns" />
        <include refid="ProductionOrderVoFrom" />
        <include refid="ProductionOrderWhere" />
        order by po.id desc
        order by po.nps_no desc
    </select>
    <select id="listProductionOrder" resultMap="ProductionOrderVoResultMap">
@@ -232,10 +232,8 @@
                and po.create_time &lt; #{endTime}
            </if>
        </where>
        order by po.id desc
        <if test="size != null and size &gt;= 0">
            limit #{offset}, #{size}
        </if>
        order by po.nps_no desc
        limit #{offset}, #{size}
    </select>
    <select id="countHomeOrderProgress" resultType="java.lang.Long">
src/main/resources/mapper/production/ProductionPlanMapper.xml
@@ -58,7 +58,7 @@
                </if>
            </if>
        </where>
        ORDER BY COALESCE(pp.id) DESC
        ORDER BY pp.required_date DESC
    </select>
    <select id="selectWithMaterialByIds" resultType="com.ruoyi.production.bean.dto.ProductionPlanDto">
src/main/resources/mapper/production/ProductionProductInputMapper.xml
@@ -42,5 +42,15 @@
        </where>
        order by ppi.create_time desc
    </select>
    <select id="selectInputStats" resultType="java.util.Map">
        select DATE_FORMAT(create_time, '%Y-%m-%d') as statDate,
               sum(ifnull(quantity, 0)) as quantity
#                sum(ifnull(scrap_qty, 0)) as scrapQty
        from production_product_input
        where create_time &gt;= #{startDate}
          and create_time &lt;= #{endDate}
        group by DATE_FORMAT(create_time, '%Y-%m-%d')
        order by statDate asc
    </select>
</mapper>
src/main/resources/mapper/production/ProductionProductOutputMapper.xml
@@ -48,13 +48,13 @@
    </select>
    <select id="selectDailyOutputStats" resultType="java.util.Map">
        select date(create_time) as statDate,
        select DATE_FORMAT(create_time, '%Y-%m-%d') as statDate,
               sum(ifnull(quantity, 0)) as quantity,
               sum(ifnull(scrap_qty, 0)) as scrapQty
        from production_product_output
        where create_time &gt;= #{startDate}
          and create_time &lt;= #{endDate}
        group by date(create_time)
        group by DATE_FORMAT(create_time, '%Y-%m-%d')
        order by statDate asc
    </select>
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml
@@ -6,8 +6,7 @@
    <update id="updateContractAmountById">
        UPDATE purchase_ledger
        SET contract_amount = #{totalTaxInclusiveAmount},
            net_contract_amount = #{totalTaxInclusiveAmount}
        SET contract_amount = #{totalTaxInclusiveAmount}
        WHERE id = #{id}
    </update>
    <select id="selectPurchaseStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
@@ -28,7 +27,6 @@
                pl.supplier_name,
                pl.project_name,
                pl.contract_amount,
                pl.net_contract_amount,
                pl.entry_date,
                pl.execution_date,
                pl.recorder_id,
@@ -90,6 +88,23 @@
                                AND slp.sales_ledger_id = qi.purchase_ledger_id
                                AND slp.product_model_id = qi.product_model_id
                            WHERE sir.approval_status = 1
                            UNION ALL
        SELECT slp.id AS sales_ledger_product_id,
        sir.stock_in_num
        FROM stock_in_record sir
        INNER JOIN quality_unqualified qu
        ON TRIM(sir.record_type) = '11'
        AND sir.record_id = qu.id
        INNER JOIN quality_inspect qi
        ON qi.id = qu.inspect_id
        INNER JOIN sales_ledger_product slp
        ON slp.type = 2
        AND slp.sales_ledger_id = qi.purchase_ledger_id
        AND slp.product_model_id = qi.product_model_id
        WHERE sir.approval_status = 1
                        ) rel
                        GROUP BY rel.sales_ledger_product_id
                    ) approved_qty ON approved_qty.sales_ledger_product_id = slp.id
@@ -122,12 +137,6 @@
                <if test="c.supplierId != null">
                    AND pl.supplier_id = #{c.supplierId}
                </if>
                <if test="c.ids != null and c.ids.size() > 0">
                    AND pl.id IN
                    <foreach collection="c.ids" item="id" open="(" separator="," close=")">
                        #{id}
                    </foreach>
                </if>
                <if test="c.approvalStatus != null">
                    AND pl.approval_status = #{c.approvalStatus}
                </if>
@@ -136,47 +145,7 @@
        <if test="c.stockInStatus != null and c.stockInStatus != ''">
            WHERE result.stock_in_status = #{c.stockInStatus}
        </if>
        ORDER BY result.entry_date DESC
    </select>
    <select id="selectReturnablePurchaseLedgerList" resultType="com.ruoyi.purchase.pojo.PurchaseLedger">
        SELECT DISTINCT pl.*
        FROM purchase_ledger pl
        WHERE 1 = 1
        <if test="c.purchaseContractNumber != null and c.purchaseContractNumber != ''">
            AND pl.purchase_contract_number LIKE CONCAT('%', #{c.purchaseContractNumber}, '%')
        </if>
        <if test="c.supplierId != null">
            AND pl.supplier_id = #{c.supplierId}
        </if>
        <if test="c.approvalStatus != null">
            AND pl.approval_status = #{c.approvalStatus}
        </if>
        AND EXISTS (
            SELECT 1
            FROM stock_in_record sir
            LEFT JOIN quality_inspect qi
                ON sir.record_type = '10'
               AND sir.record_id = qi.id
            LEFT JOIN sales_ledger_product slp
                ON slp.sales_ledger_id = pl.id
               AND slp.product_model_id = sir.product_model_id
               AND slp.type = 2
            LEFT JOIN (
                SELECT stock_in_record_id, SUM(return_quantity) AS total_return_num
                FROM purchase_return_order_products
                GROUP BY stock_in_record_id
            ) rs ON rs.stock_in_record_id = sir.id
            WHERE sir.approval_status = 1
              AND sir.record_type IN ('7','10')
              AND (
                  (sir.record_type = '7' AND sir.record_id = pl.id)
                  OR (sir.record_type = '10' AND qi.purchase_ledger_id = pl.id)
              )
              AND slp.id IS NOT NULL
              AND GREATEST(sir.stock_in_num - COALESCE(rs.total_return_num, 0), 0) > 0
        )
        ORDER BY pl.entry_date DESC
        ORDER BY result.entry_date DESC, result.purchase_contract_number DESC
    </select>
    <select id="selectTotalPurchaseAmount" resultType="java.math.BigDecimal">
src/main/resources/mapper/purchase/PurchaseReturnOrdersMapper.xml
@@ -8,10 +8,6 @@
        <result column="no" property="no" />
        <result column="return_type" property="returnType" />
        <result column="supplier_id" property="supplierId" />
        <result column="shipping_type" property="shippingType" />
        <result column="truck_plate_no" property="truckPlateNo" />
        <result column="express_company" property="expressCompany" />
        <result column="express_no" property="expressNo" />
        <result column="project_id" property="projectId" />
        <result column="project_phase" property="projectPhase" />
        <result column="prepared_at" property="preparedAt" />
@@ -26,18 +22,7 @@
        SELECT
            pro.*,
            sm.supplier_name as supplier_name,
            pl.purchase_contract_number as purchase_contract_number,
            CASE
                WHEN EXISTS (
                    SELECT 1
                    FROM purchase_return_order_products prop
                    INNER JOIN stock_out_record sor ON sor.record_id = prop.id
                        AND sor.record_type = '9'
                        AND sor.approval_status = 1
                    WHERE prop.purchase_return_order_id = pro.id
                ) THEN 1
                ELSE 0
            END AS stock_out_approved
            pl.purchase_contract_number as purchase_contract_number
        FROM purchase_return_orders pro
                 LEFT JOIN supplier_manage sm ON pro.supplier_id = sm.id
                 LEFT JOIN purchase_ledger pl ON pl.id = pro.purchase_ledger_id
@@ -74,10 +59,6 @@
                pro.no returnNo,
                t.inboundBatches,
                sm.supplier_name,
                pro.shipping_type,
                pro.truck_plate_no,
                pro.express_company,
                pro.express_no,
                pro.prepared_at,
                pro.total_amount,
                CASE pro.return_type WHEN 0 THEN '退货退款' WHEN 1 THEN '拒收' END AS returnType,
@@ -119,22 +100,13 @@
            sir.stock_in_num,
            sir.batch_no,
            slp.tax_inclusive_unit_price,
            GREATEST(
                sir.stock_in_num
                - COALESCE(rs.total_return_num, 0)
                - COALESCE(so.total_sale_out_num, 0),
                0
            ) AS un_quantity,
            COALESCE(rs.total_return_num, 0) AS total_return_num,
            COALESCE(so.total_sale_out_num, 0) AS saleOutQuantity
            GREATEST(sir.stock_in_num - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
            COALESCE(rs.total_return_num, 0) AS total_return_num
            FROM stock_in_record sir
            LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id
            LEFT JOIN purchase_ledger pl
            ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id)
            LEFT JOIN sales_ledger_product slp
                ON pl.id = slp.sales_ledger_id
               AND slp.product_model_id = sir.product_model_id
               AND slp.type = 2
            LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id
            LEFT JOIN (
                SELECT
                    stock_in_record_id,
@@ -144,24 +116,7 @@
                WHERE 1=1
                GROUP BY stock_in_record_id
            ) rs ON rs.stock_in_record_id = sir.id
            LEFT JOIN (
                SELECT
                    inv.batch_no,
                    inv.product_model_id,
                    SUM(spd.quantity) AS total_sale_out_num
                FROM shipping_product_detail spd
                INNER JOIN shipping_info si
                    ON si.id = spd.shipping_info_id
                INNER JOIN stock_out_record sor
                    ON sor.record_id = si.id
                   AND sor.record_type = '13'
                   AND sor.approval_status IN (0, 1, 3)
                INNER JOIN stock_inventory inv
                    ON inv.id = spd.stock_inventory_id
                GROUP BY inv.batch_no, inv.product_model_id
            ) so ON so.product_model_id = sir.product_model_id
               AND (so.batch_no = sir.batch_no OR (so.batch_no IS NULL AND sir.batch_no IS NULL))
        WHERE sir.approval_status = 1
        WHERE sir.approval_status = 1 AND slp.type = 2
        AND sir.record_type IN ('7','10')
         and pl.id = #{purchaseLedgerId}
    </select>
@@ -180,14 +135,8 @@
           slp.tax_inclusive_unit_price,
           prop.return_quantity,
           prop.purchase_return_order_id,
           GREATEST(
               sir.stock_in_num
               - COALESCE(rs.total_return_num, 0)
               - COALESCE(so.total_sale_out_num, 0),
               0
           ) AS un_quantity,
           COALESCE(rs.total_return_num, 0)                             AS total_return_num,
           COALESCE(so.total_sale_out_num, 0)                           AS saleOutQuantity
           GREATEST(sir.stock_in_num - COALESCE(rs1.total_return_num1, 0), 0) AS un_quantity,
           COALESCE(rs.total_return_num, 0)                             AS total_return_num
    from purchase_return_order_products prop
    left join purchase_return_orders pro on prop.purchase_return_order_id = pro.id
    LEFT JOIN stock_in_record sir ON prop.stock_in_record_id = sir.id and sir.record_type in ('7','10')
@@ -195,25 +144,13 @@
    LEFT JOIN (SELECT stock_in_record_id,
                      SUM(return_quantity) AS total_return_num
               FROM purchase_return_order_products
               WHERE 1 = 1
               WHERE 1 = 1 and purchase_return_order_id = #{id}
               GROUP BY stock_in_record_id) rs ON rs.stock_in_record_id = sir.id
    LEFT JOIN (
        SELECT
            inv.batch_no,
            inv.product_model_id,
            SUM(spd.quantity) AS total_sale_out_num
        FROM shipping_product_detail spd
        INNER JOIN shipping_info si
            ON si.id = spd.shipping_info_id
        INNER JOIN stock_out_record sor
            ON sor.record_id = si.id
           AND sor.record_type = '13'
           AND sor.approval_status IN (0, 1, 3)
        INNER JOIN stock_inventory inv
            ON inv.id = spd.stock_inventory_id
        GROUP BY inv.batch_no, inv.product_model_id
    ) so ON so.product_model_id = sir.product_model_id
       AND (so.batch_no = sir.batch_no OR (so.batch_no IS NULL AND sir.batch_no IS NULL))
    LEFT JOIN (SELECT stock_in_record_id,
                      SUM(return_quantity) AS total_return_num1
               FROM purchase_return_order_products
               WHERE 1 = 1 and purchase_return_order_id = #{id}
               GROUP BY stock_in_record_id) rs1 ON rs1.stock_in_record_id = sir.id
    where pro.id = #{id}
    </select>
</mapper>
src/main/resources/mapper/quality/QualityInspectMapper.xml
@@ -4,6 +4,10 @@
    <select id="qualityInspectListPage" resultType="com.ruoyi.quality.dto.QualityInspectDto">
        SELECT
        qi.*,
        CASE
            WHEN (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) = 0 THEN 0
            ELSE ROUND(IFNULL(qi.qualified_quantity, 0) / (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) * 100, 2)
        END AS passRate,
        <choose>
            <when test="qualityInspect.inspectType == 0">
                pl.purchase_contract_number as purchase_contract_no
@@ -54,21 +58,16 @@
        <if test="qualityInspect.entryDateEnd != null and qualityInspect.entryDateEnd != '' ">
            AND qi.check_time &lt;= DATE_FORMAT(#{qualityInspect.entryDateEnd},'%Y-%m-%d')
        </if>
        <if test="qualityInspect.purchaseContractNo != null and qualityInspect.purchaseContractNo != '' ">
            AND pl.purchase_contract_number like concat('%',#{qualityInspect.purchaseContractNo},'%')
        </if>
        <if test="qualityInspect.workOrderNo != null and qualityInspect.workOrderNo != '' ">
            AND pot.work_order_no like concat('%',#{qualityInspect.workOrderNo},'%')
        </if>
        <if test="qualityInspect.salesContractNo != null and qualityInspect.salesContractNo != '' ">
            AND po_sales.sales_contract_no like concat('%',#{qualityInspect.salesContractNo},'%')
        </if>
        ORDER BY qi.check_time DESC
        ORDER BY coalesce(qi.check_time, qi.create_time) DESC
    </select>
    <select id="qualityInspectExport" resultType="com.ruoyi.quality.pojo.QualityInspect">
        SELECT
        *
        qi.*,
        CASE
            WHEN (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) = 0 THEN 0
            ELSE ROUND(IFNULL(qi.qualified_quantity, 0) / (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) * 100, 2)
        END AS passRate
        FROM quality_inspect
        where
        inspect_type=#{qualityInspect.inspectType}
@@ -95,11 +94,7 @@
    </delete>
    <select id="getInspectStatistics" resultType="com.ruoyi.quality.dto.QualityInspectStatDto">
        SELECT CASE pp.product_name
                   WHEN '原材料' THEN 0
                   WHEN '半成品' THEN 1
                   WHEN '成品' THEN 2
                   END                     AS modelType,
        SELECT qi.inspect_type AS modelType,
               IFNULL(SUM(qi.quantity), 0) AS totalCount,
@@ -108,104 +103,44 @@
                              ELSE 0
                   END), 0)                AS completedCount
        FROM product p
                 INNER JOIN product pp
                            ON p.parent_id = pp.id
                 LEFT JOIN product_model pm
                           ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
        FROM quality_inspect qi
        WHERE pp.product_name IN ('原材料', '半成品', '成品')
        GROUP BY pp.product_name
        GROUP BY qi.inspect_type
    </select>
    <select id="getPassRateStatistics" resultType="com.ruoyi.quality.dto.QualityPassRateDto">
        SELECT t.modelType,
               t.totalCount,
               t.completedCount,
               t.qualifiedCount,
               t.unqualifiedCount,
               COALESCE(SUM(qi.quantity), 0) AS totalCount,
            /* å®ŒæˆçŽ‡ï¼š
               å¦‚果总数为 0 åˆ™è¿”回 0;否则计算比例,使用 TRUNCATE ä¿ç•™ 4 ä½å°æ•°ï¼ˆå¦‚ 99.9992)
            */
               IF(t.totalCount = 0, 0,
                  TRUNCATE((t.completedCount / t.totalCount) * 100, 2)
               ) AS completionRate,
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1 THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0)                AS completedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1
                                        THEN qi.qualified_quantity
                                    ELSE 0
                                    END
                        ), 0)                AS qualifiedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1
                                        THEN qi.unqualified_quantity
                                    ELSE 0
                                    END
                        ), 0)                AS unqualifiedCount,
            /* å®Œæˆçއ */
               IF(COALESCE(SUM(qi.quantity), 0) = 0, 0,
                  ROUND(
                          COALESCE(SUM(
                                           CASE
                                               WHEN qi.inspect_state = 1 THEN qi.quantity
                                               ELSE 0
                                               END
                                   ), 0)
                              / SUM(qi.quantity) * 100, 2)
               )                             AS completionRate,
            /* åˆæ ¼çއ */
               IF(COALESCE(SUM(
                                   CASE
                                       WHEN qi.inspect_state = 1 THEN qi.quantity
                                       ELSE 0
                                       END
                           ), 0) = 0, 0,
                  ROUND(
                          COALESCE(SUM(
                                           CASE
                                               WHEN qi.inspect_state = 1
                                                   THEN qi.qualified_quantity
                                               ELSE 0
                                               END
                                   ), 0)
                              /
                          COALESCE(SUM(
                                           CASE
                                               WHEN qi.inspect_state = 1 THEN qi.quantity
                                               ELSE 0
                                               END
                                   ), 0) * 100, 2)
               )                             AS passRate
        FROM (SELECT 0 AS modelType
              UNION ALL
              SELECT 1
              UNION ALL
              SELECT 2) t
                 LEFT JOIN product p
                           ON 1 = 1
                 LEFT JOIN product pp
                           ON p.parent_id = pp.id
                 LEFT JOIN product_model pm
                           ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND (
                                  (pp.product_name = '原材料' AND t.modelType = 0) OR
                                  (pp.product_name = '半成品' AND t.modelType = 1) OR
                                  (pp.product_name = '成品' AND t.modelType = 2)
                                  )
        GROUP BY t.modelType
            /* åˆæ ¼çŽ‡ï¼š
               å¦‚果完工数为 0 åˆ™è¿”回 0;使用 TRUNCATE æˆªæ–­ï¼Œé˜²æ­¢æžå…¶æŽ¥è¿‘ 100% çš„æ•°æ®è¢«å››èˆäº”入成 100
            */
               IF(t.completedCount = 0, 0,
                  TRUNCATE((t.qualifiedCount / t.completedCount) * 100, 2)
               ) AS passRate
        FROM (
                 /* å…ˆé€šè¿‡å­æŸ¥è¯¢æŠŠåŸºç¡€æ•°æ®è¿›è¡Œèšåˆç»Ÿè®¡ï¼Œé¿å…å¤–层重复编写复杂的 CASE WHEN */
                 SELECT qi.inspect_type                                                                          AS modelType,
                        COALESCE(SUM(qi.quantity), 0)                                                            AS totalCount,
                        COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END),
                                 0)                                                                              AS completedCount,
                        COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.qualified_quantity ELSE 0 END),
                                 0)                                                                              AS qualifiedCount,
                        COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.unqualified_quantity ELSE 0 END),
                                 0)                                                                              AS unqualifiedCount
                 FROM quality_inspect qi
                 GROUP BY qi.inspect_type) t
        ORDER BY t.modelType;
    </select>
@@ -226,45 +161,74 @@
                     FROM months m
                              CROSS JOIN types t)
        SELECT CASE b.month_num
                   WHEN 1 THEN '一月'
                   WHEN 2 THEN '二月'
                   WHEN 3 THEN '三月'
                   WHEN 4 THEN '四月'
                   WHEN 5 THEN '五月'
                   WHEN 6 THEN '六月'
                   WHEN 7 THEN '七月'
                   WHEN 8 THEN '八月'
                   WHEN 9 THEN '九月'
                   WHEN 10 THEN '十月'
                   WHEN 11 THEN '十一月'
                   WHEN 12 THEN '十二月'
                   END                       AS month,
        SELECT
            CASE t.month_num
                WHEN 1 THEN '一月'
                WHEN 2 THEN '二月'
                WHEN 3 THEN '三月'
                WHEN 4 THEN '四月'
                WHEN 5 THEN '五月'
                WHEN 6 THEN '六月'
                WHEN 7 THEN '七月'
                WHEN 8 THEN '八月'
                WHEN 9 THEN '九月'
                WHEN 10 THEN '十月'
                WHEN 11 THEN '十一月'
                WHEN 12 THEN '十二月'
                END AS month,
               b.modelType,
        t.modelType,
        t.totalCount,
        t.completedCount,
        t.qualifiedCount,
        t.unqualifiedCount,
            /* æ€»æ•°é‡ */
        IF(t.totalCount = 0, 0,
           TRUNCATE((t.completedCount / t.totalCount) * 100, 2)
        ) AS completionRate,
        IF(t.completedCount = 0, 0,
           TRUNCATE((t.qualifiedCount / t.completedCount) * 100, 2)
        ) AS passRate
        FROM (
            SELECT
            b.month_num,
            b.modelType,
            COALESCE(SUM(qi.quantity), 0) AS totalCount,
            COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END), 0) AS completedCount,
            COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.qualified_quantity ELSE 0 END), 0) AS qualifiedCount,
            COALESCE(SUM(CASE WHEN qi.inspect_state = 1 THEN qi.unqualified_quantity ELSE 0 END), 0) AS unqualifiedCount
            FROM base b
            LEFT JOIN quality_inspect qi ON qi.inspect_type = b.modelType
            AND YEAR(qi.check_time) = #{year}
            AND MONTH(qi.check_time) = b.month_num
            GROUP BY b.month_num, b.modelType
            ) t
        ORDER BY t.month_num, t.modelType;
    </select>
    <select id="getYearlyPassRateStatistics" resultType="com.ruoyi.quality.dto.QualityPassRateDto">
        SELECT qi.inspect_type AS modelType,
               COALESCE(SUM(qi.quantity), 0) AS totalCount,
            /* å·²å®Œæˆ */
               COALESCE(SUM(
                                CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                        ), 0)                AS completedCount,
            /* åˆæ ¼ */
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1
                                        THEN qi.qualified_quantity
                                    WHEN qi.inspect_state = 1 THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0)                AS completedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1 THEN qi.qualified_quantity
                                    ELSE 0
                                    END
                        ), 0)                AS qualifiedCount,
            /* ä¸åˆæ ¼ */
               COALESCE(SUM(
                                CASE
                                    WHEN qi.inspect_state = 1
                                        THEN qi.unqualified_quantity
                                    WHEN qi.inspect_state = 1 THEN qi.unqualified_quantity
                                    ELSE 0
                                    END
                        ), 0)                AS unqualifiedCount,
@@ -274,8 +238,8 @@
                  ROUND(
                          COALESCE(SUM(
                                           CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                                   ), 0) / SUM(qi.quantity) * 100, 2
                  )
                                   ), 0)
                              / SUM(qi.quantity) * 100, 2)
               )                             AS completionRate,
            /* åˆæ ¼çއ */
@@ -284,78 +248,18 @@
                           ), 0) = 0, 0,
                  ROUND(
                          COALESCE(SUM(
                                           CASE
                                               WHEN qi.inspect_state = 1
                                                   THEN qi.qualified_quantity
                                               ELSE 0
                                               END
                                           CASE WHEN qi.inspect_state = 1 THEN qi.qualified_quantity ELSE 0 END
                                   ), 0)
                              /
                          COALESCE(SUM(
                                           CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                                   ), 0) * 100, 2
                  )
                                   ), 0) * 100, 2)
               )                             AS passRate
        FROM base b
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND YEAR(qi.check_time) = #{year}
                               AND MONTH(qi.check_time) = b.month_num
                               AND (
                                  (pp.product_name = '原材料' AND b.modelType = 0)
                                      OR (pp.product_name = '半成品' AND b.modelType = 1)
                                      OR (pp.product_name = '成品' AND b.modelType = 2)
                                  )
        GROUP BY b.month_num, b.modelType
        ORDER BY b.month_num, b.modelType;
    </select>
    <select id="getYearlyPassRateStatistics" resultType="com.ruoyi.quality.dto.QualityPassRateDto">
        SELECT t.modelType,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0) AS totalCount,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.qualified_quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.qualified_quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.qualified_quantity
                                    ELSE 0
                                    END
                        ), 0) AS qualifiedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.unqualified_quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.unqualified_quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.unqualified_quantity
                                    ELSE 0
                                    END
                        ), 0) AS unqualifiedCount
        FROM (SELECT 0 AS modelType
              UNION ALL
              SELECT 1
              UNION ALL
              SELECT 2) t
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND YEAR(qi.check_time) = #{year}
                               AND qi.inspect_state = 1
        GROUP BY t.modelType
        ORDER BY t.modelType;
        FROM quality_inspect qi
        WHERE YEAR(qi.check_time) = #{year}
        GROUP BY qi.inspect_type
        ORDER BY qi.inspect_type;
    </select>
@@ -384,7 +288,7 @@
            /* åŽŸææ–™ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料'
                                    WHEN qi.inspect_type = 0
                                        THEN qi.quantity
                                    ELSE 0
                                    END
@@ -393,7 +297,7 @@
            /* åŠæˆå“ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '半成品'
                                    WHEN qi.inspect_type = 1
                                        THEN qi.quantity
                                    ELSE 0
                                    END
@@ -402,19 +306,15 @@
            /* æˆå“ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '成品'
                                    WHEN qi.inspect_type = 2
                                        THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0) AS outgoingCount
        FROM months m
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND qi.inspect_state = 1
                           ON qi.inspect_state = 1
                               AND YEAR(qi.check_time) = #{year}
                               AND MONTH(qi.check_time) = m.month_num
@@ -429,16 +329,8 @@
                                  FROM quality_inspect_param qip
                                           JOIN quality_inspect qi
                                                ON qip.inspect_id = qi.id
                                           JOIN product p
                                                ON qi.product_id = p.id
                                           JOIN product pp
                                                ON p.parent_id = pp.id
                                  WHERE qi.inspect_state = 1
                                    AND (
                                      (#{modelType} = 1 AND pp.product_name = '原材料')
                                          OR (#{modelType} = 2 AND pp.product_name = '半成品')
                                          OR (#{modelType} = 3 AND pp.product_name = '成品')
                                      )
                                    AND qi.inspect_type = #{modelType} - 1
                                  GROUP BY qip.parameter_item),
             ranked AS (SELECT name,
                               count,
src/main/resources/mapper/quality/QualityTestStandardMapper.xml
@@ -19,7 +19,6 @@
        <if test="c.inspectType != null ">
            AND inspect_type =#{c.inspectType}
        </if>
        order by create_time desc
    </select>
    <select id="getQualityTestStandardByProductId" resultType="com.ruoyi.quality.pojo.QualityTestStandard">
        SELECT qts.*
src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
@@ -26,25 +26,26 @@
        END AS method
        FROM quality_unqualified qu
        LEFT JOIN product_model pm ON qu.model = pm.id
        <where>
            <if test="qualityUnqualified.inspectType != null ">
                AND inspect_type = #{qualityUnqualified.inspectType}
            </if>
            <if test="qualityUnqualified.inspectState != null ">
                AND inspect_state = #{qualityUnqualified.inspectState}
            </if>
            <if test="qualityUnqualified.productName != null and qualityUnqualified.productName != '' ">
                AND product_name = #{qualityUnqualified.productName}
            </if>
            <if test="qualityUnqualified.entryDateStart != null and qualityUnqualified.entryDateStart != '' ">
                AND check_time &gt;= DATE_FORMAT(#{qualityUnqualified.entryDateStart},'%Y-%m-%d')
            </if>
            <if test="qualityUnqualified.entryDateEnd != null and qualityUnqualified.entryDateEnd != '' ">
                AND  check_time &lt;= DATE_FORMAT(#{qualityUnqualified.entryDateEnd},'%Y-%m-%d')
            </if>
        </where>
        order by qu.create_time desc
        where
        1=1
        <if test="qualityUnqualified.inspectType != null ">
            AND inspect_type = #{qualityUnqualified.inspectType}
        </if>
        <if test="qualityUnqualified.inspectState != null ">
            AND inspect_state = #{qualityUnqualified.inspectState}
        </if>
        <if test="qualityUnqualified.productName != null and qualityUnqualified.productName != '' ">
            AND product_name = #{qualityUnqualified.productName}
        </if>
        <if test="qualityUnqualified.entryDateStart != null and qualityUnqualified.entryDateStart != '' ">
            AND check_time &gt;= DATE_FORMAT(#{qualityUnqualified.entryDateStart},'%Y-%m-%d')
        </if>
        <if test="qualityUnqualified.entryDateEnd != null and qualityUnqualified.entryDateEnd != '' ">
            AND  check_time &lt;= DATE_FORMAT(#{qualityUnqualified.entryDateEnd},'%Y-%m-%d')
        </if>
        order by qu.check_time desc
    </select>
    <select id="qualityUnqualifiedExport" resultType="com.ruoyi.quality.pojo.QualityUnqualified">
        SELECT
        *
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -56,7 +56,6 @@
        T1.attachment_materials,
        T1.tenant_id,
        T1.contract_amount,
        T1.net_contract_amount,
        T1.contract_amount AS noInvoiceAmountTotal,
        T1.execution_date,
        T2.nick_name AS entry_person_name,
@@ -96,7 +95,7 @@
                AND T1.entry_date &lt;= DATE_FORMAT(#{salesLedgerDto.entryDateEnd},'%Y-%m-%d')
            </if>
        </where>
        order by T1.entry_date desc
        order by T1.entry_date desc, T1.sales_contract_no desc
    </select>
    <select id="selectIncomeStats" resultType="com.ruoyi.home.dto.IncomeExpenseAnalysisDto">
@@ -111,17 +110,19 @@
        FROM sales_ledger
        GROUP BY customer_name
    </select>
    <select id="listSalesLedgerAndShipped" resultType="com.ruoyi.sales.dto.SalesLedgerDto">
        select distinct sl.id as 'disId', sl.* from
        sales_ledger sl
        left join sales_ledger_product slp on sl.id = slp.sales_ledger_id
        left join shipping_info si on slp.id = si.sales_ledger_product_id
        where si.status = '已发货'
        <if test="ew.customerName != null and ew.customerName != '' ">
            and sl.customer_name like concat('%',#{ew.customerName},'%')
        where si.status = '审核通过'
        <if test="ew.customerId != null and ew.customerId != '' ">
            and sl.customer_id = #{ew.customerId}
        </if>
        order by sl.execution_date desc
    </select>
    <select id="selectPurchaseReportVoPage" resultType="com.ruoyi.purchase.vo.PurchaseReportVo">
        select sl.sales_contract_no customerContractNo,
               c.customer_name,
@@ -133,12 +134,10 @@
        from sales_ledger sl
        left join purchase_ledger pl on sl.id = pl.sales_ledger_id
        left join customer c on sl.customer_id = c.id
        <where>
            <if test="customerName != null and customerName != '' ">
                and c.customer_name like concat('%',#{customerName},'%')
            </if>
        </where>
        order by sl.entry_date desc
        where 1=1
        <if test="customerName != null and customerName != '' ">
            and c.customer_name like concat('%',#{customerName},'%')
        </if>
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -30,17 +30,12 @@
        pm.model as specification_model,
        pm.unit as unit,
        CASE
        WHEN (IFNULL(t2.qualitity, 0) - IFNULL(t2.locked_quantity, 0)) >0 THEN 1
        WHEN (IFNULL(t2.qualitity, 0) - IFNULL(t2.locked_quantity, 0)) >= (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) THEN 1
        ELSE 0
        END as has_sufficient_stock,
        (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0) - IFNULL(t5.pending_approval_quantity, 0)) as no_quantity,
        IFNULL(t3.shipped_quantity, 0) as shipped_quantity,
        IFNULL(t6.return_quantity, 0) as return_quantity,
        IFNULL(t4.approved_stock_in_num, 0) as approved_stock_in_num,
        IFNULL(t5.pending_approval_quantity, 0) as pending_approval_quantity,
        (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) as no_quantity,
        CASE
         WHEN IFNULL(t3.shipped_quantity, 0) = 0 AND IFNULL(t5.pending_approval_quantity, 0) = 0 THEN '待发货'
         WHEN IFNULL(t3.shipped_quantity, 0) = 0 AND IFNULL(t5.pending_approval_quantity, 0) > 0 THEN '审批中'
         WHEN IFNULL(t3.shipped_quantity, 0) = 0 THEN '待发货'
         WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN '部分发货'
        ELSE '已发货'
        END as shippingStatus,
@@ -58,60 +53,11 @@
        GROUP BY product_model_id
        ) t2 ON T1.product_model_id = t2.product_model_id
        LEFT JOIN (
        SELECT
            slp.id AS sales_ledger_product_id,
            IFNULL(SUM(so.total_sale_out_num), 0) AS shipped_quantity
        FROM sales_ledger_product slp
        LEFT JOIN (
            SELECT
                pl.id AS purchase_ledger_id,
                sir.product_model_id,
                SUM(spd.quantity) AS total_sale_out_num
            FROM shipping_product_detail spd
            INNER JOIN shipping_info si
                ON si.id = spd.shipping_info_id
            INNER JOIN stock_out_record sor
                ON sor.record_id = si.id
               AND TRIM(sor.record_type) = '13'
               AND sor.approval_status IN (0, 1, 3)
            INNER JOIN stock_inventory inv
                ON inv.id = spd.stock_inventory_id
            INNER JOIN stock_in_record sir
                ON sir.product_model_id = inv.product_model_id
               AND (
                    (sir.batch_no IS NOT NULL AND sir.batch_no = inv.batch_no)
                    OR (sir.batch_no IS NULL AND inv.batch_no IS NULL)
               )
               AND sir.approval_status = 1
               AND sir.record_type IN ('7', '10')
            LEFT JOIN quality_inspect qi
                ON sir.record_type = '10'
               AND sir.record_id = qi.id
            LEFT JOIN purchase_ledger pl
                ON pl.id = IF(sir.record_type = '7', sir.record_id, qi.purchase_ledger_id)
            WHERE si.status IN ('审核通过', '已发货')
              AND pl.id IS NOT NULL
            GROUP BY pl.id, sir.product_model_id
        ) so ON so.purchase_ledger_id = slp.sales_ledger_id
            AND so.product_model_id = slp.product_model_id
        WHERE slp.type = 2
        GROUP BY slp.id
        UNION ALL
        -- é”€å”®å°è´¦(type=1): shipping_info ç›´æŽ¥æŒ‚ sales_ledger_product_id,只统计出库审核通过(approval_status=1)的发货量,与 t5 çš„ pending é€»è¾‘互补
        SELECT
            si.sales_ledger_product_id AS sales_ledger_product_id,
            IFNULL(SUM(spd.quantity), 0) AS shipped_quantity
        SELECT sales_ledger_product_id, IFNULL(SUM(spd.quantity), 0) as shipped_quantity
        FROM shipping_info si
        INNER JOIN shipping_product_detail spd ON si.id = spd.shipping_info_id
        INNER JOIN stock_out_record sor
            ON sor.record_id = si.id
           AND TRIM(sor.record_type) = '13'
           AND sor.approval_status = 1
        WHERE si.status IN ('审核通过', '已发货')
          AND si.sales_ledger_product_id IS NOT NULL
        GROUP BY si.sales_ledger_product_id
        LEFT JOIN shipping_product_detail spd ON si.id = spd.shipping_info_id
        where si.status != '审核拒绝'
        GROUP BY sales_ledger_product_id
        ) t3 ON t3.sales_ledger_product_id = T1.id
        LEFT JOIN (
        SELECT rel.sales_ledger_product_id,
@@ -146,42 +92,6 @@
        ) rel
        GROUP BY rel.sales_ledger_product_id
        ) t4 ON t4.sales_ledger_product_id = T1.id
        LEFT JOIN (
        SELECT si.sales_ledger_product_id, IFNULL(SUM(spd.quantity), 0) as pending_approval_quantity
        FROM shipping_info si
        LEFT JOIN shipping_product_detail spd ON si.id = spd.shipping_info_id
        WHERE si.status IN ('待审核', '审核中')
           OR (si.status = '审核通过' AND NOT EXISTS (
                SELECT 1 FROM stock_out_record sor
                WHERE sor.record_id = si.id
                  AND TRIM(sor.record_type) = '13'
                  AND sor.approval_status = 1
           ))
        GROUP BY si.sales_ledger_product_id
        ) t5 ON t5.sales_ledger_product_id = T1.id
        LEFT JOIN (
            SELECT
                prop.sales_ledger_product_id,
                IFNULL(SUM(prop.return_quantity), 0) AS return_quantity
            FROM purchase_return_order_products prop
            LEFT JOIN purchase_return_orders pro
                ON pro.id = prop.purchase_return_order_id
            GROUP BY prop.sales_ledger_product_id
            UNION ALL
            SELECT
                si.sales_ledger_product_id,
                IFNULL(SUM(rsp.num), 0) AS return_quantity
            FROM return_sale_product rsp
            INNER JOIN return_management rm ON rm.id = rsp.return_management_id
            INNER JOIN shipping_info si ON si.id = rm.shipping_id
            INNER JOIN stock_in_record sir ON sir.record_id = rsp.id
                AND TRIM(sir.record_type) IN ('14','15')
                AND sir.approval_status = 1
            WHERE si.sales_ledger_product_id IS NOT NULL
            GROUP BY si.sales_ledger_product_id
        ) t6 ON t6.sales_ledger_product_id = T1.id
        left join product_model pm ON T1.product_model_id = pm.id
        left join product p ON pm.product_id = p.id
        <where>
@@ -221,7 +131,7 @@
        SUM(slp.tax_inclusive_total_price) AS purchaseAmount,
        COUNT(DISTINCT slp.sales_ledger_id) AS purchaseTimes,
        <!-- å¹³å‡å•ä»· = æ€»é‡‡è´­é‡‘额/总采购数量,保留2位小数,避免除0 -->
        ROUND(IF(SUM(slp.quantity) = 0, 0, SUM(slp.tax_inclusive_total_price) / SUM(slp.quantity)), 2) AS averagePrice,
        ROUND(IF(SUM(slp.quantity) = 0, 0, SUM(slp.tax_inclusive_total_price) / SUM(slp.quantity)), 6) AS averagePrice,
        <!-- è¯¥äº§å“å¤§ç±»ä¸‹æœ€åŽä¸€ä¸ªå½•入日期(取台账主表的entry_date) -->
        MAX(sl.entry_date) AS entryDate,
        COALESCE(NULLIF(SUM(t1.return_quantity), 0), 0) AS return_quantity,
@@ -297,7 +207,7 @@
            JOIN product pr ON slp.product_id = pr.id
        WHERE
            slp.type = 2
            AND pr.parent_id = ( SELECT id FROM product WHERE product_name = '原材料' )
            AND pr.parent_id = ( SELECT id FROM product WHERE product_name = '原料' )
        GROUP BY
            pr.id,
            pr.product_name
@@ -308,20 +218,36 @@
    <select id="selectProductCountByTypeAndDate" resultType="int">
        SELECT IFNULL(COUNT(*), 0)
        FROM sales_ledger_product
        FROM sales_ledger_product slp
        LEFT JOIN sales_ledger sl ON sl.id = slp.sales_ledger_id
        LEFT JOIN purchase_ledger pl ON pl.id = slp.sales_ledger_id
        WHERE type = #{type}
        <if test="startDate != null">
            AND register_date &gt;= #{startDate}
        </if>
        <if test="endDate != null">
            AND register_date &lt;= #{endDate}
        </if>
        <choose>
            <when test="type == 1">
                <if test="startDate != null">
                    AND sl.entry_date &gt;= #{startDate}
                </if>
                <if test="endDate != null">
                    AND sl.entry_date &lt;= #{endDate}
                </if>
            </when>
            <when test="type == 2">
                <if test="startDate != null">
                    AND pl.entry_date &gt;= #{startDate}
                </if>
                <if test="endDate != null">
                    AND pl.entry_date &lt;= #{endDate}
                </if>
            </when>
        </choose>
    </select>
    <select id="selectRawMaterialExpense" resultType="java.math.BigDecimal">
        WITH RECURSIVE product_tree AS (SELECT id
                                        FROM product
                                        WHERE product_name = '原材料'
                                        WHERE product_name = '原料'
                                        UNION ALL
src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -4,19 +4,21 @@
        "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.*,
               t2.approve_user_ids
        FROM sales_quotation t1
        <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}
            </if>
            <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
                AND t1.status = #{salesQuotationDto.status}
            </if>
        </where>
        order by t1.id desc
        LEFT JOIN approve_process t2 ON t1.quotation_no = t2.approve_reason and t2.approve_type = 6
        WHERE 1=1
          and t2.approve_delete = 0
        <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}
        </if>
        <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
            AND t1.status = #{salesQuotationDto.status}
        </if>
        order by t1.quotation_no desc
    </select>
</mapper>
src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -78,8 +78,7 @@
            sor.batch_no,
            slp.tax_inclusive_unit_price,
            GREATEST(sor.stock_out_num - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
            COALESCE(rs.return_num, 0) AS total_return_num,
            COALESCE(rs.pending_return_num, 0) AS pending_return_num
            COALESCE(rs.total_return_num, 0) AS total_return_num
        FROM shipping_info si
        LEFT JOIN shipping_product_detail spd ON spd.shipping_info_id = si.id
        LEFT JOIN stock_out_record sor ON sor.record_id = si.id and sor.record_type = '13'
@@ -87,9 +86,7 @@
        LEFT JOIN (
        SELECT
        stock_out_record_id,
        SUM(num) AS total_return_num,
        SUM( CASE WHEN rsp.STATUS = 0 THEN rsp.num ELSE 0 END ) AS pending_return_num ,
        SUM( CASE WHEN rsp.STATUS = 1 THEN rsp.num ELSE 0 END ) AS return_num
        SUM(num) AS total_return_num
        FROM return_sale_product rsp
        left join return_management rm on rm.id = rsp.return_management_id
        left join shipping_info si on si.id = rm.shipping_id
@@ -104,9 +101,8 @@
        order by sor.id
    </select>
    <select id="getShippingInfoByCustomerName" resultType="com.ruoyi.sales.pojo.ShippingInfo">
        select distinct si.* from shipping_info si
        select * from shipping_info si
        left join sales_ledger sl on si.sales_ledger_id = sl.id
        inner join stock_out_record sor on sor.record_id = si.id and sor.record_type = '13' and sor.approval_status = 1
        where sl.customer_name = #{customerName} and si.status = '审核通过'
        where  sl.customer_name = #{customerName}
    </select>
</mapper>
src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml
@@ -39,10 +39,9 @@
            and personal_attendance_records.date &gt;= #{params.date}
            and personal_attendance_records.date &lt; DATE_ADD(DATE(#{params.date}), INTERVAL 1 DAY)
        </if>
        order by personal_attendance_records.id desc
    </select>
    <!-- æŸ¥è¯¢æŒ‡å®šæ—¥æœŸæœ‰æŽ’班(非请假)但没有考勤记录的在职员工,没排班视为休息不标记缺勤 -->
    <!-- æŸ¥è¯¢æŒ‡å®šæ—¥æœŸæ²¡æœ‰è€ƒå‹¤è®°å½•的在职员工(在指定时间之前入职的) -->
    <select id="selectStaffWithoutAttendanceRecordBeforeTime" resultType="com.ruoyi.staff.pojo.StaffOnJob">
        SELECT soj.*
        FROM staff_on_job soj
@@ -50,11 +49,8 @@
        AND soj.create_time &lt; #{entryDeadline}
        AND EXISTS (
        SELECT 1
        FROM personal_shift ps
        WHERE ps.staff_on_job_id = soj.id
        AND DATE(ps.work_time) = #{date}
        AND ps.personal_attendance_location_config_id IS NOT NULL
        AND ps.personal_attendance_location_config_id != 999
        FROM personal_attendance_location_config palc
        WHERE palc.sys_dept_id = soj.sys_dept_id
        )
        AND NOT EXISTS (
        SELECT 1
src/main/resources/mapper/staff/PersonalShiftMapper.xml
@@ -18,25 +18,22 @@
        u.id user_id
        FROM personal_shift s
        LEFT JOIN staff_on_job u ON u.id = s.staff_on_job_id
        LEFT JOIN staff_leave sl ON sl.staff_on_job_id = u.id
        LEFT JOIN personal_attendance_location_config palc ON palc.id = s.personal_attendance_location_config_id
        <where>
            u.id IS NOT NULL
            <if test="sysDeptId != null and sysDeptId != ''">
                and u.sys_dept_id = #{sysDeptId}
            </if>
            <if test="time != null and time != ''">
                and DATE_FORMAT(s.work_time, '%Y-%m') = DATE_FORMAT(#{time}, '%Y-%m')
                AND (u.staff_state = 1 OR (u.staff_state = 0 AND sl.leave_date IS NOT NULL AND sl.leave_date >= DATE_FORMAT(#{time}, '%Y-%m-01')))
            </if>
            <if test="userName != null and userName != ''">
                and u.staff_name like concat('%', #{userName}, '%')
            </if>
        </where>
        GROUP BY u.id, u.staff_name
        ORDER BY MAX(s.create_time) desc
        ORDER BY MAX(s.create_time)
    </select>
    <select id="performanceShiftYear" resultType="java.util.Map">
         SELECT
         u.staff_name name,
@@ -46,12 +43,10 @@
        GROUP_CONCAT(DATE_FORMAT(s.work_time, '%c'), ':', IFNULL(palc.shift, '') order by s.work_time SEPARATOR ';') month_str
        FROM personal_shift s
        LEFT JOIN staff_on_job u ON u.id = s.staff_on_job_id
        LEFT JOIN staff_leave sl ON sl.staff_on_job_id = u.id
        LEFT JOIN personal_attendance_location_config palc ON palc.id = s.personal_attendance_location_config_id
         LEFT JOIN personal_attendance_location_config palc ON palc.id = s.personal_attendance_location_config_id
         <where>
            <if test="time != null and time != ''">
                and DATE_FORMAT(s.work_time, '%Y') = DATE_FORMAT(#{time}, '%Y' )
                AND (u.staff_state = 1 OR (u.staff_state = 0 AND sl.leave_date IS NOT NULL AND sl.leave_date >= DATE_FORMAT(#{time}, '%Y-01-01')))
            </if>
            <if test="userName != null and userName != ''">
                and u.staff_name like concat('%', #{userName}, '%')
@@ -71,12 +66,10 @@
        GROUP_CONCAT(DATE_FORMAT(s.work_time, '%c'), ':', IFNULL(palc.shift, '') order by s.work_time SEPARATOR ';') month_str
        FROM personal_shift s
        LEFT JOIN staff_on_job u ON u.id = s.staff_on_job_id
        LEFT JOIN staff_leave sl ON sl.staff_on_job_id = u.id
        LEFT JOIN personal_attendance_location_config palc ON palc.id = s.personal_attendance_location_config_id
       <where>
        <if test="time != null and time != ''">
            and DATE_FORMAT(s.work_time, '%Y') = DATE_FORMAT(#{time}, '%Y' )
            AND (u.staff_state = 1 OR (u.staff_state = 0 AND sl.leave_date IS NOT NULL AND sl.leave_date >= DATE_FORMAT(#{time}, '%Y-01-01')))
        </if>
        <if test="userName != null and userName != ''">
            and u.staff_name like concat('%', #{userName}, '%')
@@ -94,12 +87,10 @@
        GROUP_CONCAT(s.work_time, ':', IFNULL(palc.shift, ''), ':', s.id order by s.work_time SEPARATOR ';') AS shift_time, u.id user_id
        FROM personal_shift s
        LEFT JOIN staff_on_job u ON u.id = s.staff_on_job_id
        LEFT JOIN staff_leave sl ON sl.staff_on_job_id = u.id
        LEFT JOIN personal_attendance_location_config palc ON palc.id = s.personal_attendance_location_config_id
        <where>
            <if test="time != null and time != ''">
                and DATE_FORMAT(s.work_time, '%Y-%m') = DATE_FORMAT(#{time}, '%Y-%m' )
                AND (u.staff_state = 1 OR (u.staff_state = 0 AND sl.leave_date IS NOT NULL AND sl.leave_date >= DATE_FORMAT(#{time}, '%Y-%m-01')))
            </if>
            <if test="userName != null and userName != ''">
                and u.staff_name like concat('%', #{userName}, '%')
@@ -110,17 +101,6 @@
        </where>
        GROUP BY u.id
        order by s.create_time
    </select>
    <select id="existsAttendanceUsedByConfig" resultType="boolean">
        SELECT EXISTS (
            SELECT 1
            FROM personal_shift ps
            INNER JOIN personal_attendance_records par
                ON par.staff_on_job_id = ps.staff_on_job_id
               AND par.date = DATE(ps.work_time)
            WHERE ps.personal_attendance_location_config_id = #{configId}
        )
    </select>
</mapper>
src/main/resources/mapper/staff/SchemeApplicableStaffMapper.xml
@@ -25,20 +25,6 @@
        create_time DESC
    </select>
    <!-- æ ¹æ®éƒ¨é—¨ID查询最新社保方案 -->
    <select id="selectLatestSchemeByDeptId" resultType="com.ruoyi.staff.pojo.SchemeApplicableStaff">
        SELECT
        <include refid="schemeColumns"/>
        FROM
        scheme_applicable_staff
        WHERE
        1 = 1
        AND FIND_IN_SET(#{deptId}, dept_ids)
        ORDER BY
        create_time DESC
        LIMIT 1
    </select>
    <!-- æŸ¥è¯¢æ–¹æ¡ˆå¯¹åº”的明细 -->
    <select id="selectDetailBySchemeId" resultType="com.ruoyi.staff.pojo.SchemeInsuranceDetail">
        SELECT
src/main/resources/mapper/staff/StaffLeaveMapper.xml
@@ -18,24 +18,18 @@
        soj.emergency_contact as emergencyContact,
        soj.emergency_contact_phone as emergencyContactPhone,
        sp.post_name as postName,
        sd.dept_name as deptName,
        se.education as first_study,
        se.major as profession
        sd.dept_name as deptName
        FROM staff_leave
        LEFT JOIN staff_on_job soj ON soj.id = staff_leave.staff_on_job_id
        LEFT JOIN sys_post sp ON sp.post_id = soj.sys_post_id
        LEFT JOIN sys_dept sd ON sd.dept_id = soj.sys_dept_id
        LEFT JOIN staff_education se ON se.staff_on_job_id = staff_leave.staff_on_job_id
        AND se.id = (
        SELECT MAX(se2.id)
        FROM staff_education se2
        WHERE se2.staff_on_job_id = staff_leave.staff_on_job_id
        )
        WHERE 1=1
        LEFT JOIN
        staff_on_job soj ON soj.id = staff_leave.staff_on_job_id
        LEFT JOIN
        sys_post sp ON sp.post_id = soj.sys_post_id
        LEFT JOIN
        sys_dept sd ON sd.dept_id = soj.sys_dept_id
        where 1=1
        <if test="c.staffName != null and c.staffName != '' ">
            AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
        </if>
        order by staff_leave.create_time desc
    </select>
    <select id="staffLeaveList" resultType="com.ruoyi.staff.dto.StaffLeaveDto">
        SELECT
@@ -62,12 +56,10 @@
        sys_post sp ON sp.post_id = soj.sys_post_id
        LEFT JOIN
        sys_dept sd ON sd.dept_id = soj.sys_dept_id
        <where>
            <if test="c.staffName != null and c.staffName != '' ">
                AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
            </if>
        </where>
        order by staff_leave.create_time desc
        where 1=1
        <if test="c.staffName != null and c.staffName != '' ">
            AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
        </if>
    </select>
    <select id="staffLeaveReasonAnalytics" resultType="com.ruoyi.staff.dto.StaffLeaveDto">
src/main/resources/mapper/staff/StaffOnJobMapper.xml
@@ -3,83 +3,29 @@
<mapper namespace="com.ruoyi.staff.mapper.StaffOnJobMapper">
    <select id="staffOnJobListPage" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
        SELECT
        staff_on_job.id,
        staff_on_job.staff_state,
        staff_on_job.staff_no,
        staff_on_job.staff_name,
        staff_on_job.sex,
        staff_on_job.native_place,
        staff_on_job.sys_post_id,
        staff_on_job.sys_dept_id,
        staff_on_job.role_id,
        staff_on_job.adress,
        staff_on_job.first_study,
        staff_on_job.profession,
        staff_on_job.identity_card,
        staff_on_job.age,
        staff_on_job.phone,
        staff_on_job.contract_term,
        staff_on_job.contract_expire_time,
        staff_on_job.trial_end_date,
        staff_on_job.trial_start_date,
        staff_on_job.sign_date,
        staff_on_job.salary_select,
        staff_on_job.pro_salary,
        staff_on_job.date_select,
        staff_on_job.remark,
        staff_on_job.create_time,
        staff_on_job.create_user,
        staff_on_job.update_time,
        staff_on_job.update_user,
        staff_on_job.tenant_id,
        staff_on_job.alias,
        staff_on_job.birth_date,
        staff_on_job.nation,
        staff_on_job.marital_status,
        staff_on_job.pro_term,
        staff_on_job.positive_date,
        staff_on_job.basic_salary,
        staff_on_job.dept_id,
        sp.post_name AS post_name,
        sd.dept_name AS dept_name,
        MIN(t1.contract_start_time) AS contract_start_time,
        MAX(t1.contract_end_time) AS contract_end_time,
        (
        SELECT GROUP_CONCAT(sec.contact_name SEPARATOR ',')
        FROM staff_emergency_contact sec
        WHERE sec.staff_on_job_id = staff_on_job.id
        ) AS emergency_contact,
        (
        SELECT GROUP_CONCAT(sec.contact_phone SEPARATOR ',')
        FROM staff_emergency_contact sec
        WHERE sec.staff_on_job_id = staff_on_job.id
        ) AS emergency_contact_phone
        staff_on_job.*,
        sp.post_name as postName,
        sd.dept_name as deptName,
        MIN(t1.contract_start_time) as contract_start_time,  -- å–最早合同开始时间
        MAX(t1.contract_end_time) as contract_end_time
        FROM staff_on_job
        LEFT JOIN sys_post sp ON sp.post_id = staff_on_job.sys_post_id
        LEFT JOIN sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id
        LEFT JOIN staff_contract AS t1 ON t1.staff_on_job_id = staff_on_job.id
        <where>
            <if test="staffOnJob.staffState != null">
                AND staff_on_job.staff_state = #{staffOnJob.staffState}
            </if>
            <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
                AND staff_on_job.staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
            </if>
            <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
                AND staff_on_job.contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
            </if>
            <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
                AND staff_on_job.contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
            </if>
            <if test="staffOnJob.sysDeptId != null">
                AND staff_on_job.sys_dept_id = #{staffOnJob.sysDeptId}
            </if>
        </where>
        GROUP BY staff_on_job.id
        <if test="staffOnJob.contractStartTime != null">
            HAVING MIN(t1.contract_start_time) = #{staffOnJob.contractStartTime}
        LEFT JOIN staff_contract as t1 ON t1.staff_on_job_id = staff_on_job.id
        WHERE 1=1
        <if test="staffOnJob.staffState != null">
            AND staff_state = #{staffOnJob.staffState}
        </if>
        order by staff_on_job.create_time desc
        <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
            AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
        </if>
        <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
            AND contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
        </if>
        <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
            AND contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
        </if>
        GROUP BY staff_on_job.id
    </select>
    <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
        SELECT
@@ -91,15 +37,13 @@
        sys_post sp ON sp.post_id = staff_on_job.sys_post_id
        LEFT JOIN
        sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id
        <where>
            <if test="staffOnJob.staffState != null">
                AND staff_state = #{staffOnJob.staffState}
            </if>
            <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
                AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
            </if>
        </where>
        order by staff_on_job.create_time desc
        where 1=1
        <if test="staffOnJob.staffState != null">
            AND staff_state = #{staffOnJob.staffState}
        </if>
        <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
            AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
        </if>
    </select>
    <!-- ç»Ÿè®¡æŒ‡å®šæ—¥æœŸçš„在职员工数 -->
    <select id="countOnJobStaffByDate" resultType="java.lang.Integer">
src/main/resources/mapper/staff/StaffSalaryMainMapper.xml
@@ -16,11 +16,4 @@
        <result column="update_user" property="updateUser" />
    </resultMap>
    <select id="selectIssuedDeptIds" resultType="java.lang.String">
        SELECT DISTINCT dept_ids
        FROM staff_salary_main
        WHERE salary_month = #{salaryMonth}
          AND status = 5
    </select>
</mapper>
src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -21,11 +21,11 @@
        pm.unit,
        u.nick_name as createBy,
        CASE
            WHEN TRIM(sir.record_type) = '7' THEN
            WHEN #{params.topParentProductId} = 3 AND TRIM(sir.record_type) = '7' THEN
                COALESCE(pl_by_product.purchase_contract_number, pl_direct.purchase_contract_number)
            WHEN TRIM(sir.record_type) = '10' THEN
            WHEN #{params.topParentProductId} = 3 AND TRIM(sir.record_type) = '10' THEN
                pl_by_quality.purchase_contract_number
            WHEN #{params.topParentProductId} = 276 THEN
            WHEN #{params.topParentProductId} = 1 THEN
                CASE
                    WHEN TRIM(sir.record_type) IN ('14', '15') THEN
                        sl_return.sales_contract_no
@@ -132,7 +132,7 @@
                and p.id in (select id from product_tree)
            </if>
        </where>
        order by sir.id desc
        order by sir.create_time desc
    </select>
    <select id="listStockInRecordExportData" resultType="com.ruoyi.stock.execl.StockInRecordExportData">
        SELECT
@@ -159,7 +159,7 @@
                and sir.record_type = #{params.recordType}
            </if>
        </where>
        order by sir.id desc
        order by sir.create_time desc
    </select>
    <select id="listPageAccountPurchase" resultType="com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo">
        SELECT
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -113,7 +113,7 @@
        product_model_id,
        MAX(create_time) as create_time,
        MAX(update_time) as update_time,
        MAX(warn_num) as warn_num,
        SUM(warn_num) as warn_num,
        MAX(version) as version,
        model,
        MAX(remark) as remark,
@@ -222,7 +222,6 @@
        unit,
        product_name,
        product_id
        order by combined.create_time desc
    </select>
    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
@@ -352,11 +351,7 @@
        su.nick_name as create_by
        from
        stock_in_record sir
        left join (
            select product_model_id, sum(qualitity) as qualitity
            from stock_inventory
            group by product_model_id
        ) si on sir.product_model_id = si.product_model_id
        left join stock_inventory si on sir.batch_no = si.batch_no
        left join product_model pm on sir.product_model_id = pm.id
        left join product p on pm.product_id = p.id
        left join sys_user su on sir.create_user = su.user_id
@@ -372,7 +367,7 @@
                and sir.create_time &lt;= #{ew.endMonth}
            </if>
        </where>
        order by sir.id desc
        order by sir.create_time desc
    </select>
    <select id="stockInAndOutRecord" resultType="com.ruoyi.stock.dto.StockInventoryDto">
src/main/resources/mapper/stock/StockOutRecordMapper.xml
@@ -62,7 +62,7 @@
                and p.id in (select id from product_tree)
            </if>
        </where>
        order by sor.id desc
        order by sor.create_time desc
    </select>
    <select id="listStockOutRecordExportData" resultType="com.ruoyi.stock.execl.StockOutRecordExportData">
        SELECT
src/main/resources/mapper/system/SysDeptMapper.xml
@@ -22,9 +22,9 @@
        <result property="updateTime" column="update_time" />
        <result property="staffCount" column="staff_count" />
    </resultMap>
    <sql id="selectDeptVo">
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        from sys_dept d
    </sql>
@@ -49,9 +49,9 @@
        <!-- æ•°æ®èŒƒå›´è¿‡æ»¤ -->
        ${params.dataScope}
        group by d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
        order by d.parent_id asc, d.order_num asc, d.create_time desc
        order by d.parent_id, d.order_num
    </select>
    <select id="selectDeptListByRoleId" resultType="java.lang.Long">
        select d.dept_id
        from sys_dept d
@@ -60,40 +60,39 @@
            <if test="deptCheckStrictly">
              and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
            </if>
        order by d.parent_id asc, d.order_num asc, d.create_time desc
        order by d.parent_id, d.order_num
    </select>
    <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult">
        select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,
            (select dept_name from sys_dept where dept_id = d.parent_id) parent_name,
            d.dept_nick
        from sys_dept d
        where d.dept_id = #{deptId}
        order by d.create_time desc
    </select>
    <select id="checkDeptExistUser" parameterType="Long" resultType="int">
        select count(1) from sys_user_dept where dept_id = #{deptId}
    </select>
    <select id="hasChildByDeptId" parameterType="Long" resultType="int">
        select count(1) from sys_dept
        where del_flag = '0' and parent_id = #{deptId} limit 1
    </select>
    <select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
        select * from sys_dept where find_in_set(#{deptId}, ancestors)
    </select>
    <select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int">
        select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)
    </select>
    <select id="checkDeptNameUnique" resultMap="SysDeptResult">
        <include refid="selectDeptVo"/>
        where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' order by d.create_time desc limit 1
        where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
    </select>
    <insert id="insertDept" parameterType="com.ruoyi.project.system.domain.SysDept">
         insert into sys_dept(
             <if test="deptId != null and deptId != 0">dept_id,</if>
@@ -123,7 +122,7 @@
             sysdate()
         )
    </insert>
    <update id="updateDept" parameterType="com.ruoyi.project.system.domain.SysDept">
         update sys_dept
         <set>
@@ -141,7 +140,7 @@
         </set>
         where dept_id = #{deptId}
    </update>
    <update id="updateDeptChildren" parameterType="java.util.List">
        update sys_dept set ancestors =
        <foreach collection="depts" item="item" index="index"
@@ -154,14 +153,14 @@
            #{item.deptId}
        </foreach>
    </update>
    <update id="updateDeptStatusNormal" parameterType="Long">
         update sys_dept set status = '0' where dept_id in
         update sys_dept set status = '0' where dept_id in
         <foreach collection="array" item="deptId" open="(" separator="," close=")">
            #{deptId}
        </foreach>
    </update>
    <delete id="deleteDeptById" parameterType="Long">
        update sys_dept set del_flag = '2' where dept_id = #{deptId}
    </delete>
@@ -185,4 +184,4 @@
        WHERE parent_id = 100;
    </select>
</mapper>
</mapper>
src/main/resources/mapper/system/SysDictTypeMapper.xml
@@ -14,9 +14,9 @@
        <result property="updateBy"   column="update_by"   />
        <result property="updateTime" column="update_time" />
    </resultMap>
    <sql id="selectDictTypeVo">
        select dict_id, dict_name, dict_type, status, create_by, create_time, remark
        select dict_id, dict_name, dict_type, status, create_by, create_time, remark
        from sys_dict_type
    </sql>
@@ -39,37 +39,36 @@
                and date_format(create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
            </if>
        </where>
        order by create_time desc
    </select>
    <select id="selectDictTypeAll" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
    </select>
    <select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_id = #{dictId}
    </select>
    <select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_type = #{dictType}
    </select>
    <select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult">
        <include refid="selectDictTypeVo"/>
        where dict_type = #{dictType} limit 1
    </select>
    <delete id="deleteDictTypeById" parameterType="Long">
         delete from sys_dict_type where dict_id = #{dictId}
     </delete>
     <delete id="deleteDictTypeByIds" parameterType="Long">
         delete from sys_dict_type where dict_id in
         <foreach collection="array" item="dictId" open="(" separator="," close=")">
             #{dictId}
        </foreach>
        </foreach>
     </delete>
     <update id="updateDictType" parameterType="com.ruoyi.project.system.domain.SysDictType">
@@ -84,7 +83,7 @@
         </set>
         where dict_id = #{dictId}
    </update>
     <insert id="insertDictType" parameterType="com.ruoyi.project.system.domain.SysDictType">
         insert into sys_dict_type(
             <if test="dictName != null and dictName != ''">dict_name,</if>
@@ -102,5 +101,5 @@
             sysdate()
         )
    </insert>
</mapper>
</mapper>
src/main/resources/mapper/system/SysNoticeMapper.xml
@@ -10,16 +10,11 @@
        <result property="noticeType"     column="notice_type"     />
        <result property="noticeContent"  column="notice_content"  />
        <result property="status"         column="status"          />
        <result property="senderId"       column="sender_id"       />
        <result property="consigneeId"    column="consignee_id"    />
        <result property="jumpPath"       column="jump_path"       />
        <result property="appJumpPath"    column="app_jump_path"   />
        <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"          />
        <result property="tenantId"       column="tenant_id"       />
    </resultMap>
    <sql id="selectNoticeVo">
src/main/resources/mapper/system/SysPostMapper.xml
@@ -16,12 +16,12 @@
        <result property="updateTime"    column="update_time"   />
        <result property="remark"        column="remark"        />
    </resultMap>
    <sql id="selectPostVo">
        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
        select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
        from sys_post
    </sql>
    <select id="selectPostList" parameterType="com.ruoyi.project.system.domain.SysPost" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
        <where>
@@ -35,18 +35,17 @@
                AND post_name like concat('%', #{postName}, '%')
            </if>
        </where>
        order by create_time desc
    </select>
    <select id="selectPostAll" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
    </select>
    <select id="selectPostById" parameterType="Long" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
        where post_id = #{postId}
    </select>
    <select id="selectPostListByUserId" parameterType="Long" resultType="Long">
        select p.post_id
        from sys_post p
@@ -54,7 +53,7 @@
            left join sys_user u on u.user_id = up.user_id
        where u.user_id = #{userId}
    </select>
    <select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult">
        select p.post_id, p.post_name, p.post_code
        from sys_post p
@@ -62,17 +61,17 @@
             left join sys_user u on u.user_id = up.user_id
        where u.user_name = #{userName}
    </select>
    <select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
         where post_name=#{postName} limit 1
    </select>
    <select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult">
        <include refid="selectPostVo"/>
         where post_code=#{postCode} limit 1
    </select>
    <update id="updatePost" parameterType="com.ruoyi.project.system.domain.SysPost">
         update sys_post
         <set>
@@ -86,7 +85,7 @@
         </set>
         where post_id = #{postId}
    </update>
     <insert id="insertPost" parameterType="com.ruoyi.project.system.domain.SysPost" useGeneratedKeys="true" keyProperty="postId">
         insert into sys_post(
             <if test="postId != null and postId != 0">post_id,</if>
@@ -108,16 +107,16 @@
             sysdate()
         )
    </insert>
    <delete id="deletePostById" parameterType="Long">
        delete from sys_post where post_id = #{postId}
    </delete>
    <delete id="deletePostByIds" parameterType="Long">
         delete from sys_post where post_id in
         <foreach collection="array" item="postId" open="(" separator="," close=")">
             #{postId}
        </foreach>
        </foreach>
     </delete>
</mapper>
</mapper>
在上述文件截断后对比
src/main/resources/mapper/system/SysRoleMapper.xml src/main/resources/mapper/system/SysUserMapper.xml src/main/resources/mapper/technology/TechnologyOperationMapper.xml src/main/resources/mapper/technology/TechnologyOperationParamMapper.xml src/main/resources/mapper/warehouse/DocumentationBorrowManagementMapper.xml src/main/resources/mapper/warehouse/DocumentationMapper.xml src/main/resources/mapper/warehouse/DocumentationReturnManagementMapper.xml src/main/resources/mapper/warehouse/WarehouseGoodsShelvesRowcolMapper.xml