xiaoyi
4 天以前 abf1c0a35d85d38239ff5d2ed746a4e4b797c9d1
feat 功能修正
已修改66个文件
已添加233个文件
15663 ■■■■■ 文件已修改
sql/mysql/ruoyi-vue-pro.sql 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/enums/AfterSaleIssueTypeEnum.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionAlertController.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionAlertRuleController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionKpiController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionKpiDefinitionController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertPageReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertRespVO.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertRulePageReqVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastRespVO.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiByCategoryRespVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiDefinitionPageReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiDetailRespVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiOverviewRespVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiAlertRecordDO.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiAlertRuleDO.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiForecastDataDO.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiKpiDefinitionDO.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiAlertRecordMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiAlertRuleMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiKpiDefinitionMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/AlertEvaluationTask.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRecordService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRecordServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRuleService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRuleServiceImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiDecisionKpiService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiDecisionKpiServiceImpl.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiKpiDefinitionService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiKpiDefinitionServiceImpl.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/CrmCancellationController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationAuditReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationPageReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationRespVO.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationSaveReqVO.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractCommandController.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandPageReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandRespVO.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandSaveReqVO.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/CrmCreditRatingController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingPageReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingRespVO.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingSaveReqVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerPageReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerSaveReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/CrmRefundController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundAuditReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundPageReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundRespVO.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundSaveReqVO.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/cancellation/CrmCancellationDO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractCommandDO.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/credit/CrmCreditRatingDO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/refund/CrmRefundDO.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/cancellation/CrmCancellationMapper.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractCommandMapper.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/credit/CrmCreditRatingMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/refund/CrmRefundMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/CrmCancellationService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/CrmCancellationServiceImpl.java 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/listener/CrmCancellationStatusListener.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractCommandService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractCommandServiceImpl.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/credit/CrmCreditRatingService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/credit/CrmCreditRatingServiceImpl.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/CrmRefundService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/CrmRefundServiceImpl.java 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/listener/CrmRefundStatusListener.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApi.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchasePlanCreateReqDTO.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanPageReqVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanRespVO.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanSaveReqVO.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/convert/ErpPurchasePlanStatusConvert.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchasePlanDO.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchasePlanItemDO.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchasePlanItemMapper.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchasePlanMapper.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchasePlanStatusEnum.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/job/purchase/ErpPurchasePlanStockAlertJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchasePlanService.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchasePlanServiceImpl.java 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchasePlanStatusListener.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/HrmCertificateController.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificatePageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificateRespVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificateSaveReqVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/certificate/HrmCertificateDO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/certificate/HrmCertificateMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/certificate/HrmCertificateService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/certificate/HrmCertificateServiceImpl.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/dto/MesMdItemSafetyStockRespDTO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workorder/MesProWorkOrderApi.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/workorder/MesProWorkOrderApiImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachineryRespVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachinerySaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/MesDvStatisticsController.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/vo/MesDvOeeRespVO.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/vo/MesDvStatisticsRespVO.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/MesDvTelemetryController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryDeviceRespVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryLatestReqVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryPageReqVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryPullRespVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryRespVO.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemRespVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/MesPdBaseDataController.java 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicyPageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicyRespVO.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicySaveReqVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypePageReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypeRespVO.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypeSaveReqVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardPageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardRespVO.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardSaveReqVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackagePageReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackageRespVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackageSaveReqVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPricePageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPriceRespVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPriceSaveReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelPageReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelRespVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelSaveReqVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/ProductCtcController.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcAngleCalcReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcAngleCalcRespVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcPageReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcReelCalcReqVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcReelCalcRespVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcResistanceCalcReqVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcResistanceCalcRespVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcRespVO.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcSaveReqVO.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/MesPdPriceController.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcDetailRespVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcRespVO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/MesPdProductController.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductAuditReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductPageReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductRespVO.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductSaveReqVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/ProductCtcReelController.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelPageReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelRespVO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelSaveReqVO.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/ProductTechConfirmController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmPageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmRespVO.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmSaveReqVO.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/MesProEmergencyPlanController.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanPageReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanRespVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanSaveReqVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/MesProFeedbackController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackSummaryRespVO.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/MesProMaintenancePlanController.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanPageReqVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanRespVO.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanSaveReqVO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskSaveReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderRespVO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderSaveReqVO.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/MesQcOutsourceTestController.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestPageReqVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestRespVO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestSaveReqVO.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/MesWmStockTakingTaskLineController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/vo/line/MesWmStockTakingTaskLineImportExcelVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/vo/line/MesWmStockTakingTaskLineImportRespVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/area/MesWmWarehouseAreaRespVO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/area/MesWmWarehouseAreaSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/dv/machinery/MesDvMachineryDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/dv/telemetry/MesDvTelemetryDO.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdDiscountPolicyDO.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdElectricityTypeDO.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdPriceStandardDO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdServicePackageDO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdTieredPriceDO.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdVoltageLevelDO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/ctc/ProductCtcDO.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/product/MesPdProductDO.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/reel/ProductCtcReelDO.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/techconfirm/ProductTechConfirmDO.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/emergencyplan/MesProEmergencyPlanDO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/maintenanceplan/MesProMaintenancePlanDO.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/maintenanceplan/MesProMaintenancePlanItemDO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/outsourcetest/MesQcOutsourceTestDO.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueDO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/dv/telemetry/MesDvTelemetryMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdDiscountPolicyMapper.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdElectricityTypeMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdPriceStandardMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdServicePackageMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdTieredPriceMapper.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdVoltageLevelMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/ctc/ProductCtcMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/product/MesPdProductMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/reel/ProductCtcReelMapper.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/techconfirm/ProductTechConfirmMapper.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/emergencyplan/MesProEmergencyPlanMapper.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/maintenanceplan/MesProMaintenancePlanItemMapper.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/maintenanceplan/MesProMaintenancePlanMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/outsourcetest/MesQcOutsourceTestMapper.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdAuditStatusEnum.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdProductArchiveTypeEnum.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProMaintenancePlanTypeEnum.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderSourceTypeEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderTypeEnum.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMiscIssueTypeEnum.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductIssueTypeEnum.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesTelemetryProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/dv/MesDvTelemetryPullJob.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryService.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImpl.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdDiscountPolicyService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdDiscountPolicyServiceImpl.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdElectricityTypeService.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdElectricityTypeServiceImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdPriceStandardService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdPriceStandardServiceImpl.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdServicePackageService.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdServicePackageServiceImpl.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdTieredPriceService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdTieredPriceServiceImpl.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdVoltageLevelService.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdVoltageLevelServiceImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/ctc/ProductCtcService.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/ctc/ProductCtcServiceImpl.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/price/MesPdPriceService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/price/MesPdPriceServiceImpl.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductService.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/reel/ProductCtcReelService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/reel/ProductCtcReelServiceImpl.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/techconfirm/ProductTechConfirmService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/techconfirm/ProductTechConfirmServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/emergencyplan/MesProEmergencyPlanService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/emergencyplan/MesProEmergencyPlanServiceImpl.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanService.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/outsourcetest/MesQcOutsourceTestService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/outsourcetest/MesQcOutsourceTestServiceImpl.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskLineService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskLineServiceImpl.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-local.yaml 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/ruoyi-vue-pro.sql
@@ -1155,6 +1155,10 @@
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3106, 1, '自行生产', '1', 'mes_pro_work_order_type', 0, 'primary', '', '', '1', '2026-02-17 11:43:47', '1', '2026-02-17 11:43:47', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3107, 2, '代工', '2', 'mes_pro_work_order_type', 0, 'warning', '', '', '1', '2026-02-17 11:43:47', '1', '2026-02-17 11:43:47', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3108, 3, '采购', '3', 'mes_pro_work_order_type', 0, 'info', '', '', '1', '2026-02-17 11:43:47', '1', '2026-02-17 11:43:47', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3109, 3, '运维计划', '3', 'mes_pro_work_order_source_type', 0, 'primary', '', '', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3110, 4, '电力运维', '4', 'mes_pro_work_order_type', 0, 'primary', '', '', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3111, 5, '发电', '5', 'mes_pro_work_order_type', 0, 'warning', '', '', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3112, 6, '检修', '6', 'mes_pro_work_order_type', 0, 'info', '', '', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3121, 1, 'IQC(来料检验)', '1', 'mes_qc_type', 0, 'primary', '', '来料质量检验', '1', '2026-02-18 14:12:05', '1', '2026-02-18 14:12:05', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3122, 2, 'IPQC(过程检验)', '2', 'mes_qc_type', 0, 'warning', '', '生产制程质量检验', '1', '2026-02-18 14:12:05', '1', '2026-03-24 15:21:34', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3123, 3, 'OQC(出货检验)', '3', 'mes_qc_type', 0, 'success', '', '出货质量检验', '1', '2026-02-18 14:12:05', '1', '2026-02-18 14:12:05', b'0');
@@ -1234,6 +1238,8 @@
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3224, 3, '待执行领出', '3', 'mes_wm_product_issue_status', 0, 'primary', '', '已审批,拣货完成', '1', '2026-02-26 16:39:12', '1', '2026-03-23 13:18:02', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3225, 4, '已完成', '4', 'mes_wm_product_issue_status', 0, 'success', '', '已完成出库', '1', '2026-02-26 16:39:12', '1', '2026-03-23 13:18:02', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3226, 5, '已取消', '5', 'mes_wm_product_issue_status', 0, 'success', '', '已完成出库', '1', '2026-02-26 16:39:12', '1', '2026-03-23 13:18:02', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061137, 1, '正常领料', '1', 'mes_wm_product_issue_type', 0, 'primary', '', '正常生产领料', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061138, 2, '紧急补料', '2', 'mes_wm_product_issue_type', 0, 'warning', '', '生产现场紧急补料', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3232, 1, '草稿', '0', 'mes_wm_return_issue_status', 0, 'info', '', '草稿状态,可编辑', '1', '2026-02-28 14:11:12', '1', '2026-02-28 14:28:24', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3233, 2, '待检验', '1', 'mes_wm_return_issue_status', 0, 'default', '', '已确认,等待质检', '1', '2026-02-28 14:11:12', '1', '2026-02-28 14:28:28', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3234, 3, '待上架', '2', 'mes_wm_return_issue_status', 0, 'warning', '', '检验完成,等待仓库上架', '1', '2026-02-28 14:11:12', '1', '2026-02-28 14:28:31', b'0');
@@ -1263,6 +1269,15 @@
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3277, 1, '库存调整', '1', 'mes_wm_misc_receipt_type', 0, 'primary', '', '库存调整入库', '1', '2026-03-03 07:34:33', '1', '2026-03-03 07:34:33', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3278, 1, '库存调整', '1', 'mes_wm_misc_issue_type', 0, 'primary', '', '库存调整出库', '1', '2026-03-03 07:34:33', '1', '2026-03-03 07:34:33', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3279, 2, '报废出库', '2', 'mes_wm_misc_issue_type', 0, 'danger', '', '报废出库', '1', '2026-03-03 07:36:13', '1', '2026-03-03 07:36:13', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061144, 3, '废旧物资出库', '3', 'mes_wm_misc_issue_type', 0, 'warning', '', '废旧物资出库', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061145, 4, '设备出库', '4', 'mes_wm_misc_issue_type', 0, 'primary', '', '电力设备出库', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061146, 2, '设备入库', '2', 'mes_wm_misc_receipt_type', 0, 'primary', '', '电力设备入库', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061147, 3, '废旧物资入库', '3', 'mes_wm_misc_receipt_type', 0, 'warning', '', '废旧物资入库', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061148, 1, '普通仓库', '1', 'mes_wm_warehouse_type', 0, 'default', '', '通用物资仓库', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061149, 2, '设备仓', '2', 'mes_wm_warehouse_type', 0, 'primary', '', '电力设备仓储', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061150, 3, '备件仓', '3', 'mes_wm_warehouse_type', 0, 'warning', '', '备品备件仓储', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061151, 4, '耗材仓', '4', 'mes_wm_warehouse_type', 0, 'info', '', '日常耗材仓储', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061152, 5, '危化品仓', '5', 'mes_wm_warehouse_type', 0, 'danger', '', '危险化学品专用仓储', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3280, 1, '草稿', '0', 'mes_wm_outsource_receipt_status', 0, 'info', '', '草稿状态', '1', '2026-03-03 14:03:57', '1', '2026-03-03 14:03:57', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3281, 2, '待检验', '1', 'mes_wm_outsource_receipt_status', 0, 'warning', '', '已确认,等待质检', '1', '2026-03-03 14:03:57', '1', '2026-03-03 14:03:57', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3282, 3, '待上架', '2', 'mes_wm_outsource_receipt_status', 0, 'primary', '', '检验完成,等待仓库上架', '1', '2026-03-03 14:03:57', '1', '2026-03-03 14:03:57', b'0');
@@ -1334,6 +1349,11 @@
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3380, 6, '质量状态', '900', 'mes_wm_stock_taking_plan_param_type', 0, 'default', '', '按质量状态盘点', '1', '2026-03-09 00:00:00', '1', '2026-03-09 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3381, 1, '物料', 'ITEM', 'mes_md_item_or_product', 0, 'info', '', '', '1', '2026-03-15 01:55:06', '1', '2026-03-15 01:55:06', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3382, 2, '产品', 'PRODUCT', 'mes_md_item_or_product', 0, 'success', '', '', '1', '2026-03-15 01:55:06', '1', '2026-03-15 01:55:06', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061139, 1, '电力设备', '1', 'mes_material_category', 0, 'primary', '', '变压器、开关柜、输电线路、发电机组、储能设备等', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061140, 2, '备品备件', '2', 'mes_material_category', 0, 'warning', '', '电力设备维修更换用备品备件', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061141, 3, '耗材', '3', 'mes_material_category', 0, 'info', '', '日常运维消耗性材料', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061142, 4, '工程材料', '4', 'mes_material_category', 0, 'success', '', '电力工程建设用材料', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061143, 5, '安防物资', '5', 'mes_material_category', 0, 'danger', '', '安全生产防护物资', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3383, 1, '草稿', '0', 'mes_wm_item_consume_status', 0, 'info', '', '草稿状态', '1', '2026-03-19 15:06:23', '1', '2026-03-19 15:06:23', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3384, 2, '已完成', '4', 'mes_wm_item_consume_status', 0, 'success', '', '已完成', '1', '2026-03-19 15:06:23', '1', '2026-03-19 15:06:23', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3385, 1, '到货通知单', '100', 'mes_qc_source_doc_type', 0, 'primary', '', 'IQC', '1', '2026-03-26 13:01:09', '1', '2026-03-26 13:01:09', b'0');
@@ -1675,6 +1695,7 @@
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2067, 'MES é‡‡è´­å…¥åº“单状态', 'mes_wm_item_receipt_status', 0, 'MES é‡‡è´­å…¥åº“单状态', '1', '2026-02-22 14:54:05', '1', '2026-02-22 14:54:05', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2068, 'MES å•据状态', 'mes_order_status', 0, '', '1', '2026-02-23 21:16:03', '1', '2026-02-23 21:17:37', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2069, 'MES é¢†æ–™å‡ºåº“单状态', 'mes_wm_product_issue_status', 0, 'MES é¢†æ–™å‡ºåº“单状态', '1', '2026-02-26 16:39:44', '1', '2026-04-05 15:05:11', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061099, 'MES ç”Ÿäº§é¢†æ–™å•据类型', 'mes_wm_product_issue_type', 0, 'MES ç”Ÿäº§é¢†æ–™å•据类型(1=正常领料,2=紧急补料)', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2073, 'MES ç”Ÿäº§é€€æ–™å•状态', 'mes_wm_return_issue_status', 0, 'MES ç”Ÿäº§é€€æ–™å•状态', '1', '2026-02-28 14:11:09', '1', '2026-04-05 15:05:14', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2074, 'MES ç”Ÿäº§é€€æ–™ç±»åž‹', 'mes_wm_return_issue_type', 0, 'MES ç”Ÿäº§é€€æ–™ç±»åž‹', '1', '2026-02-28 14:11:09', '1', '2026-04-05 15:05:16', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2075, 'MES è´¨é‡çŠ¶æ€', 'mes_wm_quality_status', 0, 'MES è´¨é‡çŠ¶æ€ï¼ˆå¾…æ£€/合格/不合格)', '1', '2026-02-28 15:00:53', '1', '2026-02-28 15:00:53', b'0', NULL);
@@ -1683,6 +1704,7 @@
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2105, 'MES æ‚项入库类型', 'mes_wm_misc_receipt_type', 0, '杂项入库类型', '1', '2026-03-03 07:18:12', '1', '2026-04-05 15:05:23', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2106, 'MES æ‚项入库状态', 'mes_wm_misc_receipt_status', 0, '杂项入库状态', '1', '2026-03-03 07:18:12', '1', '2026-04-05 15:05:25', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2109, 'MES æ‚项出库类型', 'mes_wm_misc_issue_type', 0, 'MES æ‚项出库类型', '1', '2026-03-03 07:34:33', '1', '2026-03-03 07:34:33', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061101, 'MES ä»“库/库区类型', 'mes_wm_warehouse_type', 0, 'MES ä»“库/库区类型(1=普通仓库,2=设备仓,3=备件仓,4=耗材仓,5=危化品仓)', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2110, 'MES å¤–协入库单状态', 'mes_wm_outsource_receipt_status', 0, 'MES å¤–协入库单状态', '1', '2026-03-03 14:03:20', '1', '2026-03-03 14:03:20', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2112, 'MES å¤–协发料单状态', 'mes_wm_outsource_issue_status', 0, 'MES å¤–协发料单状态', '1', '2026-03-03 16:30:56', '1', '2026-04-05 15:05:47', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2113, 'MES ç¼–码规则分段类型', 'mes_md_auto_code_part_type', 0, 'MES ç¼–码规则分段类型', '1', '2026-03-04 14:45:46', '1', '2026-03-04 15:24:40', b'0', NULL);
@@ -1698,6 +1720,7 @@
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2126, 'MES ç›˜ç‚¹ä»»åŠ¡çŠ¶æ€', 'mes_wm_stock_taking_task_status', 0, 'MES ç›˜ç‚¹ä»»åŠ¡çŠ¶æ€', '1', '2026-03-09 00:00:00', '1', '2026-03-09 00:00:00', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2127, 'MES ç›˜ç‚¹ä»»åŠ¡è¡ŒçŠ¶æ€', 'mes_wm_stock_taking_task_line_status', 0, 'MES ç›˜ç‚¹ä»»åŠ¡è¡ŒçŠ¶æ€', '1', '2026-03-09 00:00:00', '1', '2026-04-05 15:02:18', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2129, 'MES ç‰©æ–™äº§å“æ ‡è¯†', 'mes_md_item_or_product', 0, '物料分类:物料(ITEM) / äº§å“(PRODUCT)', '1', '2026-03-15 01:55:06', '1', '2026-03-15 01:55:06', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061100, 'MES ç”µåŠ›ç‰©èµ„åˆ†ç±»', 'mes_material_category', 0, '电力物资分类(1=电力设备,2=备品备件,3=耗材,4=工程材料,5=安防物资)', '1', '2026-08-18 00:00:00', '1', '2026-08-18 00:00:00', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2130, 'MES ä¾›åº”商退货单状态', 'mes_wm_return_vendor_status', 0, '采购退货单状态', '', '2026-03-29 13:49:57', '', '2026-04-05 15:53:46', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2131, 'MES å‘货通知单状态', 'mes_wm_sales_notice_status', 0, 'MES å‘货通知单状态', '1', '2026-03-30 08:54:30', '1', '2026-04-05 15:53:46', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2132, 'MES æ‚项出库单状态', 'mes_wm_misc_issue_status', 0, '杂项出库单状态', '1', '2026-03-30 15:00:18', '1', '2026-04-05 15:05:41', b'0', NULL);
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/enums/AfterSaleIssueTypeEnum.java
@@ -16,7 +16,11 @@
    UNCLASSIFIED(0, "未判定"),
    GENERAL(1, "一般问题"),
    REPAIR(2, "维修问题"),
    RETURN(3, "退货问题");
    RETURN(3, "退货问题"),
    POWER_OUTAGE_REPAIR(4, "停电报修"),
    POWER_FAULT(5, "用电故障"),
    SERVICE_COMPLAINT(6, "服务投诉"),
    POWER_QUALITY_DISPUTE(7, "供电质量异议");
    public static final Integer[] ARRAYS = Arrays.stream(values())
            .map(AfterSaleIssueTypeEnum::getType).toArray(Integer[]::new);
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionAlertController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertPageReqVO;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertRespVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO;
import cn.iocoder.yudao.module.bi.service.decision.BiAlertRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - å†³ç­–支持 é¢„警中心")
@RestController
@RequestMapping("/bi/decision/alert")
@Validated
public class DecisionAlertController {
    @Resource
    private BiAlertRecordService alertRecordService;
    @GetMapping("/page")
    @Operation(summary = "获取预警记录分页")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert:query')")
    public CommonResult<PageResult<BiAlertRecordDO>> getAlertPage(@Valid DecisionAlertPageReqVO reqVO) {
        return success(alertRecordService.getRecordPage(reqVO));
    }
    @GetMapping("/unprocessed-count")
    @Operation(summary = "获取未处理预警数量")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert:query')")
    public CommonResult<Long> getUnprocessedCount() {
        return success(alertRecordService.getUnprocessedCount());
    }
    @PostMapping("/confirm/{id}")
    @Operation(summary = "确认预警")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert:confirm')")
    public CommonResult<Boolean> confirmAlert(@PathVariable("id") Long id) {
        alertRecordService.confirmRecord(id, 1L);
        return success(true);
    }
    @PostMapping("/handle")
    @Operation(summary = "处理预警")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert:handle')")
    public CommonResult<Boolean> handleAlert(@RequestParam("id") Long id,
                                              @RequestParam(value = "handleRemark", required = false) String handleRemark) {
        alertRecordService.handleRecord(id, 1L, handleRemark);
        return success(true);
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionAlertRuleController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertRulePageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO;
import cn.iocoder.yudao.module.bi.service.decision.BiAlertRuleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - å†³ç­–支持 é¢„警规则")
@RestController
@RequestMapping("/bi/decision/alert-rule")
@Validated
public class DecisionAlertRuleController {
    @Resource
    private BiAlertRuleService alertRuleService;
    @PostMapping("/create")
    @Operation(summary = "创建预警规则")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert-rule:create')")
    public CommonResult<Long> createRule(@Valid @RequestBody BiAlertRuleDO rule) {
        return success(alertRuleService.createRule(rule));
    }
    @PutMapping("/update")
    @Operation(summary = "更新预警规则")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert-rule:update')")
    public CommonResult<Boolean> updateRule(@Valid @RequestBody BiAlertRuleDO rule) {
        alertRuleService.updateRule(rule);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除预警规则")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert-rule:delete')")
    public CommonResult<Boolean> deleteRule(@RequestParam("id") Long id) {
        alertRuleService.deleteRule(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获取预警规则")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert-rule:query')")
    public CommonResult<BiAlertRuleDO> getRule(@RequestParam("id") Long id) {
        return success(alertRuleService.getRule(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获取预警规则分页")
    @PreAuthorize("@ss.hasPermission('bi:decision:alert-rule:query')")
    public CommonResult<PageResult<BiAlertRuleDO>> getRulePage(@Valid DecisionAlertRulePageReqVO pageReq) {
        return success(alertRuleService.getRulePage(pageReq));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastReqVO;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastRespVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
import cn.iocoder.yudao.module.bi.service.decision.BiForecastService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - å†³ç­–支持 è´Ÿè·é¢„测")
@RestController
@RequestMapping("/bi/decision/forecast")
@Validated
public class DecisionForecastController {
    @Resource
    private BiForecastService forecastService;
    @GetMapping("/list")
    @Operation(summary = "获取预测数据")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<List<BiForecastDataDO>> getForecastList(@Valid DecisionForecastReqVO reqVO) {
        // é»˜è®¤æŸ¥æœ€è¿‘30天
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime beginTime = endTime.minusDays(30);
        return success(forecastService.getForecastList(reqVO.getForecastCode(), beginTime, endTime));
    }
    @PostMapping("/generate")
    @Operation(summary = "生成预测(占位)")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<Boolean> generateForecast(@RequestParam("forecastCode") String forecastCode) {
        forecastService.generateForecast(forecastCode, LocalDateTime.now());
        return success(true);
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionKpiController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.*;
import cn.iocoder.yudao.module.bi.service.decision.BiDecisionKpiService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - å†³ç­–支持 KPI")
@RestController
@RequestMapping("/bi/decision/kpi")
@Validated
public class DecisionKpiController {
    @Resource
    private BiDecisionKpiService decisionKpiService;
    @GetMapping("/overview")
    @Operation(summary = "获取KPI概览")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<Map<String, Object>> getKpiOverview() {
        return success(decisionKpiService.getKpiOverview());
    }
    @GetMapping("/detail/{kpiCode}")
    @Operation(summary = "获取KPI详情")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<Map<String, Object>> getKpiDetail(@PathVariable("kpiCode") String kpiCode) {
        return success(decisionKpiService.getKpiDetail(kpiCode));
    }
    @GetMapping("/category/{category}")
    @Operation(summary = "按分类获取KPI")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<Map<String, Object>> getKpiByCategory(@PathVariable("category") String category) {
        return success(decisionKpiService.getKpiByCategory(category));
    }
    @PostMapping("/refresh/{kpiCode}")
    @Operation(summary = "刷新KPI值")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<BigDecimal> refreshKpi(@PathVariable("kpiCode") String kpiCode) {
        return success(decisionKpiService.refreshKpi(kpiCode));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionKpiDefinitionController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionKpiDefinitionPageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import cn.iocoder.yudao.module.bi.service.decision.BiKpiDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - å†³ç­–支持 KPI指标定义")
@RestController
@RequestMapping("/bi/decision/kpi-definition")
@Validated
public class DecisionKpiDefinitionController {
    @Resource
    private BiKpiDefinitionService kpiDefinitionService;
    @PostMapping("/create")
    @Operation(summary = "创建KPI指标")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:create')")
    public CommonResult<Long> createKpi(@Valid @RequestBody BiKpiDefinitionDO kpi) {
        return success(kpiDefinitionService.createKpi(kpi));
    }
    @PutMapping("/update")
    @Operation(summary = "更新KPI指标")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:update')")
    public CommonResult<Boolean> updateKpi(@Valid @RequestBody BiKpiDefinitionDO kpi) {
        kpiDefinitionService.updateKpi(kpi);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除KPI指标")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:delete')")
    public CommonResult<Boolean> deleteKpi(@RequestParam("id") Long id) {
        kpiDefinitionService.deleteKpi(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获取KPI指标")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<BiKpiDefinitionDO> getKpi(@RequestParam("id") Long id) {
        return success(kpiDefinitionService.getKpi(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获取KPI指标分页")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<PageResult<BiKpiDefinitionDO>> getKpiPage(@Valid DecisionKpiDefinitionPageReqVO pageReq) {
        return success(kpiDefinitionService.getKpiPage(pageReq));
    }
    @GetMapping("/list-all")
    @Operation(summary = "获取所有KPI指标")
    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
    public CommonResult<PageResult<BiKpiDefinitionDO>> getKpiList() {
        return success(kpiDefinitionService.getKpiPage(new DecisionKpiDefinitionPageReqVO()));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - é¢„警记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DecisionAlertPageReqVO extends PageParam {
    @Schema(description = "状态:0-未处理 1-已确认 2-已处理", example = "0")
    private Integer status;
    @Schema(description = "严重级别:1-警告 2-严重 3-紧急", example = "1")
    private Integer severity;
    @Schema(description = "KPI编码", example = "power_supply_amount")
    private String kpiCode;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - é¢„警记录 Response VO")
@Data
public class DecisionAlertRespVO {
    @Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long id;
    @Schema(description = "关联规则ID", example = "1")
    private Long ruleId;
    @Schema(description = "KPI编码", example = "power_supply_amount")
    private String kpiCode;
    @Schema(description = "KPI名称", example = "供电量")
    private String kpiName;
    @Schema(description = "实际值", example = "15000.00")
    private BigDecimal actualValue;
    @Schema(description = "阈值", example = "10000.00")
    private BigDecimal thresholdValue;
    @Schema(description = "严重级别:1-警告 2-严重 3-紧急", example = "1")
    private Integer severity;
    @Schema(description = "预警消息", example = "供电量当前值15000超出阈值10000")
    private String alertMessage;
    @Schema(description = "状态:0-未处理 1-已确认 2-已处理", example = "0")
    private Integer status;
    @Schema(description = "处理人ID", example = "1")
    private Long handlerId;
    @Schema(description = "处理时间", example = "2026-08-20 10:00:00")
    private LocalDateTime handleTime;
    @Schema(description = "处理备注", example = "已确认")
    private String handleRemark;
    @Schema(description = "触发时间", example = "2026-08-20 09:55:00")
    private LocalDateTime triggeredTime;
    @Schema(description = "创建时间", example = "2026-08-20 09:55:00")
    private LocalDateTime createTime;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionAlertRulePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - é¢„警规则分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DecisionAlertRulePageReqVO extends PageParam {
    @Schema(description = "关联KPI指标ID", example = "1")
    private Long kpiId;
    @Schema(description = "是否启用:0-禁用 1-启用", example = "1")
    private Integer enabled;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - è´Ÿè·é¢„测 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DecisionForecastReqVO extends PageParam {
    @Schema(description = "预测编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "load_forecast_daily")
    private String forecastCode;
    @Schema(description = "维度", example = "region")
    private String dimension;
    @Schema(description = "维度值", example = "华东")
    private String dimensionValue;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - è´Ÿè·é¢„测 Response VO")
@Data
public class DecisionForecastRespVO {
    @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long id;
    @Schema(description = "预测编码", example = "load_forecast_daily")
    private String forecastCode;
    @Schema(description = "预测名称", example = "日负荷预测")
    private String forecastName;
    @Schema(description = "预测时间点", example = "2026-08-21 00:00:00")
    private LocalDateTime pointTime;
    @Schema(description = "预测值", example = "5000.00")
    private BigDecimal forecastValue;
    @Schema(description = "实际值", example = "4800.00")
    private BigDecimal actualValue;
    @Schema(description = "置信区间下限", example = "4500.00")
    private BigDecimal lowerBound;
    @Schema(description = "置信区间上限", example = "5500.00")
    private BigDecimal upperBound;
    @Schema(description = "置信水平(%)", example = "95.00")
    private BigDecimal confidenceLevel;
    @Schema(description = "模型版本", example = "v1.0")
    private String modelVersion;
    @Schema(description = "维度", example = "region")
    private String dimension;
    @Schema(description = "维度值", example = "华东")
    private String dimensionValue;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiByCategoryRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - å†³ç­–支持 KPI åˆ†ç±» Response VO")
@Data
public class DecisionKpiByCategoryRespVO {
    @Schema(description = "分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "power_supply")
    private String category;
    @Schema(description = "KPI列表")
    private List<Map<String, Object>> kpis;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiDefinitionPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - KPI指标定义分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class DecisionKpiDefinitionPageReqVO extends PageParam {
    @Schema(description = "KPI名称", example = "供电量")
    private String name;
    @Schema(description = "分类", example = "power_supply")
    private String category;
    @Schema(description = "状态:0-禁用 1-启用", example = "1")
    private Integer status;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiDetailRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - å†³ç­–支持 KPI è¯¦æƒ… Response VO")
@Data
public class DecisionKpiDetailRespVO {
    @Schema(description = "KPI编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "power_supply_amount")
    private String code;
    @Schema(description = "KPI名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "供电量")
    private String name;
    @Schema(description = "单位", example = "万千瓦时")
    private String unit;
    @Schema(description = "分类", example = "power_supply")
    private String category;
    @Schema(description = "当前值", example = "12345.67")
    private BigDecimal value;
    @Schema(description = "时间序列数据(JSON)", example = "[{\"time\":\"2026-08\",\"value\":1000}]")
    private String timeSeries;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionKpiOverviewRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - å†³ç­–支持 KPI æ¦‚览 Response VO")
@Data
public class DecisionKpiOverviewRespVO {
    @Schema(description = "KPI编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "power_supply_amount")
    private String code;
    @Schema(description = "KPI名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "供电量")
    private String name;
    @Schema(description = "分类", example = "power_supply")
    private String category;
    @Schema(description = "单位", example = "万千瓦时")
    private String unit;
    @Schema(description = "当前值", example = "12345.67")
    private BigDecimal value;
    @Schema(description = "预警状态:normal/warn/critical", example = "normal")
    private String alertStatus;
    @Schema(description = "默认图表类型", example = "number")
    private String chartType;
    @Schema(description = "状态:0-禁用 1-启用", example = "1")
    private Integer status;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiAlertRecordDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.bi.dal.dataobject.decision;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * é¢„警记录 DO
 *
 * @author è¶…级管理员
 */
@TableName("bi_alert_record")
@KeySequence("bi_alert_record_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BiAlertRecordDO extends BaseDO {
    @TableId
    private Long id;
    /**
     * å…³è”规则ID
     */
    private Long ruleId;
    /**
     * å…³è”KPI指标ID
     */
    private Long kpiId;
    /**
     * KPI编码(冗余)
     */
    private String kpiCode;
    /**
     * KPI名称(冗余)
     */
    private String kpiName;
    /**
     * å®žé™…值
     */
    private BigDecimal actualValue;
    /**
     * é˜ˆå€¼(冗余)
     */
    private BigDecimal thresholdValue;
    /**
     * ä¸¥é‡çº§åˆ«
     */
    private Integer severity;
    /**
     * é¢„警消息
     */
    private String alertMessage;
    /**
     * çŠ¶æ€ï¼š0-未处理 1-已确认 2-已处理
     */
    private Integer status;
    /**
     * å¤„理人ID
     */
    private Long handlerId;
    /**
     * å¤„理时间
     */
    private LocalDateTime handleTime;
    /**
     * å¤„理备注
     */
    private String handleRemark;
    /**
     * è§¦å‘æ—¶é—´
     */
    private LocalDateTime triggeredTime;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiAlertRuleDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.bi.dal.dataobject.decision;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * é¢„警规则 DO
 *
 * @author è¶…级管理员
 */
@TableName("bi_alert_rule")
@KeySequence("bi_alert_rule_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BiAlertRuleDO extends BaseDO {
    @TableId
    private Long id;
    /**
     * å…³è”KPI指标ID
     */
    private Long kpiId;
    /**
     * è§„则名称
     */
    private String name;
    /**
     * ä¸¥é‡çº§åˆ«ï¼š1-警告 2-严重 3-紧急
     */
    private Integer severity;
    /**
     * æ¯”较运算符
     */
    private String comparisonOperator;
    /**
     * é˜ˆå€¼
     */
    private BigDecimal thresholdValue;
    /**
     * é€šçŸ¥æ–¹å¼ï¼šsystem/message/email(逗号分隔)
     */
    private String notificationType;
    /**
     * æ˜¯å¦å¯ç”¨ï¼š0-禁用 1-启用
     */
    private Integer enabled;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiForecastDataDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.bi.dal.dataobject.decision;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * è´Ÿè·é¢„测数据 DO
 *
 * @author è¶…级管理员
 */
@TableName("bi_forecast_data")
@KeySequence("bi_forecast_data_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BiForecastDataDO extends BaseDO {
    @TableId
    private Long id;
    /**
     * é¢„测编码,如:load_forecast_daily
     */
    private String forecastCode;
    /**
     * é¢„测名称
     */
    private String forecastName;
    /**
     * é¢„测时间点
     */
    private LocalDateTime pointTime;
    /**
     * é¢„测值
     */
    private BigDecimal forecastValue;
    /**
     * å®žé™…值(回填用于校验)
     */
    private BigDecimal actualValue;
    /**
     * ç½®ä¿¡åŒºé—´ä¸‹é™
     */
    private BigDecimal lowerBound;
    /**
     * ç½®ä¿¡åŒºé—´ä¸Šé™
     */
    private BigDecimal upperBound;
    /**
     * ç½®ä¿¡æ°´å¹³(%)
     */
    private BigDecimal confidenceLevel;
    /**
     * æ¨¡åž‹ç‰ˆæœ¬
     */
    private String modelVersion;
    /**
     * ç»´åº¦ï¼Œå¦‚:region/line/station
     */
    private String dimension;
    /**
     * ç»´åº¦å€¼
     */
    private String dimensionValue;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/dataobject/decision/BiKpiDefinitionDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
package cn.iocoder.yudao.module.bi.dal.dataobject.decision;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * KPI指标定义 DO
 *
 * @author è¶…级管理员
 */
@TableName("bi_kpi_definition")
@KeySequence("bi_kpi_definition_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BiKpiDefinitionDO extends BaseDO {
    @TableId
    private Long id;
    /**
     * KPI编码
     */
    private String code;
    /**
     * KPI名称
     */
    private String name;
    /**
     * åˆ†ç±»ï¼špower_supply/device_operation/production/quality/procurement/safety
     */
    private String category;
    /**
     * å•位
     */
    private String unit;
    /**
     * æ•°æ®æ¥æºï¼šsql/interface/manual
     */
    private String dataSource;
    /**
     * æŸ¥è¯¢SQL
     */
    private String querySql;
    /**
     * é»˜è®¤å›¾è¡¨ç±»åž‹
     */
    private String chartType;
    /**
     * é¢„警阈值-警告
     */
    private BigDecimal thresholdWarn;
    /**
     * é¢„警阈值-严重
     */
    private BigDecimal thresholdCritical;
    /**
     * æ¯”较运算符:>/</>=/<=
     */
    private String comparisonOperator;
    /**
     * åˆ·æ–°é—´éš”(秒)
     */
    private Integer refreshInterval;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * çŠ¶æ€ï¼š0-禁用 1-启用
     */
    private Integer status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiAlertRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bi.dal.mysql.decision;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BiAlertRecordMapper extends BaseMapperX<BiAlertRecordDO> {
    default Long selectCountByStatus(Integer status) {
        return selectCount(new LambdaQueryWrapper<BiAlertRecordDO>()
                .eq(BiAlertRecordDO::getStatus, status));
    }
    default List<BiAlertRecordDO> selectListByStatus(Integer status) {
        return selectList(new LambdaQueryWrapper<BiAlertRecordDO>()
                .eq(BiAlertRecordDO::getStatus, status));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiAlertRuleMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package cn.iocoder.yudao.module.bi.dal.mysql.decision;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BiAlertRuleMapper extends BaseMapperX<BiAlertRuleDO> {
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bi.dal.mysql.decision;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface BiForecastDataMapper extends BaseMapperX<BiForecastDataDO> {
    default List<BiForecastDataDO> selectListByCodeAndTimeRange(String forecastCode,
                                                                 LocalDateTime beginTime,
                                                                 LocalDateTime endTime) {
        return selectList(new LambdaQueryWrapper<BiForecastDataDO>()
                .eq(BiForecastDataDO::getForecastCode, forecastCode)
                .between(BiForecastDataDO::getPointTime, beginTime, endTime)
                .orderByAsc(BiForecastDataDO::getPointTime));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiKpiDefinitionMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package cn.iocoder.yudao.module.bi.dal.mysql.decision;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BiKpiDefinitionMapper extends BaseMapperX<BiKpiDefinitionDO> {
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java
@@ -25,4 +25,16 @@
    // ========== ä»“库坐标配置相关 1-080-151 ~ 1-080-170 ==========
    ErrorCode BI_WAREHOUSE_COORDINATE_NOT_EXISTS = new ErrorCode(1_080_151, "仓库坐标配置不存在");
    // ========== å†³ç­–支持 - KPI指标 1_080_152 ~ 1_080_180 ==========
    ErrorCode KPI_NOT_EXISTS = new ErrorCode(1_080_152, "KPI指标不存在");
    ErrorCode KPI_CODE_DUPLICATE = new ErrorCode(1_080_153, "KPI编码已存在");
    // ========== å†³ç­–支持 - é¢„警规则 1_080_181 ~ 1_080_200 ==========
    ErrorCode ALERT_RULE_NOT_EXISTS = new ErrorCode(1_080_181, "预警规则不存在");
    ErrorCode ALERT_RULE_KPI_NOT_EXISTS = new ErrorCode(1_080_182, "关联的KPI指标不存在");
    // ========== å†³ç­–支持 - é¢„警记录 1_080_201 ~ 1_080_220 ==========
    ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_080_201, "预警记录不存在");
    ErrorCode ALERT_RECORD_ALREADY_HANDLED = new ErrorCode(1_080_202, "预警记录已处理");
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/AlertEvaluationTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,139 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRecordMapper;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRuleMapper;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
 * é¢„警定时评估任务
 *
 * å®šæœŸæ‰«ææ‰€æœ‰æ´»è·ƒé¢„警规则,评估阈值并生成预警记录
 *
 * @author è¶…级管理员
 */
@Slf4j
@Component
public class AlertEvaluationTask {
    @Resource
    private BiAlertRuleMapper alertRuleMapper;
    @Resource
    private BiKpiDefinitionMapper kpiMapper;
    @Resource
    private BiAlertRecordMapper alertRecordMapper;
    @Resource
    private JdbcTemplate jdbcTemplate;
    /**
     * æ¯5分钟评估一次所有活跃规则
     */
    @Scheduled(fixedDelayString = "${bi.alert.evaluation.interval:300000}")
    public void evaluateAllAlertRules() {
        log.debug("[evaluateAllAlertRules][开始评估预警规则]");
        List<BiAlertRuleDO> rules = alertRuleMapper.selectList(
                BiAlertRuleDO::getEnabled, 1);
        if (rules.isEmpty()) {
            return;
        }
        for (BiAlertRuleDO rule : rules) {
            try {
                evaluateRule(rule);
            } catch (Exception e) {
                log.error("[evaluateAllAlertRules][规则 {} è¯„估异常]", rule.getId(), e);
            }
        }
    }
    private void evaluateRule(BiAlertRuleDO rule) {
        BiKpiDefinitionDO kpi = kpiMapper.selectById(rule.getKpiId());
        if (kpi == null || kpi.getStatus() == 0) {
            return;
        }
        // æ‰§è¡Œ KPI æŸ¥è¯¢
        BigDecimal currentValue = executeQuery(kpi.getQuerySql());
        if (currentValue == null) {
            return;
        }
        // æ¯”较阈值
        int cmp = currentValue.compareTo(rule.getThresholdValue());
        boolean triggered = switch (rule.getComparisonOperator()) {
            case ">" -> cmp > 0;
            case "<" -> cmp < 0;
            case ">=" -> cmp >= 0;
            case "<=" -> cmp <= 0;
            default -> false;
        };
        if (!triggered) {
            return;
        }
        // æ£€æŸ¥æ˜¯å¦å·²æœ‰æœªå¤„理的同KPI预警(去重)
        Long existingCount = alertRecordMapper.selectCountByStatus(0);
        if (existingCount > 0) {
            List<BiAlertRecordDO> existing = alertRecordMapper.selectListByStatus(0);
            boolean dup = existing.stream().anyMatch(r -> r.getKpiId().equals(rule.getKpiId()));
            if (dup) {
                return;
            }
        }
        // ç”Ÿæˆé¢„警记录
        BiAlertRecordDO record = BiAlertRecordDO.builder()
                .ruleId(rule.getId())
                .kpiId(kpi.getId())
                .kpiCode(kpi.getCode())
                .kpiName(kpi.getName())
                .actualValue(currentValue)
                .thresholdValue(rule.getThresholdValue())
                .severity(rule.getSeverity())
                .alertMessage(String.format("%s å½“前值 %s è¶…出阈值 %s",
                        kpi.getName(), currentValue, rule.getThresholdValue()))
                .status(0)
                .triggeredTime(LocalDateTime.now())
                .build();
        alertRecordMapper.insert(record);
        log.warn("[evaluateRule][触发预警] KPI={}, å½“前值={}, é˜ˆå€¼={}",
                kpi.getName(), currentValue, rule.getThresholdValue());
    }
    private BigDecimal executeQuery(String sql) {
        if (sql == null || sql.isBlank()) {
            return null;
        }
        try {
            List<Map<String, Object>> result = jdbcTemplate.queryForList(sql);
            if (result.isEmpty()) {
                return null;
            }
            Object value = result.get(0).values().iterator().next();
            if (value instanceof Number) {
                return BigDecimal.valueOf(((Number) value).doubleValue());
            }
            return null;
        } catch (Exception e) {
            log.error("[executeQuery][SQL执行失败] {}", sql, e);
            return null;
        }
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertPageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
/**
 * é¢„警记录 Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface BiAlertRecordService {
    Long createRecord(@Valid BiAlertRecordDO record);
    void confirmRecord(Long id, Long handlerId);
    void handleRecord(Long id, Long handlerId, String handleRemark);
    PageResult<BiAlertRecordDO> getRecordPage(DecisionAlertPageReqVO pageReq);
    Long getUnprocessedCount();
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertPageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRecordMapper;
import cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
 * é¢„警记录 Service å®žçް
 *
 * @author è¶…级管理员
 */
@Service
public class BiAlertRecordServiceImpl implements BiAlertRecordService {
    @Resource
    private BiAlertRecordMapper alertRecordMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRecord(BiAlertRecordDO record) {
        record.setStatus(0); // æœªå¤„理
        record.setTriggeredTime(LocalDateTime.now());
        alertRecordMapper.insert(record);
        return record.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmRecord(Long id, Long handlerId) {
        BiAlertRecordDO record = alertRecordMapper.selectById(id);
        if (record == null) {
            throw exception(ErrorCodeConstants.ALERT_RECORD_NOT_EXISTS);
        }
        if (record.getStatus() >= 1) {
            throw exception(ErrorCodeConstants.ALERT_RECORD_ALREADY_HANDLED);
        }
        alertRecordMapper.updateById(BiAlertRecordDO.builder()
                .id(id)
                .status(1) // å·²ç¡®è®¤
                .handlerId(handlerId)
                .build());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleRecord(Long id, Long handlerId, String handleRemark) {
        BiAlertRecordDO record = alertRecordMapper.selectById(id);
        if (record == null) {
            throw exception(ErrorCodeConstants.ALERT_RECORD_NOT_EXISTS);
        }
        if (record.getStatus() >= 2) {
            throw exception(ErrorCodeConstants.ALERT_RECORD_ALREADY_HANDLED);
        }
        alertRecordMapper.updateById(BiAlertRecordDO.builder()
                .id(id)
                .status(2) // å·²å¤„理
                .handlerId(handlerId)
                .handleTime(LocalDateTime.now())
                .handleRemark(handleRemark)
                .build());
    }
    @Override
    public PageResult<BiAlertRecordDO> getRecordPage(DecisionAlertPageReqVO pageReq) {
        return alertRecordMapper.selectPage(pageReq, new LambdaQueryWrapperX<BiAlertRecordDO>()
                .eqIfPresent(BiAlertRecordDO::getStatus, pageReq.getStatus())
                .eqIfPresent(BiAlertRecordDO::getSeverity, pageReq.getSeverity())
                .eqIfPresent(BiAlertRecordDO::getKpiCode, pageReq.getKpiCode())
                .orderByDesc(BiAlertRecordDO::getTriggeredTime));
    }
    @Override
    public Long getUnprocessedCount() {
        return alertRecordMapper.selectCountByStatus(0);
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRuleService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertRulePageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO;
import jakarta.validation.Valid;
/**
 * é¢„警规则 Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface BiAlertRuleService {
    Long createRule(@Valid BiAlertRuleDO rule);
    void updateRule(@Valid BiAlertRuleDO rule);
    void deleteRule(Long id);
    BiAlertRuleDO getRule(Long id);
    PageResult<BiAlertRuleDO> getRulePage(DecisionAlertRulePageReqVO pageReq);
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiAlertRuleServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionAlertRulePageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRuleMapper;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper;
import cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
 * é¢„警规则 Service å®žçް
 *
 * @author è¶…级管理员
 */
@Service
public class BiAlertRuleServiceImpl implements BiAlertRuleService {
    @Resource
    private BiAlertRuleMapper alertRuleMapper;
    @Resource
    private BiKpiDefinitionMapper kpiMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRule(BiAlertRuleDO rule) {
        // æ ¡éªŒKPI存在
        if (kpiMapper.selectById(rule.getKpiId()) == null) {
            throw exception(ErrorCodeConstants.ALERT_RULE_KPI_NOT_EXISTS);
        }
        alertRuleMapper.insert(rule);
        return rule.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRule(BiAlertRuleDO rule) {
        if (alertRuleMapper.selectById(rule.getId()) == null) {
            throw exception(ErrorCodeConstants.ALERT_RULE_NOT_EXISTS);
        }
        if (rule.getKpiId() != null && kpiMapper.selectById(rule.getKpiId()) == null) {
            throw exception(ErrorCodeConstants.ALERT_RULE_KPI_NOT_EXISTS);
        }
        alertRuleMapper.updateById(rule);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRule(Long id) {
        if (alertRuleMapper.selectById(id) == null) {
            throw exception(ErrorCodeConstants.ALERT_RULE_NOT_EXISTS);
        }
        alertRuleMapper.deleteById(id);
    }
    @Override
    public BiAlertRuleDO getRule(Long id) {
        return alertRuleMapper.selectById(id);
    }
    @Override
    public PageResult<BiAlertRuleDO> getRulePage(DecisionAlertRulePageReqVO pageReq) {
        return alertRuleMapper.selectPage(pageReq, new LambdaQueryWrapperX<BiAlertRuleDO>()
                .eqIfPresent(BiAlertRuleDO::getKpiId, pageReq.getKpiId())
                .eqIfPresent(BiAlertRuleDO::getEnabled, pageReq.getEnabled())
                .orderByDesc(BiAlertRuleDO::getId));
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiDecisionKpiService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.bi.service.decision;
import java.math.BigDecimal;
import java.util.Map;
/**
 * å†³ç­–支持 KPI èšåˆ Service æŽ¥å£
 *
 * è·¨æ¨¡å—聚合实时 KPI æ•°æ®
 *
 * @author è¶…级管理员
 */
public interface BiDecisionKpiService {
    /**
     * èŽ·å– KPI æ¦‚览数据
     */
    Map<String, Object> getKpiOverview();
    /**
     * èŽ·å–å•ä¸ª KPI è¯¦æƒ…(含时间序列)
     */
    Map<String, Object> getKpiDetail(String kpiCode);
    /**
     * æŒ‰åˆ†ç±»èŽ·å– KPI
     */
    Map<String, Object> getKpiByCategory(String category);
    /**
     * åˆ·æ–° KPI å€¼
     */
    BigDecimal refreshKpi(String kpiCode);
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiDecisionKpiServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
/**
 * å†³ç­–支持 KPI èšåˆ Service å®žçް
 *
 * é€šè¿‡ JdbcTemplate è·¨æ¨¡å—执行聚合 SQL
 *
 * @author è¶…级管理员
 */
@Slf4j
@Service
public class BiDecisionKpiServiceImpl implements BiDecisionKpiService {
    @Resource
    private BiKpiDefinitionMapper kpiMapper;
    @Resource
    private JdbcTemplate jdbcTemplate;
    @Override
    public Map<String, Object> getKpiOverview() {
        List<BiKpiDefinitionDO> kpis = kpiMapper.selectList();
        List<Map<String, Object>> items = new ArrayList<>();
        for (BiKpiDefinitionDO kpi : kpis) {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("code", kpi.getCode());
            item.put("name", kpi.getName());
            item.put("category", kpi.getCategory());
            item.put("unit", kpi.getUnit());
            item.put("chartType", kpi.getChartType());
            item.put("status", kpi.getStatus());
            // æ‰§è¡ŒæŸ¥è¯¢èŽ·å–å½“å‰å€¼
            BigDecimal value = executeKpiSql(kpi.getQuerySql());
            item.put("value", value);
            // åˆ¤æ–­é¢„警状态
            String alertStatus = evaluateAlertStatus(kpi, value);
            item.put("alertStatus", alertStatus);
            items.add(item);
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("kpis", items);
        result.put("total", items.size());
        return result;
    }
    @Override
    public Map<String, Object> getKpiDetail(String kpiCode) {
        BiKpiDefinitionDO kpi = kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpiCode);
        if (kpi == null) {
            return Collections.emptyMap();
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("code", kpi.getCode());
        result.put("name", kpi.getName());
        result.put("unit", kpi.getUnit());
        result.put("category", kpi.getCategory());
        result.put("value", executeKpiSql(kpi.getQuerySql()));
        return result;
    }
    @Override
    public Map<String, Object> getKpiByCategory(String category) {
        List<BiKpiDefinitionDO> kpis = kpiMapper.selectList(
                BiKpiDefinitionDO::getCategory, category);
        List<Map<String, Object>> items = new ArrayList<>();
        for (BiKpiDefinitionDO kpi : kpis) {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("code", kpi.getCode());
            item.put("name", kpi.getName());
            item.put("unit", kpi.getUnit());
            item.put("value", executeKpiSql(kpi.getQuerySql()));
            items.add(item);
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("category", category);
        result.put("kpis", items);
        return result;
    }
    @Override
    public BigDecimal refreshKpi(String kpiCode) {
        BiKpiDefinitionDO kpi = kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpiCode);
        if (kpi == null) {
            return BigDecimal.ZERO;
        }
        return executeKpiSql(kpi.getQuerySql());
    }
    /**
     * æ‰§è¡Œ KPI æŸ¥è¯¢ SQL
     */
    private BigDecimal executeKpiSql(String querySql) {
        if (querySql == null || querySql.isBlank()) {
            return BigDecimal.ZERO;
        }
        try {
            List<Map<String, Object>> result = jdbcTemplate.queryForList(querySql);
            if (result.isEmpty()) {
                return BigDecimal.ZERO;
            }
            // å–第一行第一列的值
            Map<String, Object> row = result.get(0);
            Object value = row.values().iterator().next();
            if (value instanceof Number) {
                return BigDecimal.valueOf(((Number) value).doubleValue());
            }
            return BigDecimal.ZERO;
        } catch (Exception e) {
            log.error("[executeKpiSql][SQL执行失败] {}", querySql, e);
            return BigDecimal.ZERO;
        }
    }
    /**
     * è¯„估预警状态
     */
    private String evaluateAlertStatus(BiKpiDefinitionDO kpi, BigDecimal value) {
        if (kpi.getThresholdCritical() != null) {
            int cmp = value.compareTo(kpi.getThresholdCritical());
            boolean triggered = ">".equals(kpi.getComparisonOperator()) ? cmp > 0
                    : "<".equals(kpi.getComparisonOperator()) ? cmp < 0
                    : ">=".equals(kpi.getComparisonOperator()) ? cmp >= 0
                    : "<=".equals(kpi.getComparisonOperator()) ? cmp <= 0 : false;
            if (triggered) {
                return "critical";
            }
        }
        if (kpi.getThresholdWarn() != null) {
            int cmp = value.compareTo(kpi.getThresholdWarn());
            boolean triggered = ">".equals(kpi.getComparisonOperator()) ? cmp > 0
                    : "<".equals(kpi.getComparisonOperator()) ? cmp < 0
                    : ">=".equals(kpi.getComparisonOperator()) ? cmp >= 0
                    : "<=".equals(kpi.getComparisonOperator()) ? cmp <= 0 : false;
            if (triggered) {
                return "warn";
            }
        }
        return "normal";
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
import java.time.LocalDateTime;
import java.util.List;
/**
 * è´Ÿè·é¢„测 Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface BiForecastService {
    List<BiForecastDataDO> getForecastList(String forecastCode, LocalDateTime beginTime, LocalDateTime endTime);
    void generateForecast(String forecastCode, LocalDateTime pointTime);
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiForecastDataMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
 * è´Ÿè·é¢„测 Service å®žçް
 *
 * æœ¬æœŸä½¿ç”¨ç®€å•移动平均占位,预留 ML æ¨¡åž‹æŽ¥å£
 *
 * @author è¶…级管理员
 */
@Slf4j
@Service
public class BiForecastServiceImpl implements BiForecastService {
    @Resource
    private BiForecastDataMapper forecastDataMapper;
    @Override
    public List<BiForecastDataDO> getForecastList(String forecastCode, LocalDateTime beginTime, LocalDateTime endTime) {
        return forecastDataMapper.selectListByCodeAndTimeRange(forecastCode, beginTime, endTime);
    }
    @Override
    public void generateForecast(String forecastCode, LocalDateTime pointTime) {
        // æœ¬æœŸå ä½ï¼šç®€å•移动平均预测
        // å®žé™…项目应集成 ML æ¨¡åž‹ï¼ˆARIMA/Prophet/LSTM)
        log.info("[generateForecast][生成预测] forecastCode={}, pointTime={}, å½“前为占位实现", forecastCode, pointTime);
        BiForecastDataDO forecast = BiForecastDataDO.builder()
                .forecastCode(forecastCode)
                .forecastName("负荷预测(占位)")
                .pointTime(pointTime)
                .forecastValue(BigDecimal.ZERO)
                .lowerBound(BigDecimal.ZERO)
                .upperBound(BigDecimal.ZERO)
                .confidenceLevel(new BigDecimal("95.00"))
                .modelVersion("v1.0-placeholder")
                .build();
        forecastDataMapper.insert(forecast);
    }
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiKpiDefinitionService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionKpiDefinitionPageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import jakarta.validation.Valid;
/**
 * KPI指标定义 Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface BiKpiDefinitionService {
    Long createKpi(@Valid BiKpiDefinitionDO kpi);
    void updateKpi(@Valid BiKpiDefinitionDO kpi);
    void deleteKpi(Long id);
    BiKpiDefinitionDO getKpi(Long id);
    BiKpiDefinitionDO getKpiByCode(String code);
    PageResult<BiKpiDefinitionDO> getKpiPage(DecisionKpiDefinitionPageReqVO pageReq);
}
yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiKpiDefinitionServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.bi.service.decision;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionKpiDefinitionPageReqVO;
import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO;
import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper;
import cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
 * KPI指标定义 Service å®žçް
 *
 * @author è¶…级管理员
 */
@Service
public class BiKpiDefinitionServiceImpl implements BiKpiDefinitionService {
    @Resource
    private BiKpiDefinitionMapper kpiMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createKpi(BiKpiDefinitionDO kpi) {
        // æ ¡éªŒç¼–码唯一
        if (kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpi.getCode()) != null) {
            throw exception(ErrorCodeConstants.KPI_CODE_DUPLICATE);
        }
        kpiMapper.insert(kpi);
        return kpi.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateKpi(BiKpiDefinitionDO kpi) {
        // æ ¡éªŒå­˜åœ¨
        BiKpiDefinitionDO exist = kpiMapper.selectById(kpi.getId());
        if (exist == null) {
            throw exception(ErrorCodeConstants.KPI_NOT_EXISTS);
        }
        // æ ¡éªŒç¼–码唯一(排除自身)
        BiKpiDefinitionDO byCode = kpiMapper.selectOne(BiKpiDefinitionDO::getCode, kpi.getCode());
        if (byCode != null && !byCode.getId().equals(kpi.getId())) {
            throw exception(ErrorCodeConstants.KPI_CODE_DUPLICATE);
        }
        kpiMapper.updateById(kpi);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteKpi(Long id) {
        if (kpiMapper.selectById(id) == null) {
            throw exception(ErrorCodeConstants.KPI_NOT_EXISTS);
        }
        kpiMapper.deleteById(id);
    }
    @Override
    public BiKpiDefinitionDO getKpi(Long id) {
        return kpiMapper.selectById(id);
    }
    @Override
    public BiKpiDefinitionDO getKpiByCode(String code) {
        return kpiMapper.selectOne(BiKpiDefinitionDO::getCode, code);
    }
    @Override
    public PageResult<BiKpiDefinitionDO> getKpiPage(DecisionKpiDefinitionPageReqVO pageReq) {
        return kpiMapper.selectPage(pageReq, new LambdaQueryWrapperX<BiKpiDefinitionDO>()
                .likeIfPresent(BiKpiDefinitionDO::getName, pageReq.getName())
                .eqIfPresent(BiKpiDefinitionDO::getCategory, pageReq.getCategory())
                .eqIfPresent(BiKpiDefinitionDO::getStatus, pageReq.getStatus())
                .orderByAsc(BiKpiDefinitionDO::getSort));
    }
}
yudao-module-crm/pom.xml
@@ -55,6 +55,13 @@
            <version>${revision}</version>
        </dependency>
        <!-- MES ç”Ÿäº§å·¥å• API -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-mes-api</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- ä¸šåŠ¡ç»„ä»¶ -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/CrmCancellationController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package cn.iocoder.yudao.module.crm.controller.admin.cancellation;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationSaveReqVO;
import cn.iocoder.yudao.module.crm.service.cancellation.CrmCancellationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - CRM é”€æˆ·ç”³è¯·")
@RestController
@RequestMapping("/crm/cancellation")
@Validated
public class CrmCancellationController {
    @Resource
    private CrmCancellationService cancellationService;
    @PostMapping("/create")
    @Operation(summary = "创建销户申请")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:create')")
    public CommonResult<Long> createCancellation(@Valid @RequestBody CrmCancellationSaveReqVO createReqVO) {
        return success(cancellationService.createCancellation(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新销户申请")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:update')")
    public CommonResult<Boolean> updateCancellation(@Valid @RequestBody CrmCancellationSaveReqVO updateReqVO) {
        cancellationService.updateCancellation(updateReqVO);
        return success(true);
    }
    @PutMapping("/audit")
    @Operation(summary = "审批销户申请(通过/驳回)")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:update')")
    public CommonResult<Boolean> auditCancellation(@Valid @RequestBody CrmCancellationAuditReqVO auditReqVO) {
        cancellationService.auditCancellation(auditReqVO);
        return success(true);
    }
    @PutMapping("/submit")
    @Operation(summary = "提交销户申请审批")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:update')")
    public CommonResult<Boolean> submitCancellation(@RequestParam("id") Long id,
                                                    @RequestParam("processDefinitionKey") String processDefinitionKey) {
        cancellationService.submitCancellation(id, processDefinitionKey, getLoginUserId());
        return success(true);
    }
    @GetMapping("/approve-process-list")
    @Operation(summary = "获得销户申请审批流程定义列表")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:query')")
    public CommonResult<List<Map<String, Object>>> getCancellationApproveProcessDefinitionList() {
        return success(cancellationService.getCancellationApproveProcessDefinitionList());
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除销户申请")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:cancellation:delete')")
    public CommonResult<Boolean> deleteCancellation(@RequestParam("id") Long id) {
        cancellationService.deleteCancellation(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得销户申请")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:cancellation:query')")
    public CommonResult<CrmCancellationRespVO> getCancellation(@RequestParam("id") Long id) {
        return success(cancellationService.getCancellation(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获得销户申请分页")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:query')")
    public CommonResult<PageResult<CrmCancellationRespVO>> getCancellationPage(@Valid CrmCancellationPageReqVO pageReqVO) {
        return success(cancellationService.getCancellationPage(pageReqVO));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出销户申请 Excel")
    @PreAuthorize("@ss.hasPermission('crm:cancellation:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportCancellationExcel(@Valid CrmCancellationPageReqVO exportReqVO,
                                        HttpServletResponse response) throws IOException {
        exportReqVO.setPageSize(PAGE_SIZE_NONE);
        List<CrmCancellationRespVO> list = cancellationService.getCancellationList(exportReqVO);
        ExcelUtils.write(response, "销户申请.xls", "数据", CrmCancellationRespVO.class, list);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationAuditReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - CRM é”€æˆ·ç”³è¯·å®¡æ‰¹ Request VO")
@Data
public class CrmCancellationAuditReqVO {
    @Schema(description = "销户申请编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "销户申请编号不能为空")
    private Long id;
    @Schema(description = "审批结果:20=通过 / 30=驳回", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "审批结果不能为空")
    private Integer auditStatus;
    @Schema(description = "审批意见")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - CRM é”€æˆ·ç”³è¯·åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CrmCancellationPageReqVO extends PageParam {
    @Schema(description = "销户编号", example = "CL202608190001")
    private String cancellationNo;
    @Schema(description = "客户ID", example = "2048")
    private Long customerId;
    @Schema(description = "销户类型", example = "ELECTRICITY")
    private String cancellationType;
    @Schema(description = "审批状态", example = "0")
    private Integer auditStatus;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_AUDIT_STATUS;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CANCELLATION_TYPE;
@Schema(description = "管理后台 - CRM é”€æˆ·ç”³è¯· Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmCancellationRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "销户编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("销户编号")
    private String cancellationNo;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("客户ID")
    private Long customerId;
    @Schema(description = "客户名称")
    @ExcelProperty("客户名称")
    private String customerName;
    @Schema(description = "合同ID")
    @ExcelProperty("合同ID")
    private Long contractId;
    @Schema(description = "合同编号")
    @ExcelProperty("合同编号")
    private String contractNo;
    @Schema(description = "销户类型")
    @ExcelProperty(value = "销户类型", converter = DictConvert.class)
    @DictFormat(CRM_CANCELLATION_TYPE)
    private String cancellationType;
    @Schema(description = "销户原因")
    @ExcelProperty("销户原因")
    private String cancelReason;
    @Schema(description = "预计销户日期")
    @ExcelProperty("预计销户日期")
    private LocalDate expectedCancelDate;
    @Schema(description = "实际销户日期")
    @ExcelProperty("实际销户日期")
    private LocalDate actualCancelDate;
    @Schema(description = "退费金额")
    @ExcelProperty("退费金额")
    private BigDecimal refundAmount;
    @Schema(description = "申请用户ID")
    @ExcelProperty("申请用户ID")
    private Long applyUserId;
    @Schema(description = "申请用户姓名")
    @ExcelProperty("申请用户姓名")
    private String applyUserName;
    @Schema(description = "申请时间")
    @ExcelProperty("申请时间")
    private LocalDateTime applyTime;
    @Schema(description = "审批状态")
    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
    @DictFormat(CRM_AUDIT_STATUS)
    private Integer auditStatus;
    @Schema(description = "流程实例ID")
    private String processInstanceId;
    @Schema(description = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间")
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/cancellation/vo/CrmCancellationSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CANCELLATION_TYPE;
@Schema(description = "管理后台 - CRM é”€æˆ·ç”³è¯·æ–°å¢ž/修改 Request VO")
@Data
public class CrmCancellationSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "销户编号(不传则自动生成)")
    private String cancellationNo;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "客户ID不能为空")
    private Long customerId;
    @Schema(description = "客户名称")
    private String customerName;
    @Schema(description = "合同ID", example = "2048")
    private Long contractId;
    @Schema(description = "合同编号")
    private String contractNo;
    @Schema(description = "销户类型(用电销户/服务终止/合同到期)", example = "ELECTRICITY")
    @DictFormat(CRM_CANCELLATION_TYPE)
    private String cancellationType;
    @Schema(description = "销户原因")
    private String cancelReason;
    @Schema(description = "预计销户日期")
    private LocalDate expectedCancelDate;
    @Schema(description = "实际销户日期")
    private LocalDate actualCancelDate;
    @Schema(description = "退费金额")
    private BigDecimal refundAmount;
    @Schema(description = "申请用户ID", example = "1")
    private Long applyUserId;
    @Schema(description = "申请用户姓名", example = "李工")
    private String applyUserName;
    @Schema(description = "申请时间")
    private LocalDateTime applyTime;
    @Schema(description = "审批状态(0草稿/10待审批/20通过/30驳回)", example = "0")
    private Integer auditStatus;
    @Schema(description = "流程实例ID")
    private String processInstanceId;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractCommandController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandSaveReqVO;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractCommandService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - CRM åˆåŒæŒ‡ä»¤")
@RestController
@RequestMapping("/crm/contract-command")
@Validated
public class CrmContractCommandController {
    @Resource
    private CrmContractCommandService commandService;
    @PostMapping("/create")
    @Operation(summary = "创建合同指令")
    @PreAuthorize("@ss.hasPermission('crm:contract-command:create')")
    public CommonResult<Long> createCommand(@Valid @RequestBody CrmContractCommandSaveReqVO createReqVO) {
        return success(commandService.createCommand(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新合同指令")
    @PreAuthorize("@ss.hasPermission('crm:contract-command:update')")
    public CommonResult<Boolean> updateCommand(@Valid @RequestBody CrmContractCommandSaveReqVO updateReqVO) {
        commandService.updateCommand(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除合同指令")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contract-command:delete')")
    public CommonResult<Boolean> deleteCommand(@RequestParam("id") Long id) {
        commandService.deleteCommand(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得合同指令")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contract-command:query')")
    public CommonResult<CrmContractCommandRespVO> getCommand(@RequestParam("id") Long id) {
        return success(commandService.getCommand(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获得合同指令分页")
    @PreAuthorize("@ss.hasPermission('crm:contract-command:query')")
    public CommonResult<PageResult<CrmContractCommandRespVO>> getCommandPage(@Valid CrmContractCommandPageReqVO pageReqVO) {
        return success(commandService.getCommandPage(pageReqVO));
    }
    @PutMapping("/execute")
    @Operation(summary = "执行合同指令")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contract-command:update')")
    public CommonResult<Boolean> executeCommand(@RequestParam("id") Long id) {
        commandService.executeCommand(id);
        return success(true);
    }
    @PutMapping("/cancel")
    @Operation(summary = "取消合同指令")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contract-command:update')")
    public CommonResult<Boolean> cancelCommand(@RequestParam("id") Long id) {
        commandService.cancelCommand(id);
        return success(true);
    }
    @GetMapping("/count-by-contract")
    @Operation(summary = "统计某合同指令数量")
    @Parameter(name = "contractId", description = "合同ID", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contract-command:query')")
    public CommonResult<Long> countByContract(@RequestParam("contractId") Long contractId) {
        return success(commandService.countByContractId(contractId));
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -24,6 +24,7 @@
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractCommandService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService;
@@ -74,6 +75,8 @@
    private CrmReceivableService receivableService;
    @Resource
    private CrmInvoiceService invoiceService;
    @Resource
    private CrmContractCommandService contractCommandService;
    @Resource
    private MdmItemApi mdmItemApi;
@@ -247,6 +250,8 @@
            if (contractVO.getTotalPrice() != null && contractVO.getTotalInvoicePrice() != null) {
                contractVO.setUnInvoicePrice(contractVO.getTotalPrice().subtract(contractVO.getTotalInvoicePrice()));
            }
            // 2.8 è®¾ç½®æŒ‡ä»¤ä¸‹å‘数量
            contractVO.setCommandCount(contractCommandService.countByContractId(contractVO.getId()));
        });
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - CRM åˆåŒæŒ‡ä»¤åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CrmContractCommandPageReqVO extends PageParam {
    @Schema(description = "合同ID", example = "2048")
    private Long contractId;
    @Schema(description = "指令类型", example = "METER_READ")
    private String commandType;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "业务单号", example = "MD001")
    private String bizNo;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_COMMAND_STATUS;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_COMMAND_TYPE;
@Schema(description = "管理后台 - CRM åˆåŒæŒ‡ä»¤ Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmContractCommandRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("合同ID")
    private Long contractId;
    @Schema(description = "合同编号")
    @ExcelProperty("合同编号")
    private String contractNo;
    @Schema(description = "指令类型")
    @ExcelProperty(value = "指令类型", converter = DictConvert.class)
    @DictFormat(CRM_COMMAND_TYPE)
    private String commandType;
    @Schema(description = "目标模块")
    @ExcelProperty("目标模块")
    private String targetModule;
    @Schema(description = "目标记录ID")
    @ExcelProperty("目标记录ID")
    private Long targetId;
    @Schema(description = "业务单号")
    @ExcelProperty("业务单号")
    private String bizNo;
    @Schema(description = "操作人ID")
    @ExcelProperty("操作人ID")
    private Long operatorId;
    @Schema(description = "操作人姓名")
    @ExcelProperty("操作人姓名")
    private String operatorName;
    @Schema(description = "操作时间")
    @ExcelProperty("操作时间")
    private LocalDateTime operateTime;
    @Schema(description = "状态")
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(CRM_COMMAND_STATUS)
    private Integer status;
    @Schema(description = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间")
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/command/CrmContractCommandSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_COMMAND_STATUS;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_COMMAND_TYPE;
@Schema(description = "管理后台 - CRM åˆåŒæŒ‡ä»¤æ–°å¢ž/修改 Request VO")
@Data
public class CrmContractCommandSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "合同ID不能为空")
    private Long contractId;
    @Schema(description = "合同编号")
    private String contractNo;
    @Schema(description = "指令类型(下发表/抄表任务/服务工单)", example = "METER_READ")
    @DictFormat(CRM_COMMAND_TYPE)
    private String commandType;
    @Schema(description = "目标模块", example = "mes-metering")
    private String targetModule;
    @Schema(description = "目标记录ID")
    private Long targetId;
    @Schema(description = "业务单号")
    private String bizNo;
    @Schema(description = "操作人ID", example = "1")
    private Long operatorId;
    @Schema(description = "操作人姓名", example = "李工")
    private String operatorName;
    @Schema(description = "操作时间")
    private LocalDateTime operateTime;
    @Schema(description = "状态(0待执行/10已下发/20已完成/30已取消)", example = "0")
    @DictFormat(CRM_COMMAND_STATUS)
    private Integer status;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
@@ -124,6 +124,30 @@
    @ExcelProperty(value = "备注", index = 11)
    private String remark;
    @Schema(description = "合同类型", example = "power")
    private String contractType;
    @Schema(description = "电压等级", example = "10kV")
    private String voltageLevel;
    @Schema(description = "用电类型", example = "industrial")
    private String electricityType;
    @Schema(description = "合同容量(kVA)", example = "800")
    private BigDecimal contractCapacity;
    @Schema(description = "电价模式", example = "two-part")
    private String tariffMode;
    @Schema(description = "计量方式", example = "high-high")
    private String meterMode;
    @Schema(description = "缴费周期(月)", example = "12")
    private Integer payCycle;
    @Schema(description = "指令数量")
    private Long commandCount;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty(value = "创建时间", index = 18)
    private LocalDateTime createTime;
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java
@@ -85,6 +85,28 @@
    @DiffLogField(name = "备注")
    private String remark;
    @Schema(description = "合同类型(power/energy/resident/solar)", example = "power")
    @DiffLogField(name = "合同类型")
    private String contractType;
    @Schema(description = "电压等级", example = "10kV")
    private String voltageLevel;
    @Schema(description = "用电类型", example = "industrial")
    private String electricityType;
    @Schema(description = "合同容量(kVA)", example = "800")
    private BigDecimal contractCapacity;
    @Schema(description = "电价模式", example = "two-part")
    private String tariffMode;
    @Schema(description = "计量方式", example = "high-high")
    private String meterMode;
    @Schema(description = "缴费周期(月)", example = "12")
    private Integer payCycle;
    @Schema(description = "产品列表")
    private List<Product> products;
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/CrmCreditRatingController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.crm.controller.admin.credit;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingSaveReqVO;
import cn.iocoder.yudao.module.crm.service.credit.CrmCreditRatingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
@Tag(name = "管理后台 - CRM å®¢æˆ·ä¿¡ç”¨è¯„级")
@RestController
@RequestMapping("/crm/credit-rating")
@Validated
public class CrmCreditRatingController {
    @Resource
    private CrmCreditRatingService creditRatingService;
    @PostMapping("/create")
    @Operation(summary = "创建客户信用评级")
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:create')")
    public CommonResult<Long> createCreditRating(@Valid @RequestBody CrmCreditRatingSaveReqVO createReqVO) {
        return success(creditRatingService.createCreditRating(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新客户信用评级")
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:update')")
    public CommonResult<Boolean> updateCreditRating(@Valid @RequestBody CrmCreditRatingSaveReqVO updateReqVO) {
        creditRatingService.updateCreditRating(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除客户信用评级")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:delete')")
    public CommonResult<Boolean> deleteCreditRating(@RequestParam("id") Long id) {
        creditRatingService.deleteCreditRating(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得客户信用评级")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:query')")
    public CommonResult<CrmCreditRatingRespVO> getCreditRating(@RequestParam("id") Long id) {
        return success(creditRatingService.getCreditRating(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获得客户信用评级分页")
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:query')")
    public CommonResult<PageResult<CrmCreditRatingRespVO>> getCreditRatingPage(@Valid CrmCreditRatingPageReqVO pageReqVO) {
        return success(creditRatingService.getCreditRatingPage(pageReqVO));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出客户信用评级 Excel")
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportCreditRatingExcel(@Valid CrmCreditRatingPageReqVO exportReqVO,
                                       HttpServletResponse response) throws IOException {
        exportReqVO.setPageSize(PAGE_SIZE_NONE);
        List<CrmCreditRatingRespVO> list = creditRatingService.getCreditRatingList(exportReqVO);
        ExcelUtils.write(response, "客户信用评级.xls", "数据", CrmCreditRatingRespVO.class, list);
    }
    @GetMapping("/latest-by-customer")
    @Operation(summary = "获取客户最新信用评级")
    @Parameter(name = "customerId", description = "客户ID", required = true)
    @PreAuthorize("@ss.hasPermission('crm:credit-rating:query')")
    public CommonResult<CrmCreditRatingRespVO> getLatestByCustomer(@RequestParam("customerId") Long customerId) {
        return success(creditRatingService.getLatestByCustomerId(customerId));
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.crm.controller.admin.credit.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - CRM å®¢æˆ·ä¿¡ç”¨è¯„级分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CrmCreditRatingPageReqVO extends PageParam {
    @Schema(description = "客户ID", example = "2048")
    private Long customerId;
    @Schema(description = "信用等级", example = "B")
    private String level;
    @Schema(description = "评定类型", example = "INITIAL")
    private String evalType;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.crm.controller.admin.credit.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CREDIT_LEVEL;
@Schema(description = "管理后台 - CRM å®¢æˆ·ä¿¡ç”¨è¯„级 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmCreditRatingRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("客户ID")
    private Long customerId;
    @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("客户名称")
    private String customerName;
    @Schema(description = "信用等级")
    @ExcelProperty(value = "信用等级", converter = DictConvert.class)
    @DictFormat(CRM_CREDIT_LEVEL)
    private String level;
    @Schema(description = "信用分")
    @ExcelProperty("信用分")
    private Integer score;
    @Schema(description = "评定类型")
    @ExcelProperty("评定类型")
    private String evalType;
    @Schema(description = "评定理由")
    @ExcelProperty("评定理由")
    private String evalReason;
    @Schema(description = "操作人ID")
    @ExcelProperty("操作人ID")
    private Long operatorId;
    @Schema(description = "操作人姓名")
    @ExcelProperty("操作人姓名")
    private String operatorName;
    @Schema(description = "操作时间")
    @ExcelProperty("操作时间")
    private LocalDateTime operateTime;
    @Schema(description = "有效期至")
    @ExcelProperty("有效期至")
    private LocalDateTime validUntil;
    @Schema(description = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间")
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/credit/vo/CrmCreditRatingSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.crm.controller.admin.credit.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CREDIT_LEVEL;
@Schema(description = "管理后台 - CRM å®¢æˆ·ä¿¡ç”¨è¯„级新增/修改 Request VO")
@Data
public class CrmCreditRatingSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "客户ID不能为空")
    private Long customerId;
    @Schema(description = "信用等级(A+/A/B+/B/C/C-/D)", example = "B")
    @DictFormat(CRM_CREDIT_LEVEL)
    private String level;
    @Schema(description = "信用分(0-100)", example = "85")
    private Integer score;
    @Schema(description = "评定类型(初评/复评/动态调整)", example = "INITIAL")
    private String evalType;
    @Schema(description = "评定理由", example = "近12个月用电信用良好")
    private String evalReason;
    @Schema(description = "操作人ID", example = "1")
    private Long operatorId;
    @Schema(description = "操作人姓名", example = "李工")
    private String operatorName;
    @Schema(description = "操作时间")
    private LocalDateTime operateTime;
    @Schema(description = "有效期至")
    private LocalDateTime validUntil;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -17,6 +17,7 @@
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivablePlanService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -33,10 +34,12 @@
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
@@ -56,6 +59,8 @@
    private CrmCustomerService customerService;
    @Resource
    private CrmCustomerPoolConfigService customerPoolConfigService;
    @Resource
    private CrmReceivablePlanService receivablePlanService;
    @Resource
    private DeptApi deptApi;
@@ -139,6 +144,10 @@
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
        // 1.2 èŽ·å–è·ç¦»è¿›å…¥å…¬æµ·çš„æ—¶é—´
        Map<Long, Long> poolDayMap = getPoolDayMap(list);
        // 1.3 èŽ·å–æ¬ è´¹é‡‘é¢ã€æ¬ è´¹ç¬”æ•°ï¼ˆæœªå›žæ¬¾ + å·²é€¾æœŸçš„回款计划)
        Set<Long> customerIds = convertSet(list, CrmCustomerDO::getId);
        Map<Long, BigDecimal> overdueAmountMap = receivablePlanService.getOverdueAmountMapByCustomerIds(customerIds);
        Map<Long, Integer> overdueCountMap = receivablePlanService.getOverdueCountMapByCustomerIds(customerIds);
        // 2. è½¬æ¢æˆ VO
        return BeanUtils.toBean(list, CrmCustomerRespVO.class, customerVO -> {
            customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId()));
@@ -153,6 +162,9 @@
            if (customerVO.getOwnerUserId() != null) {
                customerVO.setPoolDay(poolDayMap.get(customerVO.getId()));
            }
            // 2.3 è®¾ç½®æ¬ è´¹é£Žé™©ï¼ˆæ¬ è´¹é‡‘额/欠费笔数)
            customerVO.setOverdueAmount(overdueAmountMap.get(customerVO.getId()));
            customerVO.setOverdueCount(overdueCountMap.get(customerVO.getId()));
        });
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerPageReqVO.java
@@ -42,6 +42,12 @@
    @Schema(description = "客户来源", example = "1")
    private Integer source;
    @Schema(description = "客户分类", example = "industrial")
    private String customerType;
    @Schema(description = "当前信用等级", example = "B")
    private String creditLevel;
    @Schema(description = "场景类型", example = "1")
    @InEnum(CrmSceneTypeEnum.class)
    private Integer sceneType; // åœºæ™¯ç±»åž‹ï¼Œä¸º null æ—¶åˆ™è¡¨ç¤ºå…¨éƒ¨
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java
@@ -8,6 +8,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - CRM å®¢æˆ· Response VO")
@@ -127,4 +128,26 @@
    @Schema(description = "距离加入公海时间", example = "1")
    private Long poolDay;
    @Schema(description = "客户分类(电力CRM扩展)", example = "industrial")
    @ExcelProperty(value = "客户分类", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_TYPE)
    private String customerType;
    @Schema(description = "当前信用等级", example = "B")
    @ExcelProperty(value = "信用等级", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CREDIT_LEVEL)
    private String creditLevel;
    @Schema(description = "当前信用分", example = "85")
    @ExcelProperty("信用分")
    private Integer creditScore;
    @Schema(description = "欠费金额(未回款+逾期的回款计划合计)", example = "1200.00")
    @ExcelProperty("欠费金额")
    private BigDecimal overdueAmount;
    @Schema(description = "欠费笔数", example = "3")
    @ExcelProperty("欠费笔数")
    private Integer overdueCount;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerSaveReqVO.java
@@ -22,6 +22,7 @@
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_TYPE;
@Schema(description = "管理后台 - CRM å®¢æˆ·æ–°å¢ž/修改 Request VO")
@Data
@@ -96,4 +97,9 @@
    @DiffLogField(name = "备注")
    private String remark;
    @Schema(description = "客户分类(政企/工业/居民/商业/农业)", example = "industrial")
    @DiffLogField(name = "客户分类")
    @DictFormat(CRM_CUSTOMER_TYPE)
    private String customerType;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java
@@ -111,6 +111,34 @@
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "报价类型", example = "power")
    @ExcelProperty("报价类型")
    private String quotationType;
    @Schema(description = "合同容量(kVA)", example = "800")
    @ExcelProperty("合同容量")
    private BigDecimal capacity;
    @Schema(description = "最大需量(kW)", example = "400")
    @ExcelProperty("最大需量")
    private BigDecimal maxDemand;
    @Schema(description = "电度单价(元/kWh)", example = "0.5283")
    @ExcelProperty("电度单价")
    private BigDecimal unitPrice;
    @Schema(description = "电价模式", example = "two-part")
    @ExcelProperty("电价模式")
    private String tariffMode;
    @Schema(description = "力调电费(元)", example = "120.5")
    @ExcelProperty("力调电费")
    private BigDecimal powerFactorFee;
    @Schema(description = "结算周期(月)", example = "12")
    @ExcelProperty("结算周期")
    private Integer billingCycle;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java
@@ -59,6 +59,27 @@
    @Schema(description = "备注", example = "备注信息")
    private String remark;
    @Schema(description = "报价类型(general=通用/power=电力/energy=能源服务)", example = "power")
    private String quotationType;
    @Schema(description = "合同容量(kVA)", example = "800")
    private BigDecimal capacity;
    @Schema(description = "最大需量(kW)", example = "400")
    private BigDecimal maxDemand;
    @Schema(description = "电度单价(元/kWh)", example = "0.5283")
    private BigDecimal unitPrice;
    @Schema(description = "电价模式(single/two-part/tier/tou)", example = "two-part")
    private String tariffMode;
    @Schema(description = "力调电费(元)", example = "120.5")
    private BigDecimal powerFactorFee;
    @Schema(description = "结算周期(月)", example = "12")
    private Integer billingCycle;
    @Schema(description = "物料明细列表", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "物料明细不能为空")
    private List<Item> items;
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/CrmRefundController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package cn.iocoder.yudao.module.crm.controller.admin.refund;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundSaveReqVO;
import cn.iocoder.yudao.module.crm.service.refund.CrmRefundService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - CRM é€€è´¹å•")
@RestController
@RequestMapping("/crm/refund")
@Validated
public class CrmRefundController {
    @Resource
    private CrmRefundService refundService;
    @PostMapping("/create")
    @Operation(summary = "创建退费单")
    @PreAuthorize("@ss.hasPermission('crm:refund:create')")
    public CommonResult<Long> createRefund(@Valid @RequestBody CrmRefundSaveReqVO createReqVO) {
        return success(refundService.createRefund(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新退费单")
    @PreAuthorize("@ss.hasPermission('crm:refund:update')")
    public CommonResult<Boolean> updateRefund(@Valid @RequestBody CrmRefundSaveReqVO updateReqVO) {
        refundService.updateRefund(updateReqVO);
        return success(true);
    }
    @PutMapping("/audit")
    @Operation(summary = "审批退费单(通过/驳回)")
    @PreAuthorize("@ss.hasPermission('crm:refund:update')")
    public CommonResult<Boolean> auditRefund(@Valid @RequestBody CrmRefundAuditReqVO auditReqVO) {
        refundService.auditRefund(auditReqVO);
        return success(true);
    }
    @PutMapping("/submit")
    @Operation(summary = "提交退费单审批")
    @PreAuthorize("@ss.hasPermission('crm:refund:update')")
    public CommonResult<Boolean> submitRefund(@RequestParam("id") Long id,
                                              @RequestParam("processDefinitionKey") String processDefinitionKey) {
        refundService.submitRefund(id, processDefinitionKey, getLoginUserId());
        return success(true);
    }
    @GetMapping("/approve-process-list")
    @Operation(summary = "获得退费单审批流程定义列表")
    @PreAuthorize("@ss.hasPermission('crm:refund:query')")
    public CommonResult<List<Map<String, Object>>> getRefundApproveProcessDefinitionList() {
        return success(refundService.getRefundApproveProcessDefinitionList());
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除退费单")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:refund:delete')")
    public CommonResult<Boolean> deleteRefund(@RequestParam("id") Long id) {
        refundService.deleteRefund(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得退费单")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:refund:query')")
    public CommonResult<CrmRefundRespVO> getRefund(@RequestParam("id") Long id) {
        return success(refundService.getRefund(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获得退费单分页")
    @PreAuthorize("@ss.hasPermission('crm:refund:query')")
    public CommonResult<PageResult<CrmRefundRespVO>> getRefundPage(@Valid CrmRefundPageReqVO pageReqVO) {
        return success(refundService.getRefundPage(pageReqVO));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出退费单 Excel")
    @PreAuthorize("@ss.hasPermission('crm:refund:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportRefundExcel(@Valid CrmRefundPageReqVO exportReqVO,
                                  HttpServletResponse response) throws IOException {
        exportReqVO.setPageSize(PAGE_SIZE_NONE);
        List<CrmRefundRespVO> list = refundService.getRefundList(exportReqVO);
        ExcelUtils.write(response, "退费单.xls", "数据", CrmRefundRespVO.class, list);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundAuditReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.crm.controller.admin.refund.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - CRM é€€è´¹å•审批 Request VO")
@Data
public class CrmRefundAuditReqVO {
    @Schema(description = "退费单编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "退费单编号不能为空")
    private Long id;
    @Schema(description = "审批结果:20=通过 / 30=驳回", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "审批结果不能为空")
    private Integer auditStatus;
    @Schema(description = "审批意见")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.crm.controller.admin.refund.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - CRM é€€è´¹å•分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CrmRefundPageReqVO extends PageParam {
    @Schema(description = "退费单号", example = "RF202608190001")
    private String refundNo;
    @Schema(description = "客户ID", example = "2048")
    private Long customerId;
    @Schema(description = "退费类型", example = "ELECTRICITY")
    private String refundType;
    @Schema(description = "审批状态", example = "0")
    private Integer auditStatus;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.crm.controller.admin.refund.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_AUDIT_STATUS;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_REFUND_TYPE;
@Schema(description = "管理后台 - CRM é€€è´¹å• Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmRefundRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "退费单号", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("退费单号")
    private String refundNo;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("客户ID")
    private Long customerId;
    @Schema(description = "客户名称")
    @ExcelProperty("客户名称")
    private String customerName;
    @Schema(description = "合同ID")
    @ExcelProperty("合同ID")
    private Long contractId;
    @Schema(description = "合同编号")
    @ExcelProperty("合同编号")
    private String contractNo;
    @Schema(description = "退费类型")
    @ExcelProperty(value = "退费类型", converter = DictConvert.class)
    @DictFormat(CRM_REFUND_TYPE)
    private String refundType;
    @Schema(description = "退费金额")
    @ExcelProperty("退费金额")
    private BigDecimal refundAmount;
    @Schema(description = "退费原因")
    @ExcelProperty("退费原因")
    private String refundReason;
    @Schema(description = "申请用户ID")
    @ExcelProperty("申请用户ID")
    private Long applyUserId;
    @Schema(description = "申请用户姓名")
    @ExcelProperty("申请用户姓名")
    private String applyUserName;
    @Schema(description = "申请时间")
    @ExcelProperty("申请时间")
    private LocalDateTime applyTime;
    @Schema(description = "审批状态")
    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
    @DictFormat(CRM_AUDIT_STATUS)
    private Integer auditStatus;
    @Schema(description = "流程实例ID")
    private String processInstanceId;
    @Schema(description = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间")
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/refund/vo/CrmRefundSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.crm.controller.admin.refund.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_REFUND_TYPE;
@Schema(description = "管理后台 - CRM é€€è´¹å•新增/修改 Request VO")
@Data
public class CrmRefundSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "退费单号(不传则自动生成)")
    private String refundNo;
    @Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "客户ID不能为空")
    private Long customerId;
    @Schema(description = "客户名称")
    private String customerName;
    @Schema(description = "合同ID", example = "2048")
    private Long contractId;
    @Schema(description = "合同编号")
    private String contractNo;
    @Schema(description = "退费类型(电费退费/服务退订/容量退费)", example = "ELECTRICITY")
    @DictFormat(CRM_REFUND_TYPE)
    private String refundType;
    @Schema(description = "退费金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "500.00")
    @NotNull(message = "退费金额不能为空")
    private BigDecimal refundAmount;
    @Schema(description = "退费原因")
    private String refundReason;
    @Schema(description = "申请用户ID", example = "1")
    private Long applyUserId;
    @Schema(description = "申请用户姓名", example = "李工")
    private String applyUserName;
    @Schema(description = "申请时间")
    private LocalDateTime applyTime;
    @Schema(description = "审批状态(0草稿/10待审批/20通过/30驳回)", example = "0")
    private Integer auditStatus;
    @Schema(description = "流程实例ID")
    private String processInstanceId;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/cancellation/CrmCancellationDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.cancellation;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * CRM é”€æˆ·ç”³è¯· DO
 *
 * @author è¶…级管理员
 */
@TableName("crm_cancellation")
@KeySequence("crm_cancellation_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmCancellationDO extends BaseDO {
    @TableId
    private Long id;
    private String cancellationNo;
    private Long customerId;
    private String customerName;
    private Long contractId;
    private String contractNo;
    /**
     * é”€æˆ·ç±»åž‹ï¼šç”¨ç”µé”€æˆ·/服务终止/合同到期(字典 crm_cancellation_type)
     */
    private String cancellationType;
    private String cancelReason;
    private LocalDate expectedCancelDate;
    private LocalDate actualCancelDate;
    private BigDecimal refundAmount;
    private Long applyUserId;
    private String applyUserName;
    private LocalDateTime applyTime;
    /**
     * å®¡æ‰¹çŠ¶æ€ï¼š0草稿/10待审批/20通过/30驳回
     */
    private Integer auditStatus;
    private String processInstanceId;
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractCommandDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.contract;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * CRM åˆåŒæŒ‡ä»¤ä¸‹å‘ DO
 *
 * @author è¶…级管理员
 */
@TableName("crm_contract_command")
@KeySequence("crm_contract_command_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmContractCommandDO extends BaseDO {
    @TableId
    private Long id;
    private Long contractId;
    private String contractNo;
    /**
     * æŒ‡ä»¤ç±»åž‹ï¼šä¸‹å‘表/抄表任务/服务工单(字典 crm_command_type)
     */
    private String commandType;
    /**
     * ç›®æ ‡æ¨¡å—:mes-work-order/mes-metering/srm-service
     */
    private String targetModule;
    private Long targetId;
    private String bizNo;
    private Long operatorId;
    private String operatorName;
    private LocalDateTime operateTime;
    /**
     * çŠ¶æ€ï¼š0待执行/10已下发/20已完成/30已取消(字典 crm_command_status)
     */
    private Integer status;
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
@@ -132,4 +132,43 @@
     */
    private String remark;
    /**
     * åˆåŒç±»åž‹ï¼ˆç”µåŠ›CRM扩展)
     *
     * å­—å…¸ crm_contract_type:power=供用电合同 / energy=能源服务合同 / resident=居配合同 / solar=光伏合同
     */
    private String contractType;
    /**
     * ç”µåŽ‹ç­‰çº§
     *
     * å­—å…¸ crm_voltage_level
     */
    private String voltageLevel;
    /**
     * ç”¨ç”µç±»åž‹
     *
     * å­—å…¸ crm_electricity_type
     */
    private String electricityType;
    /**
     * åˆåŒå®¹é‡ï¼ˆkVA)
     */
    private BigDecimal contractCapacity;
    /**
     * ç”µä»·æ¨¡å¼
     *
     * å­—å…¸ crm_tariff_mode
     */
    private String tariffMode;
    /**
     * è®¡é‡æ–¹å¼
     *
     * å­—å…¸ crm_meter_mode
     */
    private String meterMode;
    /**
     * ç¼´è´¹å‘¨æœŸï¼ˆæœˆï¼‰
     */
    private Integer payCycle;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/credit/CrmCreditRatingDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.credit;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * CRM å®¢æˆ·ä¿¡ç”¨è¯„级 DO
 *
 * @author è¶…级管理员
 */
@TableName("crm_credit_rating")
@KeySequence("crm_credit_rating_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmCreditRatingDO extends BaseDO {
    @TableId
    private Long id;
    private Long customerId;
    private String customerName;
    private String level;
    private Integer score;
    private String evalType;
    private String evalReason;
    private Long operatorId;
    private String operatorName;
    private LocalDateTime operateTime;
    private LocalDateTime validUntil;
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
@@ -124,4 +124,21 @@
     */
    private String remark;
    /**
     * å®¢æˆ·åˆ†ç±»ï¼ˆç”µåŠ›CRM扩展)
     *
     * å¯¹åº”å­—å…¸ {@link DictTypeConstants#CRM_CUSTOMER_TYPE}
     */
    private String customerType;
    /**
     * å½“前信用等级(最新一次信用评级的等级,由信用评级模块写入)
     *
     * å¯¹åº”å­—å…¸ {@link DictTypeConstants#CRM_CREDIT_LEVEL}
     */
    private String creditLevel;
    /**
     * å½“前信用分
     */
    private Integer creditScore;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java
@@ -129,4 +129,37 @@
     */
    private String remark;
    /**
     * æŠ¥ä»·ç±»åž‹ï¼ˆç”µåŠ›CRM扩展)
     *
     * å­—å…¸ crm_quotation_type:general=通用 / power=电力报价 / energy=能源服务
     */
    private String quotationType;
    /**
     * åˆåŒå®¹é‡ï¼ˆkVA,仅电力/能源报价)
     */
    private BigDecimal capacity;
    /**
     * æœ€å¤§éœ€é‡ï¼ˆkW,仅两部制电价)
     */
    private BigDecimal maxDemand;
    /**
     * ç”µåº¦å•价(元/kWh)
     */
    private BigDecimal unitPrice;
    /**
     * ç”µä»·æ¨¡å¼
     *
     * å­—å…¸ crm_tariff_mode:single=单一制 / two-part=两部制 / tier=阶梯 / tou=峰谷分时
     */
    private String tariffMode;
    /**
     * åŠ›è°ƒç”µè´¹ï¼ˆå…ƒï¼‰
     */
    private BigDecimal powerFactorFee;
    /**
     * ç»“算周期(月)
     */
    private Integer billingCycle;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/refund/CrmRefundDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.refund;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * CRM é€€è´¹å• DO
 *
 * @author è¶…级管理员
 */
@TableName("crm_refund")
@KeySequence("crm_refund_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmRefundDO extends BaseDO {
    @TableId
    private Long id;
    private String refundNo;
    private Long customerId;
    private String customerName;
    private Long contractId;
    private String contractNo;
    /**
     * é€€è´¹ç±»åž‹ï¼šç”µè´¹é€€è´¹/服务退订/容量退费(字典 crm_refund_type)
     */
    private String refundType;
    private BigDecimal refundAmount;
    private String refundReason;
    private Long applyUserId;
    private String applyUserName;
    private LocalDateTime applyTime;
    /**
     * å®¡æ‰¹çŠ¶æ€ï¼š0草稿/10待审批/20通过/30驳回
     */
    private Integer auditStatus;
    private String processInstanceId;
    private String remark;
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/cancellation/CrmCancellationMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.crm.dal.mysql.cancellation;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.cancellation.CrmCancellationDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CrmCancellationMapper extends BaseMapperX<CrmCancellationDO> {
    default PageResult<CrmCancellationDO> selectPage(CrmCancellationPageReqVO reqVO) {
        return selectPage(reqVO, buildQuery(reqVO));
    }
    default List<CrmCancellationDO> selectList(CrmCancellationPageReqVO reqVO) {
        return selectList(buildQuery(reqVO));
    }
    default CrmCancellationDO selectByCancellationNo(String cancellationNo) {
        return selectOne(new LambdaQueryWrapperX<CrmCancellationDO>()
                .eq(CrmCancellationDO::getCancellationNo, cancellationNo));
    }
    private LambdaQueryWrapperX<CrmCancellationDO> buildQuery(CrmCancellationPageReqVO reqVO) {
        return new LambdaQueryWrapperX<CrmCancellationDO>()
                .likeIfPresent(CrmCancellationDO::getCancellationNo, reqVO.getCancellationNo())
                .eqIfPresent(CrmCancellationDO::getCustomerId, reqVO.getCustomerId())
                .eqIfPresent(CrmCancellationDO::getCancellationType, reqVO.getCancellationType())
                .eqIfPresent(CrmCancellationDO::getAuditStatus, reqVO.getAuditStatus())
                .orderByDesc(CrmCancellationDO::getId);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractCommandMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.crm.dal.mysql.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractCommandDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CrmContractCommandMapper extends BaseMapperX<CrmContractCommandDO> {
    default PageResult<CrmContractCommandDO> selectPage(CrmContractCommandPageReqVO reqVO) {
        return selectPage(reqVO, buildQuery(reqVO));
    }
    default List<CrmContractCommandDO> selectListByContractId(Long contractId) {
        return selectList(new LambdaQueryWrapperX<CrmContractCommandDO>()
                .eq(CrmContractCommandDO::getContractId, contractId)
                .orderByDesc(CrmContractCommandDO::getOperateTime));
    }
    private LambdaQueryWrapperX<CrmContractCommandDO> buildQuery(CrmContractCommandPageReqVO reqVO) {
        return new LambdaQueryWrapperX<CrmContractCommandDO>()
                .eqIfPresent(CrmContractCommandDO::getContractId, reqVO.getContractId())
                .eqIfPresent(CrmContractCommandDO::getCommandType, reqVO.getCommandType())
                .eqIfPresent(CrmContractCommandDO::getStatus, reqVO.getStatus())
                .likeIfPresent(CrmContractCommandDO::getBizNo, reqVO.getBizNo())
                .orderByDesc(CrmContractCommandDO::getOperateTime);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/credit/CrmCreditRatingMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.crm.dal.mysql.credit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.credit.CrmCreditRatingDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CrmCreditRatingMapper extends BaseMapperX<CrmCreditRatingDO> {
    default PageResult<CrmCreditRatingDO> selectPage(CrmCreditRatingPageReqVO reqVO) {
        return selectPage(reqVO, buildQuery(reqVO));
    }
    default List<CrmCreditRatingDO> selectList(CrmCreditRatingPageReqVO reqVO) {
        return selectList(buildQuery(reqVO));
    }
    default CrmCreditRatingDO selectLatestByCustomerId(Long customerId) {
        return selectOne(new LambdaQueryWrapperX<CrmCreditRatingDO>()
                .eq(CrmCreditRatingDO::getCustomerId, customerId)
                .orderByDesc(CrmCreditRatingDO::getOperateTime)
                .orderByDesc(CrmCreditRatingDO::getId)
                .last("LIMIT 1"));
    }
    private LambdaQueryWrapperX<CrmCreditRatingDO> buildQuery(CrmCreditRatingPageReqVO reqVO) {
        return new LambdaQueryWrapperX<CrmCreditRatingDO>()
                .eqIfPresent(CrmCreditRatingDO::getCustomerId, reqVO.getCustomerId())
                .eqIfPresent(CrmCreditRatingDO::getLevel, reqVO.getLevel())
                .eqIfPresent(CrmCreditRatingDO::getEvalType, reqVO.getEvalType())
                .orderByDesc(CrmCreditRatingDO::getOperateTime);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
@@ -3,6 +3,7 @@
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -13,6 +14,8 @@
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
@@ -86,4 +89,22 @@
        return selectCount(query);
    }
    /**
     * æŸ¥è¯¢ä¸€æ‰¹å®¢æˆ·çš„æ¬ è´¹å›žæ¬¾è®¡åˆ’(未回款 + å·²é€¾æœŸï¼‰
     *
     * å£å¾„与 {@link #selectPage(CrmReceivablePlanPageReqVO, Long)} çš„「已逾期」一致:
     * receivableId ä¸ºç©ºï¼ˆæœªå›žæ¬¾ï¼‰ä¸” returnTime æ—©äºŽå½“前时间
     *
     * @param customerIds å®¢æˆ·ç¼–号集合
     * @param now         å½“前时间(逾期判定时间点)
     * @return æ¬ è´¹çš„回款计划列表
     */
    default List<CrmReceivablePlanDO> selectOverdueListByCustomerIds(Collection<Long> customerIds,
                                                                     LocalDateTime now) {
        return selectList(new LambdaQueryWrapperX<CrmReceivablePlanDO>()
                .in(CrmReceivablePlanDO::getCustomerId, customerIds)
                .isNull(CrmReceivablePlanDO::getReceivableId) // æœªå›žæ¬¾
                .lt(CrmReceivablePlanDO::getReturnTime, now)); // å·²é€¾æœŸ
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/refund/CrmRefundMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.crm.dal.mysql.refund;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.refund.CrmRefundDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CrmRefundMapper extends BaseMapperX<CrmRefundDO> {
    default PageResult<CrmRefundDO> selectPage(CrmRefundPageReqVO reqVO) {
        return selectPage(reqVO, buildQuery(reqVO));
    }
    default List<CrmRefundDO> selectList(CrmRefundPageReqVO reqVO) {
        return selectList(buildQuery(reqVO));
    }
    default CrmRefundDO selectByRefundNo(String refundNo) {
        return selectOne(new LambdaQueryWrapperX<CrmRefundDO>().eq(CrmRefundDO::getRefundNo, refundNo));
    }
    private LambdaQueryWrapperX<CrmRefundDO> buildQuery(CrmRefundPageReqVO reqVO) {
        return new LambdaQueryWrapperX<CrmRefundDO>()
                .likeIfPresent(CrmRefundDO::getRefundNo, reqVO.getRefundNo())
                .eqIfPresent(CrmRefundDO::getCustomerId, reqVO.getCustomerId())
                .eqIfPresent(CrmRefundDO::getRefundType, reqVO.getRefundType())
                .eqIfPresent(CrmRefundDO::getAuditStatus, reqVO.getAuditStatus())
                .orderByDesc(CrmRefundDO::getId);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
@@ -20,4 +20,18 @@
    String CRM_SALES_TARGET_DIMENSION = "crm_sales_target_dimension"; // CRM é”€å”®ç›®æ ‡ç»´åº¦
    String CRM_SALES_TARGET_STATUS = "crm_sales_target_status"; // CRM é”€å”®ç›®æ ‡çŠ¶æ€
    // ========== ç”µåŠ›è¥é”€æ‰©å±• ==========
    String CRM_CUSTOMER_TYPE = "crm_customer_type"; // ç”µåŠ›å®¢æˆ·åˆ†ç±»ï¼ˆæ”¿ä¼/工业/居民/商业/农业)
    String CRM_CREDIT_LEVEL = "crm_credit_level"; // å®¢æˆ·ä¿¡ç”¨ç­‰çº§ï¼ˆA+/A/B+/B/C/C-/D)
    String CRM_QUOTATION_TYPE = "crm_quotation_type"; // æŠ¥ä»·å•类型(通用/电力/能源服务)
    String CRM_TARIFF_MODE = "crm_tariff_mode"; // ç”µä»·æ¨¡å¼ï¼ˆå•一制/两部制/阶梯/峰谷分时)
    String CRM_CONTRACT_TYPE = "crm_contract_type"; // åˆåŒç±»åž‹ï¼ˆä¾›ç”¨ç”µ/能源服务/居配/光伏)
    String CRM_VOLTAGE_LEVEL = "crm_voltage_level"; // ç”µåŽ‹ç­‰çº§ï¼ˆ10kV/35kV/110kV/220kV/380V/220V)
    String CRM_ELECTRICITY_TYPE = "crm_electricity_type"; // ç”¨ç”µç±»åž‹ï¼ˆå¤§å·¥ä¸š/一般工商业/居民/农业)
    String CRM_METER_MODE = "crm_meter_mode"; // è®¡é‡æ–¹å¼ï¼ˆé«˜ä¾›é«˜è®¡/高供低计/低供低计)
    String CRM_COMMAND_TYPE = "crm_command_type"; // åˆåŒæŒ‡ä»¤ç±»åž‹ï¼ˆä¸‹å‘表/抄表任务/服务工单)
    String CRM_COMMAND_STATUS = "crm_command_status"; // åˆåŒæŒ‡ä»¤çŠ¶æ€ï¼ˆå¾…æ‰§è¡Œ/已下发/已完成/已取消)
    String CRM_REFUND_TYPE = "crm_refund_type"; // é€€è´¹ç±»åž‹ï¼ˆç”µè´¹é€€è´¹/服务退订/容量退费)
    String CRM_CANCELLATION_TYPE = "crm_cancellation_type"; // é”€æˆ·ç±»åž‹ï¼ˆç”¨ç”µé”€æˆ·/服务终止/合同到期)
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -140,4 +140,26 @@
    ErrorCode CRM_SALES_TARGET_PERIOD_VALUE_INVALID = new ErrorCode(1_020_017_001, "周期值【{}】格式不正确,应为 {}");
    ErrorCode CRM_SALES_TARGET_DIMENSION_TARGET_MISSING = new ErrorCode(1_020_017_002, "请填写目标{}编号");
    // ========== ç”µåŠ›è¥é”€æ‰©å±• 1_020_018_000 ==========
    ErrorCode CRM_CREDIT_RATING_NOT_EXISTS = new ErrorCode(1_020_018_000, "信用评级记录不存在");
    ErrorCode CRM_CREDIT_RATING_DUPLICATE = new ErrorCode(1_020_018_001, "同一客户已存在生效中的信用评级");
    ErrorCode CRM_CONTRACT_COMMAND_NOT_EXISTS = new ErrorCode(1_020_018_002, "合同指令不存在");
    ErrorCode CRM_COMMAND_EXECUTE_FAIL_STATUS = new ErrorCode(1_020_018_003, "合同指令执行失败,原因:状态不允许执行");
    ErrorCode CRM_COMMAND_DISPATCH_TARGET_MISSING = new ErrorCode(1_020_018_014, "合同指令下发失败,原因:指令未关联目标工单(targetId)");
    ErrorCode CRM_REFUND_NOT_EXISTS = new ErrorCode(1_020_018_004, "退费单不存在");
    ErrorCode CRM_REFUND_NO_DUPLICATE = new ErrorCode(1_020_018_005, "退费单号已存在");
    ErrorCode CRM_CANCELLATION_NOT_EXISTS = new ErrorCode(1_020_018_006, "销户申请不存在");
    ErrorCode CRM_CANCELLATION_NO_DUPLICATE = new ErrorCode(1_020_018_007, "销户申请编号已存在");
    ErrorCode CRM_REFUND_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_018_008, "退费单审批失败,原因:退费单不处于草稿状态");
    ErrorCode CRM_REFUND_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_020_018_009, "退费单审批失败,原因:BPM å®¡æ‰¹æµç¨‹ä¸å­˜åœ¨ï¼Œè¯·å…ˆåœ¨ BPM ç®¡ç†åŽå°é…ç½®é€€è´¹å®¡æ‰¹æµç¨‹");
    ErrorCode CRM_REFUND_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_018_010, "退费单审批失败,原因:退费单不处于审批中,无法更新审批结果");
    ErrorCode CRM_CANCELLATION_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_018_011, "销户申请审批失败,原因:销户申请不处于草稿状态");
    ErrorCode CRM_CANCELLATION_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_020_018_012, "销户申请审批失败,原因:BPM å®¡æ‰¹æµç¨‹ä¸å­˜åœ¨ï¼Œè¯·å…ˆåœ¨ BPM ç®¡ç†åŽå°é…ç½®é”€æˆ·å®¡æ‰¹æµç¨‹");
    ErrorCode CRM_CANCELLATION_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_018_013, "销户申请审批失败,原因:销户申请不处于审批中,无法更新审批结果");
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/CrmCancellationService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.crm.service.cancellation;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationSaveReqVO;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
public interface CrmCancellationService {
    Long createCancellation(@Valid CrmCancellationSaveReqVO createReqVO);
    void updateCancellation(@Valid CrmCancellationSaveReqVO updateReqVO);
    void deleteCancellation(Long id);
    CrmCancellationRespVO getCancellation(Long id);
    PageResult<CrmCancellationRespVO> getCancellationPage(CrmCancellationPageReqVO pageReqVO);
    List<CrmCancellationRespVO> getCancellationList(CrmCancellationPageReqVO reqVO);
    void auditCancellation(@Valid CrmCancellationAuditReqVO auditReqVO);
    void submitCancellation(Long id, String processDefinitionKey, Long userId);
    void updateCancellationAuditStatus(Long id, Integer bpmResult);
    List<Map<String, Object>> getCancellationApproveProcessDefinitionList();
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/CrmCancellationServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,259 @@
package cn.iocoder.yudao.module.crm.service.cancellation;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.cancellation.vo.CrmCancellationSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.cancellation.CrmCancellationDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.mysql.cancellation.CrmCancellationMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
@Service
@Validated
@Slf4j
public class CrmCancellationServiceImpl implements CrmCancellationService {
    private static final Integer AUDIT_DRAFT = 0;
    /**
     * BPM é”€æˆ·å®¡æ‰¹åˆ†ç±»ç¼–码
     */
    private static final String CANCELLATION_APPROVE_CATEGORY_CODE = "cancellation_approve";
    @Resource
    private CrmCancellationMapper cancellationMapper;
    @Resource
    private CrmCustomerMapper customerMapper;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Resource
    private BpmCategoryService bpmCategoryService;
    @Resource
    private BpmProcessDefinitionService bpmProcessDefinitionService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCancellation(CrmCancellationSaveReqVO createReqVO) {
        if (StrUtil.isBlank(createReqVO.getCancellationNo())) {
            createReqVO.setCancellationNo(generateCancellationNo());
        } else if (cancellationMapper.selectByCancellationNo(createReqVO.getCancellationNo()) != null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NO_DUPLICATE);
        }
        CrmCustomerDO customer = customerMapper.selectById(createReqVO.getCustomerId());
        if (customer == null) {
            throw exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        if (StrUtil.isBlank(createReqVO.getCustomerName())) {
            createReqVO.setCustomerName(customer.getName());
        }
        if (createReqVO.getApplyTime() == null) {
            createReqVO.setApplyTime(LocalDateTime.now());
        }
        if (createReqVO.getAuditStatus() == null) {
            createReqVO.setAuditStatus(AUDIT_DRAFT);
        }
        CrmCancellationDO entity = BeanUtils.toBean(createReqVO, CrmCancellationDO.class);
        cancellationMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateCancellation(CrmCancellationSaveReqVO updateReqVO) {
        CrmCancellationDO exists = cancellationMapper.selectById(updateReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NOT_EXISTS);
        }
        CrmCancellationDO entity = BeanUtils.toBean(updateReqVO, CrmCancellationDO.class);
        cancellationMapper.updateById(entity);
    }
    @Override
    public void deleteCancellation(Long id) {
        CrmCancellationDO exists = cancellationMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NOT_EXISTS);
        }
        cancellationMapper.deleteById(id);
    }
    @Override
    public CrmCancellationRespVO getCancellation(Long id) {
        CrmCancellationDO entity = cancellationMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, CrmCancellationRespVO.class);
    }
    @Override
    public PageResult<CrmCancellationRespVO> getCancellationPage(CrmCancellationPageReqVO pageReqVO) {
        return BeanUtils.toBean(cancellationMapper.selectPage(pageReqVO), CrmCancellationRespVO.class);
    }
    @Override
    public List<CrmCancellationRespVO> getCancellationList(CrmCancellationPageReqVO reqVO) {
        return BeanUtils.toBean(cancellationMapper.selectList(reqVO), CrmCancellationRespVO.class);
    }
    @Override
    public void auditCancellation(CrmCancellationAuditReqVO auditReqVO) {
        CrmCancellationDO exists = cancellationMapper.selectById(auditReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NOT_EXISTS);
        }
        CrmCancellationDO update = new CrmCancellationDO();
        update.setId(exists.getId());
        update.setAuditStatus(auditReqVO.getAuditStatus());
        if (StrUtil.isNotBlank(auditReqVO.getRemark())) {
            update.setRemark(auditReqVO.getRemark());
        }
        cancellationMapper.updateById(update);
    }
    @Override
    public void submitCancellation(Long id, String processDefinitionKey, Long userId) {
        // 1. æ ¡éªŒé”€æˆ·ç”³è¯·æ˜¯å¦åœ¨è‰ç¨¿çŠ¶æ€
        CrmCancellationDO cancellation = cancellationMapper.selectById(id);
        if (cancellation == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NOT_EXISTS);
        }
        if (ObjUtil.notEqual(cancellation.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. æ ¡éªŒé”€æˆ·å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨ï¼Œä¸”传入的流程定义处于激活状态
        validateCancellationApproveCategoryAndProcessDefinition();
        ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
        if (processDefinition == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 3. åˆ›å»ºé”€æˆ·å®¡æ‰¹æµç¨‹å®žä¾‹
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(processDefinitionKey)
                        .setBusinessKey(String.valueOf(id)));
        // 4. æ›´æ–°é”€æˆ·ç”³è¯·æµç¨‹ç¼–号与审批状态
        cancellationMapper.updateById(new CrmCancellationDO().setId(id).setProcessInstanceId(processInstanceId)
                .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
    }
    @Override
    public void updateCancellationAuditStatus(Long id, Integer bpmResult) {
        // 1.1 æ ¡éªŒé”€æˆ·ç”³è¯·æ˜¯å¦å­˜åœ¨
        CrmCancellationDO cancellation = cancellationMapper.selectById(id);
        if (cancellation == null) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_NOT_EXISTS);
        }
        // 1.2 åªæœ‰å®¡æ‰¹ä¸­ï¼Œå¯ä»¥æ›´æ–°å®¡æ‰¹ç»“æžœ
        if (ObjUtil.notEqual(cancellation.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
            log.error("[updateCancellationAuditStatus][cancellation({}) ä¸å¤„于审批中,无法更新审批结果({})]",
                    cancellation.getId(), bpmResult);
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 2. æ›´æ–°é”€æˆ·ç”³è¯·å®¡æ‰¹ç»“æžœ
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        cancellationMapper.updateById(new CrmCancellationDO().setId(id).setAuditStatus(auditStatus));
    }
    @Override
    public List<Map<String, Object>> getCancellationApproveProcessDefinitionList() {
        // 1. æ ¡éªŒé”€æˆ·å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨
        validateCancellationApproveCategoryAndProcessDefinition();
        // 2. èŽ·å–åˆ†ç±»ä¸‹çš„æµç¨‹å®šä¹‰ä¿¡æ¯
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(CANCELLATION_APPROVE_CATEGORY_CODE);
        // 3. èŽ·å–æµç¨‹å®šä¹‰è¯¦æƒ…
        Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
        List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
        // 4. è¿‡æ»¤ï¼šåªä¿ç•™æ¿€æ´»çŠ¶æ€ï¼Œä¸”ç›¸åŒ key åªä¿ç•™æœ€æ–°ç‰ˆæœ¬
        Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
        for (ProcessDefinition pd : processDefinitions) {
            if (pd.isSuspended()) {
                continue;
            }
            ProcessDefinition existing = latestVersionMap.get(pd.getKey());
            if (existing == null || pd.getVersion() > existing.getVersion()) {
                latestVersionMap.put(pd.getKey(), pd);
            }
        }
        // 5. è¿”回流程定义列表
        List<Map<String, Object>> result = new ArrayList<>();
        for (ProcessDefinition pd : latestVersionMap.values()) {
            Map<String, Object> item = new HashMap<>();
            item.put("id", pd.getId());
            item.put("key", pd.getKey());
            item.put("name", pd.getName());
            item.put("version", pd.getVersion());
            result.add(item);
        }
        return result;
    }
    /**
     * æ ¡éªŒ BPM é”€æˆ·å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨
     */
    private void validateCancellationApproveCategoryAndProcessDefinition() {
        // 1. æ ¡éªŒåˆ†ç±»æ˜¯å¦å­˜åœ¨
        Map<String, ?> categoryMap = bpmCategoryService.getCategoryMap(Collections.singletonList(CANCELLATION_APPROVE_CATEGORY_CODE));
        if (!categoryMap.containsKey(CANCELLATION_APPROVE_CATEGORY_CODE)) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 2. æ ¡éªŒåˆ†ç±»ä¸‹æ˜¯å¦æœ‰å¯ç”¨çš„æµç¨‹å®šä¹‰
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(CANCELLATION_APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 3. æ ¡éªŒæ˜¯å¦æœ‰æ¿€æ´»çŠ¶æ€çš„æµç¨‹å®šä¹‰
        Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
        List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
        boolean hasActiveDefinition = processDefinitions.stream().anyMatch(pd -> !pd.isSuspended());
        if (!hasActiveDefinition) {
            throw exception(ErrorCodeConstants.CRM_CANCELLATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
    }
    private String generateCancellationNo() {
        return "CL" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + RandomUtil.randomNumbers(4);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/cancellation/listener/CrmCancellationStatusListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.crm.service.cancellation.listener;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.crm.dal.dataobject.cancellation.CrmCancellationDO;
import cn.iocoder.yudao.module.crm.dal.mysql.cancellation.CrmCancellationMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.service.cancellation.CrmCancellationService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
 * é”€æˆ·å®¡æ‰¹çš„结果监听器实现类
 *
 * é€šè¿‡ businessKey(销户申请ID)匹配,而不是固定的流程定义 Key,
 * è¿™æ ·å¯ä»¥æ”¯æŒåˆ†ç±»ä¸‹ä»»æ„ Key çš„æµç¨‹æ¨¡åž‹ã€‚
 */
@Component
@Slf4j
public class CrmCancellationStatusListener implements ApplicationListener<BpmProcessInstanceStatusEvent> {
    @Resource
    private CrmCancellationService cancellationService;
    @Resource
    private CrmCancellationMapper cancellationMapper;
    @Override
    public void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
        // é€šè¿‡ businessKey æŸ¥è¯¢é”€æˆ·ç”³è¯·
        Long cancellationId;
        try {
            cancellationId = Long.parseLong(event.getBusinessKey());
        } catch (NumberFormatException e) {
            return; // businessKey ä¸æ˜¯æ•°å­—,忽略
        }
        CrmCancellationDO cancellation = cancellationMapper.selectById(cancellationId);
        if (cancellation == null) {
            return; // ä¸æ˜¯é”€æˆ·ç”³è¯·ï¼Œå¿½ç•¥
        }
        // åªæœ‰å®¡æ‰¹ä¸­çŠ¶æ€çš„é”€æˆ·ç”³è¯·æ‰å¤„ç†
        if (!CrmAuditStatusEnum.PROCESS.getStatus().equals(cancellation.getAuditStatus())) {
            log.warn("[onApplicationEvent] é”€æˆ·ç”³è¯·({}) ä¸å¤„于审批中状态,当前状态: {}", cancellationId, cancellation.getAuditStatus());
            return;
        }
        log.info("[onApplicationEvent] é”€æˆ·ç”³è¯·({}) å®¡æ‰¹çŠ¶æ€æ›´æ–°: {}", cancellationId, event.getStatus());
        // æ›´æ–°å®¡æ‰¹çŠ¶æ€
        cancellationService.updateCancellationAuditStatus(cancellationId, event.getStatus());
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractCommandService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.crm.service.contract;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandSaveReqVO;
import jakarta.validation.Valid;
public interface CrmContractCommandService {
    Long createCommand(@Valid CrmContractCommandSaveReqVO createReqVO);
    void updateCommand(@Valid CrmContractCommandSaveReqVO updateReqVO);
    void deleteCommand(Long id);
    CrmContractCommandRespVO getCommand(Long id);
    PageResult<CrmContractCommandRespVO> getCommandPage(CrmContractCommandPageReqVO pageReqVO);
    /**
     * æ‰§è¡ŒæŒ‡ä»¤ï¼ˆçŠ¶æ€ 0/10 -> 20 å·²å®Œæˆï¼‰
     */
    void executeCommand(Long id);
    /**
     * å–消指令(仅待执行/已下发状态可取消)
     */
    void cancelCommand(Long id);
    Long countByContractId(Long contractId);
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractCommandServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.crm.service.contract;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.command.CrmContractCommandSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractCommandDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractCommandMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.mes.api.workorder.MesProWorkOrderApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@Service
@Validated
public class CrmContractCommandServiceImpl implements CrmContractCommandService {
    private static final Integer STATUS_WAIT = 0;
    private static final Integer STATUS_DISPATCHED = 10;
    private static final Integer STATUS_DONE = 20;
    private static final Integer STATUS_CANCELED = 30;
    /** ç›®æ ‡æ¨¡å—:MES ç”Ÿäº§å·¥å•(有真实落地单据,执行时确认工单) */
    private static final String TARGET_MODULE_MES_WORK_ORDER = "mes-work-order";
    @Resource
    private CrmContractCommandMapper commandMapper;
    @Resource
    private CrmContractMapper contractMapper;
    @Resource
    private MesProWorkOrderApi workOrderApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCommand(CrmContractCommandSaveReqVO createReqVO) {
        CrmContractDO contract = contractMapper.selectById(createReqVO.getContractId());
        if (contract == null) {
            throw exception(ErrorCodeConstants.CONTRACT_NOT_EXISTS);
        }
        if (StrUtil.isBlank(createReqVO.getContractNo())) {
            createReqVO.setContractNo(contract.getNo());
        }
        if (createReqVO.getOperateTime() == null) {
            createReqVO.setOperateTime(LocalDateTime.now());
        }
        if (createReqVO.getStatus() == null) {
            createReqVO.setStatus(STATUS_WAIT);
        }
        CrmContractCommandDO entity = BeanUtils.toBean(createReqVO, CrmContractCommandDO.class);
        commandMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateCommand(CrmContractCommandSaveReqVO updateReqVO) {
        CrmContractCommandDO exists = commandMapper.selectById(updateReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CONTRACT_COMMAND_NOT_EXISTS);
        }
        CrmContractCommandDO entity = BeanUtils.toBean(updateReqVO, CrmContractCommandDO.class);
        commandMapper.updateById(entity);
    }
    @Override
    public void deleteCommand(Long id) {
        CrmContractCommandDO exists = commandMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CONTRACT_COMMAND_NOT_EXISTS);
        }
        commandMapper.deleteById(id);
    }
    @Override
    public CrmContractCommandRespVO getCommand(Long id) {
        CrmContractCommandDO entity = commandMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, CrmContractCommandRespVO.class);
    }
    @Override
    public PageResult<CrmContractCommandRespVO> getCommandPage(CrmContractCommandPageReqVO pageReqVO) {
        return BeanUtils.toBean(commandMapper.selectPage(pageReqVO), CrmContractCommandRespVO.class);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeCommand(Long id) {
        CrmContractCommandDO exists = commandMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CONTRACT_COMMAND_NOT_EXISTS);
        }
        if (!STATUS_WAIT.equals(exists.getStatus()) && !STATUS_DISPATCHED.equals(exists.getStatus())) {
            throw exception(ErrorCodeConstants.CRM_COMMAND_EXECUTE_FAIL_STATUS);
        }
        // è·¨æ¨¡å—真实下发:mes-work-order ç›®æ ‡æ¨¡å—,确认指令关联的工单
        if (TARGET_MODULE_MES_WORK_ORDER.equals(exists.getTargetModule())) {
            if (exists.getTargetId() == null) {
                throw exception(ErrorCodeConstants.CRM_COMMAND_DISPATCH_TARGET_MISSING);
            }
            workOrderApi.confirmWorkOrder(exists.getTargetId());
        }
        // å…¶ä½™ç›®æ ‡æ¨¡å—(mes-metering/srm-service)暂无真实落地单据,仅记录状态流转
        CrmContractCommandDO update = new CrmContractCommandDO();
        update.setId(id);
        update.setStatus(STATUS_DONE);
        commandMapper.updateById(update);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelCommand(Long id) {
        CrmContractCommandDO exists = commandMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CONTRACT_COMMAND_NOT_EXISTS);
        }
        if (!STATUS_WAIT.equals(exists.getStatus()) && !STATUS_DISPATCHED.equals(exists.getStatus())) {
            throw exception(ErrorCodeConstants.CRM_COMMAND_EXECUTE_FAIL_STATUS);
        }
        // è·¨æ¨¡å—联动取消:已下发的 MES å·¥å•同步取消
        if (TARGET_MODULE_MES_WORK_ORDER.equals(exists.getTargetModule())
                && exists.getTargetId() != null) {
            workOrderApi.cancelWorkOrder(exists.getTargetId());
        }
        CrmContractCommandDO update = new CrmContractCommandDO();
        update.setId(id);
        update.setStatus(STATUS_CANCELED);
        commandMapper.updateById(update);
    }
    @Override
    public Long countByContractId(Long contractId) {
        List<CrmContractCommandDO> list = commandMapper.selectListByContractId(contractId);
        return (long) list.size();
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/credit/CrmCreditRatingService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.crm.service.credit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingSaveReqVO;
import jakarta.validation.Valid;
import java.util.List;
public interface CrmCreditRatingService {
    Long createCreditRating(@Valid CrmCreditRatingSaveReqVO createReqVO);
    void updateCreditRating(@Valid CrmCreditRatingSaveReqVO updateReqVO);
    void deleteCreditRating(Long id);
    CrmCreditRatingRespVO getCreditRating(Long id);
    PageResult<CrmCreditRatingRespVO> getCreditRatingPage(CrmCreditRatingPageReqVO pageReqVO);
    List<CrmCreditRatingRespVO> getCreditRatingList(CrmCreditRatingPageReqVO reqVO);
    CrmCreditRatingRespVO getLatestByCustomerId(Long customerId);
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/credit/CrmCreditRatingServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
package cn.iocoder.yudao.module.crm.service.credit;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.credit.vo.CrmCreditRatingSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.credit.CrmCreditRatingDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.mysql.credit.CrmCreditRatingMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@Service
@Validated
public class CrmCreditRatingServiceImpl implements CrmCreditRatingService {
    @Resource
    private CrmCreditRatingMapper creditRatingMapper;
    @Resource
    private CrmCustomerMapper customerMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCreditRating(CrmCreditRatingSaveReqVO createReqVO) {
        CrmCustomerDO customer = customerMapper.selectById(createReqVO.getCustomerId());
        if (customer == null) {
            throw exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        if (StrUtil.isBlank(createReqVO.getOperatorName())) {
            createReqVO.setOperatorName(customer.getName());
        }
        if (createReqVO.getOperateTime() == null) {
            createReqVO.setOperateTime(LocalDateTime.now());
        }
        CrmCreditRatingDO entity = BeanUtils.toBean(createReqVO, CrmCreditRatingDO.class, vo -> vo.setCustomerName(customer.getName()));
        creditRatingMapper.insert(entity);
        customer.setCreditLevel(entity.getLevel());
        customer.setCreditScore(entity.getScore());
        customerMapper.updateById(customer);
        return entity.getId();
    }
    @Override
    public void updateCreditRating(CrmCreditRatingSaveReqVO updateReqVO) {
        CrmCreditRatingDO exists = creditRatingMapper.selectById(updateReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CREDIT_RATING_NOT_EXISTS);
        }
        CrmCreditRatingDO entity = BeanUtils.toBean(updateReqVO, CrmCreditRatingDO.class);
        creditRatingMapper.updateById(entity);
        CrmCreditRatingDO latest = creditRatingMapper.selectLatestByCustomerId(exists.getCustomerId());
        CrmCustomerDO customer = customerMapper.selectById(exists.getCustomerId());
        if (customer != null && latest != null && latest.getId().equals(entity.getId())) {
            customer.setCreditLevel(latest.getLevel());
            customer.setCreditScore(latest.getScore());
            customerMapper.updateById(customer);
        }
    }
    @Override
    public void deleteCreditRating(Long id) {
        CrmCreditRatingDO exists = creditRatingMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_CREDIT_RATING_NOT_EXISTS);
        }
        creditRatingMapper.deleteById(id);
        CrmCreditRatingDO latest = creditRatingMapper.selectLatestByCustomerId(exists.getCustomerId());
        CrmCustomerDO customer = customerMapper.selectById(exists.getCustomerId());
        if (customer != null) {
            customer.setCreditLevel(latest == null ? null : latest.getLevel());
            customer.setCreditScore(latest == null ? null : latest.getScore());
            customerMapper.updateById(customer);
        }
    }
    @Override
    public CrmCreditRatingRespVO getCreditRating(Long id) {
        CrmCreditRatingDO entity = creditRatingMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, CrmCreditRatingRespVO.class);
    }
    @Override
    public PageResult<CrmCreditRatingRespVO> getCreditRatingPage(CrmCreditRatingPageReqVO pageReqVO) {
        return BeanUtils.toBean(creditRatingMapper.selectPage(pageReqVO), CrmCreditRatingRespVO.class);
    }
    @Override
    public List<CrmCreditRatingRespVO> getCreditRatingList(CrmCreditRatingPageReqVO reqVO) {
        return BeanUtils.toBean(creditRatingMapper.selectList(reqVO), CrmCreditRatingRespVO.class);
    }
    @Override
    public CrmCreditRatingRespVO getLatestByCustomerId(Long customerId) {
        CrmCreditRatingDO entity = creditRatingMapper.selectLatestByCustomerId(customerId);
        return entity == null ? null : BeanUtils.toBean(entity, CrmCreditRatingRespVO.class);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
@@ -7,8 +7,10 @@
import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * CRM å›žæ¬¾è®¡åˆ’ Service æŽ¥å£
@@ -92,4 +94,20 @@
     */
    Long getReceivablePlanRemindCount(Long userId);
    /**
     * èŽ·å¾—å®¢æˆ·çš„æ¬ è´¹é‡‘é¢ Map(未回款 + å·²é€¾æœŸçš„回款计划金额合计)
     *
     * @param customerIds å®¢æˆ·ç¼–号集合
     * @return key=客户编号,value=欠费金额
     */
    Map<Long, BigDecimal> getOverdueAmountMapByCustomerIds(Collection<Long> customerIds);
    /**
     * èŽ·å¾—å®¢æˆ·çš„æ¬ è´¹ç¬”æ•° Map(未回款 + å·²é€¾æœŸçš„回款计划数量)
     *
     * @param customerIds å®¢æˆ·ç¼–号集合
     * @return key=客户编号,value=欠费笔数
     */
    Map<Long, Integer> getOverdueCountMapByCustomerIds(Collection<Long> customerIds);
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
@@ -24,8 +24,12 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -185,4 +189,32 @@
        return receivablePlanMapper.selectReceivablePlanCountByRemind(userId);
    }
    @Override
    public Map<Long, BigDecimal> getOverdueAmountMapByCustomerIds(Collection<Long> customerIds) {
        Map<Long, BigDecimal> result = new HashMap<>();
        if (CollUtil.isEmpty(customerIds)) {
            return result;
        }
        List<CrmReceivablePlanDO> overdueList =
                receivablePlanMapper.selectOverdueListByCustomerIds(customerIds, LocalDateTime.now());
        for (CrmReceivablePlanDO plan : overdueList) {
            result.merge(plan.getCustomerId(), plan.getPrice(), BigDecimal::add);
        }
        return result;
    }
    @Override
    public Map<Long, Integer> getOverdueCountMapByCustomerIds(Collection<Long> customerIds) {
        Map<Long, Integer> result = new HashMap<>();
        if (CollUtil.isEmpty(customerIds)) {
            return result;
        }
        List<CrmReceivablePlanDO> overdueList =
                receivablePlanMapper.selectOverdueListByCustomerIds(customerIds, LocalDateTime.now());
        for (CrmReceivablePlanDO plan : overdueList) {
            result.merge(plan.getCustomerId(), 1, Integer::sum);
        }
        return result;
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/CrmRefundService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.crm.service.refund;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundSaveReqVO;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
public interface CrmRefundService {
    Long createRefund(@Valid CrmRefundSaveReqVO createReqVO);
    void updateRefund(@Valid CrmRefundSaveReqVO updateReqVO);
    void deleteRefund(Long id);
    CrmRefundRespVO getRefund(Long id);
    PageResult<CrmRefundRespVO> getRefundPage(CrmRefundPageReqVO pageReqVO);
    List<CrmRefundRespVO> getRefundList(CrmRefundPageReqVO reqVO);
    void auditRefund(@Valid CrmRefundAuditReqVO auditReqVO);
    void submitRefund(Long id, String processDefinitionKey, Long userId);
    void updateRefundAuditStatus(Long id, Integer bpmResult);
    List<Map<String, Object>> getRefundApproveProcessDefinitionList();
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/CrmRefundServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,259 @@
package cn.iocoder.yudao.module.crm.service.refund;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundAuditReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.refund.vo.CrmRefundSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.refund.CrmRefundDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.refund.CrmRefundMapper;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
@Service
@Validated
@Slf4j
public class CrmRefundServiceImpl implements CrmRefundService {
    private static final Integer AUDIT_DRAFT = 0;
    private static final Integer AUDIT_PASS = 20;
    /**
     * BPM é€€è´¹å®¡æ‰¹åˆ†ç±»ç¼–码
     */
    private static final String REFUND_APPROVE_CATEGORY_CODE = "refund_approve";
    @Resource
    private CrmRefundMapper refundMapper;
    @Resource
    private CrmCustomerMapper customerMapper;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Resource
    private BpmCategoryService bpmCategoryService;
    @Resource
    private BpmProcessDefinitionService bpmProcessDefinitionService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRefund(CrmRefundSaveReqVO createReqVO) {
        if (StrUtil.isBlank(createReqVO.getRefundNo())) {
            createReqVO.setRefundNo(generateRefundNo());
        } else if (refundMapper.selectByRefundNo(createReqVO.getRefundNo()) != null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NO_DUPLICATE);
        }
        CrmCustomerDO customer = customerMapper.selectById(createReqVO.getCustomerId());
        if (customer == null) {
            throw exception(ErrorCodeConstants.CUSTOMER_NOT_EXISTS);
        }
        if (StrUtil.isBlank(createReqVO.getCustomerName())) {
            createReqVO.setCustomerName(customer.getName());
        }
        if (createReqVO.getApplyTime() == null) {
            createReqVO.setApplyTime(LocalDateTime.now());
        }
        if (createReqVO.getAuditStatus() == null) {
            createReqVO.setAuditStatus(AUDIT_DRAFT);
        }
        CrmRefundDO entity = BeanUtils.toBean(createReqVO, CrmRefundDO.class);
        refundMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateRefund(CrmRefundSaveReqVO updateReqVO) {
        CrmRefundDO exists = refundMapper.selectById(updateReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NOT_EXISTS);
        }
        CrmRefundDO entity = BeanUtils.toBean(updateReqVO, CrmRefundDO.class);
        refundMapper.updateById(entity);
    }
    @Override
    public void deleteRefund(Long id) {
        CrmRefundDO exists = refundMapper.selectById(id);
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NOT_EXISTS);
        }
        refundMapper.deleteById(id);
    }
    @Override
    public CrmRefundRespVO getRefund(Long id) {
        CrmRefundDO entity = refundMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, CrmRefundRespVO.class);
    }
    @Override
    public PageResult<CrmRefundRespVO> getRefundPage(CrmRefundPageReqVO pageReqVO) {
        return BeanUtils.toBean(refundMapper.selectPage(pageReqVO), CrmRefundRespVO.class);
    }
    @Override
    public List<CrmRefundRespVO> getRefundList(CrmRefundPageReqVO reqVO) {
        return BeanUtils.toBean(refundMapper.selectList(reqVO), CrmRefundRespVO.class);
    }
    @Override
    public void auditRefund(CrmRefundAuditReqVO auditReqVO) {
        CrmRefundDO exists = refundMapper.selectById(auditReqVO.getId());
        if (exists == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NOT_EXISTS);
        }
        CrmRefundDO update = new CrmRefundDO();
        update.setId(exists.getId());
        update.setAuditStatus(auditReqVO.getAuditStatus());
        if (StrUtil.isNotBlank(auditReqVO.getRemark())) {
            update.setRemark(auditReqVO.getRemark());
        }
        refundMapper.updateById(update);
    }
    @Override
    public void submitRefund(Long id, String processDefinitionKey, Long userId) {
        // 1. æ ¡éªŒé€€è´¹å•是否在草稿状态
        CrmRefundDO refund = refundMapper.selectById(id);
        if (refund == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NOT_EXISTS);
        }
        if (ObjUtil.notEqual(refund.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
            throw exception(ErrorCodeConstants.CRM_REFUND_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. æ ¡éªŒé€€è´¹å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨ï¼Œä¸”传入的流程定义处于激活状态
        validateRefundApproveCategoryAndProcessDefinition();
        ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
        if (processDefinition == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 3. åˆ›å»ºé€€è´¹å®¡æ‰¹æµç¨‹å®žä¾‹
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(processDefinitionKey)
                        .setBusinessKey(String.valueOf(id)));
        // 4. æ›´æ–°é€€è´¹å•流程编号与审批状态
        refundMapper.updateById(new CrmRefundDO().setId(id).setProcessInstanceId(processInstanceId)
                .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
    }
    @Override
    public void updateRefundAuditStatus(Long id, Integer bpmResult) {
        // 1.1 æ ¡éªŒé€€è´¹å•是否存在
        CrmRefundDO refund = refundMapper.selectById(id);
        if (refund == null) {
            throw exception(ErrorCodeConstants.CRM_REFUND_NOT_EXISTS);
        }
        // 1.2 åªæœ‰å®¡æ‰¹ä¸­ï¼Œå¯ä»¥æ›´æ–°å®¡æ‰¹ç»“æžœ
        if (ObjUtil.notEqual(refund.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
            log.error("[updateRefundAuditStatus][refund({}) ä¸å¤„于审批中,无法更新审批结果({})]", refund.getId(), bpmResult);
            throw exception(ErrorCodeConstants.CRM_REFUND_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 2. æ›´æ–°é€€è´¹å•审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        refundMapper.updateById(new CrmRefundDO().setId(id).setAuditStatus(auditStatus));
    }
    @Override
    public List<Map<String, Object>> getRefundApproveProcessDefinitionList() {
        // 1. æ ¡éªŒé€€è´¹å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨
        validateRefundApproveCategoryAndProcessDefinition();
        // 2. èŽ·å–åˆ†ç±»ä¸‹çš„æµç¨‹å®šä¹‰ä¿¡æ¯
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(REFUND_APPROVE_CATEGORY_CODE);
        // 3. èŽ·å–æµç¨‹å®šä¹‰è¯¦æƒ…
        Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
        List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
        // 4. è¿‡æ»¤ï¼šåªä¿ç•™æ¿€æ´»çŠ¶æ€ï¼Œä¸”ç›¸åŒ key åªä¿ç•™æœ€æ–°ç‰ˆæœ¬
        Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
        for (ProcessDefinition pd : processDefinitions) {
            if (pd.isSuspended()) {
                continue;
            }
            ProcessDefinition existing = latestVersionMap.get(pd.getKey());
            if (existing == null || pd.getVersion() > existing.getVersion()) {
                latestVersionMap.put(pd.getKey(), pd);
            }
        }
        // 5. è¿”回流程定义列表
        List<Map<String, Object>> result = new ArrayList<>();
        for (ProcessDefinition pd : latestVersionMap.values()) {
            Map<String, Object> item = new HashMap<>();
            item.put("id", pd.getId());
            item.put("key", pd.getKey());
            item.put("name", pd.getName());
            item.put("version", pd.getVersion());
            result.add(item);
        }
        return result;
    }
    /**
     * æ ¡éªŒ BPM é€€è´¹å®¡æ‰¹åˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨
     */
    private void validateRefundApproveCategoryAndProcessDefinition() {
        // 1. æ ¡éªŒåˆ†ç±»æ˜¯å¦å­˜åœ¨
        Map<String, ?> categoryMap = bpmCategoryService.getCategoryMap(Collections.singletonList(REFUND_APPROVE_CATEGORY_CODE));
        if (!categoryMap.containsKey(REFUND_APPROVE_CATEGORY_CODE)) {
            throw exception(ErrorCodeConstants.CRM_REFUND_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 2. æ ¡éªŒåˆ†ç±»ä¸‹æ˜¯å¦æœ‰å¯ç”¨çš„æµç¨‹å®šä¹‰
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(REFUND_APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            throw exception(ErrorCodeConstants.CRM_REFUND_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 3. æ ¡éªŒæ˜¯å¦æœ‰æ¿€æ´»çŠ¶æ€çš„æµç¨‹å®šä¹‰
        Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
        List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
        boolean hasActiveDefinition = processDefinitions.stream().anyMatch(pd -> !pd.isSuspended());
        if (!hasActiveDefinition) {
            throw exception(ErrorCodeConstants.CRM_REFUND_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
    }
    private String generateRefundNo() {
        return "RF" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + RandomUtil.randomNumbers(4);
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/refund/listener/CrmRefundStatusListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.crm.service.refund.listener;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.crm.dal.dataobject.refund.CrmRefundDO;
import cn.iocoder.yudao.module.crm.dal.mysql.refund.CrmRefundMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.service.refund.CrmRefundService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
 * é€€è´¹å®¡æ‰¹çš„结果监听器实现类
 *
 * é€šè¿‡ businessKey(退费单ID)匹配,而不是固定的流程定义 Key,
 * è¿™æ ·å¯ä»¥æ”¯æŒåˆ†ç±»ä¸‹ä»»æ„ Key çš„æµç¨‹æ¨¡åž‹ã€‚
 */
@Component
@Slf4j
public class CrmRefundStatusListener implements ApplicationListener<BpmProcessInstanceStatusEvent> {
    @Resource
    private CrmRefundService refundService;
    @Resource
    private CrmRefundMapper refundMapper;
    @Override
    public void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
        // é€šè¿‡ businessKey æŸ¥è¯¢é€€è´¹å•
        Long refundId;
        try {
            refundId = Long.parseLong(event.getBusinessKey());
        } catch (NumberFormatException e) {
            return; // businessKey ä¸æ˜¯æ•°å­—,忽略
        }
        CrmRefundDO refund = refundMapper.selectById(refundId);
        if (refund == null) {
            return; // ä¸æ˜¯é€€è´¹å•,忽略
        }
        // åªæœ‰å®¡æ‰¹ä¸­çŠ¶æ€çš„é€€è´¹å•æ‰å¤„ç†
        if (!CrmAuditStatusEnum.PROCESS.getStatus().equals(refund.getAuditStatus())) {
            log.warn("[onApplicationEvent] é€€è´¹å•({}) ä¸å¤„于审批中状态,当前状态: {}", refundId, refund.getAuditStatus());
            return;
        }
        log.info("[onApplicationEvent] é€€è´¹å•({}) å®¡æ‰¹çŠ¶æ€æ›´æ–°: {}", refundId, event.getStatus());
        // æ›´æ–°å®¡æ‰¹çŠ¶æ€
        refundService.updateRefundAuditStatus(refundId, event.getStatus());
    }
}
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.erp.api.purchase;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchasePlanCreateReqDTO;
/**
 * ERP é‡‡è´­è®¡åˆ’ API æŽ¥å£
 *
 * <p>供其他模块(如 MES è®¡åˆ’排程)自动生成采购计划
 *
 * @author xiaoyi
 */
public interface ErpPurchasePlanApi {
    /**
     * åˆ›å»ºé‡‡è´­è®¡åˆ’(草稿)
     *
     * @param createReqDTO åˆ›å»ºä¿¡æ¯
     * @return é‡‡è´­è®¡åˆ’编号
     */
    Long createPurchasePlan(ErpPurchasePlanCreateReqDTO createReqDTO);
}
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/dto/ErpPurchasePlanCreateReqDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.erp.api.purchase.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
 * ERP é‡‡è´­è®¡åˆ’创建 Request DTO
 *
 * @author xiaoyi
 */
@Data
public class ErpPurchasePlanCreateReqDTO {
    /**
     * è®¡åˆ’名称
     */
    private String name;
    /**
     * éœ€æ±‚来源
     *
     * 1: åº“存预警
     * 2: è¿ç»´/生产计划
     */
    private Integer demandSource;
    /**
     * è®¡åˆ’日期
     */
    private LocalDateTime planDate;
    /**
     * ä¾›åº”商编号(可选)
     */
    private Long supplierId;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * è®¡åˆ’明细列表
     */
    private List<Item> items;
    @Data
    public static class Item {
        /**
         * äº§å“ç¼–号(MDM ç‰©æ–™ç¼–号)
         */
        private Long productId;
        /**
         * äº§å“å•位编号
         */
        private Long productUnitId;
        /**
         * è®¡åˆ’采购数量
         */
        private BigDecimal count;
        /**
         * å¤‡æ³¨
         */
        private String remark;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.erp.api.purchase;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchasePlanCreateReqDTO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanSaveReqVO;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchasePlanService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
/**
 * ERP é‡‡è´­è®¡åˆ’ API å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
public class ErpPurchasePlanApiImpl implements ErpPurchasePlanApi {
    @Resource
    private ErpPurchasePlanService purchasePlanService;
    @Override
    public Long createPurchasePlan(ErpPurchasePlanCreateReqDTO createReqDTO) {
        ErpPurchasePlanSaveReqVO createReqVO = new ErpPurchasePlanSaveReqVO();
        createReqVO.setName(createReqDTO.getName());
        createReqVO.setDemandSource(createReqDTO.getDemandSource());
        createReqVO.setPlanDate(createReqDTO.getPlanDate());
        createReqVO.setSupplierId(createReqDTO.getSupplierId());
        createReqVO.setRemark(createReqDTO.getRemark());
        createReqVO.setItems(convertItems(createReqDTO.getItems()));
        return purchasePlanService.createPurchasePlan(createReqVO);
    }
    private List<ErpPurchasePlanSaveReqVO.Item> convertItems(List<ErpPurchasePlanCreateReqDTO.Item> items) {
        if (items == null) {
            return List.of();
        }
        return items.stream().map(item -> {
            ErpPurchasePlanSaveReqVO.Item vo = new ErpPurchasePlanSaveReqVO.Item();
            vo.setProductId(item.getProductId());
            vo.setProductUnitId(item.getProductUnitId());
            vo.setCount(item.getCount());
            vo.setRemark(item.getRemark());
            return vo;
        }).toList();
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,210 @@
package cn.iocoder.yudao.module.erp.controller.admin.purchase;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanItemDO;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchasePlanService;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchasePlanServiceImpl;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - ERP é‡‡è´­è®¡åˆ’")
@RestController
@RequestMapping("/erp/purchase-plan")
@Validated
public class ErpPurchasePlanController {
    @Resource
    private ErpPurchasePlanService purchasePlanService;
    @Resource
    private SrmSupplierApi srmSupplierApi;
    @Resource
    private MdmItemApi mdmItemApi;
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private DeptApi deptApi;
    @PostMapping("/create")
    @Operation(summary = "创建采购计划")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:create')")
    public CommonResult<Long> createPurchasePlan(@Valid @RequestBody ErpPurchasePlanSaveReqVO createReqVO) {
        return success(purchasePlanService.createPurchasePlan(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新采购计划")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:update')")
    public CommonResult<Boolean> updatePurchasePlan(@Valid @RequestBody ErpPurchasePlanSaveReqVO updateReqVO) {
        purchasePlanService.updatePurchasePlan(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除采购计划")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:delete')")
    public CommonResult<Boolean> deletePurchasePlan(@RequestParam("ids") List<Long> ids) {
        purchasePlanService.deletePurchasePlan(ids);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得采购计划")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:query')")
    public CommonResult<ErpPurchasePlanRespVO> getPurchasePlan(@RequestParam("id") Long id) {
        ErpPurchasePlanDO plan = purchasePlanService.getPurchasePlan(id);
        if (plan == null) {
            return success(null);
        }
        List<ErpPurchasePlanItemDO> planItemList = purchasePlanService.getPurchasePlanItemListByPlanId(id);
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
                convertSet(planItemList, ErpPurchasePlanItemDO::getProductId));
        Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureMap(
                convertSet(planItemList, ErpPurchasePlanItemDO::getProductUnitId));
        return success(BeanUtils.toBean(plan, ErpPurchasePlanRespVO.class, planVO -> {
            planVO.setDemandSourceName(convertDemandSourceName(plan.getDemandSource()));
            planVO.setItems(BeanUtils.toBean(planItemList, ErpPurchasePlanRespVO.Item.class, item -> {
                MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName()));
                MapUtils.findAndThen(unitMeasureMap, item.getProductUnitId(), unit -> item.setProductUnitName(unit.getName()));
            }));
        }));
    }
    @GetMapping("/page")
    @Operation(summary = "获得采购计划分页")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:query')")
    public CommonResult<PageResult<ErpPurchasePlanRespVO>> getPurchasePlanPage(@Valid ErpPurchasePlanPageReqVO pageReqVO) {
        PageResult<ErpPurchasePlanDO> pageResult = purchasePlanService.getPurchasePlanPage(pageReqVO);
        return success(buildPurchasePlanVOPageResult(pageResult));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出采购计划 Excel")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportPurchasePlanExcel(@Valid ErpPurchasePlanPageReqVO pageReqVO,
                                        HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<ErpPurchasePlanRespVO> list = buildPurchasePlanVOPageResult(purchasePlanService.getPurchasePlanPage(pageReqVO)).getList();
        ExcelUtils.write(response, "采购计划.xls", "数据", ErpPurchasePlanRespVO.class, list);
    }
    @PutMapping("/submit")
    @Operation(summary = "提交采购计划审批")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:submit')")
    public CommonResult<Boolean> submitPurchasePlan(@RequestParam("id") Long id,
                                                    @RequestParam("processDefinitionKey") String processDefinitionKey) {
        Long userId = getLoginUserId();
        purchasePlanService.submitPurchasePlan(id, processDefinitionKey, userId);
        return success(true);
    }
    @GetMapping("/approve-process-list")
    @Operation(summary = "获取采购计划审批流程列表")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:query')")
    public CommonResult<List<Map<String, Object>>> getApproveProcessList() {
        return success(purchasePlanService.getPurchasePlanApproveProcessDefinitionList());
    }
    @PostMapping("/generate-from-stock-alert")
    @Operation(summary = "根据库存预警生成采购计划", description = "遍历启用安全库存的物料,按缺量生成草稿采购计划")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:create')")
    public CommonResult<Long> generatePlanFromStockAlert(@RequestParam(value = "name", required = false) String name,
                                                         @RequestParam(value = "supplierId", required = false) Long supplierId) {
        ErpPurchasePlanDO plan = purchasePlanService.generatePlanFromStockAlert(name, supplierId);
        return success(plan.getId());
    }
    @PostMapping("/generate-requests")
    @Operation(summary = "生成采购申请")
    @PreAuthorize("@ss.hasPermission('erp:purchase-plan:generate-request')")
    public CommonResult<List<Long>> generatePurchaseRequests(@RequestParam("id") Long id) {
        return success(purchasePlanService.generatePurchaseRequests(id));
    }
    private PageResult<ErpPurchasePlanRespVO> buildPurchasePlanVOPageResult(PageResult<ErpPurchasePlanDO> pageResult) {
        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }
        // 1.1 è®¡åˆ’明细
        List<ErpPurchasePlanItemDO> planItemList = purchasePlanService.getPurchasePlanItemListByPlanIds(
                convertSet(pageResult.getList(), ErpPurchasePlanDO::getId));
        Map<Long, List<ErpPurchasePlanItemDO>> planItemMap = convertMultiMap(planItemList, ErpPurchasePlanItemDO::getPlanId);
        // 1.2 ç‰©æ–™ä¿¡æ¯
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
                convertSet(planItemList, ErpPurchasePlanItemDO::getProductId));
        // 1.3 ä¾›åº”商信息
        Map<Long, SrmSupplierRespDTO> supplierMap = srmSupplierApi.getSupplierList(
                convertSet(pageResult.getList(), ErpPurchasePlanDO::getSupplierId)).getCheckedData().stream()
                .collect(Collectors.toMap(SrmSupplierRespDTO::getId, v -> v, (a, b) -> a));
        // 1.4 ç”¨æˆ·ä¿¡æ¯
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(pageResult.getList(), ErpPurchasePlanDO::getPlanUserId));
        // 1.5 éƒ¨é—¨ä¿¡æ¯
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
                convertSet(pageResult.getList(), ErpPurchasePlanDO::getPlanDeptId));
        // 2. å¼€å§‹æ‹¼æŽ¥
        return BeanUtils.toBean(pageResult, ErpPurchasePlanRespVO.class, plan -> {
            plan.setDemandSourceName(convertDemandSourceName(plan.getDemandSource()));
            plan.setItems(BeanUtils.toBean(planItemMap.get(plan.getId()), ErpPurchasePlanRespVO.Item.class,
                    item -> MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName()))));
            plan.setProductNames(CollUtil.join(plan.getItems(), ",", ErpPurchasePlanRespVO.Item::getProductName));
            MapUtils.findAndThen(supplierMap, plan.getSupplierId(), supplier -> plan.setSupplierName(supplier.getName()));
            MapUtils.findAndThen(userMap, plan.getPlanUserId(), user -> plan.setPlanUserName(user.getNickname()));
            MapUtils.findAndThen(deptMap, plan.getPlanDeptId(), dept -> plan.setPlanDeptName(dept.getName()));
        });
    }
    private String convertDemandSourceName(Integer demandSource) {
        if (ErpPurchasePlanServiceImpl.DEMAND_SOURCE_STOCK_ALERT.equals(demandSource)) {
            return "库存预警";
        }
        if (ErpPurchasePlanServiceImpl.DEMAND_SOURCE_PLAN.equals(demandSource)) {
            return "运维/生产计划";
        }
        return null;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - ERP é‡‡è´­è®¡åˆ’分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ErpPurchasePlanPageReqVO extends PageParam {
    @Schema(description = "采购计划单号", example = "CGJH001")
    private String no;
    @Schema(description = "计划名称", example = "2026å¹´8月运维备件计划")
    private String name;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "需求来源(1=库存预警,2=运维/生产计划)", example = "1")
    private Integer demandSource;
    @Schema(description = "编制人编号", example = "1")
    private Long planUserId;
    @Schema(description = "编制部门编号", example = "1")
    private Long planDeptId;
    @Schema(description = "计划日期")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDateTime[] planDate;
    @Schema(description = "产品编号", example = "1")
    private Long productId;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - ERP é‡‡è´­è®¡åˆ’ Response VO")
@Data
@ExcelIgnoreUnannotated
public class ErpPurchasePlanRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "采购计划单号", example = "CGJH001")
    @ExcelProperty("计划单号")
    private String no;
    @Schema(description = "计划名称", example = "2026å¹´8月运维备件计划")
    @ExcelProperty("计划名称")
    private String name;
    @Schema(description = "状态", example = "0")
    @ExcelProperty(value = "状态", converter = cn.iocoder.yudao.module.erp.convert.ErpPurchasePlanStatusConvert.class)
    private Integer status;
    @Schema(description = "需求来源(1=库存预警,2=运维/生产计划)", example = "1")
    private Integer demandSource;
    @Schema(description = "需求来源名称", example = "库存预警")
    private String demandSourceName;
    @Schema(description = "计划日期")
    @ExcelProperty("计划日期")
    private LocalDateTime planDate;
    @Schema(description = "编制人编号", example = "1")
    private Long planUserId;
    @Schema(description = "编制人姓名", example = "张三")
    @ExcelProperty("编制人")
    private String planUserName;
    @Schema(description = "编制部门编号", example = "1")
    private Long planDeptId;
    @Schema(description = "编制部门名称", example = "运维部")
    @ExcelProperty("编制部门")
    private String planDeptName;
    @Schema(description = "供应商编号(可选)", example = "1")
    private Long supplierId;
    @Schema(description = "供应商名称", example = "供应商A")
    @ExcelProperty("供应商")
    private String supplierName;
    @Schema(description = "合计数量", example = "100")
    @ExcelProperty("合计数量")
    private BigDecimal totalCount;
    @Schema(description = "是否已生成采购申请", example = "false")
    private Boolean generatedFlag;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    @Schema(description = "计划明细列表")
    private List<Item> items;
    @Schema(description = "产品名称列表(逗号拼接)", example = "产品A,产品B")
    private String productNames;
    @Schema(description = "计划明细项")
    @Data
    public static class Item {
        @Schema(description = "编号", example = "1")
        private Long id;
        @Schema(description = "产品编号", example = "1")
        private Long productId;
        @Schema(description = "产品名称", example = "产品A")
        private String productName;
        @Schema(description = "产品单位编号", example = "1")
        private Long productUnitId;
        @Schema(description = "产品单位名称", example = "个")
        private String productUnitName;
        @Schema(description = "计划采购数量", example = "10")
        private BigDecimal count;
        @Schema(description = "需求日期")
        private LocalDate demandTime;
        @Schema(description = "备注", example = "备注")
        private String remark;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/plan/ErpPurchasePlanSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - ERP é‡‡è´­è®¡åˆ’新增/修改 Request VO")
@Data
public class ErpPurchasePlanSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1")
    private Long id;
    @Schema(description = "计划名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026å¹´8月运维备件计划")
    @NotEmpty(message = "计划名称不能为空")
    private String name;
    @Schema(description = "需求来源(1=库存预警,2=运维/生产计划)", example = "1")
    private Integer demandSource;
    @Schema(description = "计划日期", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "计划日期不能为空")
    private LocalDateTime planDate;
    @Schema(description = "供应商编号(可选)", example = "1")
    private Long supplierId;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "计划明细列表", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "计划明细不能为空")
    @Valid
    private List<Item> items;
    @Schema(description = "计划明细项")
    @Data
    public static class Item {
        @Schema(description = "编号(修改时必填)", example = "1")
        private Long id;
        @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
        @NotNull(message = "产品编号不能为空")
        private Long productId;
        @Schema(description = "产品单位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
        @NotNull(message = "产品单位不能为空")
        private Long productUnitId;
        @Schema(description = "计划采购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
        @NotNull(message = "数量不能为空")
        private BigDecimal count;
        @Schema(description = "需求日期")
        private LocalDate demandTime;
        @Schema(description = "备注", example = "备注")
        private String remark;
        @Schema(description = "产品名称(前端展示用,后端忽略)", example = "产品A")
        private String productName;
        @Schema(description = "产品单位名称(前端展示用,后端忽略)", example = "个")
        private String productUnitName;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/convert/ErpPurchasePlanStatusConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.erp.convert;
import cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.metadata.property.ExcelContentProperty;
import cn.iocoder.yudao.module.erp.enums.ErpPurchasePlanStatusEnum;
/**
 * ERP é‡‡è´­è®¡åˆ’状态 Excel è½¬æ¢å™¨
 *
 * <p>将 Integer çŠ¶æ€ç è½¬æ¢ä¸ºä¸­æ–‡åç§°ï¼Œç”¨äºŽ Excel å¯¼å‡º
 *
 * @author xiaoyi
 */
public class ErpPurchasePlanStatusConvert implements Converter<Integer> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return Integer.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    @Override
    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) {
        if (value == null) {
            return new WriteCellData<>("");
        }
        ErpPurchasePlanStatusEnum statusEnum = ErpPurchasePlanStatusEnum.fromStatus(value);
        return new WriteCellData<>(statusEnum != null ? statusEnum.getName() : String.valueOf(value));
    }
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
                                     GlobalConfiguration globalConfiguration) {
        String label = cellData.getStringValue();
        ErpPurchasePlanStatusEnum statusEnum = ErpPurchasePlanStatusEnum.fromName(label);
        return statusEnum != null ? statusEnum.getStatus() : null;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchasePlanDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * ERP é‡‡è´­è®¡åˆ’ DO
 *
 * @author xiaoyi
 */
@TableName("erp_purchase_plan")
@KeySequence("erp_purchase_plan_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErpPurchasePlanDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é‡‡è´­è®¡åˆ’单号
     */
    private String no;
    /**
     * è®¡åˆ’名称
     */
    private String name;
    /**
     * çŠ¶æ€
     *
     * 0: æœªæäº¤
     * 10: å®¡æ‰¹ä¸­
     * 20: å®¡æ ¸é€šè¿‡
     * 30: å®¡æ ¸ä¸é€šè¿‡
     * 40: å·²å–消
     */
    private Integer status;
    /**
     * éœ€æ±‚来源
     *
     * 1: åº“存预警
     * 2: è¿ç»´/生产计划
     */
    private Integer demandSource;
    /**
     * è®¡åˆ’日期
     */
    private LocalDateTime planDate;
    /**
     * ç¼–制人编号
     */
    private Long planUserId;
    /**
     * ç¼–制部门编号
     */
    private Long planDeptId;
    /**
     * ä¾›åº”商编号(可选)
     */
    private Long supplierId;
    /**
     * åˆè®¡æ•°é‡
     */
    private BigDecimal totalCount;
    /**
     * æ˜¯å¦å·²ç”Ÿæˆé‡‡è´­ç”³è¯·
     */
    private Boolean generatedFlag;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * BPM æµç¨‹å®žä¾‹ç¼–号
     */
    private String processInstanceId;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchasePlanItemDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.erp.dal.dataobject.purchase;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * ERP é‡‡è´­è®¡åˆ’明细 DO
 *
 * @author xiaoyi
 */
@TableName("erp_purchase_plan_items")
@KeySequence("erp_purchase_plan_items_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErpPurchasePlanItemDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é‡‡è´­è®¡åˆ’编号
     */
    private Long planId;
    /**
     * äº§å“ç¼–号(MDM ç‰©æ–™ç¼–号)
     */
    private Long productId;
    /**
     * äº§å“å•位编号
     */
    private Long productUnitId;
    /**
     * è®¡åˆ’采购数量
     */
    private BigDecimal count;
    /**
     * éœ€æ±‚日期
     */
    private LocalDate demandTime;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchasePlanItemMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.erp.dal.mysql.purchase;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanItemDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * ERP é‡‡è´­è®¡åˆ’明细 Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface ErpPurchasePlanItemMapper extends BaseMapperX<ErpPurchasePlanItemDO> {
    default List<ErpPurchasePlanItemDO> selectListByPlanId(Long planId) {
        return selectList(ErpPurchasePlanItemDO::getPlanId, planId);
    }
    default List<ErpPurchasePlanItemDO> selectListByPlanIds(Collection<Long> planIds) {
        return selectList(ErpPurchasePlanItemDO::getPlanId, planIds);
    }
    default void deleteByPlanId(Long planId) {
        delete(new LambdaQueryWrapperX<ErpPurchasePlanItemDO>().eq(ErpPurchasePlanItemDO::getPlanId, planId));
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchasePlanMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.erp.dal.mysql.purchase;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * ERP é‡‡è´­è®¡åˆ’ Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface ErpPurchasePlanMapper extends BaseMapperX<ErpPurchasePlanDO> {
    default ErpPurchasePlanDO selectByNo(String no) {
        return selectOne(ErpPurchasePlanDO::getNo, no);
    }
    default List<ErpPurchasePlanDO> selectPlanListByIds(Collection<Long> ids) {
        return selectList(ErpPurchasePlanDO::getId, ids);
    }
    default PageResult<ErpPurchasePlanDO> selectPage(ErpPurchasePlanPageReqVO pageReqVO) {
        return selectPage(pageReqVO, new LambdaQueryWrapperX<ErpPurchasePlanDO>()
                .likeIfPresent(ErpPurchasePlanDO::getNo, pageReqVO.getNo())
                .likeIfPresent(ErpPurchasePlanDO::getName, pageReqVO.getName())
                .eqIfPresent(ErpPurchasePlanDO::getStatus, pageReqVO.getStatus())
                .eqIfPresent(ErpPurchasePlanDO::getDemandSource, pageReqVO.getDemandSource())
                .eqIfPresent(ErpPurchasePlanDO::getPlanUserId, pageReqVO.getPlanUserId())
                .eqIfPresent(ErpPurchasePlanDO::getPlanDeptId, pageReqVO.getPlanDeptId())
                .betweenIfPresent(ErpPurchasePlanDO::getPlanDate, pageReqVO.getPlanDate())
                .orderByDesc(ErpPurchasePlanDO::getId));
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
@@ -35,6 +35,11 @@
    public static final String PURCHASE_REQUEST_NO_PREFIX = "CGSQ";
    /**
     * é‡‡è´­è®¡åˆ’ {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO}
     */
    public static final String PURCHASE_PLAN_NO_PREFIX = "CGJH";
    /**
     * é‡‡è´­æ¥ç¥¨ {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO}
     */
    public static final String PURCHASE_INVOICE_NO_PREFIX = "FPD";
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpPurchasePlanStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.erp.enums;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * ERP é‡‡è´­è®¡åˆ’状态枚举
 *
 * @author xiaoyi
 */
@Getter
@AllArgsConstructor
public enum ErpPurchasePlanStatusEnum implements ArrayValuable<Integer> {
    DRAFT(0, "未提交"),
    PROCESS(10, "审批中"),
    APPROVE(20, "审核通过"),
    REJECT(30, "审核不通过"),
    CANCEL(40, "已取消");
    public static final Integer[] ARRAYS = Arrays.stream(values())
            .map(ErpPurchasePlanStatusEnum::getStatus).toArray(Integer[]::new);
    public static ErpPurchasePlanStatusEnum fromStatus(Integer status) {
        if (status == null) {
            return null;
        }
        return Arrays.stream(values()).filter(item -> item.getStatus().equals(status)).findFirst().orElse(null);
    }
    public static ErpPurchasePlanStatusEnum fromName(String name) {
        if (name == null) {
            return null;
        }
        return Arrays.stream(values()).filter(item -> item.getName().equals(name)).findFirst().orElse(null);
    }
    /**
     * çŠ¶æ€
     */
    private final Integer status;
    /**
     * çŠ¶æ€å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -54,6 +54,19 @@
    ErrorCode PURCHASE_REQUEST_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_104_008, "采购申请审批分类不存在,请先在BPM模块创建采购申请审批流程分类");
    ErrorCode PURCHASE_REQUEST_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_104_009, "采购申请审批分类下没有可用的流程模型,请先创建并发布流程");
    // ========== ERP é‡‡è´­è®¡åˆ’(1-030-104-010) ==========
    ErrorCode PURCHASE_PLAN_NOT_EXISTS = new ErrorCode(1_030_104_010, "采购计划不存在");
    ErrorCode PURCHASE_PLAN_DELETE_FAIL_NOT_DRAFT = new ErrorCode(1_030_104_011, "采购计划({})不是草稿状态,无法删除");
    ErrorCode PURCHASE_PLAN_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_030_104_012, "采购计划({})不是草稿状态,无法修改");
    ErrorCode PURCHASE_PLAN_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_104_013, "提交失败,只有草稿状态的采购计划才能提交");
    ErrorCode PURCHASE_PLAN_NO_EXISTS = new ErrorCode(1_030_104_014, "生成计划单号失败,请重新提交");
    ErrorCode PURCHASE_PLAN_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_030_104_015, "更新审批状态失败,只有审批中的采购计划才能更新");
    ErrorCode PURCHASE_PLAN_NOT_APPROVE = new ErrorCode(1_030_104_016, "采购计划未审核通过,无法操作");
    ErrorCode PURCHASE_PLAN_REQUEST_EXISTS = new ErrorCode(1_030_104_017, "采购计划已生成采购申请,请勿重复操作");
    ErrorCode PURCHASE_PLAN_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_104_018, "采购计划审批分类不存在,请先在BPM模块创建采购计划审批流程分类");
    ErrorCode PURCHASE_PLAN_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_104_019, "采购计划审批分类下没有可用的流程模型,请先创建并发布流程");
    ErrorCode PURCHASE_PLAN_ITEM_EMPTY = new ErrorCode(1_030_104_020, "采购计划明细不能为空");
    // ========== ERP é‡‡è´­æ¥ç¥¨ï¼ˆ1-030-105-000) ==========
    ErrorCode PURCHASE_INVOICE_NOT_EXISTS = new ErrorCode(1_030_105_000, "来票不存在");
    ErrorCode PURCHASE_INVOICE_NO_EXISTS = new ErrorCode(1_030_105_001, "生成来票编号失败,请重新提交");
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/job/purchase/ErpPurchasePlanStockAlertJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.erp.job.purchase;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchasePlanService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
 * åº“存预警自动生成采购计划 Job
 *
 * <p>定时扫描启用安全库存的物料,当前库存低于安全库存下限时自动生成草稿采购计划,
 * æ»¡è¶³ã€Œç»“合库存预警制定采购计划」的自动化诉求。
 */
@Component
@Slf4j
public class ErpPurchasePlanStockAlertJob implements JobHandler {
    @Resource
    private ErpPurchasePlanService purchasePlanService;
    @Override
    @TenantJob
    public String execute(String param) {
        try {
            ErpPurchasePlanDO plan = purchasePlanService.generatePlanFromStockAlert("库存预警自动生成", null);
            return plan == null ? "库存预警自动生成采购计划:无缺量物料,跳过"
                    : String.format("库存预警自动生成采购计划成功,计划编号:%s", plan.getId());
        } catch (Exception e) {
            log.warn("[execute][库存预警自动生成采购计划跳过/失败,原因:{}]", e.getMessage());
            return "库存预警自动生成采购计划跳过:" + e.getMessage();
        }
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchasePlanService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.erp.service.purchase;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanItemDO;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * ERP é‡‡è´­è®¡åˆ’ Service æŽ¥å£
 *
 * @author xiaoyi
 */
public interface ErpPurchasePlanService {
    /**
     * åˆ›å»ºé‡‡è´­è®¡åˆ’
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createPurchasePlan(ErpPurchasePlanSaveReqVO createReqVO);
    /**
     * æ›´æ–°é‡‡è´­è®¡åˆ’
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updatePurchasePlan(ErpPurchasePlanSaveReqVO updateReqVO);
    /**
     * åˆ é™¤é‡‡è´­è®¡åˆ’
     *
     * @param ids ç¼–号数组
     */
    void deletePurchasePlan(Collection<Long> ids);
    /**
     * èŽ·å¾—é‡‡è´­è®¡åˆ’
     *
     * @param id ç¼–号
     * @return é‡‡è´­è®¡åˆ’
     */
    ErpPurchasePlanDO getPurchasePlan(Long id);
    /**
     * èŽ·å¾—é‡‡è´­è®¡åˆ’æ˜Žç»†åˆ—è¡¨
     *
     * @param planId é‡‡è´­è®¡åˆ’编号
     * @return æ˜Žç»†åˆ—表
     */
    List<ErpPurchasePlanItemDO> getPurchasePlanItemListByPlanId(Long planId);
    /**
     * æŒ‰é‡‡è´­è®¡åˆ’编号集合获得明细列表
     *
     * @param planIds é‡‡è´­è®¡åˆ’编号集合
     * @return æ˜Žç»†åˆ—表
     */
    List<ErpPurchasePlanItemDO> getPurchasePlanItemListByPlanIds(Collection<Long> planIds);
    /**
     * èŽ·å¾—é‡‡è´­è®¡åˆ’åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return é‡‡è´­è®¡åˆ’分页
     */
    PageResult<ErpPurchasePlanDO> getPurchasePlanPage(ErpPurchasePlanPageReqVO pageReqVO);
    /**
     * æäº¤é‡‡è´­è®¡åˆ’审批
     *
     * @param id                   ç¼–号
     * @param processDefinitionKey BPM æµç¨‹å®šä¹‰ Key
     * @param userId               ç”¨æˆ·ç¼–号
     */
    void submitPurchasePlan(Long id, String processDefinitionKey, Long userId);
    /**
     * èŽ·å–é‡‡è´­è®¡åˆ’å®¡æ‰¹æµç¨‹åˆ—è¡¨
     *
     * @return æµç¨‹å®šä¹‰åˆ—表
     */
    List<Map<String, Object>> getPurchasePlanApproveProcessDefinitionList();
    /**
     * BPM å®¡æ‰¹ç»“果回调更新采购计划状态
     *
     * @param id        é‡‡è´­è®¡åˆ’编号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updatePurchasePlanAuditStatus(Long id, Integer bpmResult);
    /**
     * æ ¹æ®åº“存预警自动生成采购计划(草稿)
     *
     * <p>遍历启用安全库存的物料,当前库存低于安全库存下限时,
     * æŒ‰ç¼ºé‡ç”Ÿæˆè®¡åˆ’明细。返回草稿计划,由编制人核对后提交审批。
     *
     * @param name       è®¡åˆ’名称
     * @param supplierId ä¾›åº”商编号(可选)
     * @return ç”Ÿæˆçš„采购计划
     */
    ErpPurchasePlanDO generatePlanFromStockAlert(String name, Long supplierId);
    /**
     * å®¡æ‰¹é€šè¿‡åŽç”Ÿæˆé‡‡è´­ç”³è¯·
     *
     * @param id é‡‡è´­è®¡åˆ’编号
     * @return ç”Ÿæˆçš„采购申请编号列表
     */
    List<Long> generatePurchaseRequests(Long id);
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchasePlanServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,431 @@
package cn.iocoder.yudao.module.erp.service.purchase;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.plan.ErpPurchasePlanSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchasePlanItemDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO;
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchasePlanItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchasePlanMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseRequestItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseRequestMapper;
import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.yudao.module.erp.enums.ErpPurchasePlanStatusEnum;
import cn.iocoder.yudao.module.erp.enums.ErpPurchaseRequestStatusEnum;
import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSafetyStockRespDTO;
import cn.iocoder.yudao.module.mes.api.wms.MesWmStockApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
/**
 * ERP é‡‡è´­è®¡åˆ’ Service å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
@Slf4j
public class ErpPurchasePlanServiceImpl implements ErpPurchasePlanService {
    /**
     * BPM é‡‡è´­è®¡åˆ’审批分类编码
     */
    private static final String PURCHASE_PLAN_APPROVE_CATEGORY_CODE = "purchase_plan_approve";
    /**
     * éœ€æ±‚来源:库存预警
     */
    public static final Integer DEMAND_SOURCE_STOCK_ALERT = 1;
    /**
     * éœ€æ±‚来源:运维/生产计划
     */
    public static final Integer DEMAND_SOURCE_PLAN = 2;
    @Resource
    private ErpPurchasePlanMapper purchasePlanMapper;
    @Resource
    private ErpPurchasePlanItemMapper purchasePlanItemMapper;
    @Resource
    private ErpPurchaseRequestMapper purchaseRequestMapper;
    @Resource
    private ErpPurchaseRequestItemMapper purchaseRequestItemMapper;
    @Resource
    private ErpNoRedisDAO noRedisDAO;
    @Resource
    private BpmProcessInstanceApi processInstanceApi;
    @Resource
    private BpmCategoryService bpmCategoryService;
    @Resource
    private BpmProcessDefinitionService bpmProcessDefinitionService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MesMdItemApi mesMdItemApi;
    @Resource
    private MesWmStockApi mesWmStockApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPurchasePlan(ErpPurchasePlanSaveReqVO createReqVO) {
        // 1. æ ¡éªŒæ˜Žç»†
        List<ErpPurchasePlanItemDO> planItems = convertList(createReqVO.getItems(), item ->
                BeanUtils.toBean(item, ErpPurchasePlanItemDO.class));
        if (CollUtil.isEmpty(planItems)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 2. ç”Ÿæˆè®¡åˆ’单号
        String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_PLAN_NO_PREFIX);
        if (purchasePlanMapper.selectByNo(no) != null) {
            throw exception(PURCHASE_PLAN_NO_EXISTS);
        }
        // 3. èŽ·å–å½“å‰ç”¨æˆ·ä¿¡æ¯
        Long userId = Long.parseLong(getCurrentUserId());
        AdminUserRespDTO user = adminUserApi.getUser(userId);
        Long deptId = user != null ? user.getDeptId() : null;
        // 4. æ’入采购计划
        ErpPurchasePlanDO plan = BeanUtils.toBean(createReqVO, ErpPurchasePlanDO.class, req -> req
                .setNo(no)
                .setStatus(ErpPurchasePlanStatusEnum.DRAFT.getStatus())
                .setPlanUserId(userId)
                .setPlanDeptId(deptId)
                .setGeneratedFlag(false)
                .setTotalCount(planItems.stream()
                        .map(ErpPurchasePlanItemDO::getCount).filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)));
        purchasePlanMapper.insert(plan);
        // 5. æ’入明细
        purchasePlanItemMapper.insertBatch(convertList(planItems, item -> item.setPlanId(plan.getId())));
        return plan.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchasePlan(ErpPurchasePlanSaveReqVO updateReqVO) {
        // 1.1 æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        ErpPurchasePlanDO oldPlan = validatePurchasePlanCanModify(updateReqVO.getId());
        // 1.2 æ ¡éªŒæ˜Žç»†
        List<ErpPurchasePlanItemDO> planItems = convertList(updateReqVO.getItems(), item ->
                BeanUtils.toBean(item, ErpPurchasePlanItemDO.class));
        if (CollUtil.isEmpty(planItems)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 2. æ›´æ–°ä¸»è¡¨
        ErpPurchasePlanDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchasePlanDO.class, req -> req
                .setNo(oldPlan.getNo())
                .setStatus(oldPlan.getStatus())
                .setPlanUserId(oldPlan.getPlanUserId())
                .setPlanDeptId(oldPlan.getPlanDeptId())
                .setGeneratedFlag(oldPlan.getGeneratedFlag())
                .setTotalCount(planItems.stream()
                        .map(ErpPurchasePlanItemDO::getCount).filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)));
        purchasePlanMapper.updateById(updateObj);
        // 3. æ›´æ–°æ˜Žç»†ï¼šå…ˆåˆ åŽæ’
        purchasePlanItemMapper.deleteByPlanId(updateReqVO.getId());
        purchasePlanItemMapper.insertBatch(convertList(planItems, item -> item.setPlanId(updateReqVO.getId())));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePurchasePlan(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        for (Long id : ids) {
            // 1. æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
            validatePurchasePlanCanModify(id);
            // 2. åˆ é™¤æ˜Žç»†
            purchasePlanItemMapper.deleteByPlanId(id);
            // 3. åˆ é™¤ä¸»è¡¨
            purchasePlanMapper.deleteById(id);
        }
    }
    @Override
    public ErpPurchasePlanDO getPurchasePlan(Long id) {
        return purchasePlanMapper.selectById(id);
    }
    @Override
    public List<ErpPurchasePlanItemDO> getPurchasePlanItemListByPlanId(Long planId) {
        return purchasePlanItemMapper.selectListByPlanId(planId);
    }
    @Override
    public List<ErpPurchasePlanItemDO> getPurchasePlanItemListByPlanIds(Collection<Long> planIds) {
        return purchasePlanItemMapper.selectListByPlanIds(planIds);
    }
    @Override
    public PageResult<ErpPurchasePlanDO> getPurchasePlanPage(ErpPurchasePlanPageReqVO pageReqVO) {
        return purchasePlanMapper.selectPage(pageReqVO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitPurchasePlan(Long id, String processDefinitionKey, Long userId) {
        // 1. æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        ErpPurchasePlanDO plan = validatePurchasePlanCanModify(id);
        // 2. æ ¡éªŒæ˜¯å¦æœ‰æ˜Žç»†
        List<ErpPurchasePlanItemDO> items = purchasePlanItemMapper.selectListByPlanId(id);
        if (CollUtil.isEmpty(items)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 3. åˆ›å»º BPM æµç¨‹å®žä¾‹
        String processInstanceId = processInstanceApi.createProcessInstance(userId,
                new BpmProcessInstanceCreateReqDTO()
                        .setProcessDefinitionKey(processDefinitionKey)
                        .setBusinessKey(String.valueOf(id)));
        // 4. æ›´æ–°é‡‡è´­è®¡åˆ’状态为审批中
        purchasePlanMapper.updateById(new ErpPurchasePlanDO()
                .setId(id)
                .setStatus(ErpPurchasePlanStatusEnum.PROCESS.getStatus())
                .setProcessInstanceId(processInstanceId));
    }
    @Override
    public List<Map<String, Object>> getPurchasePlanApproveProcessDefinitionList() {
        // 1. æ ¡éªŒåˆ†ç±»å’Œæµç¨‹å®šä¹‰æ˜¯å¦å­˜åœ¨
        validatePurchasePlanApproveCategoryAndProcessDefinition();
        // 2. èŽ·å–åˆ†ç±»ä¸‹çš„æµç¨‹å®šä¹‰ä¿¡æ¯
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(PURCHASE_PLAN_APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            return Collections.emptyList();
        }
        // 3. éåŽ†èŽ·å–æµç¨‹å®šä¹‰è¯¦æƒ…ï¼Œè¿‡æ»¤æ¿€æ´»çŠ¶æ€ï¼Œä¿ç•™æœ€æ–°ç‰ˆæœ¬
        Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
        for (BpmProcessDefinitionInfoDO info : definitionInfoList) {
            ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId());
            if (pd == null || pd.isSuspended()) {
                continue;
            }
            ProcessDefinition existing = latestVersionMap.get(pd.getKey());
            if (existing == null || pd.getVersion() > existing.getVersion()) {
                latestVersionMap.put(pd.getKey(), pd);
            }
        }
        // 4. è¿”回流程定义列表
        List<Map<String, Object>> result = new ArrayList<>();
        for (ProcessDefinition pd : latestVersionMap.values()) {
            Map<String, Object> item = new HashMap<>();
            item.put("id", pd.getId());
            item.put("key", pd.getKey());
            item.put("name", pd.getName());
            result.add(item);
        }
        return result;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchasePlanAuditStatus(Long id, Integer bpmResult) {
        // 1. æ ¡éªŒå­˜åœ¨
        ErpPurchasePlanDO plan = validatePurchasePlanExists(id);
        if (!ErpPurchasePlanStatusEnum.PROCESS.getStatus().equals(plan.getStatus())) {
            log.warn("[updatePurchasePlanAuditStatus] é‡‡è´­è®¡åˆ’({}) ä¸å¤„于审批中状态", id);
            return;
        }
        // 2. æ ¹æ®å®¡æ‰¹ç»“果更新状态
        Integer newStatus = convertBpmResultToStatus(bpmResult);
        if (newStatus != null) {
            purchasePlanMapper.updateById(new ErpPurchasePlanDO().setId(id).setStatus(newStatus));
        }
        // 3. å®¡æ‰¹é€šè¿‡ä¸”未生成申请,自动生成采购申请
        if (ErpPurchasePlanStatusEnum.APPROVE.getStatus().equals(newStatus)
                && !Boolean.TRUE.equals(plan.getGeneratedFlag())) {
            try {
                List<Long> requestIds = doGeneratePurchaseRequests(plan);
                log.info("[updatePurchasePlanAuditStatus] é‡‡è´­è®¡åˆ’({}) è‡ªåŠ¨ç”Ÿæˆé‡‡è´­ç”³è¯·({})", id, requestIds);
            } catch (Exception e) {
                log.error("[updatePurchasePlanAuditStatus] é‡‡è´­è®¡åˆ’({}) è‡ªåŠ¨ç”Ÿæˆé‡‡è´­ç”³è¯·å¤±è´¥", id, e);
                // ä¸æŠ›å‡ºå¼‚常,避免影响状态更新
            }
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ErpPurchasePlanDO generatePlanFromStockAlert(String name, Long supplierId) {
        // 1. èŽ·å–å¯ç”¨å®‰å…¨åº“å­˜çš„ç‰©æ–™
        List<MesMdItemSafetyStockRespDTO> safetyStockItems = mesMdItemApi.getSafetyStockItemList();
        if (CollUtil.isEmpty(safetyStockItems)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 2. è®¡ç®—缺量,生成计划明细
        List<ErpPurchasePlanSaveReqVO.Item> items = new ArrayList<>();
        for (MesMdItemSafetyStockRespDTO item : safetyStockItems) {
            if (item.getMinStock() == null) {
                continue;
            }
            BigDecimal stock = mesWmStockApi.getStockQuantity(item.getId());
            BigDecimal shortage = item.getMinStock().subtract(stock == null ? BigDecimal.ZERO : stock);
            if (shortage.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            ErpPurchasePlanSaveReqVO.Item planItem = new ErpPurchasePlanSaveReqVO.Item();
            planItem.setProductId(item.getMdmItemId());
            planItem.setProductUnitId(item.getUnitMeasureId());
            planItem.setCount(shortage);
            items.add(planItem);
        }
        if (CollUtil.isEmpty(items)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 3. åˆ›å»ºè‰ç¨¿é‡‡è´­è®¡åˆ’
        ErpPurchasePlanSaveReqVO createReqVO = new ErpPurchasePlanSaveReqVO();
        createReqVO.setName(name != null ? name : "库存预警采购计划");
        createReqVO.setDemandSource(DEMAND_SOURCE_STOCK_ALERT);
        createReqVO.setPlanDate(LocalDateTime.now());
        createReqVO.setSupplierId(supplierId);
        createReqVO.setItems(items);
        Long planId = createPurchasePlan(createReqVO);
        return purchasePlanMapper.selectById(planId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> generatePurchaseRequests(Long id) {
        // 1. æ ¡éªŒå­˜åœ¨ + å®¡æ ¸é€šè¿‡çŠ¶æ€
        ErpPurchasePlanDO plan = validatePurchasePlanExists(id);
        if (!ErpPurchasePlanStatusEnum.APPROVE.getStatus().equals(plan.getStatus())) {
            throw exception(PURCHASE_PLAN_NOT_APPROVE);
        }
        // 2. æ ¡éªŒæ˜¯å¦å·²ç”Ÿæˆç”³è¯·
        if (Boolean.TRUE.equals(plan.getGeneratedFlag())) {
            throw exception(PURCHASE_PLAN_REQUEST_EXISTS);
        }
        // 3. ç”Ÿæˆé‡‡è´­ç”³è¯·
        return doGeneratePurchaseRequests(plan);
    }
    /**
     * æ‰§è¡Œç”Ÿæˆé‡‡è´­ç”³è¯·ï¼ˆä¸€ä¸ªè®¡åˆ’生成一张申请单)
     *
     * @param plan é‡‡è´­è®¡åˆ’
     * @return é‡‡è´­ç”³è¯·ç¼–号列表
     */
    private List<Long> doGeneratePurchaseRequests(ErpPurchasePlanDO plan) {
        // 1. èŽ·å–è®¡åˆ’æ˜Žç»†
        List<ErpPurchasePlanItemDO> planItems = purchasePlanItemMapper.selectListByPlanId(plan.getId());
        if (CollUtil.isEmpty(planItems)) {
            throw exception(PURCHASE_PLAN_ITEM_EMPTY);
        }
        // 2. ç”Ÿæˆç”³è¯·å•号
        String requestNo = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_REQUEST_NO_PREFIX);
        if (purchaseRequestMapper.selectByNo(requestNo) != null) {
            throw exception(PURCHASE_REQUEST_NO_EXISTS);
        }
        // 3. åˆ›å»ºé‡‡è´­ç”³è¯·
        ErpPurchaseRequestDO request = new ErpPurchaseRequestDO();
        request.setNo(requestNo);
        request.setStatus(ErpPurchaseRequestStatusEnum.DRAFT.getStatus());
        request.setRequestUserId(plan.getPlanUserId());
        request.setRequestDeptId(plan.getPlanDeptId());
        request.setRequestTime(LocalDateTime.now());
        request.setSupplierId(plan.getSupplierId());
        request.setTotalCount(planItems.stream()
                .map(ErpPurchasePlanItemDO::getCount).filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        request.setTotalPrice(BigDecimal.ZERO);
        request.setTotalProductPrice(BigDecimal.ZERO);
        request.setTotalTaxPrice(BigDecimal.ZERO);
        request.setRemark(plan.getRemark());
        request.setInStatus(0);
        purchaseRequestMapper.insert(request);
        // 4. åˆ›å»ºé‡‡è´­ç”³è¯·æ˜Žç»†
        List<ErpPurchaseRequestItemDO> requestItems = convertList(planItems, item -> {
            ErpPurchaseRequestItemDO requestItem = new ErpPurchaseRequestItemDO();
            requestItem.setRequestId(request.getId());
            requestItem.setProductId(item.getProductId());
            requestItem.setProductUnitId(item.getProductUnitId());
            requestItem.setCount(item.getCount());
            requestItem.setDemandTime(item.getDemandTime());
            requestItem.setRemark(item.getRemark());
            requestItem.setInCount(BigDecimal.ZERO);
            return requestItem;
        });
        purchaseRequestItemMapper.insertBatch(requestItems);
        // 5. å›žå†™è®¡åˆ’已生成标记
        purchasePlanMapper.updateById(new ErpPurchasePlanDO()
                .setId(plan.getId()).setGeneratedFlag(true));
        return List.of(request.getId());
    }
    private Integer convertBpmResultToStatus(Integer bpmResult) {
        if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
            return ErpPurchasePlanStatusEnum.APPROVE.getStatus();
        }
        if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
            return ErpPurchasePlanStatusEnum.REJECT.getStatus();
        }
        if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
            return ErpPurchasePlanStatusEnum.CANCEL.getStatus();
        }
        return null;
    }
    private void validatePurchasePlanApproveCategoryAndProcessDefinition() {
        List<cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO> categories =
                bpmCategoryService.getCategoryListByCode(Collections.singletonList(PURCHASE_PLAN_APPROVE_CATEGORY_CODE));
        if (CollUtil.isEmpty(categories)) {
            throw exception(PURCHASE_PLAN_BPM_CATEGORY_NOT_EXISTS);
        }
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(PURCHASE_PLAN_APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            throw exception(PURCHASE_PLAN_BPM_PROCESS_DEFINITION_NOT_EXISTS);
        }
    }
    private ErpPurchasePlanDO validatePurchasePlanExists(Long id) {
        ErpPurchasePlanDO plan = purchasePlanMapper.selectById(id);
        if (plan == null) {
            throw exception(PURCHASE_PLAN_NOT_EXISTS);
        }
        return plan;
    }
    private ErpPurchasePlanDO validatePurchasePlanCanModify(Long id) {
        ErpPurchasePlanDO plan = validatePurchasePlanExists(id);
        if (!ErpPurchasePlanStatusEnum.DRAFT.getStatus().equals(plan.getStatus())) {
            throw exception(PURCHASE_PLAN_UPDATE_FAIL_NOT_DRAFT, plan.getNo());
        }
        return plan;
    }
    private String getCurrentUserId() {
        return cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId().toString();
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchasePlanStatusListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.erp.service.purchase.listener;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchasePlanService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
 * é‡‡è´­è®¡åˆ’审批状态监听器
 *
 * ç›‘听 BPM æµç¨‹å®žä¾‹çŠ¶æ€å˜æ›´äº‹ä»¶ï¼Œæ›´æ–°é‡‡è´­è®¡åˆ’çš„å®¡æ‰¹çŠ¶æ€
 *
 * @author xiaoyi
 */
@Component
@Slf4j
public class ErpPurchasePlanStatusListener extends cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener {
    /**
     * é‡‡è´­è®¡åˆ’审批流程定义 Key
     */
    private static final String PROCESS_DEFINITION_KEY = "purchase_plan_approve";
    @Resource
    private ErpPurchasePlanService purchasePlanService;
    @Override
    protected String getProcessDefinitionKey() {
        return PROCESS_DEFINITION_KEY;
    }
    @Override
    protected void onEvent(BpmProcessInstanceStatusEvent event) {
        Long planId;
        try {
            planId = Long.parseLong(event.getBusinessKey());
        } catch (NumberFormatException e) {
            log.warn("[onEvent] businessKey({}) ä¸æ˜¯æœ‰æ•ˆçš„采购计划编号", event.getBusinessKey());
            return;
        }
        log.info("[onEvent] æ”¶åˆ°BPM审批事件,planId={}, status={}", planId, event.getStatus());
        try {
            purchasePlanService.updatePurchasePlanAuditStatus(planId, event.getStatus());
            log.info("[onEvent] é‡‡è´­è®¡åˆ’({}) å®¡æ‰¹çŠ¶æ€æ›´æ–°å®Œæˆ", planId);
        } catch (Exception e) {
            log.error("[onEvent] æ›´æ–°é‡‡è´­è®¡åˆ’审批状态失败,planId={}", planId, e);
        }
    }
}
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java
@@ -26,6 +26,8 @@
    ErrorCode EMPLOYEE_CONTRACT_END_DATE_REQUIRE_FOR_FIXED = new ErrorCode(1_030_000_013, "固定期限合同必须填写结束日期");
    ErrorCode EMPLOYEE_CONTRACT_DATE_INVALID = new ErrorCode(1_030_000_014, "合同日期不合法,开始日期不能晚于结束日期");
    ErrorCode EMPLOYEE_CONTRACT_DATE_OVERLAP = new ErrorCode(1_030_000_015, "该员工在所选时间段已存在未解除/未终止的合同,请先解除/终止后再签订");
    ErrorCode HRM_CERTIFICATE_NOT_EXISTS = new ErrorCode(1_030_000_016, "证书资质不存在");
    ErrorCode HRM_EMPLOYEE_NOT_EXISTS = new ErrorCode(1_030_000_017, "员工不存在");
    // ========== è¯·å‡ç”³è¯· 1-030-001-000 ==========
    ErrorCode LEAVE_APPLICATION_NOT_EXISTS = new ErrorCode(1_030_001_000, "请假申请不存在");
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/HrmCertificateController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package cn.iocoder.yudao.module.hrm.controller.admin.certificate;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificatePageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificateRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificateSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.service.certificate.HrmCertificateService;
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - HRM è¯ä¹¦èµ„è´¨")
@RestController
@RequestMapping("/hrm/certificate")
@Validated
public class HrmCertificateController {
    @Resource
    private HrmCertificateService certificateService;
    @Resource
    private HrmEmployeeService employeeService;
    @PostMapping("/create")
    @Operation(summary = "创建证书资质")
    @PreAuthorize("@ss.hasPermission('hrm:certificate:create')")
    public CommonResult<Long> createCertificate(@Valid @RequestBody HrmCertificateSaveReqVO createReqVO) {
        return success(certificateService.createCertificate(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新证书资质")
    @PreAuthorize("@ss.hasPermission('hrm:certificate:update')")
    public CommonResult<Boolean> updateCertificate(@Valid @RequestBody HrmCertificateSaveReqVO updateReqVO) {
        certificateService.updateCertificate(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除证书资质")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('hrm:certificate:delete')")
    public CommonResult<Boolean> deleteCertificate(@RequestParam("ids") List<Long> ids) {
        certificateService.deleteCertificate(ids);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得证书资质")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('hrm:certificate:query')")
    public CommonResult<HrmCertificateRespVO> getCertificate(@RequestParam("id") Long id) {
        HrmCertificateDO certificate = certificateService.getCertificate(id);
        return success(certificate != null ? buildRespVO(certificate) : null);
    }
    @GetMapping("/page")
    @Operation(summary = "获得证书资质分页")
    @PreAuthorize("@ss.hasPermission('hrm:certificate:query')")
    public CommonResult<PageResult<HrmCertificateRespVO>> getCertificatePage(@Valid HrmCertificatePageReqVO pageReqVO) {
        PageResult<HrmCertificateDO> pageResult = certificateService.getCertificatePage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        Map<Long, HrmEmployeeDO> employeeMap = employeeService.getEmployeeList(
                convertSet(pageResult.getList(), HrmCertificateDO::getEmployeeId)).stream()
                .collect(java.util.stream.Collectors.toMap(HrmEmployeeDO::getId, e -> e, (a, b) -> a));
        return success(BeanUtils.toBean(pageResult, HrmCertificateRespVO.class,
                vo -> buildRespVOItem(vo, employeeMap.get(vo.getEmployeeId()))));
    }
    private HrmCertificateRespVO buildRespVO(HrmCertificateDO certificate) {
        HrmCertificateRespVO vo = BeanUtils.toBean(certificate, HrmCertificateRespVO.class);
        HrmEmployeeDO employee = employeeService.getEmployee(certificate.getEmployeeId());
        buildRespVOItem(vo, employee);
        return vo;
    }
    private void buildRespVOItem(HrmCertificateRespVO vo, HrmEmployeeDO employee) {
        if (employee != null) {
            vo.setEmployeeName(employee.getName());
        }
        vo.setCertTypeName(convertCertTypeName(vo.getCertType()));
        vo.setExpireStatus(calcExpireStatus(vo.getExpireDate()));
    }
    private String convertCertTypeName(Integer certType) {
        return switch (certType == null ? 0 : certType) {
            case 1 -> "特种作业证书";
            case 2 -> "从业资质";
            case 3 -> "技能等级证书";
            case 4 -> "安全培训合格证";
            default -> "其他";
        };
    }
    /**
     * åˆ°æœŸçŠ¶æ€ï¼š0=有效,1=即将到期(30天内),2=已过期
     */
    private Integer calcExpireStatus(LocalDate expireDate) {
        if (expireDate == null) {
            return 0;
        }
        LocalDate today = LocalDate.now();
        if (expireDate.isBefore(today)) {
            return 2;
        }
        if (expireDate.isBefore(today.plusDays(30))) {
            return 1;
        }
        return 0;
    }
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificatePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - HRM è¯ä¹¦èµ„质分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HrmCertificatePageReqVO extends PageParam {
    @Schema(description = "员工编号", example = "1")
    private Long employeeId;
    @Schema(description = "证书类型(1=特种作业证书,2=从业资质,3=技能等级证书,4=安全培训合格证)", example = "1")
    private Integer certType;
    @Schema(description = "证书名称", example = "高压电工特种作业操作证")
    private String certName;
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificateRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - HRM è¯ä¹¦èµ„è´¨ Response VO")
@Data
public class HrmCertificateRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long id;
    @Schema(description = "员工编号", example = "1")
    private Long employeeId;
    @Schema(description = "员工姓名", example = "张三")
    private String employeeName;
    @Schema(description = "证书类型", example = "1")
    private Integer certType;
    @Schema(description = "证书类型名称", example = "特种作业证书")
    private String certTypeName;
    @Schema(description = "证书名称", example = "高压电工特种作业操作证")
    private String certName;
    @Schema(description = "证书编号", example = "T2019-0001")
    private String certNo;
    @Schema(description = "发证机构", example = "国家能源局")
    private String issueOrg;
    @Schema(description = "发证日期")
    private LocalDate issueDate;
    @Schema(description = "有效期至")
    private LocalDate expireDate;
    @Schema(description = "到期状态(0=有效,1=即将到期,2=已过期)")
    private Integer expireStatus;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/certificate/vo/HrmCertificateSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDate;
@Schema(description = "管理后台 - HRM è¯ä¹¦èµ„质新增/修改 Request VO")
@Data
public class HrmCertificateSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1")
    private Long id;
    @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "员工不能为空")
    private Long employeeId;
    @Schema(description = "证书类型(1=特种作业证书,2=从业资质,3=技能等级证书,4=安全培训合格证)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "证书类型不能为空")
    private Integer certType;
    @Schema(description = "证书名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "高压电工特种作业操作证")
    @NotEmpty(message = "证书名称不能为空")
    private String certName;
    @Schema(description = "证书编号", example = "T2019-0001")
    private String certNo;
    @Schema(description = "发证机构", example = "国家能源局")
    private String issueOrg;
    @Schema(description = "发证日期")
    private LocalDate issueDate;
    @Schema(description = "有效期至", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "有效期至不能为空")
    private LocalDate expireDate;
    @Schema(description = "备注", example = "备注")
    private String remark;
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/certificate/HrmCertificateDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.hrm.dal.dataobject.certificate;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDate;
/**
 * HRM è¯ä¹¦èµ„è´¨ DO
 *
 * <p>管理安全培训、技能培训、特种作业证书、从业资质,满足电力行业合规管理要求
 *
 * @author xiaoyi
 */
@TableName("hrm_certificate")
@KeySequence("hrm_certificate_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HrmCertificateDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å‘˜å·¥ç¼–号
     *
     * å…³è” {@link cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO#getId()}
     */
    private Long employeeId;
    /**
     * è¯ä¹¦ç±»åž‹
     *
     * 1: ç‰¹ç§ä½œä¸šè¯ä¹¦
     * 2: ä»Žä¸šèµ„è´¨
     * 3: æŠ€èƒ½ç­‰çº§è¯ä¹¦
     * 4: å®‰å…¨åŸ¹è®­åˆæ ¼è¯
     */
    private Integer certType;
    /**
     * è¯ä¹¦åç§°
     */
    private String certName;
    /**
     * è¯ä¹¦ç¼–号
     */
    private String certNo;
    /**
     * å‘证机构
     */
    private String issueOrg;
    /**
     * å‘证日期
     */
    private LocalDate issueDate;
    /**
     * æœ‰æ•ˆæœŸè‡³
     */
    private LocalDate expireDate;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/certificate/HrmCertificateMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.hrm.dal.mysql.certificate;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificatePageReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO;
import org.apache.ibatis.annotations.Mapper;
/**
 * HRM è¯ä¹¦èµ„è´¨ Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface HrmCertificateMapper extends BaseMapperX<HrmCertificateDO> {
    default PageResult<HrmCertificateDO> selectPage(HrmCertificatePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<HrmCertificateDO>()
                .eqIfPresent(HrmCertificateDO::getEmployeeId, reqVO.getEmployeeId())
                .eqIfPresent(HrmCertificateDO::getCertType, reqVO.getCertType())
                .likeIfPresent(HrmCertificateDO::getCertName, reqVO.getCertName())
                .orderByDesc(HrmCertificateDO::getId));
    }
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/certificate/HrmCertificateService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.hrm.service.certificate;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificatePageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificateSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO;
import java.util.Collection;
/**
 * HRM è¯ä¹¦èµ„è´¨ Service æŽ¥å£
 *
 * @author xiaoyi
 */
public interface HrmCertificateService {
    Long createCertificate(HrmCertificateSaveReqVO createReqVO);
    void updateCertificate(HrmCertificateSaveReqVO updateReqVO);
    void deleteCertificate(Collection<Long> ids);
    HrmCertificateDO getCertificate(Long id);
    PageResult<HrmCertificateDO> getCertificatePage(HrmCertificatePageReqVO pageReqVO);
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/certificate/HrmCertificateServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.hrm.service.certificate;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificatePageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.certificate.vo.HrmCertificateSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.certificate.HrmCertificateDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.certificate.HrmCertificateMapper;
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.HRM_CERTIFICATE_NOT_EXISTS;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.HRM_EMPLOYEE_NOT_EXISTS;
/**
 * HRM è¯ä¹¦èµ„è´¨ Service å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
public class HrmCertificateServiceImpl implements HrmCertificateService {
    @Resource
    private HrmCertificateMapper certificateMapper;
    @Resource
    private HrmEmployeeService employeeService;
    @Override
    public Long createCertificate(HrmCertificateSaveReqVO createReqVO) {
        validateEmployee(createReqVO.getEmployeeId());
        HrmCertificateDO certificate = BeanUtils.toBean(createReqVO, HrmCertificateDO.class);
        certificateMapper.insert(certificate);
        return certificate.getId();
    }
    @Override
    public void updateCertificate(HrmCertificateSaveReqVO updateReqVO) {
        validateCertificateExists(updateReqVO.getId());
        validateEmployee(updateReqVO.getEmployeeId());
        certificateMapper.updateById(BeanUtils.toBean(updateReqVO, HrmCertificateDO.class));
    }
    @Override
    public void deleteCertificate(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        for (Long id : ids) {
            validateCertificateExists(id);
            certificateMapper.deleteById(id);
        }
    }
    @Override
    public HrmCertificateDO getCertificate(Long id) {
        return certificateMapper.selectById(id);
    }
    @Override
    public PageResult<HrmCertificateDO> getCertificatePage(HrmCertificatePageReqVO pageReqVO) {
        return certificateMapper.selectPage(pageReqVO);
    }
    private void validateEmployee(Long employeeId) {
        if (employeeService.getEmployee(employeeId) == null) {
            throw exception(HRM_EMPLOYEE_NOT_EXISTS);
        }
    }
    private void validateCertificateExists(Long id) {
        if (certificateMapper.selectById(id) == null) {
            throw exception(HRM_CERTIFICATE_NOT_EXISTS);
        }
    }
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java
@@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.mes.api.item;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSafetyStockRespDTO;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
@@ -59,4 +61,13 @@
     */
    Set<Long> getSyncedMdmItemIds(Collection<Long> mdmItemIds);
    /**
     * èŽ·å–å¯ç”¨å®‰å…¨åº“å­˜çš„ç‰©æ–™åˆ—è¡¨
     *
     * <p>供 ERP é‡‡è´­è®¡åˆ’做库存预警缺量计算(safeStockFlag=true ä¸” minStock éžç©ºï¼‰ã€‚
     *
     * @return å®‰å…¨åº“存物料列表
     */
    List<MesMdItemSafetyStockRespDTO> getSafetyStockItemList();
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/dto/MesMdItemSafetyStockRespDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.api.item.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
 * MES å®‰å…¨åº“存物料 Response DTO
 *
 * ä¾› ERP é‡‡è´­è®¡åˆ’做库存预警缺量计算
 *
 * @author xiaoyi
 */
@Data
public class MesMdItemSafetyStockRespDTO {
    /**
     * MES ç‰©æ–™ç¼–号
     */
    private Long id;
    /**
     * MDM ç‰©æ–™ç¼–号(ERP/MDM ä¸»æ•°æ® ID)
     */
    private Long mdmItemId;
    /**
     * ç‰©æ–™ç¼–码
     */
    private String code;
    /**
     * ç‰©æ–™åç§°
     */
    private String name;
    /**
     * æœ€ä½Žåº“存量(安全库存下限)
     */
    private BigDecimal minStock;
    /**
     * æœ€é«˜åº“存量
     */
    private BigDecimal maxStock;
    /**
     * è®¡é‡å•位编号
     */
    private Long unitMeasureId;
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/workorder/MesProWorkOrderApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.mes.api.workorder;
/**
 * MES ç”Ÿäº§å·¥å• API æŽ¥å£
 *
 * <p>供 CRM æ¨¡å—调用,实现合同指令「下发」真实跨模块联动:执行指令时确认目标工单、
 * å–消指令时取消目标工单,使 CRM åˆåŒæŒ‡ä»¤ä¸å†åªæ˜¯è®°å½•与状态流转。
 *
 * @author è¶…级管理员
 */
public interface MesProWorkOrderApi {
    /**
     * ç¡®è®¤å·¥å•(草稿 â†’ å·²ç¡®è®¤ï¼‰
     *
     * <p>用于合同指令「下发表」执行时,将指令关联的工单从草稿状态下发为已确认。
     *
     * @param id å·¥å•编号
     */
    void confirmWorkOrder(Long id);
    /**
     * å–消工单(已确认 â†’ å·²å–消)
     *
     * <p>用于合同指令取消时,同步取消已下发的目标工单。
     *
     * @param id å·¥å•编号
     */
    void cancelWorkOrder(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.mes.api.item;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSafetyStockRespDTO;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.batchconfig.MesMdItemBatchConfigSaveReqVO;
@@ -18,6 +20,7 @@
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -135,6 +138,14 @@
        return itemMapper.selectMdmItemIdsByMdmItemIds(mdmItemIds);
    }
    @Override
    public List<MesMdItemSafetyStockRespDTO> getSafetyStockItemList() {
        List<MesMdItemDO> items = itemMapper.selectList(new LambdaQueryWrapperX<MesMdItemDO>()
                .eq(MesMdItemDO::getSafeStockFlag, true)
                .isNotNull(MesMdItemDO::getMinStock));
        return BeanUtils.toBean(items, MesMdItemSafetyStockRespDTO.class);
    }
    /**
     * ä¿å­˜æ‰¹æ¬¡é…ç½®
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/workorder/MesProWorkOrderApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.api.workorder;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
/**
 * MES ç”Ÿäº§å·¥å• API å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesProWorkOrderApiImpl implements MesProWorkOrderApi {
    @Resource
    private MesProWorkOrderService workOrderService;
    @Override
    public void confirmWorkOrder(Long id) {
        workOrderService.confirmWorkOrder(id);
    }
    @Override
    public void cancelWorkOrder(Long id) {
        workOrderService.cancelWorkOrder(id);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachineryRespVO.java
@@ -23,6 +23,10 @@
    @ExcelProperty("设备编码")
    private String code;
    @Schema(description = "数采设备ID(外部数采 tbDeviceId)", example = "tb_001")
    @ExcelProperty("数采设备ID")
    private String tbDeviceId;
    @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CNC åŠ å·¥ä¸­å¿ƒ")
    @ExcelProperty("设备名称")
    private String name;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachinerySaveReqVO.java
@@ -18,6 +18,9 @@
    @NotEmpty(message = "设备编码不能为空")
    private String code;
    @Schema(description = "数采设备ID(外部数采 tbDeviceId)", example = "tb_001")
    private String tbDeviceId;
    @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CNC åŠ å·¥ä¸­å¿ƒ")
    @NotEmpty(message = "设备名称不能为空")
    private String name;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/MesDvStatisticsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,221 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.statistics;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo.MesDvOeeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo.MesDvStatisticsRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.repair.MesDvRepairDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryTypeMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.dv.repair.MesDvRepairMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
import cn.iocoder.yudao.module.mes.enums.dv.MesDvMachineryStatusEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES è®¾å¤‡ç»Ÿè®¡")
@RestController
@RequestMapping("/mes/dv/statistics")
@Validated
public class MesDvStatisticsController {
    @Resource
    private MesDvMachineryMapper machineryMapper;
    @Resource
    private MesDvMachineryTypeMapper machineryTypeMapper;
    @Resource
    private MesDvRepairMapper repairMapper;
    @Resource
    private MesDvMaintenRecordMapper maintenRecordMapper;
    @Resource
    private MesProTaskMapper taskMapper;
    @GetMapping("/summary")
    @Operation(summary = "设备统计汇总(台账/ç»´ä¿®/保养/类型分布)")
    @PreAuthorize("@ss.hasPermission('mes:dv-machinery:query')")
    public CommonResult<MesDvStatisticsRespVO> getSummary() {
        MesDvStatisticsRespVO vo = new MesDvStatisticsRespVO();
        // 1. è®¾å¤‡å°è´¦ç»Ÿè®¡
        Long total = machineryMapper.selectCount();
        Long producing = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>()
                .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.PRODUCING.getStatus()));
        Long stopped = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>()
                .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.STOP.getStatus()));
        Long maintaining = machineryMapper.selectCount(new LambdaQueryWrapperX<MesDvMachineryDO>()
                .eq(MesDvMachineryDO::getStatus, MesDvMachineryStatusEnum.MAINTENANCE.getStatus()));
        vo.setTotalMachinery(total);
        vo.setProducingCount(producing);
        vo.setStoppedCount(stopped);
        vo.setMaintainingCount(maintaining);
        vo.setFaultRate(total != null && total > 0
                ? (double) (stopped + maintaining) / total * 100 : 0D);
        // 2. ç»´ä¿®ç»Ÿè®¡
        vo.setTotalRepair(repairMapper.selectCount());
        vo.setFinishedRepair(repairMapper.selectCount(new LambdaQueryWrapperX<MesDvRepairDO>()
                .eq(MesDvRepairDO::getStatus, cn.iocoder.yudao.module.mes.enums.dv.MesDvRepairStatusEnum.FINISHED.getStatus())));
        vo.setProcessingRepair((vo.getTotalRepair() != null ? vo.getTotalRepair() : 0L)
                - (vo.getFinishedRepair() != null ? vo.getFinishedRepair() : 0L));
        // 3. ä¿å…»ç»Ÿè®¡
        vo.setTotalMainten(maintenRecordMapper.selectCount());
        vo.setFinishedMainten(maintenRecordMapper.selectCount(new LambdaQueryWrapperX<MesDvMaintenRecordDO>()
                .eq(MesDvMaintenRecordDO::getStatus, cn.iocoder.yudao.module.mes.enums.dv.MesDvMaintenRecordStatusEnum.SUBMITTED.getStatus())));
        // 4. æŒ‰è®¾å¤‡ç±»åž‹åˆ†å¸ƒ
        List<MesDvMachineryDO> machineryList = machineryMapper.selectList();
        Map<Long, Long> typeCountMap = new HashMap<>();
        for (MesDvMachineryDO machinery : machineryList) {
            if (machinery.getMachineryTypeId() == null) {
                continue;
            }
            typeCountMap.merge(machinery.getMachineryTypeId(), 1L, Long::sum);
        }
        List<MesDvMachineryTypeDO> typeList = machineryTypeMapper.selectList();
        Map<Long, String> typeNameMap = new HashMap<>();
        for (MesDvMachineryTypeDO type : typeList) {
            typeNameMap.put(type.getId(), type.getName());
        }
        List<MesDvStatisticsRespVO.TypeItem> typeDistribution = new ArrayList<>();
        typeCountMap.forEach((typeId, count) -> {
            MesDvStatisticsRespVO.TypeItem item = new MesDvStatisticsRespVO.TypeItem();
            item.setMachineryTypeId(typeId);
            item.setMachineryTypeName(typeNameMap.get(typeId));
            item.setCount(count);
            typeDistribution.add(item);
        });
        vo.setTypeDistribution(typeDistribution);
        return success(vo);
    }
    @GetMapping("/oee")
    @Operation(summary = "设备 OEE ç»Ÿè®¡ï¼ˆç¨¼åŠ¨çŽ‡Ã—æ€§èƒ½å¼€åŠ¨çŽ‡Ã—è‰¯å“çŽ‡ï¼‰")
    @PreAuthorize("@ss.hasPermission('mes:dv-machinery:query')")
    public CommonResult<MesDvOeeRespVO> getOee(
            @RequestParam(value = "startTime", required = false)
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
            @RequestParam(value = "endTime", required = false)
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
        // 1. æŸ¥è¯¢ç»Ÿè®¡åŒºé—´å†…任务(按完成时间过滤;不传区间则统计全部)
        List<MesProTaskDO> allTasks = taskMapper.selectList(new LambdaQueryWrapperX<MesProTaskDO>()
                .betweenIfPresent(MesProTaskDO::getFinishDate, new LocalDateTime[]{startTime, endTime}));
        // 2. æ•´ä½“ OEE
        MesDvOeeRespVO vo = new MesDvOeeRespVO();
        OeeCalc overall = calcOee(allTasks);
        vo.setOverallOee(overall.oee);
        vo.setOverallAvailability(overall.availability);
        vo.setOverallPerformance(overall.performance);
        vo.setOverallQuality(overall.quality);
        vo.setTaskCount((long) allTasks.size());
        // 3. æŒ‰è®¾å¤‡èšåˆï¼šä»»åŠ¡æŒ‰å·¥ä½œç«™åˆ†ç»„ï¼Œè®¾å¤‡é€šè¿‡ workstationId å…³è”工作站
        Map<Long, List<MesProTaskDO>> tasksByWorkstation = new HashMap<>();
        for (MesProTaskDO task : allTasks) {
            if (task.getWorkstationId() == null) {
                continue;
            }
            tasksByWorkstation.computeIfAbsent(task.getWorkstationId(), k -> new ArrayList<>()).add(task);
        }
        List<MesDvOeeRespVO.MachineryOee> items = new ArrayList<>();
        for (MesDvMachineryDO machinery : machineryMapper.selectList()) {
            if (machinery.getWorkstationId() == null) {
                continue; // æœªç»‘定工作站,无法关联任务
            }
            List<MesProTaskDO> tasks = tasksByWorkstation.getOrDefault(machinery.getWorkstationId(), Collections.emptyList());
            OeeCalc calc = calcOee(tasks);
            MesDvOeeRespVO.MachineryOee item = new MesDvOeeRespVO.MachineryOee();
            item.setMachineryId(machinery.getId());
            item.setMachineryCode(machinery.getCode());
            item.setMachineryName(machinery.getName());
            item.setOee(calc.oee);
            item.setAvailability(calc.availability);
            item.setPerformance(calc.performance);
            item.setQuality(calc.quality);
            item.setTaskCount((long) tasks.size());
            item.setPlannedQuantity(calc.plannedQuantity);
            item.setProducedQuantity(calc.producedQuantity);
            items.add(item);
        }
        vo.setItems(items);
        return success(vo);
    }
    private static OeeCalc calcOee(List<MesProTaskDO> tasks) {
        double totalPlannedHours = 0, totalActualHours = 0;
        BigDecimal totalPlannedQty = BigDecimal.ZERO, totalProducedQty = BigDecimal.ZERO, totalQualifyQty = BigDecimal.ZERO;
        for (MesProTaskDO task : tasks) {
            if (task.getDuration() != null) {
                totalPlannedHours += task.getDuration() * 8D; // 1 å·¥ä½œæ—¥ = 8 å°æ—¶
            }
            if (task.getFirstFeedbackTime() != null && task.getLastFeedbackTime() != null
                    && task.getLastFeedbackTime().isAfter(task.getFirstFeedbackTime())) {
                totalActualHours += Duration.between(task.getFirstFeedbackTime(), task.getLastFeedbackTime()).toMinutes() / 60D;
            }
            if (task.getQuantity() != null) {
                totalPlannedQty = totalPlannedQty.add(task.getQuantity());
            }
            if (task.getProducedQuantity() != null) {
                totalProducedQty = totalProducedQty.add(task.getProducedQuantity());
            }
            if (task.getQualifyQuantity() != null) {
                totalQualifyQty = totalQualifyQty.add(task.getQualifyQuantity());
            }
        }
        OeeCalc calc = new OeeCalc();
        // ç¨¼åŠ¨çŽ‡ = å®žé™…报工时长 / è®¡åˆ’时长(无计划时长时按 1 å¤„理)
        calc.availability = percent(totalPlannedHours > 0 ? Math.min(1D, totalActualHours / totalPlannedHours) : 1D);
        // æ€§èƒ½å¼€åŠ¨çŽ‡ = å®žé™…产量 / è®¡åˆ’产量
        calc.performance = totalPlannedQty.compareTo(BigDecimal.ZERO) > 0
                ? percent(Math.min(1D, totalProducedQty.divide(totalPlannedQty, 4, RoundingMode.HALF_UP).doubleValue())) : 0D;
        // è‰¯å“çއ = åˆæ ¼å“ / å®žé™…产量(无产量时按 1 å¤„理)
        calc.quality = totalProducedQty.compareTo(BigDecimal.ZERO) > 0
                ? percent(Math.min(1D, totalQualifyQty.divide(totalProducedQty, 4, RoundingMode.HALF_UP).doubleValue())) : 100D;
        // OEE(%) = ç¨¼åŠ¨çŽ‡(%) Ã— æ€§èƒ½(%) Ã— è‰¯å“çއ(%) / 10000,保留两位小数
        calc.oee = Math.round(calc.availability * calc.performance * calc.quality / 10000D * 100D) / 100D;
        calc.plannedQuantity = totalPlannedQty;
        calc.producedQuantity = totalProducedQty;
        return calc;
    }
    /** æ¯”例(0-1) è½¬ç™¾åˆ†æ¯”,保留两位小数 */
    private static double percent(double ratio) {
        return Math.round(ratio * 10000D) / 100D;
    }
    private static class OeeCalc {
        private Double availability;
        private Double performance;
        private Double quality;
        private Double oee;
        private BigDecimal plannedQuantity;
        private BigDecimal producedQuantity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/vo/MesDvOeeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - MES è®¾å¤‡ OEE ç»Ÿè®¡ Response VO")
@Data
public class MesDvOeeRespVO {
    @Schema(description = "整体 OEE(%)")
    private Double overallOee;
    @Schema(description = "整体稼动率(%)")
    private Double overallAvailability;
    @Schema(description = "整体性能开动率(%)")
    private Double overallPerformance;
    @Schema(description = "整体良品率(%)")
    private Double overallQuality;
    @Schema(description = "统计任务总数")
    private Long taskCount;
    @Schema(description = "按设备 OEE æ˜Žç»†")
    private List<MachineryOee> items;
    @Schema(description = "设备 OEE æ˜Žç»†é¡¹")
    @Data
    public static class MachineryOee {
        @Schema(description = "设备编号")
        private Long machineryId;
        @Schema(description = "设备编码")
        private String machineryCode;
        @Schema(description = "设备名称")
        private String machineryName;
        @Schema(description = "OEE(%)")
        private Double oee;
        @Schema(description = "稼动率(%)")
        private Double availability;
        @Schema(description = "性能开动率(%)")
        private Double performance;
        @Schema(description = "良品率(%)")
        private Double quality;
        @Schema(description = "关联任务数")
        private Long taskCount;
        @Schema(description = "计划产量")
        private java.math.BigDecimal plannedQuantity;
        @Schema(description = "实际产量")
        private java.math.BigDecimal producedQuantity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/statistics/vo/MesDvStatisticsRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - MES è®¾å¤‡ç»Ÿè®¡ Response VO")
@Data
public class MesDvStatisticsRespVO {
    @Schema(description = "设备总数")
    private Long totalMachinery;
    @Schema(description = "生产中设备数")
    private Long producingCount;
    @Schema(description = "停机设备数")
    private Long stoppedCount;
    @Schema(description = "维护中设备数")
    private Long maintainingCount;
    @Schema(description = "设备故障率(停机+维护 / æ€»æ•° * 100,%)")
    private Double faultRate;
    @Schema(description = "维修单总数")
    private Long totalRepair;
    @Schema(description = "维修中/待验收单数")
    private Long processingRepair;
    @Schema(description = "已确认维修单数")
    private Long finishedRepair;
    @Schema(description = "保养记录总数")
    private Long totalMainten;
    @Schema(description = "已完成保养记录数")
    private Long finishedMainten;
    @Schema(description = "按设备类型分布")
    private List<TypeItem> typeDistribution;
    @Schema(description = "设备类型分布项")
    @Data
    public static class TypeItem {
        @Schema(description = "设备类型编号")
        private Long machineryTypeId;
        @Schema(description = "设备类型名称")
        private String machineryTypeName;
        @Schema(description = "设备数量")
        private Long count;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/MesDvTelemetryController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryDeviceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryLatestReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPullRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry.MesDvTelemetryDO;
import cn.iocoder.yudao.module.mes.service.dv.telemetry.MesDvTelemetryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES è®¾å¤‡æ•°é‡‡")
@RestController
@RequestMapping("/mes/dv/telemetry")
@Validated
public class MesDvTelemetryController {
    @Resource
    private MesDvTelemetryService telemetryService;
    @GetMapping("/pull")
    @Operation(summary = "拉取数采数据并入库(近 5 åˆ†é’Ÿè®¾å¤‡æ•°æ®ï¼‰")
    @PreAuthorize("@ss.hasPermission('mes:dv-telemetry:query')")
    public CommonResult<MesDvTelemetryPullRespVO> pullTelemetry() {
        return success(telemetryService.pullTelemetry());
    }
    @GetMapping("/page")
    @Operation(summary = "分页查询数采遥测数据")
    @PreAuthorize("@ss.hasPermission('mes:dv-telemetry:query')")
    public CommonResult<PageResult<MesDvTelemetryRespVO>> getTelemetryPage(@Valid MesDvTelemetryPageReqVO pageReqVO) {
        PageResult<MesDvTelemetryDO> pageResult = telemetryService.getTelemetryPage(pageReqVO);
        return success(BeanUtils.toBean(pageResult, MesDvTelemetryRespVO.class));
    }
    @GetMapping("/latest")
    @Operation(summary = "查询各设备最新遥测数据(实时监控)")
    @PreAuthorize("@ss.hasPermission('mes:dv-telemetry:query')")
    public CommonResult<List<MesDvTelemetryRespVO>> getLatestTelemetry(MesDvTelemetryLatestReqVO reqVO) {
        List<MesDvTelemetryDO> list = telemetryService.getLatestTelemetry(reqVO);
        return success(BeanUtils.toBean(list, MesDvTelemetryRespVO.class));
    }
    @GetMapping("/device-list")
    @Operation(summary = "查询数采设备下拉列表")
    @PreAuthorize("@ss.hasPermission('mes:dv-telemetry:query')")
    public CommonResult<List<MesDvTelemetryDeviceRespVO>> getDeviceList() {
        return success(telemetryService.getDeviceList());
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryDeviceRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - MES è®¾å¤‡æ•°é‡‡è®¾å¤‡ä¸‹æ‹‰ Response VO")
@Data
public class MesDvTelemetryDeviceRespVO {
    @Schema(description = "数采设备ID")
    private String tbDeviceId;
    @Schema(description = "设备名称")
    private String deviceName;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryLatestReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - MES è®¾å¤‡æ•°é‡‡æœ€æ–°é¥æµ‹ Request VO")
@Data
public class MesDvTelemetryLatestReqVO {
    @Schema(description = "数采设备ID", example = "tb_001")
    private String tbDeviceId;
    @Schema(description = "设备名称", example = "1号变压器")
    private String deviceName;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - MES è®¾å¤‡æ•°é‡‡é¥æµ‹åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesDvTelemetryPageReqVO extends PageParam {
    @Schema(description = "数采设备ID", example = "tb_001")
    private String tbDeviceId;
    @Schema(description = "设备名称", example = "1号变压器")
    private String deviceName;
    @Schema(description = "信号名称", example = "温度")
    private String paramName;
    @Schema(description = "是否异常")
    private Boolean whetherAnomaly;
    @Schema(description = "班次", example = "早班")
    private String shiftName;
    @Schema(description = "遥测数据时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] telemetryDataTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryPullRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾å¤‡æ•°é‡‡æ‹‰å–结果 Response VO")
@Data
public class MesDvTelemetryPullRespVO {
    @Schema(description = "本次拉取新增数据条数")
    private Integer recordCount;
    @Schema(description = "本次拉取涉及设备数")
    private Integer deviceCount;
    @Schema(description = "拉取时间")
    private LocalDateTime pullTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/telemetry/vo/MesDvTelemetryRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾å¤‡æ•°é‡‡é¥æµ‹ Response VO")
@Data
public class MesDvTelemetryRespVO {
    @Schema(description = "编号")
    private Long id;
    @Schema(description = "数采设备ID")
    private String tbDeviceId;
    @Schema(description = "设备名称")
    private String deviceName;
    @Schema(description = "信号名称")
    private String paramName;
    @Schema(description = "设备KEY名称")
    private String paramKeyName;
    @Schema(description = "标准值")
    private String standardValue;
    @Schema(description = "信号值(实时)")
    private String timelyValue;
    @Schema(description = "均值")
    private String avgValue;
    @Schema(description = "最大值")
    private String maxValue;
    @Schema(description = "最小值")
    private String minValue;
    @Schema(description = "是否异常")
    private Boolean whetherAnomaly;
    @Schema(description = "遥测数据时间")
    private LocalDateTime telemetryDataTime;
    @Schema(description = "单据号")
    private String billNo;
    @Schema(description = "班次")
    private String shiftName;
    @Schema(description = "拉取时间")
    private LocalDateTime pullTime;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemRespVO.java
@@ -65,6 +65,11 @@
    @ExcelProperty("最高库存量")
    private BigDecimal maxStock;
    @Schema(description = "电力物资分类", example = "1")
    @ExcelProperty(value = "电力物资分类", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_MATERIAL_CATEGORY)
    private Integer materialCategory;
    @Schema(description = "是否高值物料", example = "false")
    @ExcelProperty("是否高值物料")
    private Boolean highValue;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemSaveReqVO.java
@@ -42,6 +42,9 @@
    @Schema(description = "最高库存量", example = "10000.0000")
    private BigDecimal maxStock;
    @Schema(description = "电力物资分类(字典 mes_material_category)", example = "1")
    private Integer materialCategory;
    @Schema(description = "是否高值物料", example = "false")
    private Boolean highValue;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/MesPdBaseDataController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,386 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdElectricityTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdServicePackageDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdVoltageLevelDO;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdDiscountPolicyService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdElectricityTypeService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdPriceStandardService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdServicePackageService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdTieredPriceService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdVoltageLevelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES äº§å“åŸºç¡€æ•°æ®")
@RestController
@RequestMapping("/mes/pd")
@Validated
public class MesPdBaseDataController {
    @Resource
    private MesPdElectricityTypeService electricityTypeService;
    @Resource
    private MesPdVoltageLevelService voltageLevelService;
    @Resource
    private MesPdServicePackageService servicePackageService;
    @Resource
    private MesPdPriceStandardService priceStandardService;
    @Resource
    private MesPdDiscountPolicyService discountPolicyService;
    @Resource
    private MesPdTieredPriceService tieredPriceService;
    // ==================== ç”¨ç”µç±»åž‹ ====================
    @PostMapping("/electricity-type/create")
    @Operation(summary = "创建用电类型")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createElectricityType(@Valid @RequestBody MesPdElectricityTypeSaveReqVO createReqVO) {
        return success(electricityTypeService.createElectricityType(createReqVO));
    }
    @PutMapping("/electricity-type/update")
    @Operation(summary = "更新用电类型")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updateElectricityType(@Valid @RequestBody MesPdElectricityTypeSaveReqVO updateReqVO) {
        electricityTypeService.updateElectricityType(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/electricity-type/delete")
    @Operation(summary = "删除用电类型")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deleteElectricityType(@RequestParam("id") Long id) {
        electricityTypeService.deleteElectricityType(id);
        return success(true);
    }
    @GetMapping("/electricity-type/get")
    @Operation(summary = "获得用电类型")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdElectricityTypeRespVO> getElectricityType(@RequestParam("id") Long id) {
        return success(electricityTypeService.getElectricityType(id));
    }
    @GetMapping("/electricity-type/page")
    @Operation(summary = "获得用电类型分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdElectricityTypeRespVO>> getElectricityTypePage(@Valid MesPdElectricityTypePageReqVO pageReqVO) {
        return success(electricityTypeService.getElectricityTypePage(pageReqVO));
    }
    @GetMapping("/electricity-type/list")
    @Operation(summary = "获得用电类型列表")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<List<MesPdElectricityTypeDO>> getElectricityTypeList() {
        return success(electricityTypeService.getElectricityTypeList());
    }
    @GetMapping("/electricity-type/export-excel")
    @Operation(summary = "导出用电类型 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportElectricityTypeExcel(@Valid MesPdElectricityTypePageReqVO pageReqVO,
                                           HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "用电类型.xls", "数据", MesPdElectricityTypeRespVO.class,
                electricityTypeService.getElectricityTypePage(pageReqVO).getList());
    }
    // ==================== ç”µåŽ‹ç­‰çº§ ====================
    @PostMapping("/voltage-level/create")
    @Operation(summary = "创建电压等级")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createVoltageLevel(@Valid @RequestBody MesPdVoltageLevelSaveReqVO createReqVO) {
        return success(voltageLevelService.createVoltageLevel(createReqVO));
    }
    @PutMapping("/voltage-level/update")
    @Operation(summary = "更新电压等级")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updateVoltageLevel(@Valid @RequestBody MesPdVoltageLevelSaveReqVO updateReqVO) {
        voltageLevelService.updateVoltageLevel(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/voltage-level/delete")
    @Operation(summary = "删除电压等级")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deleteVoltageLevel(@RequestParam("id") Long id) {
        voltageLevelService.deleteVoltageLevel(id);
        return success(true);
    }
    @GetMapping("/voltage-level/get")
    @Operation(summary = "获得电压等级")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdVoltageLevelRespVO> getVoltageLevel(@RequestParam("id") Long id) {
        return success(voltageLevelService.getVoltageLevel(id));
    }
    @GetMapping("/voltage-level/page")
    @Operation(summary = "获得电压等级分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdVoltageLevelRespVO>> getVoltageLevelPage(@Valid MesPdVoltageLevelPageReqVO pageReqVO) {
        return success(voltageLevelService.getVoltageLevelPage(pageReqVO));
    }
    @GetMapping("/voltage-level/list")
    @Operation(summary = "获得电压等级列表")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<List<MesPdVoltageLevelDO>> getVoltageLevelList() {
        return success(voltageLevelService.getVoltageLevelList());
    }
    @GetMapping("/voltage-level/export-excel")
    @Operation(summary = "导出电压等级 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportVoltageLevelExcel(@Valid MesPdVoltageLevelPageReqVO pageReqVO,
                                        HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "电压等级.xls", "数据", MesPdVoltageLevelRespVO.class,
                voltageLevelService.getVoltageLevelPage(pageReqVO).getList());
    }
    // ==================== æœåС套餐 ====================
    @PostMapping("/service-package/create")
    @Operation(summary = "创建服务套餐")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createServicePackage(@Valid @RequestBody MesPdServicePackageSaveReqVO createReqVO) {
        return success(servicePackageService.createServicePackage(createReqVO));
    }
    @PutMapping("/service-package/update")
    @Operation(summary = "更新服务套餐")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updateServicePackage(@Valid @RequestBody MesPdServicePackageSaveReqVO updateReqVO) {
        servicePackageService.updateServicePackage(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/service-package/delete")
    @Operation(summary = "删除服务套餐")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deleteServicePackage(@RequestParam("id") Long id) {
        servicePackageService.deleteServicePackage(id);
        return success(true);
    }
    @GetMapping("/service-package/get")
    @Operation(summary = "获得服务套餐")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdServicePackageRespVO> getServicePackage(@RequestParam("id") Long id) {
        return success(servicePackageService.getServicePackage(id));
    }
    @GetMapping("/service-package/page")
    @Operation(summary = "获得服务套餐分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdServicePackageRespVO>> getServicePackagePage(@Valid MesPdServicePackagePageReqVO pageReqVO) {
        return success(servicePackageService.getServicePackagePage(pageReqVO));
    }
    @GetMapping("/service-package/list")
    @Operation(summary = "获得服务套餐列表")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<List<MesPdServicePackageDO>> getServicePackageList() {
        return success(servicePackageService.getServicePackageList());
    }
    @GetMapping("/service-package/export-excel")
    @Operation(summary = "导出服务套餐 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportServicePackageExcel(@Valid MesPdServicePackagePageReqVO pageReqVO,
                                          HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "服务套餐.xls", "数据", MesPdServicePackageRespVO.class,
                servicePackageService.getServicePackagePage(pageReqVO).getList());
    }
    // ==================== å®šä»·æ ‡å‡† ====================
    @PostMapping("/price-standard/create")
    @Operation(summary = "创建定价标准")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createPriceStandard(@Valid @RequestBody MesPdPriceStandardSaveReqVO createReqVO) {
        return success(priceStandardService.createPriceStandard(createReqVO));
    }
    @PutMapping("/price-standard/update")
    @Operation(summary = "更新定价标准")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updatePriceStandard(@Valid @RequestBody MesPdPriceStandardSaveReqVO updateReqVO) {
        priceStandardService.updatePriceStandard(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/price-standard/delete")
    @Operation(summary = "删除定价标准")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deletePriceStandard(@RequestParam("id") Long id) {
        priceStandardService.deletePriceStandard(id);
        return success(true);
    }
    @GetMapping("/price-standard/get")
    @Operation(summary = "获得定价标准")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdPriceStandardRespVO> getPriceStandard(@RequestParam("id") Long id) {
        return success(priceStandardService.getPriceStandard(id));
    }
    @GetMapping("/price-standard/page")
    @Operation(summary = "获得定价标准分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdPriceStandardRespVO>> getPriceStandardPage(@Valid MesPdPriceStandardPageReqVO pageReqVO) {
        return success(priceStandardService.getPriceStandardPage(pageReqVO));
    }
    @GetMapping("/price-standard/export-excel")
    @Operation(summary = "导出定价标准 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportPriceStandardExcel(@Valid MesPdPriceStandardPageReqVO pageReqVO,
                                         HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "定价标准.xls", "数据", MesPdPriceStandardRespVO.class,
                priceStandardService.getPriceStandardPage(pageReqVO).getList());
    }
    // ==================== ä¼˜æƒ ç­–ç•¥ ====================
    @PostMapping("/discount-policy/create")
    @Operation(summary = "创建优惠策略")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createDiscountPolicy(@Valid @RequestBody MesPdDiscountPolicySaveReqVO createReqVO) {
        return success(discountPolicyService.createDiscountPolicy(createReqVO));
    }
    @PutMapping("/discount-policy/update")
    @Operation(summary = "更新优惠策略")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updateDiscountPolicy(@Valid @RequestBody MesPdDiscountPolicySaveReqVO updateReqVO) {
        discountPolicyService.updateDiscountPolicy(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/discount-policy/delete")
    @Operation(summary = "删除优惠策略")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deleteDiscountPolicy(@RequestParam("id") Long id) {
        discountPolicyService.deleteDiscountPolicy(id);
        return success(true);
    }
    @GetMapping("/discount-policy/get")
    @Operation(summary = "获得优惠策略")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdDiscountPolicyRespVO> getDiscountPolicy(@RequestParam("id") Long id) {
        return success(discountPolicyService.getDiscountPolicy(id));
    }
    @GetMapping("/discount-policy/page")
    @Operation(summary = "获得优惠策略分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdDiscountPolicyRespVO>> getDiscountPolicyPage(@Valid MesPdDiscountPolicyPageReqVO pageReqVO) {
        return success(discountPolicyService.getDiscountPolicyPage(pageReqVO));
    }
    @GetMapping("/discount-policy/export-excel")
    @Operation(summary = "导出优惠策略 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportDiscountPolicyExcel(@Valid MesPdDiscountPolicyPageReqVO pageReqVO,
                                          HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "优惠策略.xls", "数据", MesPdDiscountPolicyRespVO.class,
                discountPolicyService.getDiscountPolicyPage(pageReqVO).getList());
    }
    // ==================== é˜¶æ¢¯ç”µä»· ====================
    @PostMapping("/tiered-price/create")
    @Operation(summary = "创建阶梯电价")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:create')")
    public CommonResult<Long> createTieredPrice(@Valid @RequestBody MesPdTieredPriceSaveReqVO createReqVO) {
        return success(tieredPriceService.createTieredPrice(createReqVO));
    }
    @PutMapping("/tiered-price/update")
    @Operation(summary = "更新阶梯电价")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:update')")
    public CommonResult<Boolean> updateTieredPrice(@Valid @RequestBody MesPdTieredPriceSaveReqVO updateReqVO) {
        tieredPriceService.updateTieredPrice(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/tiered-price/delete")
    @Operation(summary = "删除阶梯电价")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:delete')")
    public CommonResult<Boolean> deleteTieredPrice(@RequestParam("id") Long id) {
        tieredPriceService.deleteTieredPrice(id);
        return success(true);
    }
    @GetMapping("/tiered-price/get")
    @Operation(summary = "获得阶梯电价")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdTieredPriceRespVO> getTieredPrice(@RequestParam("id") Long id) {
        return success(tieredPriceService.getTieredPrice(id));
    }
    @GetMapping("/tiered-price/page")
    @Operation(summary = "获得阶梯电价分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<PageResult<MesPdTieredPriceRespVO>> getTieredPricePage(@Valid MesPdTieredPricePageReqVO pageReqVO) {
        return success(tieredPriceService.getTieredPricePage(pageReqVO));
    }
    @GetMapping("/tiered-price/export-excel")
    @Operation(summary = "导出阶梯电价 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportTieredPriceExcel(@Valid MesPdTieredPricePageReqVO pageReqVO,
                                       HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        ExcelUtils.write(response, "阶梯电价.xls", "数据", MesPdTieredPriceRespVO.class,
                tieredPriceService.getTieredPricePage(pageReqVO).getList());
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicyPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - ä¼˜æƒ ç­–略分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdDiscountPolicyPageReqVO extends PageParam {
    @Schema(description = "策略名称")
    private String name;
    @Schema(description = "策略类型(1折扣/2立减)", example = "1")
    private Integer policyType;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicyRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - ä¼˜æƒ ç­–ç•¥ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdDiscountPolicyRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "策略名称")
    @ExcelProperty("策略名称")
    private String name;
    @Schema(description = "策略类型(1折扣/2立减)", example = "1")
    @ExcelProperty(value = "策略类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_DISCOUNT_POLICY_TYPE)
    private Integer policyType;
    @Schema(description = "策略类型名称")
    private String policyTypeName;
    @Schema(description = "策略值(折扣:0-1 æ¯”例;立减:金额)")
    @ExcelProperty("策略值")
    private BigDecimal policyValue;
    @Schema(description = "适用对象类型(1服务套餐/2电压等级/3用电类型,可空=全局)")
    @ExcelProperty("适用对象类型")
    private Integer targetType;
    @Schema(description = "适用对象ID")
    @ExcelProperty("适用对象ID")
    private Long targetId;
    @Schema(description = "生效日期")
    @ExcelProperty("生效日期")
    private LocalDateTime startDate;
    @Schema(description = "失效日期")
    @ExcelProperty("失效日期")
    private LocalDateTime endDate;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty("启用状态")
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdDiscountPolicySaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - ä¼˜æƒ ç­–略新增/修改 Request VO")
@Data
public class MesPdDiscountPolicySaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "策略名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "策略名称不能为空")
    private String name;
    @Schema(description = "策略类型(1折扣/2立减)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "策略类型不能为空")
    private Integer policyType;
    @Schema(description = "策略值(折扣:0-1 æ¯”例;立减:金额)")
    private BigDecimal policyValue;
    @Schema(description = "适用对象类型(1服务套餐/2电压等级/3用电类型,可空=全局)")
    private Integer targetType;
    @Schema(description = "适用对象ID")
    private Long targetId;
    @Schema(description = "生效日期")
    private LocalDateTime startDate;
    @Schema(description = "失效日期")
    private LocalDateTime endDate;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - ç”¨ç”µç±»åž‹åŸºç¡€ä¿¡æ¯åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdElectricityTypePageReqVO extends PageParam {
    @Schema(description = "类型名称")
    private String name;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - ç”¨ç”µç±»åž‹åŸºç¡€ä¿¡æ¯ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdElectricityTypeRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "类型编码")
    @ExcelProperty("类型编码")
    private String code;
    @Schema(description = "类型名称")
    @ExcelProperty("类型名称")
    private String name;
    @Schema(description = "类型说明")
    @ExcelProperty("类型说明")
    private String description;
    @Schema(description = "排序")
    @ExcelProperty("排序")
    private Integer sort;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty("启用状态")
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdElectricityTypeSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - ç”¨ç”µç±»åž‹åŸºç¡€ä¿¡æ¯æ–°å¢ž/修改 Request VO")
@Data
public class MesPdElectricityTypeSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "类型编码")
    private String code;
    @Schema(description = "类型名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "类型名称不能为空")
    private String name;
    @Schema(description = "类型说明")
    private String description;
    @Schema(description = "排序")
    private Integer sort;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - å®šä»·æ ‡å‡†åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdPriceStandardPageReqVO extends PageParam {
    @Schema(description = "定价对象类型(1服务套餐/2电压等级/3用电类型)", example = "1")
    private Integer objectType;
    @Schema(description = "定价对象ID")
    private Long objectId;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - å®šä»·æ ‡å‡† Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdPriceStandardRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "定价对象类型(1服务套餐/2电压等级/3用电类型)", example = "1")
    @ExcelProperty(value = "定价对象类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_PRICE_OBJECT_TYPE)
    private Integer objectType;
    @Schema(description = "定价对象类型名称")
    private String objectTypeName;
    @Schema(description = "定价对象ID")
    @ExcelProperty("定价对象ID")
    private Long objectId;
    @Schema(description = "定价对象名称")
    @ExcelProperty("定价对象名称")
    private String objectName;
    @Schema(description = "ä»·æ ¼")
    @ExcelProperty("ä»·æ ¼")
    private BigDecimal price;
    @Schema(description = "计价单位")
    @ExcelProperty("计价单位")
    private String unit;
    @Schema(description = "生效日期")
    @ExcelProperty("生效日期")
    private LocalDateTime effectiveDate;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty("启用状态")
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdPriceStandardSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - å®šä»·æ ‡å‡†æ–°å¢ž/修改 Request VO")
@Data
public class MesPdPriceStandardSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "定价对象类型(1服务套餐/2电压等级/3用电类型)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "定价对象类型不能为空")
    private Integer objectType;
    @Schema(description = "定价对象ID", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "定价对象ID不能为空")
    private Long objectId;
    @Schema(description = "定价对象名称(冗余,便于展示)")
    private String objectName;
    @Schema(description = "ä»·æ ¼")
    private BigDecimal price;
    @Schema(description = "计价单位")
    private String unit;
    @Schema(description = "生效日期")
    private LocalDateTime effectiveDate;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackagePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - æœåŠ¡å¥—é¤åŸºç¡€ä¿¡æ¯åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdServicePackagePageReqVO extends PageParam {
    @Schema(description = "套餐名称")
    private String name;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackageRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - æœåŠ¡å¥—é¤åŸºç¡€ä¿¡æ¯ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdServicePackageRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "套餐编码")
    @ExcelProperty("套餐编码")
    private String code;
    @Schema(description = "套餐名称")
    @ExcelProperty("套餐名称")
    private String name;
    @Schema(description = "套餐说明")
    @ExcelProperty("套餐说明")
    private String description;
    @Schema(description = "套餐价格")
    @ExcelProperty("套餐价格")
    private BigDecimal price;
    @Schema(description = "生效日期")
    @ExcelProperty("生效日期")
    private LocalDateTime effectiveDate;
    @Schema(description = "失效日期")
    @ExcelProperty("失效日期")
    private LocalDateTime expireDate;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty("启用状态")
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdServicePackageSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - æœåŠ¡å¥—é¤åŸºç¡€ä¿¡æ¯æ–°å¢ž/修改 Request VO")
@Data
public class MesPdServicePackageSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "套餐编码(编码规则 PD_PACKAGE_CODE è‡ªåŠ¨ç”Ÿæˆï¼‰")
    private String code;
    @Schema(description = "套餐名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "套餐名称不能为空")
    private String name;
    @Schema(description = "套餐说明")
    private String description;
    @Schema(description = "套餐价格")
    private BigDecimal price;
    @Schema(description = "生效日期")
    private LocalDateTime effectiveDate;
    @Schema(description = "失效日期")
    private LocalDateTime expireDate;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPricePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»·åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdTieredPricePageReqVO extends PageParam {
    @Schema(description = "服务套餐ID(与电压等级二选一)")
    private Long packageId;
    @Schema(description = "电压等级ID(与服务套餐二选一)")
    private Long voltageLevelId;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPriceRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»· Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdTieredPriceRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "服务套餐ID(与电压等级二选一)")
    @ExcelProperty("服务套餐ID")
    private Long packageId;
    @Schema(description = "电压等级ID(与服务套餐二选一)")
    @ExcelProperty("电压等级ID")
    private Long voltageLevelId;
    @Schema(description = "档位下限(含,kWh)")
    @ExcelProperty("档位下限")
    private BigDecimal startValue;
    @Schema(description = "档位上限(不含,kWh,可空=上不封顶)")
    @ExcelProperty("档位上限")
    private BigDecimal endValue;
    @Schema(description = "档位单价")
    @ExcelProperty("档位单价")
    private BigDecimal pricePerUnit;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty(value = "启用状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_TIERED_PRICE_STATUS)
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdTieredPriceSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»·æ–°å¢ž/修改 Request VO")
@Data
public class MesPdTieredPriceSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "服务套餐ID(与电压等级二选一)")
    private Long packageId;
    @Schema(description = "电压等级ID(与服务套餐二选一)")
    private Long voltageLevelId;
    @Schema(description = "档位下限(含,kWh)", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "档位下限不能为空")
    private BigDecimal startValue;
    @Schema(description = "档位上限(不含,kWh,可空=上不封顶)")
    private BigDecimal endValue;
    @Schema(description = "档位单价", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "档位单价不能为空")
    private BigDecimal pricePerUnit;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - ç”µåŽ‹ç­‰çº§åŸºç¡€ä¿¡æ¯åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdVoltageLevelPageReqVO extends PageParam {
    @Schema(description = "等级名称")
    private String name;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - ç”µåŽ‹ç­‰çº§åŸºç¡€ä¿¡æ¯ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdVoltageLevelRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "等级编码")
    @ExcelProperty("等级编码")
    private String code;
    @Schema(description = "等级名称")
    @ExcelProperty("等级名称")
    private String name;
    @Schema(description = "电压数值(kV)")
    @ExcelProperty("电压数值(kV)")
    private BigDecimal voltageValue;
    @Schema(description = "排序")
    @ExcelProperty("排序")
    private Integer sort;
    @Schema(description = "启用状态(1启用/0禁用)", example = "1")
    @ExcelProperty("启用状态")
    private Integer status;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/basedata/vo/MesPdVoltageLevelSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç”µåŽ‹ç­‰çº§åŸºç¡€ä¿¡æ¯æ–°å¢ž/修改 Request VO")
@Data
public class MesPdVoltageLevelSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "等级编码")
    private String code;
    @Schema(description = "等级名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "等级名称不能为空")
    private String name;
    @Schema(description = "电压数值(kV)")
    private BigDecimal voltageValue;
    @Schema(description = "排序")
    private Integer sort;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/ProductCtcController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
import cn.iocoder.yudao.module.mes.service.pd.ctc.ProductCtcService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚æ•°")
@RestController
@RequestMapping("/mes/pd/ctc")
@Validated
public class ProductCtcController {
    @Resource
    private ProductCtcService ctcService;
    @Resource
    private MdmItemApi mdmItemApi;
    @PostMapping("/create")
    @Operation(summary = "创建换位导线产品技术参数")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:create')")
    public CommonResult<Long> createCtc(@Valid @RequestBody ProductCtcSaveReqVO createReqVO) {
        return success(ctcService.createCtc(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新换位导线产品技术参数")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:update')")
    public CommonResult<Boolean> updateCtc(@Valid @RequestBody ProductCtcSaveReqVO updateReqVO) {
        ctcService.updateCtc(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除换位导线产品技术参数")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:delete')")
    public CommonResult<Boolean> deleteCtc(@RequestParam("id") Long id) {
        ctcService.deleteCtc(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得换位导线产品技术参数")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:query')")
    public CommonResult<ProductCtcRespVO> getCtc(@RequestParam("id") Long id) {
        ProductCtcDO ctc = ctcService.getCtc(id);
        if (ctc == null) {
            return success(null);
        }
        return success(buildCtcRespVOList(Collections.singletonList(ctc)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得换位导线产品技术参数分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:query')")
    public CommonResult<PageResult<ProductCtcRespVO>> getCtcPage(@Valid ProductCtcPageReqVO pageReqVO) {
        PageResult<ProductCtcDO> pageResult = ctcService.getCtcPage(pageReqVO);
        return success(new PageResult<>(buildCtcRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出换位导线产品技术参数 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportCtcExcel(@Valid ProductCtcPageReqVO pageReqVO,
                               HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<ProductCtcDO> list = ctcService.getCtcPage(pageReqVO).getList();
        ExcelUtils.write(response, "换位导线产品技术参数.xls", "数据", ProductCtcRespVO.class,
                buildCtcRespVOList(list));
    }
    @PostMapping("/calc-angle")
    @Operation(summary = "绕包角度计算(已知纸带宽度)")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:query')")
    public CommonResult<ProductCtcAngleCalcRespVO> calcAngle(@Valid @RequestBody ProductCtcAngleCalcReqVO reqVO) {
        return success(ctcService.calcAngle(reqVO));
    }
    @PostMapping("/calc-resistance")
    @Operation(summary = "直流电阻计算(按温度换算)")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:query')")
    public CommonResult<ProductCtcResistanceCalcRespVO> calcResistance(@Valid @RequestBody ProductCtcResistanceCalcReqVO reqVO) {
        return success(ctcService.calcResistance(reqVO));
    }
    @PostMapping("/calc-reel")
    @Operation(summary = "盘具容量计算(每盘最大长度)")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc:query')")
    public CommonResult<ProductCtcReelCalcRespVO> calcReelCapacity(@Valid @RequestBody ProductCtcReelCalcReqVO reqVO) {
        return success(ctcService.calcReelCapacity(reqVO));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<ProductCtcRespVO> buildCtcRespVOList(List<ProductCtcDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // æ‰¹é‡èŽ·å–ç‰©æ–™ä¿¡æ¯
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(convertSet(list, ProductCtcDO::getItemId));
        // æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, ProductCtcRespVO.class, vo -> {
            MapUtils.findAndThen(itemMap, vo.getItemId(),
                    item -> {
                        vo.setItemName(item.getName());
                        vo.setItemCode(item.getCode());
                    });
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcAngleCalcReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç»•包角度计算 Request VO")
@Data
public class ProductCtcAngleCalcReqVO {
    @Schema(description = "导体厚度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19.08")
    @NotNull(message = "导体厚度不能为空")
    private BigDecimal conductorThickness;
    @Schema(description = "导体宽度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "12.6")
    @NotNull(message = "导体宽度不能为空")
    private BigDecimal conductorWidth;
    @Schema(description = "圆角半径(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "圆角半径不能为空")
    private BigDecimal cornerRadius;
    @Schema(description = "绝缘厚度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.75")
    @NotNull(message = "绝缘厚度不能为空")
    private BigDecimal insulationThickness;
    @Schema(description = "纸带宽度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "22")
    @NotNull(message = "纸带宽度不能为空")
    private BigDecimal tapeWidth;
    @Schema(description = "重叠宽度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    @NotNull(message = "重叠宽度不能为空")
    private BigDecimal overlapWidth;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcAngleCalcRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç»•包角度计算 Response VO")
@Data
public class ProductCtcAngleCalcRespVO {
    @Schema(description = "导线周长(mm)", example = "64.64")
    private BigDecimal circumference;
    @Schema(description = "绕包角度(°)", example = "18.02")
    private BigDecimal wrappingAngle;
    @Schema(description = "绕包节距(mm)", example = "21.03")
    private BigDecimal wrappingPitch;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚数分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductCtcPageReqVO extends PageParam {
    @Schema(description = "物料ID", example = "2048")
    private Long itemId;
    @Schema(description = "订货规格", example = "QQNB-2/120")
    private String orderSpec;
    @Schema(description = "导体类型", example = "copper")
    private String conductorType;
    @Schema(description = "执行标准代号", example = "GB/T 7095-2008")
    private String standardCode;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcReelCalcReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç›˜å…·å®¹é‡è®¡ç®— Request VO")
@Data
public class ProductCtcReelCalcReqVO {
    @Schema(description = "盘具外径 d1(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1600")
    @NotNull(message = "盘具外径不能为空")
    private BigDecimal reelOuterDiameter;
    @Schema(description = "盘芯内径 d2(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1100")
    @NotNull(message = "盘芯内径不能为空")
    private BigDecimal reelInnerDiameter;
    @Schema(description = "盘宽 l2(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "500")
    @NotNull(message = "盘宽不能为空")
    private BigDecimal reelWidth;
    @Schema(description = "CTC æœ€å¤§é«˜åº¦/厚度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19.08")
    @NotNull(message = "CTC最大高度不能为空")
    private BigDecimal ctcThickness;
    @Schema(description = "CTC æœ€å¤§å®½åº¦ï¼ˆmm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "12.6")
    @NotNull(message = "CTC最大宽度不能为空")
    private BigDecimal ctcWidth;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcReelCalcRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç›˜å…·å®¹é‡è®¡ç®— Response VO")
@Data
public class ProductCtcReelCalcRespVO {
    @Schema(description = "盘具容量/每盘长度(m)", example = "1670.09")
    private BigDecimal capacityMeter;
    @Schema(description = "盘具容量/每盘长度(km)", example = "1.67")
    private BigDecimal capacityKm;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcResistanceCalcReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç›´æµç”µé˜»è®¡ç®— Request VO")
@Data
public class ProductCtcResistanceCalcReqVO {
    @Schema(description = "导体厚度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.27")
    @NotNull(message = "导体厚度不能为空")
    private BigDecimal conductorThickness;
    @Schema(description = "导体宽度(mm)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.7")
    @NotNull(message = "导体宽度不能为空")
    private BigDecimal conductorWidth;
    @Schema(description = "计算温度(℃)", requiredMode = Schema.RequiredMode.REQUIRED, example = "75")
    @NotNull(message = "计算温度不能为空")
    private BigDecimal temperature;
    @Schema(description = "导体材质(copper=铜/aluminum=铝)", requiredMode = Schema.RequiredMode.REQUIRED, example = "copper")
    @NotNull(message = "导体材质不能为空")
    private String material;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcResistanceCalcRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - ç›´æµç”µé˜»è®¡ç®— Response VO")
@Data
public class ProductCtcResistanceCalcRespVO {
    @Schema(description = "截面积(mm²)", example = "7.239")
    private BigDecimal crossSection;
    @Schema(description = "20℃直流电阻(Ω/km)", example = "2.418")
    private BigDecimal resistance20cKm;
    @Schema(description = "指定温度直流电阻(Ω/m)", example = "0.002937")
    private BigDecimal resistanceAtTemp;
    @Schema(description = "指定温度直流电阻(Ω/km)", example = "2.937")
    private BigDecimal resistanceAtTempKm;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚æ•° Response VO")
@Data
@ExcelIgnoreUnannotated
public class ProductCtcRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "物料ID(关联 mdm_item.id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long itemId;
    @Schema(description = "物料名称", example = "换位导线")
    private String itemName;
    @Schema(description = "物料编码", example = "ML0001")
    private String itemCode;
    @Schema(description = "订货规格", example = "QQNB-2/120")
    @ExcelProperty("订货规格")
    private String orderSpec;
    @Schema(description = "裸导线规格", example = "1.27×5.7")
    @ExcelProperty("裸导线规格")
    private String bareConductorSpec;
    @Schema(description = "漆包线规格", example = "0.11×5.7")
    @ExcelProperty("漆包线规格")
    private String enameledSpec;
    @Schema(description = "换位规格", example = "19.08×12.6")
    @ExcelProperty("换位规格")
    private String transpositionSpec;
    @Schema(description = "导体类型(铜/铝)", example = "copper")
    @ExcelProperty(value = "导体类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.PRODUCT_CONDUCTOR_TYPE)
    private String conductorType;
    @Schema(description = "截面积(mm²)", example = "120.00")
    @ExcelProperty("截面积")
    private BigDecimal crossSection;
    @Schema(description = "外径(mm)", example = "19.08")
    @ExcelProperty("外径")
    private BigDecimal outerDiameter;
    @Schema(description = "20℃直流电阻(Ω/km)", example = "0.002937")
    @ExcelProperty("20℃直流电阻")
    private BigDecimal resistance20c;
    @Schema(description = "绕包角度(°)", example = "18.02")
    @ExcelProperty("绕包角度")
    private BigDecimal wrappingAngle;
    @Schema(description = "绝缘厚度(mm)", example = "0.75")
    @ExcelProperty("绝缘厚度")
    private BigDecimal insulationThickness;
    @Schema(description = "额定电压(kV)", example = "110")
    @ExcelProperty("额定电压")
    private String ratedVoltage;
    @Schema(description = "试验电压(kV)", example = "230")
    @ExcelProperty("试验电压")
    private String testVoltage;
    @Schema(description = "最小弯曲半径(mm)", example = "500")
    @ExcelProperty("最小弯曲半径")
    private BigDecimal minBendingRadius;
    @Schema(description = "每公里重量(kg/km)", example = "1200")
    @ExcelProperty("每公里重量")
    private BigDecimal weightPerKm;
    @Schema(description = "执行标准代号", example = "GB/T 7095-2008")
    @ExcelProperty("执行标准")
    private String standardCode;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/ctc/vo/ProductCtcSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚数新增/修改 Request VO")
@Data
public class ProductCtcSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "物料ID(关联 mdm_item.id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "物料ID不能为空")
    private Long itemId;
    @Schema(description = "订货规格", example = "QQNB-2/120")
    private String orderSpec;
    @Schema(description = "裸导线规格", example = "1.27×5.7")
    private String bareConductorSpec;
    @Schema(description = "漆包线规格", example = "0.11×5.7")
    private String enameledSpec;
    @Schema(description = "换位规格", example = "19.08×12.6")
    private String transpositionSpec;
    @Schema(description = "导体类型(铜/铝)", example = "copper")
    private String conductorType;
    @Schema(description = "截面积(mm²)", example = "120.00")
    private BigDecimal crossSection;
    @Schema(description = "外径(mm)", example = "19.08")
    private BigDecimal outerDiameter;
    @Schema(description = "20℃直流电阻(Ω/km)", example = "0.002937")
    private BigDecimal resistance20c;
    @Schema(description = "绕包角度(°)", example = "18.02")
    private BigDecimal wrappingAngle;
    @Schema(description = "绝缘厚度(mm)", example = "0.75")
    private BigDecimal insulationThickness;
    @Schema(description = "额定电压(kV)", example = "110")
    private String ratedVoltage;
    @Schema(description = "试验电压(kV)", example = "230")
    private String testVoltage;
    @Schema(description = "最小弯曲半径(mm)", example = "500")
    private BigDecimal minBendingRadius;
    @Schema(description = "每公里重量(kg/km)", example = "1200")
    private BigDecimal weightPerKm;
    @Schema(description = "执行标准代号", example = "GB/T 7095-2008")
    private String standardCode;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/MesPdPriceController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.price;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcRespVO;
import cn.iocoder.yudao.module.mes.service.pd.price.MesPdPriceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES é˜¶æ¢¯ç”µä»·æµ‹ç®—")
@RestController
@RequestMapping("/mes/pd/price")
@Validated
public class MesPdPriceController {
    @Resource
    private MesPdPriceService priceService;
    @PostMapping("/calc")
    @Operation(summary = "阶梯电价测算:分档累计 + ä¼˜æƒ ç­–ç•¥")
    @PreAuthorize("@ss.hasPermission('mes:pd-basedata:query')")
    public CommonResult<MesPdPriceCalcRespVO> calcPrice(@Valid @RequestBody MesPdPriceCalcReqVO calcReqVO) {
        return success(priceService.calcPrice(calcReqVO));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcDetailRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»·æµ‹ç®—分档明细 Response VO")
@Data
public class MesPdPriceCalcDetailRespVO {
    @Schema(description = "档位下限(含,kWh)")
    private BigDecimal startValue;
    @Schema(description = "档位上限(不含,kWh,可空=上不封顶)")
    private BigDecimal endValue;
    @Schema(description = "落入本档电量(kWh)")
    private BigDecimal tierQuantity;
    @Schema(description = "档位单价")
    private BigDecimal pricePerUnit;
    @Schema(description = "小计")
    private BigDecimal subtotal;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»·æµ‹ç®— Request VO")
@Data
public class MesPdPriceCalcReqVO {
    @Schema(description = "测算类型(1服务套餐/2电压等级)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "测算类型不能为空")
    private Integer calcType;
    @Schema(description = "服务套餐ID(calcType=1 æ—¶å¿…填)")
    private Long packageId;
    @Schema(description = "电压等级ID(calcType=2 æ—¶å¿…填)")
    private Long voltageLevelId;
    @Schema(description = "用电量(kWh)", requiredMode = Schema.RequiredMode.REQUIRED, example = "500")
    @NotNull(message = "用电量不能为空")
    private BigDecimal quantity;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/price/vo/MesPdPriceCalcRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - é˜¶æ¢¯ç”µä»·æµ‹ç®— Response VO")
@Data
public class MesPdPriceCalcRespVO {
    @Schema(description = "服务套餐名称")
    private String packageName;
    @Schema(description = "电压等级名称")
    private String voltageLevelName;
    @Schema(description = "原价(未优惠)")
    private BigDecimal originalPrice;
    @Schema(description = "优惠金额")
    private BigDecimal discountAmount;
    @Schema(description = "优惠后总价")
    private BigDecimal finalPrice;
    @Schema(description = "分档明细")
    private List<MesPdPriceCalcDetailRespVO> details;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/MesPdProductController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.product;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductAuditReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductSaveReqVO;
import cn.iocoder.yudao.module.mes.service.pd.product.MesPdProductService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES äº§å“æ¡£æ¡ˆ")
@RestController
@RequestMapping("/mes/pd/product")
@Validated
public class MesPdProductController {
    @Resource
    private MesPdProductService productService;
    @PostMapping("/create")
    @Operation(summary = "创建产品档案")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:create')")
    public CommonResult<Long> createProduct(@Valid @RequestBody MesPdProductSaveReqVO createReqVO) {
        return success(productService.createProduct(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新产品档案")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:update')")
    public CommonResult<Boolean> updateProduct(@Valid @RequestBody MesPdProductSaveReqVO updateReqVO) {
        productService.updateProduct(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除产品档案")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-product:delete')")
    public CommonResult<Boolean> deleteProduct(@RequestParam("id") Long id) {
        productService.deleteProduct(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得产品档案")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:query')")
    public CommonResult<MesPdProductRespVO> getProduct(@RequestParam("id") Long id) {
        return success(productService.getProduct(id));
    }
    @GetMapping("/page")
    @Operation(summary = "获得产品档案分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:query')")
    public CommonResult<PageResult<MesPdProductRespVO>> getProductPage(@Valid MesPdProductPageReqVO pageReqVO) {
        return success(productService.getProductPage(pageReqVO));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出产品档案 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportProductExcel(@Valid MesPdProductPageReqVO pageReqVO,
                                   HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<MesPdProductRespVO> pageResult = productService.getProductPage(pageReqVO);
        ExcelUtils.write(response, "产品档案.xls", "数据", MesPdProductRespVO.class, pageResult.getList());
    }
    @PutMapping("/submit")
    @Operation(summary = "提交审批(未提交/驳回→审批中)")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-product:submit')")
    public CommonResult<Boolean> submitProduct(@RequestParam("id") Long id) {
        productService.submitProduct(id);
        return success(true);
    }
    @PutMapping("/audit")
    @Operation(summary = "审批产品档案(审批中→通过/驳回)")
    @PreAuthorize("@ss.hasPermission('mes:pd-product:audit')")
    public CommonResult<Boolean> auditProduct(@Valid @RequestBody MesPdProductAuditReqVO auditReqVO) {
        productService.auditProduct(auditReqVO.getId(), auditReqVO.getPass(), auditReqVO.getAuditRemark());
        return success(true);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductAuditReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - äº§å“æ¡£æ¡ˆå®¡æ‰¹ Request VO")
@Data
public class MesPdProductAuditReqVO {
    @Schema(description = "产品档案编号", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "产品档案编号不能为空")
    private Long id;
    @Schema(description = "审批结果:true=通过 / false=驳回", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "审批结果不能为空")
    private Boolean pass;
    @Schema(description = "审批意见")
    private String auditRemark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - äº§å“æ¡£æ¡ˆåˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdProductPageReqVO extends PageParam {
    @Schema(description = "档案类型", example = "1")
    private Integer archiveType;
    @Schema(description = "档案名称", example = "电力抢修服务")
    private String name;
    @Schema(description = "审批状态", example = "0")
    private Integer auditStatus;
    @Schema(description = "启用状态", example = "1")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - äº§å“æ¡£æ¡ˆ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdProductRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "档案类型", example = "1")
    @ExcelProperty(value = "档案类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_PRODUCT_ARCHIVE_TYPE)
    private Integer archiveType;
    @Schema(description = "档案类型名称", example = "电力服务")
    private String archiveTypeName;
    @Schema(description = "档案编码", example = "P_20260820_0001")
    @ExcelProperty("档案编码")
    private String code;
    @Schema(description = "档案名称", example = "电力抢修服务")
    @ExcelProperty("档案名称")
    private String name;
    @Schema(description = "规格型号", example = "24h")
    @ExcelProperty("规格型号")
    private String spec;
    @Schema(description = "单位", example = "次")
    @ExcelProperty("单位")
    private String unit;
    @Schema(description = "基准价格", example = "500.00")
    @ExcelProperty("基准价格")
    private BigDecimal price;
    @Schema(description = "审批状态", example = "0")
    @ExcelProperty(value = "审批状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_PRODUCT_AUDIT_STATUS)
    private Integer auditStatus;
    @Schema(description = "审批状态名称", example = "未提交")
    private String auditStatusName;
    @Schema(description = "审批意见")
    private String auditRemark;
    @Schema(description = "审批时间")
    private LocalDateTime auditTime;
    @Schema(description = "启用状态", example = "1")
    private Integer status;
    @Schema(description = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/product/vo/MesPdProductSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - äº§å“æ¡£æ¡ˆæ–°å¢ž/修改 Request VO")
@Data
public class MesPdProductSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "档案类型(1电力服务/2供电产品/3增值能源产品)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "档案类型不能为空")
    private Integer archiveType;
    @Schema(description = "档案名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "档案名称不能为空")
    private String name;
    @Schema(description = "规格型号")
    private String spec;
    @Schema(description = "单位")
    private String unit;
    @Schema(description = "基准价格")
    private BigDecimal price;
    @Schema(description = "启用状态(1启用/0禁用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "启用状态不能为空")
    private Integer status;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/ProductCtcReelController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.reel;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.reel.ProductCtcReelDO;
import cn.iocoder.yudao.module.mes.service.pd.ctc.ProductCtcService;
import cn.iocoder.yudao.module.mes.service.pd.reel.ProductCtcReelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®—")
@RestController
@RequestMapping("/mes/pd/ctc-reel")
@Validated
public class ProductCtcReelController {
    @Resource
    private ProductCtcReelService reelService;
    @Resource
    private ProductCtcService ctcService;
    @PostMapping("/create")
    @Operation(summary = "创建盘具计算记录")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:create')")
    public CommonResult<Long> createReel(@Valid @RequestBody ProductCtcReelSaveReqVO createReqVO) {
        return success(reelService.createReel(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新盘具计算记录")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:update')")
    public CommonResult<Boolean> updateReel(@Valid @RequestBody ProductCtcReelSaveReqVO updateReqVO) {
        reelService.updateReel(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除盘具计算记录")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:delete')")
    public CommonResult<Boolean> deleteReel(@RequestParam("id") Long id) {
        reelService.deleteReel(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得盘具计算记录")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:query')")
    public CommonResult<ProductCtcReelRespVO> getReel(@RequestParam("id") Long id) {
        ProductCtcReelDO reel = reelService.getReel(id);
        if (reel == null) {
            return success(null);
        }
        return success(buildReelRespVOList(Collections.singletonList(reel)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得盘具计算记录分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:query')")
    public CommonResult<PageResult<ProductCtcReelRespVO>> getReelPage(@Valid ProductCtcReelPageReqVO pageReqVO) {
        PageResult<ProductCtcReelDO> pageResult = reelService.getReelPage(pageReqVO);
        return success(new PageResult<>(buildReelRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/list-by-ctc")
    @Operation(summary = "获得指定换位导线产品的盘具计算记录列表")
    @Parameter(name = "ctcId", description = "换位导线产品ID", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-ctc-reel:query')")
    public CommonResult<List<ProductCtcReelRespVO>> getReelListByCtcId(@RequestParam("ctcId") Long ctcId) {
        List<ProductCtcReelDO> list = reelService.getReelListByCtcId(ctcId);
        return success(buildReelRespVOList(list));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<ProductCtcReelRespVO> buildReelRespVOList(List<ProductCtcReelDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // æ‰¹é‡èŽ·å–æ¢ä½å¯¼çº¿äº§å“è®¢è´§è§„æ ¼
        Map<Long, ProductCtcDO> ctcFullMap = loadCtcMap(convertSet(list, ProductCtcReelDO::getCtcId));
        // æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, ProductCtcReelRespVO.class, vo -> {
            MapUtils.findAndThen(ctcFullMap, vo.getCtcId(),
                    ctc -> vo.setCtcOrderSpec(ctc.getOrderSpec()));
        });
    }
    private Map<Long, ProductCtcDO> loadCtcMap(java.util.Set<Long> ctcIds) {
        if (CollUtil.isEmpty(ctcIds)) {
            return Collections.emptyMap();
        }
        java.util.List<ProductCtcDO> list = ctcIds.stream()
                .map(ctcService::getCtc)
                .filter(java.util.Objects::nonNull)
                .toList();
        return convertMap(list, ProductCtcDO::getId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®—分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductCtcReelPageReqVO extends PageParam {
    @Schema(description = "换位导线产品ID", example = "2048")
    private Long ctcId;
    @Schema(description = "盘具类型", example = "wood")
    private String reelType;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®— Response VO")
@Data
@ExcelIgnoreUnannotated
public class ProductCtcReelRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "换位导线产品ID(关联 product_ctc.id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long ctcId;
    @Schema(description = "换位导线订货规格", example = "QQNB-2/120")
    private String ctcOrderSpec;
    @Schema(description = "盘具类型(木盘/铁盘/钢盘)", example = "wood")
    @ExcelProperty(value = "盘具类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.PRODUCT_REEL_TYPE)
    private String reelType;
    @Schema(description = "盘具外径(mm)", example = "1600")
    @ExcelProperty("盘具外径")
    private BigDecimal reelOuterDiameter;
    @Schema(description = "盘芯内径(mm)", example = "1100")
    @ExcelProperty("盘芯内径")
    private BigDecimal reelInnerDiameter;
    @Schema(description = "盘宽(mm)", example = "500")
    @ExcelProperty("盘宽")
    private BigDecimal reelWidth;
    @Schema(description = "最大容量(km)", example = "1.67")
    @ExcelProperty("最大容量")
    private BigDecimal maxCapacity;
    @Schema(description = "推荐每盘长度(km)", example = "1.50")
    @ExcelProperty("推荐每盘长度")
    private BigDecimal recommendedLength;
    @Schema(description = "净重(kg)", example = "800")
    @ExcelProperty("净重")
    private BigDecimal netWeight;
    @Schema(description = "毛重(kg)", example = "1200")
    @ExcelProperty("毛重")
    private BigDecimal grossWeight;
    @Schema(description = "计算公式(保存计算逻辑)", example = "((d1-60)+(d2+10))*INT(l2/(宽+1))*(d1-d2-70)/(高+0.2)/1274")
    private String calcFormula;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/reel/vo/ProductCtcReelSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®—新增/修改 Request VO")
@Data
public class ProductCtcReelSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "换位导线产品ID(关联 product_ctc.id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    @NotNull(message = "换位导线产品ID不能为空")
    private Long ctcId;
    @Schema(description = "盘具类型(木盘/铁盘/钢盘)", example = "wood")
    private String reelType;
    @Schema(description = "盘具外径(mm)", example = "1600")
    private BigDecimal reelOuterDiameter;
    @Schema(description = "盘芯内径(mm)", example = "1100")
    private BigDecimal reelInnerDiameter;
    @Schema(description = "盘宽(mm)", example = "500")
    private BigDecimal reelWidth;
    @Schema(description = "最大容量(km)", example = "1.67")
    private BigDecimal maxCapacity;
    @Schema(description = "推荐每盘长度(km)", example = "1.50")
    private BigDecimal recommendedLength;
    @Schema(description = "净重(kg)", example = "800")
    private BigDecimal netWeight;
    @Schema(description = "毛重(kg)", example = "1200")
    private BigDecimal grossWeight;
    @Schema(description = "计算公式(保存计算逻辑)", example = "((d1-60)+(d2+10))*INT(l2/(宽+1))*(d1-d2-70)/(高+0.2)/1274")
    private String calcFormula;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/ProductTechConfirmController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.techconfirm.ProductTechConfirmDO;
import cn.iocoder.yudao.module.mes.service.pd.techconfirm.ProductTechConfirmService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - äº§å“æŠ€æœ¯ç¡®è®¤å‡½")
@RestController
@RequestMapping("/mes/pd/tech-confirm")
@Validated
public class ProductTechConfirmController {
    @Resource
    private ProductTechConfirmService techConfirmService;
    @PostMapping("/create")
    @Operation(summary = "创建产品技术确认函")
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:create')")
    public CommonResult<Long> createTechConfirm(@Valid @RequestBody ProductTechConfirmSaveReqVO createReqVO) {
        return success(techConfirmService.createTechConfirm(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新产品技术确认函")
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:update')")
    public CommonResult<Boolean> updateTechConfirm(@Valid @RequestBody ProductTechConfirmSaveReqVO updateReqVO) {
        techConfirmService.updateTechConfirm(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除产品技术确认函")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:delete')")
    public CommonResult<Boolean> deleteTechConfirm(@RequestParam("id") Long id) {
        techConfirmService.deleteTechConfirm(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得产品技术确认函")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:query')")
    public CommonResult<ProductTechConfirmRespVO> getTechConfirm(@RequestParam("id") Long id) {
        ProductTechConfirmDO techConfirm = techConfirmService.getTechConfirm(id);
        if (techConfirm == null) {
            return success(null);
        }
        return success(BeanUtils.toBean(techConfirm, ProductTechConfirmRespVO.class));
    }
    @GetMapping("/page")
    @Operation(summary = "获得产品技术确认函分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:query')")
    public CommonResult<PageResult<ProductTechConfirmRespVO>> getTechConfirmPage(@Valid ProductTechConfirmPageReqVO pageReqVO) {
        PageResult<ProductTechConfirmDO> pageResult = techConfirmService.getTechConfirmPage(pageReqVO);
        return success(new PageResult<>(BeanUtils.toBean(pageResult.getList(), ProductTechConfirmRespVO.class),
                pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出产品技术确认函 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-tech-confirm:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportTechConfirmExcel(@Valid ProductTechConfirmPageReqVO pageReqVO,
                                       HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<ProductTechConfirmDO> list = techConfirmService.getTechConfirmPage(pageReqVO).getList();
        ExcelUtils.write(response, "产品技术确认函.xls", "数据", ProductTechConfirmRespVO.class,
                BeanUtils.toBean(list, ProductTechConfirmRespVO.class));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - äº§å“æŠ€æœ¯ç¡®è®¤å‡½åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ProductTechConfirmPageReqVO extends PageParam {
    @Schema(description = "确认函编号", example = "TC20260801001")
    private String confirmNo;
    @Schema(description = "客户ID", example = "2048")
    private Long customerId;
    @Schema(description = "产品名称", example = "漆包扁铜线")
    private String productName;
    @Schema(description = "模板类型", example = "general")
    private String templateType;
    @Schema(description = "状态", example = "0")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - äº§å“æŠ€æœ¯ç¡®è®¤å‡½ Response VO")
@Data
@ExcelIgnoreUnannotated
public class ProductTechConfirmRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "确认函编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "TC20260801001")
    @ExcelProperty("确认函编号")
    private String confirmNo;
    @Schema(description = "客户ID(关联 crm_customer.id)", example = "2048")
    private Long customerId;
    @Schema(description = "客户名称", example = "江苏华鹏变压器有限公司")
    @ExcelProperty("客户名称")
    private String customerName;
    @Schema(description = "产品名称", example = "漆包扁铜线")
    @ExcelProperty("产品名称")
    private String productName;
    @Schema(description = "规格型号", example = "QQNB-2/120")
    @ExcelProperty("规格型号")
    private String specification;
    @Schema(description = "技术参数(JSON æ ¼å¼ï¼‰", example = "{\"thickness\":\"1.27\"}")
    private String techParams;
    @Schema(description = "模板类型(奥地利/日本/通用)", example = "general")
    @ExcelProperty(value = "模板类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.PRODUCT_TECH_CONFIRM_TEMPLATE)
    private String templateType;
    @Schema(description = "编制人ID", example = "1")
    private Long drafterId;
    @Schema(description = "编制人姓名", example = "胡东")
    @ExcelProperty("编制人")
    private String drafterName;
    @Schema(description = "编制时间")
    @ExcelProperty("编制时间")
    private LocalDateTime draftTime;
    @Schema(description = "审核人ID", example = "2")
    private Long reviewerId;
    @Schema(description = "审核人姓名", example = "李工")
    @ExcelProperty("审核人")
    private String reviewerName;
    @Schema(description = "审核时间")
    @ExcelProperty("审核时间")
    private LocalDateTime reviewTime;
    @Schema(description = "客户确认人", example = "王经理")
    @ExcelProperty("客户确认人")
    private String customerConfirmUser;
    @Schema(description = "客户确认时间")
    @ExcelProperty("客户确认时间")
    private LocalDateTime customerConfirmTime;
    @Schema(description = "状态(0=草稿,1=待审核,2=已审核,3=客户已确认)", example = "0")
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.PRODUCT_TECH_CONFIRM_STATUS)
    private Integer status;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/techconfirm/vo/ProductTechConfirmSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - äº§å“æŠ€æœ¯ç¡®è®¤å‡½æ–°å¢ž/修改 Request VO")
@Data
public class ProductTechConfirmSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "确认函编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "TC20260801001")
    private String confirmNo;
    @Schema(description = "客户ID(关联 crm_customer.id)", example = "2048")
    private Long customerId;
    @Schema(description = "客户名称", example = "江苏华鹏变压器有限公司")
    private String customerName;
    @Schema(description = "产品名称", example = "漆包扁铜线")
    private String productName;
    @Schema(description = "规格型号", example = "QQNB-2/120")
    private String specification;
    @Schema(description = "技术参数(JSON æ ¼å¼ï¼‰", example = "{\"thickness\":\"1.27\"}")
    private String techParams;
    @Schema(description = "模板类型(奥地利/日本/通用)", example = "general")
    private String templateType;
    @Schema(description = "编制人ID", example = "1")
    private Long drafterId;
    @Schema(description = "编制人姓名", example = "胡东")
    private String drafterName;
    @Schema(description = "编制时间")
    private LocalDateTime draftTime;
    @Schema(description = "审核人ID", example = "2")
    private Long reviewerId;
    @Schema(description = "审核人姓名", example = "李工")
    private String reviewerName;
    @Schema(description = "审核时间")
    private LocalDateTime reviewTime;
    @Schema(description = "客户确认人", example = "王经理")
    private String customerConfirmUser;
    @Schema(description = "客户确认时间")
    private LocalDateTime customerConfirmTime;
    @Schema(description = "状态(0=草稿,1=待审核,2=已审核,3=客户已确认)", example = "0")
    private Integer status;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/MesProEmergencyPlanController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.emergencyplan.MesProEmergencyPlanDO;
import cn.iocoder.yudao.module.mes.service.pro.emergencyplan.MesProEmergencyPlanService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES åº”急处置预案")
@RestController
@RequestMapping("/mes/pro/emergency-plan")
@Validated
public class MesProEmergencyPlanController {
    @Resource
    private MesProEmergencyPlanService planService;
    @PostMapping("/create")
    @Operation(summary = "创建应急处置预案")
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:create')")
    public CommonResult<Long> createPlan(@Valid @RequestBody MesProEmergencyPlanSaveReqVO createReqVO) {
        return success(planService.createPlan(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新应急处置预案")
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:update')")
    public CommonResult<Boolean> updatePlan(@Valid @RequestBody MesProEmergencyPlanSaveReqVO updateReqVO) {
        planService.updatePlan(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除应急处置预案")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:delete')")
    public CommonResult<Boolean> deletePlan(@RequestParam("ids") List<Long> ids) {
        planService.deletePlan(ids);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得应急处置预案")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:query')")
    public CommonResult<MesProEmergencyPlanRespVO> getPlan(@RequestParam("id") Long id) {
        MesProEmergencyPlanDO plan = planService.getPlan(id);
        return success(plan != null ? BeanUtils.toBean(plan, MesProEmergencyPlanRespVO.class) : null);
    }
    @GetMapping("/page")
    @Operation(summary = "获得应急处置预案分页")
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:query')")
    public CommonResult<PageResult<MesProEmergencyPlanRespVO>> getPlanPage(@Valid MesProEmergencyPlanPageReqVO pageReqVO) {
        PageResult<MesProEmergencyPlanDO> pageResult = planService.getPlanPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        return success(BeanUtils.toBean(pageResult, MesProEmergencyPlanRespVO.class));
    }
    @PutMapping("/publish")
    @Operation(summary = "发布应急处置预案")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-emergency-plan:update')")
    public CommonResult<Boolean> publishPlan(@RequestParam("id") Long id) {
        planService.publishPlan(id);
        return success(true);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - MES åº”急处置预案分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesProEmergencyPlanPageReqVO extends PageParam {
    @Schema(description = "预案编码", example = "YJ20260818001")
    private String code;
    @Schema(description = "预案名称", example = "变电站停电处置预案")
    private String name;
    @Schema(description = "预案类型(1=停电,2=设备故障,3=火灾,4=自然灾害,5=其他)", example = "1")
    private Integer planType;
    @Schema(description = "状态(0=草稿,10=已发布)", example = "0")
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES åº”急处置预案 Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesProEmergencyPlanRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "预案编码", example = "YJ20260818001")
    @ExcelProperty("预案编码")
    private String code;
    @Schema(description = "预案名称", example = "变电站停电处置预案")
    @ExcelProperty("预案名称")
    private String name;
    @Schema(description = "预案类型", example = "1")
    @ExcelProperty(value = "预案类型", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_PRO_EMERGENCY_PLAN_TYPE)
    private Integer planType;
    @Schema(description = "处置内容", example = "1. ç«‹å³éš”离故障...")
    private String content;
    @Schema(description = "责任单位/部门", example = "运维部")
    @ExcelProperty("责任单位")
    private String orgUnit;
    @Schema(description = "状态(0=草稿,10=已发布)", example = "0")
    private Integer status;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/emergencyplan/vo/MesProEmergencyPlanSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - MES åº”急处置预案新增/修改 Request VO")
@Data
public class MesProEmergencyPlanSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1")
    private Long id;
    @Schema(description = "预案名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "变电站停电处置预案")
    @NotEmpty(message = "预案名称不能为空")
    private String name;
    @Schema(description = "预案类型(1=停电,2=设备故障,3=火灾,4=自然灾害,5=其他)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "预案类型不能为空")
    private Integer planType;
    @Schema(description = "处置内容", example = "1. ç«‹å³éš”离故障...")
    private String content;
    @Schema(description = "责任单位/部门", example = "运维部")
    private String orgUnit;
    @Schema(description = "备注", example = "备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/MesProFeedbackController.java
@@ -17,6 +17,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackQuickCreateRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSummaryRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
@@ -43,6 +44,7 @@
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -61,6 +63,7 @@
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Stream;
@@ -251,6 +254,16 @@
        return success(trace);
    }
    @GetMapping("/summary")
    @Operation(summary = "作业成果汇总统计", description = "按时间范围汇总已批准报工数量/合格数量,按工单类型、日期分组")
    @PreAuthorize("@ss.hasPermission('mes:pro-feedback:query')")
    public CommonResult<MesProFeedbackSummaryRespVO> getFeedbackSummary(
            @RequestParam(value = "beginDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate beginDate,
            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate,
            @RequestParam(value = "workOrderType", required = false) Integer workOrderType) {
        return success(feedbackService.getFeedbackSummary(beginDate, endDate, workOrderType));
    }
    private List<MesProFeedbackRespVO> buildFeedbackRespVOList(List<MesProFeedbackDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackSummaryRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
@Schema(description = "管理后台 - MES ç”Ÿäº§æŠ¥å·¥ä½œä¸šæˆæžœæ±‡æ€» Response VO")
@Data
public class MesProFeedbackSummaryRespVO {
    @Schema(description = "累计报工数量")
    private BigDecimal totalFeedbackQuantity;
    @Schema(description = "累计合格数量")
    private BigDecimal totalQualifiedQuantity;
    @Schema(description = "累计报工单数")
    private Integer totalFeedbackCount;
    @Schema(description = "按工单类型汇总")
    private List<TypeSummaryItem> byType;
    @Schema(description = "按日期汇总")
    private List<DateSummaryItem> byDate;
    @Schema(description = "按工单类型汇总项")
    @Data
    public static class TypeSummaryItem {
        @Schema(description = "工单类型")
        private Integer workOrderType;
        @Schema(description = "工单类型名称")
        private String workOrderTypeName;
        @Schema(description = "报工数量")
        private BigDecimal feedbackQuantity;
        @Schema(description = "合格数量")
        private BigDecimal qualifiedQuantity;
        @Schema(description = "报工单数")
        private Integer feedbackCount;
    }
    @Schema(description = "按日期汇总项")
    @Data
    public static class DateSummaryItem {
        @Schema(description = "日期")
        private LocalDate date;
        @Schema(description = "报工数量")
        private BigDecimal feedbackQuantity;
        @Schema(description = "合格数量")
        private BigDecimal qualifiedQuantity;
        @Schema(description = "报工单数")
        private Integer feedbackCount;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/MesProMaintenancePlanController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,175 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanItemDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.maintenanceplan.MesProMaintenancePlanService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - MES ç”µåŠ›ä½œä¸šè®¡åˆ’")
@RestController
@RequestMapping("/mes/pro/maintenance-plan")
@Validated
public class MesProMaintenancePlanController {
    @Resource
    private MesProMaintenancePlanService planService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private MesMdUnitMeasureService unitMeasureService;
    @PostMapping("/create")
    @Operation(summary = "创建电力作业计划")
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:create')")
    public CommonResult<Long> createPlan(@Valid @RequestBody MesProMaintenancePlanSaveReqVO createReqVO) {
        return success(planService.createPlan(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新电力作业计划")
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:update')")
    public CommonResult<Boolean> updatePlan(@Valid @RequestBody MesProMaintenancePlanSaveReqVO updateReqVO) {
        planService.updatePlan(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除电力作业计划")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:delete')")
    public CommonResult<Boolean> deletePlan(@RequestParam("ids") List<Long> ids) {
        planService.deletePlan(ids);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得电力作业计划")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:query')")
    public CommonResult<MesProMaintenancePlanRespVO> getPlan(@RequestParam("id") Long id) {
        MesProMaintenancePlanDO plan = planService.getPlan(id);
        if (plan == null) {
            return success(null);
        }
        List<MesProMaintenancePlanItemDO> planItemList = planService.getPlanItemListByPlanId(id);
        return success(buildPlanRespVO(plan, planItemList));
    }
    @GetMapping("/page")
    @Operation(summary = "获得电力作业计划分页")
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:query')")
    public CommonResult<PageResult<MesProMaintenancePlanRespVO>> getPlanPage(@Valid MesProMaintenancePlanPageReqVO pageReqVO) {
        PageResult<MesProMaintenancePlanDO> pageResult = planService.getPlanPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        List<MesProMaintenancePlanItemDO> planItemList = planService.getPlanItemListByPlanIds(
                convertSet(pageResult.getList(), MesProMaintenancePlanDO::getId));
        Map<Long, List<MesProMaintenancePlanItemDO>> planItemMap = convertMultiMap(planItemList, MesProMaintenancePlanItemDO::getPlanId);
        return success(BeanUtils.toBean(pageResult, MesProMaintenancePlanRespVO.class,
                plan -> buildPlanRespVOItems(plan, planItemMap.get(plan.getId()))));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出电力作业计划 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportPlanExcel(@Valid MesProMaintenancePlanPageReqVO pageReqVO, HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<MesProMaintenancePlanRespVO> list = planService.getPlanPage(pageReqVO).getList().stream()
                .map(plan -> buildPlanRespVO(plan, planService.getPlanItemListByPlanId(plan.getId())))
                .collect(java.util.stream.Collectors.toList());
        ExcelUtils.write(response, "电力作业计划.xls", "数据", MesProMaintenancePlanRespVO.class, list);
    }
    @PutMapping("/issue")
    @Operation(summary = "下发电力作业计划")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:update')")
    public CommonResult<Boolean> issuePlan(@RequestParam("id") Long id) {
        planService.issuePlan(id);
        return success(true);
    }
    @PutMapping("/finish")
    @Operation(summary = "完成电力作业计划")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:update')")
    public CommonResult<Boolean> finishPlan(@RequestParam("id") Long id) {
        planService.finishPlan(id);
        return success(true);
    }
    @PostMapping("/generate-purchase-plan")
    @Operation(summary = "根据备件需求生成采购计划")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:generate-purchase-plan')")
    public CommonResult<Long> generatePurchasePlan(@RequestParam("id") Long id) {
        return success(planService.generatePurchasePlan(id));
    }
    @PostMapping("/generate-work-order")
    @Operation(summary = "生成运维/发电/检修工单")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-maintenance-plan:generate-work-order')")
    public CommonResult<Long> generateWorkOrder(@RequestParam("id") Long id) {
        return success(planService.generateWorkOrder(id));
    }
    private MesProMaintenancePlanRespVO buildPlanRespVO(MesProMaintenancePlanDO plan,
                                                        List<MesProMaintenancePlanItemDO> planItems) {
        MesProMaintenancePlanRespVO vo = BeanUtils.toBean(plan, MesProMaintenancePlanRespVO.class);
        buildPlanRespVOItems(vo, planItems);
        return vo;
    }
    private void buildPlanRespVOItems(MesProMaintenancePlanRespVO vo, List<MesProMaintenancePlanItemDO> planItems) {
        if (CollUtil.isEmpty(planItems)) {
            vo.setItems(Collections.emptyList());
            return;
        }
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(convertSet(planItems, MesProMaintenancePlanItemDO::getItemId));
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        vo.setItems(BeanUtils.toBean(planItems, MesProMaintenancePlanRespVO.Item.class, item -> {
            MapUtils.findAndThen(itemMap, item.getItemId(), mesItem -> item.setItemCode(mesItem.getCode())
                    .setItemName(mesItem.getName())
                    .setUnitMeasureName(unitMeasureMap.get(mesItem.getUnitMeasureId()) != null
                            ? unitMeasureMap.get(mesItem.getUnitMeasureId()).getName() : null));
        }));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - MES ç”µåŠ›ä½œä¸šè®¡åˆ’åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesProMaintenancePlanPageReqVO extends PageParam {
    @Schema(description = "计划编码", example = "MP20260818001")
    private String code;
    @Schema(description = "计划名称", example = "8月变电运维计划")
    private String name;
    @Schema(description = "计划类型(1=电网运行,2=发电,3=变电运维,4=线路检修)", example = "3")
    private Integer planType;
    @Schema(description = "状态(0=草稿,10=已下发,20=已完成,30=已取消)", example = "0")
    private Integer status;
    @Schema(description = "计划日期")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDateTime[] planDate;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES ç”µåŠ›ä½œä¸šè®¡åˆ’ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesProMaintenancePlanRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "计划编码", example = "MP20260818001")
    @ExcelProperty("计划编码")
    private String code;
    @Schema(description = "计划名称", example = "8月变电运维计划")
    @ExcelProperty("计划名称")
    private String name;
    @Schema(description = "计划类型", example = "3")
    @ExcelProperty(value = "计划类型", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_PRO_MAINTENANCE_PLAN_TYPE)
    private Integer planType;
    @Schema(description = "站点", example = "220kV城东变电站")
    @ExcelProperty("站点")
    private String station;
    @Schema(description = "线路", example = "城东-工业线路")
    @ExcelProperty("线路")
    private String line;
    @Schema(description = "班组编号", example = "1")
    private Long teamId;
    @Schema(description = "班组名称", example = "运维一班")
    @ExcelProperty("班组")
    private String teamName;
    @Schema(description = "计划日期")
    @ExcelProperty("计划日期")
    private LocalDateTime planDate;
    @Schema(description = "状态(0=草稿,10=已下发,20=已完成,30=已取消)", example = "0")
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_PRO_MAINTENANCE_PLAN_STATUS)
    private Integer status;
    @Schema(description = "生成的工单编号", example = "1")
    private Long workOrderId;
    @Schema(description = "生成的工单编码", example = "WO-001")
    @ExcelProperty("生成的工单")
    private String workOrderCode;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    @Schema(description = "备件/物资需求明细")
    private List<Item> items;
    @Schema(description = "备件/物资需求明细项")
    @Data
    public static class Item {
        @Schema(description = "编号", example = "1")
        private Long id;
        @Schema(description = "物料/备件编号", example = "1")
        private Long itemId;
        @Schema(description = "物料名称", example = "绝缘子")
        private String itemName;
        @Schema(description = "物料编码", example = "ITEM001")
        private String itemCode;
        @Schema(description = "计量单位名称", example = "个")
        private String unitMeasureName;
        @Schema(description = "需求数量", example = "10")
        private BigDecimal quantity;
        @Schema(description = "备注", example = "备注")
        private String remark;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/maintenanceplan/vo/MesProMaintenancePlanSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES ç”µåŠ›ä½œä¸šè®¡åˆ’æ–°å¢ž/修改 Request VO")
@Data
public class MesProMaintenancePlanSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1")
    private Long id;
    @Schema(description = "计划名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "8月变电运维计划")
    @NotEmpty(message = "计划名称不能为空")
    private String name;
    @Schema(description = "计划类型(1=电网运行,2=发电,3=变电运维,4=线路检修)", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
    @NotNull(message = "计划类型不能为空")
    private Integer planType;
    @Schema(description = "站点", example = "220kV城东变电站")
    private String station;
    @Schema(description = "线路", example = "城东-工业线路")
    private String line;
    @Schema(description = "班组编号", example = "1")
    private Long teamId;
    @Schema(description = "班组名称", example = "运维一班")
    private String teamName;
    @Schema(description = "计划日期", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "计划日期不能为空")
    private LocalDateTime planDate;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "备件/物资需求明细", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "备件/物资需求明细不能为空")
    @Valid
    private List<Item> items;
    @Schema(description = "备件/物资需求明细项")
    @Data
    public static class Item {
        @Schema(description = "编号(修改时必填)", example = "1")
        private Long id;
        @Schema(description = "物料/备件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
        @NotNull(message = "物料/备件不能为空")
        private Long itemId;
        @Schema(description = "需求数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
        @NotNull(message = "需求数量不能为空")
        private BigDecimal quantity;
        @Schema(description = "备注", example = "备注")
        private String remark;
        @Schema(description = "物料名称(前端展示用,后端忽略)", example = "绝缘子")
        private String itemName;
        @Schema(description = "物料编码(前端展示用,后端忽略)", example = "ITEM001")
        private String itemCode;
        @Schema(description = "计量单位名称(前端展示用,后端忽略)", example = "个")
        private String unitMeasureName;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessRespVO.java
@@ -25,6 +25,11 @@
    @Schema(description = "工序名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "下料工序")
    private String name;
    @ExcelProperty(value = "电力工艺分类", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_PRO_PROCESS_TYPE)
    @Schema(description = "电力工艺分类", example = "2")
    private Integer type;
    @ExcelProperty("工艺要求")
    @Schema(description = "工艺要求", example = "按照图纸尺寸进行切割")
    private String attention;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/process/vo/MesProProcessSaveReqVO.java
@@ -20,6 +20,9 @@
    @NotBlank(message = "工序名称不能为空")
    private String name;
    @Schema(description = "电力工艺分类(字典 mes_pro_process_type)", example = "2")
    private Integer type;
    @Schema(description = "工艺要求", example = "按照图纸尺寸进行切割")
    private String attention;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
@@ -47,6 +47,12 @@
    @Schema(description = "工序名称", example = "注塑")
    private String processName;
    @Schema(description = "班组编号", example = "1")
    private Long teamId;
    @Schema(description = "班组名称", example = "运维一班")
    private String teamName;
    @Schema(description = "产品物料编号", example = "100")
    private Long itemId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskSaveReqVO.java
@@ -28,6 +28,12 @@
    @Schema(description = "工序编号", example = "1")
    private Long processId; // ç¼–辑时,可不传递
    @Schema(description = "班组编号", example = "1")
    private Long teamId;
    @Schema(description = "班组名称", example = "运维一班")
    private String teamName;
    @Schema(description = "产品物料编号", example = "100")
    private Long itemId; // ç¼–辑时,可不传递
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderRespVO.java
@@ -92,6 +92,13 @@
    @ExcelProperty("客户名称")
    private String clientName;
    @Schema(description = "关联营销订单(ERP销售订单)编号", example = "500")
    private Long saleOrderId;
    @Schema(description = "关联营销订单(ERP销售订单)单号", example = "SO-001")
    @ExcelProperty("营销订单号")
    private String saleOrderNo;
    @Schema(description = "供应商编号", example = "400")
    private Long vendorId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/MesProWorkOrderSaveReqVO.java
@@ -34,8 +34,7 @@
    @Schema(description = "来源单据编号", example = "SO-001")
    private String orderSourceCode;
    @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
    @NotNull(message = "产品不能为空")
    @Schema(description = "产品编号(运维/发电/检修类工单可不填)", example = "100")
    private Long productId;
    @Schema(description = "生产数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
@@ -54,6 +53,12 @@
    @Schema(description = "客户编号", example = "300")
    private Long clientId;
    @Schema(description = "关联营销订单(ERP销售订单)编号", example = "500")
    private Long saleOrderId;
    @Schema(description = "关联营销订单(ERP销售订单)单号", example = "SO-001")
    private String saleOrderNo;
    @Schema(description = "供应商编号", example = "400")
    private Long vendorId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/MesQcOutsourceTestController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest.MesQcOutsourceTestDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.qc.outsourcetest.MesQcOutsourceTestService;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - MES å§”外检测/试验单")
@RestController
@RequestMapping("/mes/qc/outsource-test")
@Validated
public class MesQcOutsourceTestController {
    @Resource
    private MesQcOutsourceTestService testService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private StorageAttachmentApi storageAttachmentApi;
    @PostMapping("/create")
    @Operation(summary = "创建委外检测/试验单")
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:create')")
    public CommonResult<Long> createTest(@Valid @RequestBody MesQcOutsourceTestSaveReqVO createReqVO) {
        return success(testService.createTest(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新委外检测/试验单")
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:update')")
    public CommonResult<Boolean> updateTest(@Valid @RequestBody MesQcOutsourceTestSaveReqVO updateReqVO) {
        testService.updateTest(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除委外检测/试验单")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:delete')")
    public CommonResult<Boolean> deleteTest(@RequestParam("ids") List<Long> ids) {
        testService.deleteTest(ids);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得委外检测/试验单")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:query')")
    public CommonResult<MesQcOutsourceTestRespVO> getTest(@RequestParam("id") Long id) {
        MesQcOutsourceTestDO test = testService.getTest(id);
        if (test == null) {
            return success(null);
        }
        return success(buildRespVO(test));
    }
    @GetMapping("/page")
    @Operation(summary = "获得委外检测/试验单分页")
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:query')")
    public CommonResult<PageResult<MesQcOutsourceTestRespVO>> getTestPage(@Valid MesQcOutsourceTestPageReqVO pageReqVO) {
        PageResult<MesQcOutsourceTestDO> pageResult = testService.getTestPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        return success(new PageResult<>(BeanUtils.toBean(pageResult.getList(), MesQcOutsourceTestRespVO.class,
                vo -> fillItemName(vo, vo.getItemId())), pageResult.getTotal()));
    }
    @PutMapping("/submit")
    @Operation(summary = "提交委外检测/试验单")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:update')")
    public CommonResult<Boolean> submitTest(@RequestParam("id") Long id) {
        testService.submitTest(id);
        return success(true);
    }
    @PutMapping("/finish")
    @Operation(summary = "完成委外检测/试验单")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:qc-outsource-test:update')")
    public CommonResult<Boolean> finishTest(@RequestParam("id") Long id) {
        testService.finishTest(id);
        return success(true);
    }
    private MesQcOutsourceTestRespVO buildRespVO(MesQcOutsourceTestDO test) {
        MesQcOutsourceTestRespVO vo = BeanUtils.toBean(test, MesQcOutsourceTestRespVO.class);
        fillItemName(vo, test.getItemId());
        // é™„ä»¶
        vo.setAttachmentList(BeanUtils.toBean(storageAttachmentApi.listAttachments(
                "mes_qc_outsource_test", test.getId()), MesQcOutsourceTestRespVO.Attachment.class));
        return vo;
    }
    private void fillItemName(MesQcOutsourceTestRespVO vo, Long itemId) {
        if (itemId == null) {
            return;
        }
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(Collections.singleton(itemId));
        MapUtils.findAndThen(itemMap, itemId, item -> vo.setItemName(item.getName()));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - MES å§”外检测/试验单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesQcOutsourceTestPageReqVO extends PageParam {
    @Schema(description = "单据编码", example = "WT20260818001")
    private String code;
    @Schema(description = "单据名称", example = "变压器第三方检测")
    private String name;
    @Schema(description = "检测类型(1=第三方检测,2=委外试验)", example = "1")
    private Integer testType;
    @Schema(description = "检测结果(1=合格,2=不合格)", example = "1")
    private Integer result;
    @Schema(description = "状态(0=草稿,10=已提交,20=已完成)", example = "0")
    private Integer status;
    @Schema(description = "检测日期")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDateTime[] testDate;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES å§”外检测/试验单 Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesQcOutsourceTestRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "单据编码", example = "WT20260818001")
    @ExcelProperty("单据编码")
    private String code;
    @Schema(description = "单据名称", example = "变压器第三方检测")
    @ExcelProperty("单据名称")
    private String name;
    @Schema(description = "检测类型", example = "1")
    @ExcelProperty(value = "检测类型", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_QC_OUTSOURCE_TEST_TYPE)
    private Integer testType;
    @Schema(description = "检测对象(物料/设备编号)", example = "1")
    private Long itemId;
    @Schema(description = "检测对象名称", example = "变压器A")
    @ExcelProperty("检测对象")
    private String itemName;
    @Schema(description = "检测机构", example = "国家变压器质检中心")
    @ExcelProperty("检测机构")
    private String testOrg;
    @Schema(description = "检测日期")
    @ExcelProperty("检测日期")
    private LocalDateTime testDate;
    @Schema(description = "检测结果", example = "1")
    @ExcelProperty(value = "检测结果", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_QC_OUTSOURCE_TEST_RESULT)
    private Integer result;
    @Schema(description = "检测报告编号", example = "BG-2026-0001")
    @ExcelProperty("报告编号")
    private String reportNo;
    @Schema(description = "状态(0=草稿,10=已提交,20=已完成)", example = "0")
    private Integer status;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    @Schema(description = "附件列表(检测报告/资质文件)")
    private List<Attachment> attachmentList;
    @Schema(description = "附件项")
    @Data
    public static class Attachment {
        private Long id;
        private String name;
        private String url;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/outsourcetest/vo/MesQcOutsourceTestSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES å§”外检测/试验单新增/修改 Request VO")
@Data
public class MesQcOutsourceTestSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1")
    private Long id;
    @Schema(description = "单据名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "变压器第三方检测")
    @NotEmpty(message = "单据名称不能为空")
    private String name;
    @Schema(description = "检测类型(1=第三方检测,2=委外试验)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "检测类型不能为空")
    private Integer testType;
    @Schema(description = "检测对象(物料/设备编号)", example = "1")
    private Long itemId;
    @Schema(description = "检测机构", example = "国家变压器质检中心")
    private String testOrg;
    @Schema(description = "检测日期", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "检测日期不能为空")
    private LocalDateTime testDate;
    @Schema(description = "检测结果(1=合格,2=不合格)", example = "1")
    private Integer result;
    @Schema(description = "检测报告编号", example = "BG-2026-0001")
    private String reportNo;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "附件(检测报告/资质文件)blobId åˆ—表", example = "[1, 2, 3]")
    private List<Long> blobIds;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueRespVO.java
@@ -51,6 +51,9 @@
    @Schema(description = "需求时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime requiredTime;
    @Schema(description = "单据类型(1=正常领料,2=紧急补料)", example = "1")
    private Integer issueType;
    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
    private Integer status;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java
@@ -32,6 +32,9 @@
    @Schema(description = "工作站 ID", example = "1")
    private Long workstationId;
    @Schema(description = "单据类型(1=正常领料,2=紧急补料)", example = "1")
    private Integer issueType;
    @Schema(description = "需求时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotNull(message = "需求时间不能为空")
    private LocalDateTime requiredTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/MesWmStockTakingTaskLineController.java
@@ -4,6 +4,9 @@
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineSaveReqVO;
@@ -25,11 +28,14 @@
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -112,6 +118,26 @@
        return success(buildStockTakingTaskLineRespVOList(list));
    }
    @PostMapping("/import-excel")
    @Operation(summary = "导入盘点实盘数量", description = "按 ç‰©æ–™ç¼–码 + æ‰¹æ¬¡å· åŒ¹é…ç›˜ç‚¹è¡Œï¼Œå†™å…¥å®žç›˜æ•°é‡")
    @Parameter(name = "file", description = "Excel æ–‡ä»¶", required = true)
    @PreAuthorize("@ss.hasPermission('mes:wm-stock-taking-task:update')")
    public CommonResult<MesWmStockTakingTaskLineImportRespVO> importStockTakingQuantity(
            @RequestParam("taskId") Long taskId,
            @RequestParam("file") MultipartFile file) throws IOException {
        List<MesWmStockTakingTaskLineImportExcelVO> list =
                ExcelUtils.read(file, MesWmStockTakingTaskLineImportExcelVO.class);
        return success(stockTakingTaskLineService.importTakingQuantity(taskId, list));
    }
    @GetMapping("/get-import-template")
    @Operation(summary = "下载盘点导入模板")
    @PreAuthorize("@ss.hasPermission('mes:wm-stock-taking-task:query')")
    public void getImportTemplate(HttpServletResponse response) throws IOException {
        ExcelUtils.write(response, "盘点实盘数导入模板.xls", "实盘数",
                MesWmStockTakingTaskLineImportExcelVO.class, Collections.emptyList());
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesWmStockTakingTaskLineRespVO> buildStockTakingTaskLineRespVOList(List<MesWmStockTakingTaskLineDO> list) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/vo/line/MesWmStockTakingTaskLineImportExcelVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
 * MES ç›˜ç‚¹ä»»åŠ¡è¡Œ Excel å¯¼å…¥ VO
 *
 * <p>导入实盘数量,按 ç‰©æ–™ç¼–码 + æ‰¹æ¬¡å· åŒ¹é…ç›˜ç‚¹ä»»åŠ¡è¡Œ
 *
 * @author xiaoyi
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MesWmStockTakingTaskLineImportExcelVO {
    @ExcelProperty("物料编码")
    private String itemCode;
    @ExcelProperty("批次号")
    private String batchCode;
    @ExcelProperty("实盘数量")
    private BigDecimal takingQuantity;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/stocktaking/task/vo/line/MesWmStockTakingTaskLineImportRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
 * MES ç›˜ç‚¹ä»»åŠ¡è¡Œ Excel å¯¼å…¥ç»“æžœ VO
 *
 * @author xiaoyi
 */
@Schema(description = "管理后台 - MES ç›˜ç‚¹ä»»åŠ¡è¡Œå¯¼å…¥ç»“æžœ Response VO")
@Data
public class MesWmStockTakingTaskLineImportRespVO {
    @Schema(description = "导入成功条数")
    private Integer successCount;
    @Schema(description = "导入失败条数")
    private Integer failureCount;
    @Schema(description = "失败原因(与导入行顺序对应)")
    private List<String> failureMessages;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java
@@ -2,6 +2,8 @@
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -25,6 +27,11 @@
    @ExcelProperty("仓库名称")
    private String name;
    @Schema(description = "仓库类型", example = "1")
    @ExcelProperty(value = "仓库类型", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_WM_WAREHOUSE_TYPE)
    private Integer type;
    @Schema(description = "仓库地址", example = "A åŒºä¸€å·æ¥¼")
    @ExcelProperty("仓库地址")
    private String address;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java
@@ -22,6 +22,9 @@
    @NotEmpty(message = "仓库名称不能为空")
    private String name;
    @Schema(description = "仓库类型(字典 mes_wm_warehouse_type)", example = "1")
    private Integer type;
    @Schema(description = "仓库地址", example = "A åŒºä¸€å·æ¥¼")
    private String address;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/area/MesWmWarehouseAreaRespVO.java
@@ -2,6 +2,8 @@
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -25,6 +27,11 @@
    @ExcelProperty("库位名称")
    private String name;
    @Schema(description = "库区类型", example = "1")
    @ExcelProperty(value = "库区类型", converter = DictConvert.class)
    @DictFormat(cn.iocoder.yudao.module.mes.enums.DictTypeConstants.MES_WM_WAREHOUSE_TYPE)
    private Integer type;
    @Schema(description = "库区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long locationId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/area/MesWmWarehouseAreaSaveReqVO.java
@@ -22,6 +22,9 @@
    @NotEmpty(message = "库位名称不能为空")
    private String name;
    @Schema(description = "库区类型(字典 mes_wm_warehouse_type)", example = "1")
    private Integer type;
    @Schema(description = "库区编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "库区不能为空")
    private Long locationId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/dv/machinery/MesDvMachineryDO.java
@@ -34,6 +34,10 @@
     */
    private String code;
    /**
     * æ•°é‡‡è®¾å¤‡ID(外部数采 tbDeviceId,用于关联数采遥测数据)
     */
    private String tbDeviceId;
    /**
     * è®¾å¤‡åç§°
     */
    private String name;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/dv/telemetry/MesDvTelemetryDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES è®¾å¤‡æ•°é‡‡é¥æµ‹æ•°æ® DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_dv_telemetry")
@KeySequence("mes_dv_telemetry_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesDvTelemetryDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æ•°é‡‡è®¾å¤‡ID(外部 tbDeviceId)
     */
    private String tbDeviceId;
    /**
     * è®¾å¤‡åç§°
     */
    private String deviceName;
    /**
     * ä¿¡å·åç§°
     */
    private String paramName;
    /**
     * è®¾å¤‡KEY名称
     */
    private String paramKeyName;
    /**
     * æ ‡å‡†å€¼
     */
    private String standardValue;
    /**
     * ä¿¡å·å€¼ï¼ˆå®žæ—¶ï¼‰
     */
    private String timelyValue;
    /**
     * å‡å€¼
     */
    private String avgValue;
    /**
     * æœ€å¤§å€¼
     */
    private String maxValue;
    /**
     * æœ€å°å€¼
     */
    private String minValue;
    /**
     * æ˜¯å¦å¼‚常
     */
    private Boolean whetherAnomaly;
    /**
     * é¥æµ‹æ•°æ®æ—¶é—´
     */
    private LocalDateTime telemetryDataTime;
    /**
     * å•据号
     */
    private String billNo;
    /**
     * ç­æ¬¡
     *
     * å­—å…¸ {@link cn.iocoder.yudao.module.mes.enums.DictTypeConstants#MES_CAL_SHIFT_TYPE}
     */
    private String shiftName;
    /**
     * æ‹‰å–时间(本次入库时间)
     */
    private LocalDateTime pullTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemDO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.md.item;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -78,6 +79,12 @@
     */
    private BigDecimal maxStock;
    /**
     * ç”µåŠ›ç‰©èµ„åˆ†ç±»
     *
     * å­—å…¸ {@link DictTypeConstants#MES_MATERIAL_CATEGORY}
     */
    private Integer materialCategory;
    /**
     * æ˜¯å¦é«˜å€¼ç‰©æ–™
     */
    private Boolean highValue;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdDiscountPolicyDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MES äº§å“è®¾è®¡-优惠策略 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_discount_policy")
@KeySequence("mes_pd_discount_policy_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdDiscountPolicyDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç­–略名称
     */
    private String name;
    /**
     * ç­–略类型(1折扣/2立减)
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_DISCOUNT_POLICY_TYPE}
     */
    private Integer policyType;
    /**
     * ç­–略值(折扣:0-1 æ¯”例;立减:金额)
     */
    private BigDecimal policyValue;
    /**
     * é€‚用对象类型(1服务套餐/2电压等级/3用电类型,可空=全局)
     */
    private Integer targetType;
    /**
     * é€‚用对象ID
     */
    private Long targetId;
    /**
     * ç”Ÿæ•ˆæ—¥æœŸ
     */
    private LocalDateTime startDate;
    /**
     * å¤±æ•ˆæ—¥æœŸ
     */
    private LocalDateTime endDate;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdElectricityTypeDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * MES äº§å“è®¾è®¡-用电类型基础信息 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_electricity_type")
@KeySequence("mes_pd_electricity_type_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdElectricityTypeDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç±»åž‹ç¼–码
     */
    private String code;
    /**
     * ç±»åž‹åç§°
     */
    private String name;
    /**
     * ç±»åž‹è¯´æ˜Ž
     */
    private String description;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdPriceStandardDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MES äº§å“è®¾è®¡-定价标准 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_price_standard")
@KeySequence("mes_pd_price_standard_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdPriceStandardDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å®šä»·å¯¹è±¡ç±»åž‹ï¼ˆ1服务套餐/2电压等级/3用电类型)
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_PRICE_OBJECT_TYPE}
     */
    private Integer objectType;
    /**
     * å®šä»·å¯¹è±¡ID
     */
    private Long objectId;
    /**
     * å®šä»·å¯¹è±¡åç§°ï¼ˆå†—余,便于展示)
     */
    private String objectName;
    /**
     * ä»·æ ¼
     */
    private BigDecimal price;
    /**
     * è®¡ä»·å•位
     */
    private String unit;
    /**
     * ç”Ÿæ•ˆæ—¥æœŸ
     */
    private LocalDateTime effectiveDate;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdServicePackageDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MES äº§å“è®¾è®¡-服务套餐基础信息 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_service_package")
@KeySequence("mes_pd_service_package_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdServicePackageDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å¥—餐编码(编码规则 PD_PACKAGE_CODE è‡ªåŠ¨ç”Ÿæˆï¼‰
     */
    private String code;
    /**
     * å¥—餐名称
     */
    private String name;
    /**
     * å¥—餐说明
     */
    private String description;
    /**
     * å¥—餐价格
     */
    private BigDecimal price;
    /**
     * ç”Ÿæ•ˆæ—¥æœŸ
     */
    private LocalDateTime effectiveDate;
    /**
     * å¤±æ•ˆæ—¥æœŸ
     */
    private LocalDateTime expireDate;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdTieredPriceDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MES äº§å“è®¾è®¡-阶梯电价 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_tiered_price")
@KeySequence("mes_pd_tiered_price_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdTieredPriceDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æœåС套餐ID(与电压等级二选一)
     */
    private Long packageId;
    /**
     * ç”µåŽ‹ç­‰çº§ID(与服务套餐二选一)
     */
    private Long voltageLevelId;
    /**
     * æ¡£ä½ä¸‹é™ï¼ˆå«ï¼ŒkWh)
     */
    private BigDecimal startValue;
    /**
     * æ¡£ä½ä¸Šé™ï¼ˆä¸å«ï¼ŒkWh,可空=上不封顶)
     */
    private BigDecimal endValue;
    /**
     * æ¡£ä½å•ä»·
     */
    private BigDecimal pricePerUnit;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_TIERED_PRICE_STATUS}
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/basedata/MesPdVoltageLevelDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MES äº§å“è®¾è®¡-电压等级基础信息 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_voltage_level")
@KeySequence("mes_pd_voltage_level_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdVoltageLevelDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç­‰çº§ç¼–码
     */
    private String code;
    /**
     * ç­‰çº§åç§°
     */
    private String name;
    /**
     * ç”µåŽ‹æ•°å€¼ï¼ˆkV)
     */
    private BigDecimal voltageValue;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/ctc/ProductCtcDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MES æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚æ•° DO
 *
 * @author è¶…级管理员
 */
@TableName("product_ctc")
@KeySequence("product_ctc_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductCtcDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç‰©æ–™ID(关联 mdm_item.id)
     */
    private Long itemId;
    /**
     * è®¢è´§è§„æ ¼
     */
    private String orderSpec;
    /**
     * è£¸å¯¼çº¿è§„æ ¼
     */
    private String bareConductorSpec;
    /**
     * æ¼†åŒ…线规格
     */
    private String enameledSpec;
    /**
     * æ¢ä½è§„æ ¼
     */
    private String transpositionSpec;
    /**
     * å¯¼ä½“类型(铜/铝)
     *
     * å­—å…¸ {@link DictTypeConstants#PRODUCT_CONDUCTOR_TYPE}
     */
    private String conductorType;
    /**
     * æˆªé¢ç§¯ï¼ˆmm²)
     */
    private BigDecimal crossSection;
    /**
     * å¤–径(mm)
     */
    private BigDecimal outerDiameter;
    /**
     * 20℃直流电阻(Ω/km)
     */
    private BigDecimal resistance20c;
    /**
     * ç»•包角度(°)
     */
    private BigDecimal wrappingAngle;
    /**
     * ç»ç¼˜åŽšåº¦ï¼ˆmm)
     */
    private BigDecimal insulationThickness;
    /**
     * é¢å®šç”µåŽ‹ï¼ˆkV)
     */
    private String ratedVoltage;
    /**
     * è¯•验电压(kV)
     */
    private String testVoltage;
    /**
     * æœ€å°å¼¯æ›²åŠå¾„(mm)
     */
    private BigDecimal minBendingRadius;
    /**
     * æ¯å…¬é‡Œé‡é‡ï¼ˆkg/km)
     */
    private BigDecimal weightPerKm;
    /**
     * æ‰§è¡Œæ ‡å‡†ä»£å·
     */
    private String standardCode;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/product/MesPdProductDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.product;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.enums.pd.MesPdAuditStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pd.MesPdProductArchiveTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MES äº§å“è®¾è®¡-产品档案 DO
 *
 * @author è¶…级管理员
 */
@TableName("mes_pd_product")
@KeySequence("mes_pd_product_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdProductDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æ¡£æ¡ˆç±»åž‹ï¼ˆ1电力服务/2供电产品/3增值能源产品)
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_PRODUCT_ARCHIVE_TYPE}
     */
    private Integer archiveType;
    /**
     * æ¡£æ¡ˆç¼–码(编码规则 PD_PRODUCT_CODE è‡ªåŠ¨ç”Ÿæˆï¼‰
     */
    private String code;
    /**
     * æ¡£æ¡ˆåç§°
     */
    private String name;
    /**
     * è§„格型号
     */
    private String spec;
    /**
     * å•位
     */
    private String unit;
    /**
     * åŸºå‡†ä»·æ ¼
     */
    private BigDecimal price;
    /**
     * å®¡æ‰¹çŠ¶æ€ï¼ˆ0未提交/10审批中/20审核通过/30审核不通过)
     *
     * æžšä¸¾ {@link MesPdAuditStatusEnum}
     */
    private Integer auditStatus;
    /**
     * å®¡æ‰¹æ„è§
     */
    private String auditRemark;
    /**
     * å®¡æ‰¹æ—¶é—´
     */
    private LocalDateTime auditTime;
    /**
     * å¯ç”¨çŠ¶æ€ï¼ˆ1启用/0禁用)
     */
    private Integer status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/reel/ProductCtcReelDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.reel;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MES æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®— DO
 *
 * @author è¶…级管理员
 */
@TableName("product_ctc_reel")
@KeySequence("product_ctc_reel_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductCtcReelDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æ¢ä½å¯¼çº¿äº§å“ID(关联 product_ctc.id)
     */
    private Long ctcId;
    /**
     * ç›˜å…·ç±»åž‹ï¼ˆæœ¨ç›˜/铁盘/钢盘)
     *
     * å­—å…¸ {@link DictTypeConstants#PRODUCT_REEL_TYPE}
     */
    private String reelType;
    /**
     * ç›˜å…·å¤–径(mm)
     */
    private BigDecimal reelOuterDiameter;
    /**
     * ç›˜èŠ¯å†…å¾„ï¼ˆmm)
     */
    private BigDecimal reelInnerDiameter;
    /**
     * ç›˜å®½ï¼ˆmm)
     */
    private BigDecimal reelWidth;
    /**
     * æœ€å¤§å®¹é‡ï¼ˆkm)
     */
    private BigDecimal maxCapacity;
    /**
     * æŽ¨èæ¯ç›˜é•¿åº¦ï¼ˆkm)
     */
    private BigDecimal recommendedLength;
    /**
     * å‡€é‡ï¼ˆkg)
     */
    private BigDecimal netWeight;
    /**
     * æ¯›é‡ï¼ˆkg)
     */
    private BigDecimal grossWeight;
    /**
     * è®¡ç®—公式(保存计算逻辑)
     */
    private String calcFormula;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/techconfirm/ProductTechConfirmDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.techconfirm;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES äº§å“æŠ€æœ¯ç¡®è®¤å‡½ DO
 *
 * @author è¶…级管理员
 */
@TableName("product_tech_confirm")
@KeySequence("product_tech_confirm_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductTechConfirmDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç¡®è®¤å‡½ç¼–号
     */
    private String confirmNo;
    /**
     * å®¢æˆ·ID(关联 crm_customer.id)
     */
    private Long customerId;
    /**
     * å®¢æˆ·åç§°
     */
    private String customerName;
    /**
     * äº§å“åç§°
     */
    private String productName;
    /**
     * è§„格型号
     */
    private String specification;
    /**
     * æŠ€æœ¯å‚数(JSON æ ¼å¼ï¼‰
     */
    private String techParams;
    /**
     * æ¨¡æ¿ç±»åž‹ï¼ˆå¥¥åœ°åˆ©/日本/通用)
     *
     * å­—å…¸ {@link DictTypeConstants#PRODUCT_TECH_CONFIRM_TEMPLATE}
     */
    private String templateType;
    /**
     * ç¼–制人ID
     */
    private Long drafterId;
    /**
     * ç¼–制人姓名
     */
    private String drafterName;
    /**
     * ç¼–制时间
     */
    private LocalDateTime draftTime;
    /**
     * å®¡æ ¸äººID
     */
    private Long reviewerId;
    /**
     * å®¡æ ¸äººå§“名
     */
    private String reviewerName;
    /**
     * å®¡æ ¸æ—¶é—´
     */
    private LocalDateTime reviewTime;
    /**
     * å®¢æˆ·ç¡®è®¤äºº
     */
    private String customerConfirmUser;
    /**
     * å®¢æˆ·ç¡®è®¤æ—¶é—´
     */
    private LocalDateTime customerConfirmTime;
    /**
     * çŠ¶æ€ï¼ˆ0=草稿,1=待审核,2=已审核,3=客户已确认)
     *
     * å­—å…¸ {@link DictTypeConstants#PRODUCT_TECH_CONFIRM_STATUS}
     */
    private Integer status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/emergencyplan/MesProEmergencyPlanDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.emergencyplan;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * MES åº”急处置预案 DO
 *
 * @author xiaoyi
 */
@TableName("mes_pro_emergency_plan")
@KeySequence("mes_pro_emergency_plan_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProEmergencyPlanDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é¢„案编码
     */
    private String code;
    /**
     * é¢„案名称
     */
    private String name;
    /**
     * é¢„案类型
     *
     * 1: åœç”µ
     * 2: è®¾å¤‡æ•…éšœ
     * 3: ç«ç¾
     * 4: è‡ªç„¶ç¾å®³
     * 5: å…¶ä»–
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PRO_EMERGENCY_PLAN_TYPE}
     */
    private Integer planType;
    /**
     * å¤„置内容
     */
    private String content;
    /**
     * è´£ä»»å•位/部门
     */
    private String orgUnit;
    /**
     * çŠ¶æ€
     *
     * 0: è‰ç¨¿
     * 10: å·²å‘布
     */
    private Integer status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/maintenanceplan/MesProMaintenancePlanDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.enums.pro.MesProMaintenancePlanTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’ DO
 *
 * @author xiaoyi
 */
@TableName("mes_pro_maintenance_plan")
@KeySequence("mes_pro_maintenance_plan_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProMaintenancePlanDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * è®¡åˆ’编码
     */
    private String code;
    /**
     * è®¡åˆ’名称
     */
    private String name;
    /**
     * è®¡åˆ’类型
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PRO_MAINTENANCE_PLAN_TYPE}
     * æžšä¸¾ {@link MesProMaintenancePlanTypeEnum}
     */
    private Integer planType;
    /**
     * ç«™ç‚¹
     */
    private String station;
    /**
     * çº¿è·¯
     */
    private String line;
    /**
     * ç­ç»„编号
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO#getId()}
     */
    private Long teamId;
    /**
     * ç­ç»„名称
     */
    private String teamName;
    /**
     * è®¡åˆ’日期
     */
    private LocalDateTime planDate;
    /**
     * çŠ¶æ€
     *
     * 0: è‰ç¨¿
     * 10: å·²ä¸‹å‘
     * 20: å·²å®Œæˆ
     * 30: å·²å–消
     */
    private Integer status;
    /**
     * ç”Ÿæˆçš„工单编号
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO#getId()}
     */
    private Long workOrderId;
    /**
     * ç”Ÿæˆçš„工单编码
     */
    private String workOrderCode;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/maintenanceplan/MesProMaintenancePlanItemDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’æ˜Žç»†ï¼ˆå¤‡ä»¶/物资需求) DO
 *
 * @author xiaoyi
 */
@TableName("mes_pro_maintenance_plan_item")
@KeySequence("mes_pro_maintenance_plan_item_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProMaintenancePlanItemDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç”µåŠ›ä½œä¸šè®¡åˆ’ç¼–å·
     */
    private Long planId;
    /**
     * ç‰©æ–™/备件编号
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()}
     */
    private Long itemId;
    /**
     * éœ€æ±‚数量
     */
    private BigDecimal quantity;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/process/MesProProcessDO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.process;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -35,6 +36,12 @@
     */
    private String name;
    /**
     * ç”µåŠ›å·¥è‰ºåˆ†ç±»
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PRO_PROCESS_TYPE}
     */
    private Integer type;
    /**
     * å·¥è‰ºè¦æ±‚
     */
    private String attention;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java
@@ -69,6 +69,16 @@
     */
    private Long processId;
    /**
     * ç­ç»„编号
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO#getId()}
     */
    private Long teamId;
    /**
     * ç­ç»„名称
     */
    private String teamName;
    /**
     * äº§å“ç‰©æ–™ç¼–号
     *
     * å…³è” {@link MesMdItemDO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java
@@ -94,6 +94,16 @@
     */
    private Long clientId;
    /**
     * å…³è”营销订单(ERP é”€å”®è®¢å•)编号
     *
     * å…³è” {@link cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO#getId()}
     */
    private Long saleOrderId;
    /**
     * å…³è”营销订单(ERP é”€å”®è®¢å•)单号
     */
    private String saleOrderNo;
    /**
     * ä¾›åº”商编号
     *
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/outsourcetest/MesQcOutsourceTestDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES å§”外检测/试验单 DO
 *
 * <p>第三方电力检测、委外试验单据管理,留存检测报告与资质文件
 *
 * @author xiaoyi
 */
@TableName("mes_qc_outsource_test")
@KeySequence("mes_qc_outsource_test_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesQcOutsourceTestDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å•据编码
     */
    private String code;
    /**
     * å•据名称
     */
    private String name;
    /**
     * æ£€æµ‹ç±»åž‹
     *
     * 1: ç¬¬ä¸‰æ–¹æ£€æµ‹
     * 2: å§”外试验
     *
     * å­—å…¸ {@link DictTypeConstants#MES_QC_OUTSOURCE_TEST_TYPE}
     */
    private Integer testType;
    /**
     * æ£€æµ‹å¯¹è±¡ï¼ˆç‰©æ–™/设备编号)
     */
    private Long itemId;
    /**
     * æ£€æµ‹æœºæž„
     */
    private String testOrg;
    /**
     * æ£€æµ‹æ—¥æœŸ
     */
    private LocalDateTime testDate;
    /**
     * æ£€æµ‹ç»“æžœ
     *
     * 1: åˆæ ¼
     * 2: ä¸åˆæ ¼
     *
     * å­—å…¸ {@link DictTypeConstants#MES_QC_OUTSOURCE_TEST_RESULT}
     */
    private Integer result;
    /**
     * æ£€æµ‹æŠ¥å‘Šç¼–号
     */
    private String reportNo;
    /**
     * çŠ¶æ€
     *
     * 0: è‰ç¨¿
     * 10: å·²æäº¤
     * 20: å·²å®Œæˆ
     */
    private Integer status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueDO.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -58,6 +59,13 @@
     */
    private Long taskId;
    /**
     * å•据类型
     *
     * å­—å…¸ {@link DictTypeConstants#MES_WM_PRODUCT_ISSUE_TYPE}
     * æžšä¸¾ {@link MesWmProductIssueTypeEnum}
     */
    private Integer issueType;
    /**
     * é¢†æ–™æ—¥æœŸ
     */
    private LocalDateTime issueDate;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseAreaDO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -40,6 +41,12 @@
     */
    private String name;
    /**
     * åº“区类型
     *
     * å­—å…¸ {@link DictTypeConstants#MES_WM_WAREHOUSE_TYPE}
     */
    private Integer type;
    /**
     * åº“区编号
     *
     * å…³è” {@link MesWmWarehouseLocationDO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -44,6 +45,12 @@
     */
    private String name;
    /**
     * ä»“库类型
     *
     * å­—å…¸ {@link DictTypeConstants#MES_WM_WAREHOUSE_TYPE}
     */
    private Integer type;
    /**
     * ä»“库地址
     */
    private String address;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/dv/telemetry/MesDvTelemetryMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.mes.dal.mysql.dv.telemetry;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryLatestReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry.MesDvTelemetryDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * MES è®¾å¤‡æ•°é‡‡é¥æµ‹æ•°æ® Mapper
 *
 * @author è¶…级管理员
 */
@Mapper
public interface MesDvTelemetryMapper extends BaseMapperX<MesDvTelemetryDO> {
    default PageResult<MesDvTelemetryDO> selectPage(MesDvTelemetryPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesDvTelemetryDO>()
                .eqIfPresent(MesDvTelemetryDO::getTbDeviceId, reqVO.getTbDeviceId())
                .likeIfPresent(MesDvTelemetryDO::getDeviceName, reqVO.getDeviceName())
                .likeIfPresent(MesDvTelemetryDO::getParamName, reqVO.getParamName())
                .eqIfPresent(MesDvTelemetryDO::getWhetherAnomaly, reqVO.getWhetherAnomaly())
                .eqIfPresent(MesDvTelemetryDO::getShiftName, reqVO.getShiftName())
                .betweenIfPresent(MesDvTelemetryDO::getTelemetryDataTime, reqVO.getTelemetryDataTime())
                .orderByDesc(MesDvTelemetryDO::getTelemetryDataTime)
                .orderByDesc(MesDvTelemetryDO::getId));
    }
    default List<MesDvTelemetryDO> selectLatest(MesDvTelemetryLatestReqVO reqVO, int limit) {
        return selectList(new LambdaQueryWrapperX<MesDvTelemetryDO>()
                .eqIfPresent(MesDvTelemetryDO::getTbDeviceId, reqVO.getTbDeviceId())
                .likeIfPresent(MesDvTelemetryDO::getDeviceName, reqVO.getDeviceName())
                .orderByDesc(MesDvTelemetryDO::getTelemetryDataTime)
                .orderByDesc(MesDvTelemetryDO::getId)
                .last("LIMIT " + limit));
    }
    default List<MesDvTelemetryDO> selectDeviceList() {
        return selectList(new LambdaQueryWrapperX<MesDvTelemetryDO>()
                .select(MesDvTelemetryDO::getTbDeviceId, MesDvTelemetryDO::getDeviceName)
                .groupBy(MesDvTelemetryDO::getTbDeviceId, MesDvTelemetryDO::getDeviceName)
                .orderByAsc(MesDvTelemetryDO::getDeviceName));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdDiscountPolicyMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicyPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdDiscountPolicyDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MesPdDiscountPolicyMapper extends BaseMapperX<MesPdDiscountPolicyDO> {
    default PageResult<MesPdDiscountPolicyDO> selectPage(MesPdDiscountPolicyPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdDiscountPolicyDO>()
                .likeIfPresent(MesPdDiscountPolicyDO::getName, reqVO.getName())
                .eqIfPresent(MesPdDiscountPolicyDO::getPolicyType, reqVO.getPolicyType())
                .eqIfPresent(MesPdDiscountPolicyDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesPdDiscountPolicyDO::getId));
    }
    default List<MesPdDiscountPolicyDO> selectListByTarget(Integer targetType, Long targetId) {
        return selectList(new LambdaQueryWrapperX<MesPdDiscountPolicyDO>()
                .eq(MesPdDiscountPolicyDO::getStatus, 1)
                .and(w -> w.eq(MesPdDiscountPolicyDO::getTargetType, targetType)
                        .eq(MesPdDiscountPolicyDO::getTargetId, targetId)
                        .or()
                        .isNull(MesPdDiscountPolicyDO::getTargetType))
                .orderByDesc(MesPdDiscountPolicyDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdElectricityTypeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdElectricityTypeDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MesPdElectricityTypeMapper extends BaseMapperX<MesPdElectricityTypeDO> {
    default PageResult<MesPdElectricityTypeDO> selectPage(MesPdElectricityTypePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdElectricityTypeDO>()
                .likeIfPresent(MesPdElectricityTypeDO::getName, reqVO.getName())
                .eqIfPresent(MesPdElectricityTypeDO::getStatus, reqVO.getStatus())
                .orderByAsc(MesPdElectricityTypeDO::getSort)
                .orderByDesc(MesPdElectricityTypeDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdPriceStandardMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdPriceStandardDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MesPdPriceStandardMapper extends BaseMapperX<MesPdPriceStandardDO> {
    default PageResult<MesPdPriceStandardDO> selectPage(MesPdPriceStandardPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdPriceStandardDO>()
                .eqIfPresent(MesPdPriceStandardDO::getObjectType, reqVO.getObjectType())
                .eqIfPresent(MesPdPriceStandardDO::getObjectId, reqVO.getObjectId())
                .eqIfPresent(MesPdPriceStandardDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesPdPriceStandardDO::getId));
    }
    default List<MesPdPriceStandardDO> selectListByObjectTypeAndId(Integer objectType, Long objectId) {
        return selectList(new LambdaQueryWrapperX<MesPdPriceStandardDO>()
                .eq(MesPdPriceStandardDO::getObjectType, objectType)
                .eq(MesPdPriceStandardDO::getObjectId, objectId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdServicePackageMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackagePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdServicePackageDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MesPdServicePackageMapper extends BaseMapperX<MesPdServicePackageDO> {
    default PageResult<MesPdServicePackageDO> selectPage(MesPdServicePackagePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdServicePackageDO>()
                .likeIfPresent(MesPdServicePackageDO::getName, reqVO.getName())
                .eqIfPresent(MesPdServicePackageDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesPdServicePackageDO::getId));
    }
    default MesPdServicePackageDO selectByCode(String code) {
        return selectOne(MesPdServicePackageDO::getCode, code);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdTieredPriceMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPricePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdTieredPriceDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MesPdTieredPriceMapper extends BaseMapperX<MesPdTieredPriceDO> {
    default PageResult<MesPdTieredPriceDO> selectPage(MesPdTieredPricePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdTieredPriceDO>()
                .eqIfPresent(MesPdTieredPriceDO::getPackageId, reqVO.getPackageId())
                .eqIfPresent(MesPdTieredPriceDO::getVoltageLevelId, reqVO.getVoltageLevelId())
                .eqIfPresent(MesPdTieredPriceDO::getStatus, reqVO.getStatus())
                .orderByAsc(MesPdTieredPriceDO::getStartValue)
                .orderByDesc(MesPdTieredPriceDO::getId));
    }
    default List<MesPdTieredPriceDO> selectListByPackageId(Long packageId) {
        return selectList(new LambdaQueryWrapperX<MesPdTieredPriceDO>()
                .eq(MesPdTieredPriceDO::getPackageId, packageId)
                .eq(MesPdTieredPriceDO::getStatus, 1)
                .orderByAsc(MesPdTieredPriceDO::getStartValue));
    }
    default List<MesPdTieredPriceDO> selectListByVoltageLevelId(Long voltageLevelId) {
        return selectList(new LambdaQueryWrapperX<MesPdTieredPriceDO>()
                .eq(MesPdTieredPriceDO::getVoltageLevelId, voltageLevelId)
                .eq(MesPdTieredPriceDO::getStatus, 1)
                .orderByAsc(MesPdTieredPriceDO::getStartValue));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/basedata/MesPdVoltageLevelMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdVoltageLevelDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MesPdVoltageLevelMapper extends BaseMapperX<MesPdVoltageLevelDO> {
    default PageResult<MesPdVoltageLevelDO> selectPage(MesPdVoltageLevelPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdVoltageLevelDO>()
                .likeIfPresent(MesPdVoltageLevelDO::getName, reqVO.getName())
                .eqIfPresent(MesPdVoltageLevelDO::getStatus, reqVO.getStatus())
                .orderByAsc(MesPdVoltageLevelDO::getSort)
                .orderByDesc(MesPdVoltageLevelDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/ctc/ProductCtcMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.ctc;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo.ProductCtcPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductCtcMapper extends BaseMapperX<ProductCtcDO> {
    default PageResult<ProductCtcDO> selectPage(ProductCtcPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<ProductCtcDO>()
                .eqIfPresent(ProductCtcDO::getItemId, reqVO.getItemId())
                .likeIfPresent(ProductCtcDO::getOrderSpec, reqVO.getOrderSpec())
                .eqIfPresent(ProductCtcDO::getConductorType, reqVO.getConductorType())
                .likeIfPresent(ProductCtcDO::getStandardCode, reqVO.getStandardCode())
                .orderByDesc(ProductCtcDO::getId));
    }
    default ProductCtcDO selectByItemId(Long itemId) {
        return selectOne(ProductCtcDO::getItemId, itemId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/product/MesPdProductMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.product.MesPdProductDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MesPdProductMapper extends BaseMapperX<MesPdProductDO> {
    default PageResult<MesPdProductDO> selectPage(MesPdProductPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdProductDO>()
                .eqIfPresent(MesPdProductDO::getArchiveType, reqVO.getArchiveType())
                .likeIfPresent(MesPdProductDO::getName, reqVO.getName())
                .eqIfPresent(MesPdProductDO::getAuditStatus, reqVO.getAuditStatus())
                .eqIfPresent(MesPdProductDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesPdProductDO::getId));
    }
    default MesPdProductDO selectByCode(String code) {
        return selectOne(MesPdProductDO::getCode, code);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/reel/ProductCtcReelMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.reel;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.reel.ProductCtcReelDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ProductCtcReelMapper extends BaseMapperX<ProductCtcReelDO> {
    default PageResult<ProductCtcReelDO> selectPage(ProductCtcReelPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<ProductCtcReelDO>()
                .eqIfPresent(ProductCtcReelDO::getCtcId, reqVO.getCtcId())
                .eqIfPresent(ProductCtcReelDO::getReelType, reqVO.getReelType())
                .orderByDesc(ProductCtcReelDO::getId));
    }
    default List<ProductCtcReelDO> selectListByCtcId(Long ctcId) {
        return selectList(ProductCtcReelDO::getCtcId, ctcId);
    }
    default int deleteByCtcId(Long ctcId) {
        return delete(ProductCtcReelDO::getCtcId, ctcId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/techconfirm/ProductTechConfirmMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.techconfirm;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.techconfirm.ProductTechConfirmDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductTechConfirmMapper extends BaseMapperX<ProductTechConfirmDO> {
    default PageResult<ProductTechConfirmDO> selectPage(ProductTechConfirmPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<ProductTechConfirmDO>()
                .likeIfPresent(ProductTechConfirmDO::getConfirmNo, reqVO.getConfirmNo())
                .eqIfPresent(ProductTechConfirmDO::getCustomerId, reqVO.getCustomerId())
                .likeIfPresent(ProductTechConfirmDO::getProductName, reqVO.getProductName())
                .eqIfPresent(ProductTechConfirmDO::getTemplateType, reqVO.getTemplateType())
                .eqIfPresent(ProductTechConfirmDO::getStatus, reqVO.getStatus())
                .orderByDesc(ProductTechConfirmDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/emergencyplan/MesProEmergencyPlanMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.emergencyplan;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.emergencyplan.MesProEmergencyPlanDO;
import org.apache.ibatis.annotations.Mapper;
/**
 * MES åº”急处置预案 Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface MesProEmergencyPlanMapper extends BaseMapperX<MesProEmergencyPlanDO> {
    default MesProEmergencyPlanDO selectByCode(String code) {
        return selectOne(MesProEmergencyPlanDO::getCode, code);
    }
    default PageResult<MesProEmergencyPlanDO> selectPage(MesProEmergencyPlanPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesProEmergencyPlanDO>()
                .likeIfPresent(MesProEmergencyPlanDO::getCode, reqVO.getCode())
                .likeIfPresent(MesProEmergencyPlanDO::getName, reqVO.getName())
                .eqIfPresent(MesProEmergencyPlanDO::getPlanType, reqVO.getPlanType())
                .eqIfPresent(MesProEmergencyPlanDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesProEmergencyPlanDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/maintenanceplan/MesProMaintenancePlanItemMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.maintenanceplan;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanItemDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’æ˜Žç»† Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface MesProMaintenancePlanItemMapper extends BaseMapperX<MesProMaintenancePlanItemDO> {
    default List<MesProMaintenancePlanItemDO> selectListByPlanId(Long planId) {
        return selectList(MesProMaintenancePlanItemDO::getPlanId, planId);
    }
    default List<MesProMaintenancePlanItemDO> selectListByPlanIds(Collection<Long> planIds) {
        return selectList(MesProMaintenancePlanItemDO::getPlanId, planIds);
    }
    default void deleteByPlanId(Long planId) {
        delete(new LambdaQueryWrapperX<MesProMaintenancePlanItemDO>()
                .eq(MesProMaintenancePlanItemDO::getPlanId, planId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/maintenanceplan/MesProMaintenancePlanMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.maintenanceplan;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’ Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface MesProMaintenancePlanMapper extends BaseMapperX<MesProMaintenancePlanDO> {
    default MesProMaintenancePlanDO selectByCode(String code) {
        return selectOne(MesProMaintenancePlanDO::getCode, code);
    }
    default List<MesProMaintenancePlanDO> selectPlanListByIds(Collection<Long> ids) {
        return selectList(MesProMaintenancePlanDO::getId, ids);
    }
    default PageResult<MesProMaintenancePlanDO> selectPage(MesProMaintenancePlanPageReqVO pageReqVO) {
        return selectPage(pageReqVO, new LambdaQueryWrapperX<MesProMaintenancePlanDO>()
                .likeIfPresent(MesProMaintenancePlanDO::getCode, pageReqVO.getCode())
                .likeIfPresent(MesProMaintenancePlanDO::getName, pageReqVO.getName())
                .eqIfPresent(MesProMaintenancePlanDO::getPlanType, pageReqVO.getPlanType())
                .eqIfPresent(MesProMaintenancePlanDO::getStatus, pageReqVO.getStatus())
                .betweenIfPresent(MesProMaintenancePlanDO::getPlanDate, pageReqVO.getPlanDate())
                .orderByDesc(MesProMaintenancePlanDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/outsourcetest/MesQcOutsourceTestMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.mes.dal.mysql.qc.outsourcetest;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest.MesQcOutsourceTestDO;
import org.apache.ibatis.annotations.Mapper;
/**
 * MES å§”外检测/试验单 Mapper
 *
 * @author xiaoyi
 */
@Mapper
public interface MesQcOutsourceTestMapper extends BaseMapperX<MesQcOutsourceTestDO> {
    default MesQcOutsourceTestDO selectByCode(String code) {
        return selectOne(MesQcOutsourceTestDO::getCode, code);
    }
    default PageResult<MesQcOutsourceTestDO> selectPage(MesQcOutsourceTestPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesQcOutsourceTestDO>()
                .likeIfPresent(MesQcOutsourceTestDO::getCode, reqVO.getCode())
                .likeIfPresent(MesQcOutsourceTestDO::getName, reqVO.getName())
                .eqIfPresent(MesQcOutsourceTestDO::getTestType, reqVO.getTestType())
                .eqIfPresent(MesQcOutsourceTestDO::getResult, reqVO.getResult())
                .eqIfPresent(MesQcOutsourceTestDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesQcOutsourceTestDO::getTestDate, reqVO.getTestDate())
                .orderByDesc(MesQcOutsourceTestDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
@@ -9,6 +9,13 @@
    // ========== åŸºç¡€æ•°æ® (MD) ==========
    String MES_MD_ITEM_OR_PRODUCT = "mes_md_item_or_product"; // MES ç‰©æ–™äº§å“æ ‡è¯†
    String MES_MATERIAL_CATEGORY = "mes_material_category"; // MES ç”µåŠ›ç‰©èµ„åˆ†ç±»
    String MES_PRO_MAINTENANCE_PLAN_TYPE = "mes_pro_maintenance_plan_type"; // MES ç”µåŠ›ä½œä¸šè®¡åˆ’ç±»åž‹
    String MES_PRO_MAINTENANCE_PLAN_STATUS = "mes_pro_maintenance_plan_status"; // MES ç”µåŠ›ä½œä¸šè®¡åˆ’çŠ¶æ€
    String MES_QC_OUTSOURCE_TEST_TYPE = "mes_qc_outsource_test_type"; // MES å§”外检测类型
    String MES_QC_OUTSOURCE_TEST_RESULT = "mes_qc_outsource_test_result"; // MES å§”外检测结果
    String MES_PRO_PROCESS_TYPE = "mes_pro_process_type"; // MES ç”µåŠ›å·¥è‰ºåˆ†ç±»
    String MES_PRO_EMERGENCY_PLAN_TYPE = "mes_pro_emergency_plan_type"; // MES åº”急处置预案类型
    String MES_MD_AUTO_CODE_PART_TYPE = "mes_md_auto_code_part_type"; // MES åˆ†æ®µç±»åž‹
    String MES_MD_AUTO_CODE_CYCLE_METHOD = "mes_md_auto_code_cycle_method"; // MES å¾ªçŽ¯æ–¹å¼
    String MES_MD_AUTO_CODE_PADDED_METHOD = "mes_md_auto_code_padded_method"; // MES è¡¥é½æ–¹å¼
@@ -34,6 +41,7 @@
    String MES_BARCODE_FORMAT = "mes_barcode_format"; // MES æ¡ç æ ¼å¼
    String MES_BARCODE_BIZ_TYPE = "mes_barcode_biz_type"; // MES æ¡ç ä¸šåŠ¡ç±»åž‹
    String MES_WM_TRANSACTION_TYPE = "mes_wm_transaction_type"; // MES åº“存事务类型
    String MES_WM_WAREHOUSE_TYPE = "mes_wm_warehouse_type"; // MES ä»“库/库区类型(普通/设备仓/备件仓/耗材仓/危化品仓)
    String MES_WM_QUALITY_STATUS = "mes_wm_quality_status"; // MES è´¨é‡çŠ¶æ€ï¼ˆå¾…æ£€/合格/不合格)
    // ========== ä»“储入库 (WM Receipt) ==========
@@ -56,6 +64,7 @@
    String MES_WM_PRODUCT_SALES_STATUS = "mes_wm_product_sales_status"; // MES é”€å”®å‡ºåº“单状态
    String MES_WM_OUTSOURCE_ISSUE_STATUS = "mes_wm_outsource_issue_status"; // MES å§”外出库状态
    String MES_WM_PRODUCT_ISSUE_STATUS = "mes_wm_product_issue_status"; // MES ç”Ÿäº§é¢†æ–™çŠ¶æ€
    String MES_WM_PRODUCT_ISSUE_TYPE = "mes_wm_product_issue_type"; // MES ç”Ÿäº§é¢†æ–™å•据类型
    String MES_WM_RETURN_VENDOR_STATUS = "mes_wm_return_vendor_status"; // MES ä¾›åº”商退货单状态
    // ========== ä»“储移库与盘点 (WM Transfer) ==========
@@ -96,6 +105,7 @@
    String MES_DV_METERING_CATEGORY = "mes_dv_metering_category"; // MES è®¡é‡å™¨å…·ç±»åˆ«
    String MES_DV_METERING_STATUS = "mes_dv_metering_status"; // MES è®¡é‡å™¨å…·çŠ¶æ€
    String MES_DV_METERING_CHECK_RESULT = "mes_dv_metering_check_result"; // MES è®¡é‡æ£€æµ‹ç»“论
    String MES_DV_TELEMETRY_ANOMALY = "mes_dv_telemetry_anomaly"; // MES æ•°é‡‡æ˜¯å¦å¼‚常
    // ========== æŽ’班日历 (CAL) ==========
    String MES_CAL_HOLIDAY_TYPE = "mes_cal_holiday_type"; // MES å‡æœŸç±»åž‹
@@ -115,4 +125,18 @@
    String MES_PD_ARCHIVE_STATUS = "mes_pd_archive_status";               // å½’档状态
    String MES_PD_PROCESS_PARAM_TYPE = "mes_pd_process_param_type";                   // å·¥è‰ºå‚数类型
    String MES_PD_PROCESS_PARAM_TEMPLATE_TYPE = "mes_pd_process_param_template_type"; // å·¥è‰ºå‚数模板类型
    // ========== äº§å“è®¾è®¡-换位导线 (CTC) ==========
    String PRODUCT_CONDUCTOR_TYPE = "product_conductor_type";             // æ¢ä½å¯¼çº¿å¯¼ä½“类型
    String PRODUCT_REEL_TYPE = "product_reel_type";                       // æ¢ä½å¯¼çº¿ç›˜å…·ç±»åž‹
    String PRODUCT_TECH_CONFIRM_TEMPLATE = "product_tech_confirm_template"; // æŠ€æœ¯ç¡®è®¤å‡½æ¨¡æ¿ç±»åž‹
    String PRODUCT_TECH_CONFIRM_STATUS = "product_tech_confirm_status";   // æŠ€æœ¯ç¡®è®¤å‡½çŠ¶æ€
    // ========== äº§å“è®¾è®¡-产品档案与基础数据 (PD-EXT) ==========
    String MES_PD_PRODUCT_ARCHIVE_TYPE = "mes_pd_product_archive_type"; // äº§å“æ¡£æ¡ˆç±»åž‹ï¼ˆ1电力服务/2供电产品/3增值能源产品)
    String MES_PD_PRODUCT_AUDIT_STATUS = "mes_pd_product_audit_status"; // äº§å“æ¡£æ¡ˆå®¡æ‰¹çŠ¶æ€ï¼ˆ0/10/20/30)
    String MES_PD_PRODUCT_STATUS = "mes_pd_product_status";             // äº§å“æ¡£æ¡ˆå¯ç”¨çŠ¶æ€
    String MES_PD_PRICE_OBJECT_TYPE = "mes_pd_price_object_type";       // å®šä»·å¯¹è±¡ç±»åž‹ï¼ˆ1服务套餐/2电压等级/3用电类型)
    String MES_PD_DISCOUNT_POLICY_TYPE = "mes_pd_discount_policy_type"; // ä¼˜æƒ ç­–略类型(1折扣/2立减)
    String MES_PD_TIERED_PRICE_STATUS = "mes_pd_tiered_price_status";   // é˜¶æ¢¯ç”µä»·å¯ç”¨çŠ¶æ€
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -169,6 +169,11 @@
    ErrorCode DV_MACHINERY_HAS_REPAIR = new ErrorCode(1_040_301_006, "设备已关联维修工单,无法删除");
    ErrorCode DV_MACHINERY_WORKSTATION_NOT_EXISTS = new ErrorCode(1_040_301_008, "工作站编码不存在");
    // ========== MES è®¾å¤‡ç®¡ç†-设备数采(1-040-308-000) ==========
    ErrorCode DV_TELEMETRY_NOT_EXISTS = new ErrorCode(1_040_308_000, "数采遥测数据不存在");
    ErrorCode DV_TELEMETRY_PULL_ERROR = new ErrorCode(1_040_308_001, "数采数据拉取失败:{}");
    ErrorCode DV_TELEMETRY_PULL_EMPTY = new ErrorCode(1_040_308_002, "数采接口未返回有效数据");
    // ========== MES è®¾å¤‡ç®¡ç†-点检保养项目(1-040-304-000) ==========
    ErrorCode DV_SUBJECT_NOT_EXISTS = new ErrorCode(1_040_304_000, "点检保养项目不存在");
    ErrorCode DV_SUBJECT_CODE_DUPLICATE = new ErrorCode(1_040_304_001, "项目编码已存在");
@@ -286,6 +291,7 @@
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_DETAIL_NOT_IN_TEMPLATE = new ErrorCode(1_040_502_202, "参数明细不属于所选模板");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_REQUIRED_PARAM_MISSING = new ErrorCode(1_040_502_203, "必填参数【{}】未填写");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NOT_CHANGE = new ErrorCode(1_040_502_204, "不允许更换参数模板,请删除后重新创建");
    ErrorCode PRO_WORK_ORDER_SALE_ORDER_NOT_EXISTS = new ErrorCode(1_040_502_205, "关联的营销订单(销售订单)不存在");
    // ========== MES ç”Ÿäº§ç®¡ç†-生产任务(1-040-503-000) ==========
    ErrorCode PRO_TASK_NOT_EXISTS = new ErrorCode(1_040_503_000, "生产任务不存在");
@@ -798,6 +804,30 @@
    ErrorCode PD_PROCESS_PARAM_TEMPLATE_NOT_EXISTS = new ErrorCode(1_040_805_000, "工艺参数模板不存在");
    ErrorCode PD_PROCESS_PARAM_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_040_805_001, "工艺参数模板编码已存在");
    // ========== MES äº§å“è®¾è®¡-换位导线技术参数(1-040-806-000) ==========
    ErrorCode PD_CTC_NOT_EXISTS = new ErrorCode(1_040_806_000, "换位导线技术参数不存在");
    ErrorCode PD_CTC_ITEM_DUPLICATE = new ErrorCode(1_040_806_001, "该物料已存在换位导线技术参数,请勿重复添加");
    ErrorCode PD_CTC_CALC_PARAM_INVALID = new ErrorCode(1_040_806_002, "计算参数不合法,请检查输入");
    // ========== MES äº§å“è®¾è®¡-盘具计算(1-040-807-000) ==========
    ErrorCode PD_CTC_REEL_NOT_EXISTS = new ErrorCode(1_040_807_000, "盘具计算记录不存在");
    // ========== MES äº§å“è®¾è®¡-技术确认函(1-040-808-000) ==========
    ErrorCode PD_TECH_CONFIRM_NOT_EXISTS = new ErrorCode(1_040_808_000, "技术确认函不存在");
    ErrorCode PD_TECH_CONFIRM_NO_DUPLICATE = new ErrorCode(1_040_808_001, "确认函编号已存在");
    // ========== MES äº§å“è®¾è®¡-产品档案与基础数据(1-040-809-000) ==========
    ErrorCode PD_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_809_000, "产品档案不存在");
    ErrorCode PD_PRODUCT_AUDIT_STATUS_INVALID = new ErrorCode(1_040_809_001, "产品档案状态不正确,无法执行该操作");
    ErrorCode PD_PRODUCT_SUBMIT_DUPLICATE = new ErrorCode(1_040_809_002, "产品档案已提交审批,请勿重复提交");
    ErrorCode PD_ELECTRICITY_TYPE_NOT_EXISTS = new ErrorCode(1_040_809_100, "用电类型不存在");
    ErrorCode PD_VOLTAGE_LEVEL_NOT_EXISTS = new ErrorCode(1_040_809_101, "电压等级不存在");
    ErrorCode PD_SERVICE_PACKAGE_NOT_EXISTS = new ErrorCode(1_040_809_102, "服务套餐不存在");
    ErrorCode PD_PRICE_STANDARD_NOT_EXISTS = new ErrorCode(1_040_809_103, "定价标准不存在");
    ErrorCode PD_DISCOUNT_POLICY_NOT_EXISTS = new ErrorCode(1_040_809_104, "优惠策略不存在");
    ErrorCode PD_TIERED_PRICE_NOT_EXISTS = new ErrorCode(1_040_809_105, "阶梯电价不存在");
    ErrorCode PD_PRICE_CALC_OBJECT_NOT_CONFIGURED = new ErrorCode(1_040_809_200, "阶梯电价未配置,无法计算");
    // ========== MES ä»“储物流-审批配置(1-040-900-000) ==========
    ErrorCode WMS_APPROVAL_CONFIG_NOT_EXISTS = new ErrorCode(1_040_900_000, "仓库审批配置不存在");
    ErrorCode WMS_APPROVAL_ORDER_NOT_EXISTS = new ErrorCode(1_040_900_001, "审批单据不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
@@ -56,7 +56,9 @@
    PD_DOCUMENT_AUDIT_CODE("PD_DOCUMENT_AUDIT_CODE", "资料审核单编码"),
    PD_ARCHIVE_CODE("PD_ARCHIVE_CODE", "归档台账编码"),
    PD_PROCESS_PARAM_CODE("PD_PROCESS_PARAM_CODE", "工艺参数编码"),
    PD_PROCESS_PARAM_TEMPLATE_CODE("PD_PROCESS_PARAM_TEMPLATE_CODE", "工艺参数模板编码");
    PD_PROCESS_PARAM_TEMPLATE_CODE("PD_PROCESS_PARAM_TEMPLATE_CODE", "工艺参数模板编码"),
    PD_PRODUCT_CODE("PD_PRODUCT_CODE", "产品档案编码"),
    PD_PACKAGE_CODE("PD_PACKAGE_CODE", "服务套餐编码");
    /**
     * è§„则代码
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdAuditStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.mes.enums.pd;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
/**
 * äº§å“è®¾è®¡-审批状态
 *
 * @author è¶…级管理员
 */
@RequiredArgsConstructor
@Getter
public enum MesPdAuditStatusEnum implements ArrayValuable<Integer> {
    DRAFT(0, "未提交"),
    PROCESS(10, "审批中"),
    APPROVE(20, "审核通过"),
    REJECT(30, "审核不通过");
    private final Integer status;
    private final String name;
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesPdAuditStatusEnum::getStatus).toArray(Integer[]::new);
    public static String getNameByStatus(Integer status) {
        if (status == null) {
            return null;
        }
        for (MesPdAuditStatusEnum e : values()) {
            if (e.getStatus().equals(status)) {
                return e.getName();
            }
        }
        return null;
    }
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdProductArchiveTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.mes.enums.pd;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
/**
 * äº§å“è®¾è®¡-产品档案类型
 *
 * @author è¶…级管理员
 */
@RequiredArgsConstructor
@Getter
public enum MesPdProductArchiveTypeEnum implements ArrayValuable<Integer> {
    ELECTRICITY_SERVICE(1, "电力服务"),
    POWER_SUPPLY_PRODUCT(2, "供电产品"),
    VALUE_ADDED_ENERGY(3, "增值能源产品");
    private final Integer type;
    private final String name;
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesPdProductArchiveTypeEnum::getType).toArray(Integer[]::new);
    public static String getNameByType(Integer type) {
        if (type == null) {
            return null;
        }
        for (MesPdProductArchiveTypeEnum e : values()) {
            if (e.getType().equals(type)) {
                return e.getName();
            }
        }
        return null;
    }
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProMaintenancePlanTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.mes.enums.pro;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’ç±»åž‹æžšä¸¾
 *
 * @author xiaoyi
 */
@Getter
@AllArgsConstructor
public enum MesProMaintenancePlanTypeEnum implements ArrayValuable<Integer> {
    GRID_OPERATION(1, "电网运行"),
    POWER_GENERATION(2, "发电"),
    SUBSTATION_MAINTENANCE(3, "变电运维"),
    LINE_MAINTENANCE(4, "线路检修");
    public static final Integer[] ARRAYS = Arrays.stream(values())
            .map(MesProMaintenancePlanTypeEnum::getType).toArray(Integer[]::new);
    /**
     * ç±»åž‹å€¼
     */
    private final Integer type;
    /**
     * ç±»åž‹å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderSourceTypeEnum.java
@@ -16,7 +16,8 @@
public enum MesProWorkOrderSourceTypeEnum implements ArrayValuable<Integer> {
    ORDER(1, "客户订单"),
    STORE(2, "库存备货");
    STORE(2, "库存备货"),
    MAINTENANCE_PLAN(3, "运维计划");
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkOrderSourceTypeEnum::getType).toArray(Integer[]::new);
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderTypeEnum.java
@@ -17,7 +17,10 @@
    SELF(1, "自行生产"),
    OUTSOURCE(2, "代工"),
    PURCHASE(3, "采购");
    PURCHASE(3, "采购"),
    POWER_OPERATION(4, "电力运维"),
    POWER_GENERATION(5, "发电"),
    POWER_MAINTENANCE(6, "检修");
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProWorkOrderTypeEnum::getType).toArray(Integer[]::new);
@@ -30,6 +33,13 @@
     */
    private final String name;
    public static MesProWorkOrderTypeEnum valueOf(Integer type) {
        if (type == null) {
            return null;
        }
        return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null);
    }
    @Override
    public Integer[] array() {
        return ARRAYS;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMiscIssueTypeEnum.java
@@ -11,7 +11,9 @@
public enum MesWmMiscIssueTypeEnum implements ArrayValuable<Integer> {
    ADJUST(1, "库存调整"),
    SCRAP(2, "报废出库");
    SCRAP(2, "报废出库"),
    WASTE_OUT(3, "废旧物资出库"),
    EQUIPMENT_OUT(4, "设备出库");
    private final Integer type;
    private final String name;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmProductIssueTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.mes.enums.wm;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES ç”Ÿäº§é¢†æ–™å•据类型枚举
 *
 * @author xiaoyi
 */
@Getter
@AllArgsConstructor
public enum MesWmProductIssueTypeEnum implements ArrayValuable<Integer> {
    NORMAL(1, "正常领料"),
    URGENT_REPLENISH(2, "紧急补料");
    public static final Integer[] ARRAYS = Arrays.stream(values())
            .map(MesWmProductIssueTypeEnum::getType).toArray(Integer[]::new);
    /**
     * ç±»åž‹å€¼
     */
    private final Integer type;
    /**
     * ç±»åž‹å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesTelemetryProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mes.framework.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * MES æ•°é‡‡ï¼ˆè®¾å¤‡æ•°æ®é‡‡é›†ï¼‰é…ç½®
 *
 * @author è¶…级管理员
 */
@Component
@ConfigurationProperties(prefix = "yudao.mes.telemetry")
@Data
public class MesTelemetryProperties {
    /**
     * æ•°é‡‡æœåŠ¡åŸºç¡€åœ°å€ï¼ˆä¸å«è·¯å¾„ï¼‰ï¼Œå¦‚ http://192.168.0.10:8080
     */
    private String baseUrl;
    /**
     * æ•°é‡‡æŽ¥å£è·¯å¾„(默认返回近 5 åˆ†é’Ÿè®¾å¤‡æ•°æ®ï¼‰
     */
    private String path = "/api/jxjs/mesTb/getDeviceDetails";
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/job/dv/MesDvTelemetryPullJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.job.dv;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPullRespVO;
import cn.iocoder.yudao.module.mes.service.dv.telemetry.MesDvTelemetryService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
 * è®¾å¤‡æ•°é‡‡æ•°æ®å®šæ—¶æ‹‰å– Job
 *
 * æ•°é‡‡æŽ¥å£ä»…保留近 5 åˆ†é’Ÿæ•°æ®ï¼Œéœ€å®šæ—¶æ‹‰å–避免数据丢失。默认每 5 åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡ã€‚
 *
 * @author è¶…级管理员
 */
@Component
public class MesDvTelemetryPullJob implements JobHandler {
    @Resource
    private MesDvTelemetryService telemetryService;
    @Override
    @TenantJob
    public String execute(String param) {
        MesDvTelemetryPullRespVO resp = telemetryService.pullTelemetry();
        return String.format("拉取数采数据成功,新增 %s æ¡ï¼Œæ¶‰åŠè®¾å¤‡ %s å°", resp.getRecordCount(), resp.getDeviceCount());
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.service.dv.telemetry;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryDeviceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryLatestReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPullRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry.MesDvTelemetryDO;
import java.util.List;
/**
 * MES è®¾å¤‡æ•°é‡‡é¥æµ‹æ•°æ® Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesDvTelemetryService {
    /**
     * åˆ†é¡µæŸ¥è¯¢é¥æµ‹æ•°æ®
     */
    PageResult<MesDvTelemetryDO> getTelemetryPage(MesDvTelemetryPageReqVO pageReqVO);
    /**
     * æŸ¥è¯¢å„设备最新遥测数据(按设备+信号去重,保留最新)
     */
    List<MesDvTelemetryDO> getLatestTelemetry(MesDvTelemetryLatestReqVO reqVO);
    /**
     * æŸ¥è¯¢æ•°é‡‡è®¾å¤‡ä¸‹æ‹‰åˆ—表(来自遥测数据去重)
     */
    List<MesDvTelemetryDeviceRespVO> getDeviceList();
    /**
     * è°ƒç”¨å¤–部数采接口拉取近 5 åˆ†é’Ÿè®¾å¤‡æ•°æ®å¹¶å…¥åº“
     */
    MesDvTelemetryPullRespVO pullTelemetry();
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
package cn.iocoder.yudao.module.mes.service.dv.telemetry;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryDeviceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryLatestReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.dv.telemetry.vo.MesDvTelemetryPullRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry.MesDvTelemetryDO;
import cn.iocoder.yudao.module.mes.dal.mysql.dv.telemetry.MesDvTelemetryMapper;
import cn.iocoder.yudao.module.mes.framework.config.MesTelemetryProperties;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.DV_TELEMETRY_PULL_ERROR;
/**
 * MES è®¾å¤‡æ•°é‡‡é¥æµ‹æ•°æ® Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Slf4j
public class MesDvTelemetryServiceImpl implements MesDvTelemetryService {
    @Resource
    private MesDvTelemetryMapper telemetryMapper;
    @Resource
    private MesTelemetryProperties telemetryProperties;
    @Override
    public PageResult<MesDvTelemetryDO> getTelemetryPage(MesDvTelemetryPageReqVO pageReqVO) {
        return telemetryMapper.selectPage(pageReqVO);
    }
    @Override
    public List<MesDvTelemetryDO> getLatestTelemetry(MesDvTelemetryLatestReqVO reqVO) {
        // å–最近一批数据,按设备+信号去重保留最新,用于实时监控视图
        List<MesDvTelemetryDO> list = telemetryMapper.selectLatest(reqVO, 5000);
        Map<String, MesDvTelemetryDO> latestMap = new LinkedHashMap<>();
        for (MesDvTelemetryDO row : list) {
            String key = StrUtil.blankToDefault(row.getTbDeviceId(), "")
                    + ":" + StrUtil.blankToDefault(row.getParamKeyName(), "");
            latestMap.putIfAbsent(key, row);
        }
        return new ArrayList<>(latestMap.values());
    }
    @Override
    public List<MesDvTelemetryDeviceRespVO> getDeviceList() {
        List<MesDvTelemetryDeviceRespVO> result = new ArrayList<>();
        for (MesDvTelemetryDO row : telemetryMapper.selectDeviceList()) {
            MesDvTelemetryDeviceRespVO vo = new MesDvTelemetryDeviceRespVO();
            vo.setTbDeviceId(row.getTbDeviceId());
            vo.setDeviceName(row.getDeviceName());
            result.add(vo);
        }
        return result;
    }
    @Override
    public MesDvTelemetryPullRespVO pullTelemetry() {
        // 1. æ ¡éªŒé…ç½®
        if (StrUtil.isBlank(telemetryProperties.getBaseUrl())) {
            throw exception(DV_TELEMETRY_PULL_ERROR, "未配置数采服务地址 yudao.mes.telemetry.base-url");
        }
        String url = telemetryProperties.getBaseUrl() + telemetryProperties.getPath();
        // 2. è°ƒç”¨å¤–部数采接口
        String responseText;
        try {
            responseText = HttpUtils.get(url, null);
        } catch (Exception e) {
            log.error("[pullTelemetry][调用数采接口失败,url({})]", url, e);
            throw exception(DV_TELEMETRY_PULL_ERROR, e.getMessage());
        }
        if (StrUtil.isBlank(responseText)) {
            throw exception(DV_TELEMETRY_PULL_ERROR, "数采接口返回为空");
        }
        // 3. è§£æžå“åº”
        Map<?, ?> response = JsonUtils.parseObject(responseText, Map.class);
        if (response == null || !"200".equals(String.valueOf(response.get("code")))) {
            log.error("[pullTelemetry][数采接口返回异常,url({}),响应({})]", url, responseText);
            throw exception(DV_TELEMETRY_PULL_ERROR, "数采接口返回异常");
        }
        List<?> dataList = (List<?>) response.get("data");
        // 4. æ— æ•°æ®æ—¶è¿”回空结果,不视为错误
        MesDvTelemetryPullRespVO result = new MesDvTelemetryPullRespVO();
        LocalDateTime pullTime = LocalDateTime.now();
        result.setPullTime(pullTime);
        if (CollUtil.isEmpty(dataList)) {
            result.setRecordCount(0);
            result.setDeviceCount(0);
            return result;
        }
        // 5. è½¬æ¢å¹¶æ‰¹é‡å…¥åº“
        List<MesDvTelemetryDO> saveList = new ArrayList<>(dataList.size());
        for (Object rowObj : dataList) {
            if (!(rowObj instanceof Map)) {
                continue;
            }
            MesDvTelemetryDO data = convert((Map<?, ?>) rowObj);
            data.setPullTime(pullTime);
            saveList.add(data);
        }
        telemetryMapper.insertBatch(saveList);
        // 6. è¿”回统计
        result.setRecordCount(saveList.size());
        result.setDeviceCount((int) saveList.stream()
                .map(MesDvTelemetryDO::getTbDeviceId).distinct().count());
        return result;
    }
    private MesDvTelemetryDO convert(Map<?, ?> row) {
        MesDvTelemetryDO data = new MesDvTelemetryDO();
        data.setTbDeviceId(StrUtil.toStringOrNull(row.get("tbDeviceId")));
        data.setDeviceName(StrUtil.toStringOrNull(row.get("deviceName")));
        data.setParamName(StrUtil.toStringOrNull(row.get("paramName")));
        data.setParamKeyName(StrUtil.toStringOrNull(row.get("paramKeyName")));
        data.setStandardValue(StrUtil.toStringOrNull(row.get("standardValue")));
        data.setTimelyValue(StrUtil.toStringOrNull(row.get("timelyValue")));
        data.setAvgValue(StrUtil.toStringOrNull(row.get("avgValue")));
        data.setMaxValue(StrUtil.toStringOrNull(row.get("maxValue")));
        data.setMinValue(StrUtil.toStringOrNull(row.get("minValue")));
        data.setWhetherAnomaly(parseBoolean(row.get("whetherAnomaly")));
        data.setTelemetryDataTime(parseTime(row.get("telemetryDataTime")));
        data.setBillNo(StrUtil.toStringOrNull(row.get("billNo")));
        data.setShiftName(StrUtil.toStringOrNull(row.get("shiftName")));
        return data;
    }
    private Boolean parseBoolean(Object value) {
        if (value == null) {
            return null;
        }
        String str = String.valueOf(value).trim();
        if ("1".equals(str) || "true".equalsIgnoreCase(str) || "Y".equalsIgnoreCase(str)) {
            return Boolean.TRUE;
        }
        if ("0".equals(str) || "false".equalsIgnoreCase(str) || "N".equalsIgnoreCase(str)) {
            return Boolean.FALSE;
        }
        return null;
    }
    private LocalDateTime parseTime(Object value) {
        if (value == null || StrUtil.isBlank(String.valueOf(value))) {
            return null;
        }
        try {
            return DateUtil.parse(String.valueOf(value)).toLocalDateTime();
        } catch (Exception e) {
            log.warn("[parseTime][遥测时间解析失败,value({})]", value);
            return null;
        }
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdDiscountPolicyService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicyPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicyRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicySaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdDiscountPolicyDO;
/**
 * MES ä¼˜æƒ ç­–ç•¥ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdDiscountPolicyService {
    Long createDiscountPolicy(MesPdDiscountPolicySaveReqVO createReqVO);
    void updateDiscountPolicy(MesPdDiscountPolicySaveReqVO updateReqVO);
    void deleteDiscountPolicy(Long id);
    MesPdDiscountPolicyRespVO getDiscountPolicy(Long id);
    PageResult<MesPdDiscountPolicyRespVO> getDiscountPolicyPage(MesPdDiscountPolicyPageReqVO pageReqVO);
    MesPdDiscountPolicyDO validateDiscountPolicyExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdDiscountPolicyServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicyPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicyRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdDiscountPolicySaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdDiscountPolicyDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdDiscountPolicyMapper;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_DISCOUNT_POLICY_NOT_EXISTS;
/**
 * MES ä¼˜æƒ ç­–ç•¥ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdDiscountPolicyServiceImpl implements MesPdDiscountPolicyService {
    @Resource
    private MesPdDiscountPolicyMapper discountPolicyMapper;
    @Override
    public Long createDiscountPolicy(MesPdDiscountPolicySaveReqVO createReqVO) {
        MesPdDiscountPolicyDO entity = BeanUtils.toBean(createReqVO, MesPdDiscountPolicyDO.class);
        discountPolicyMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateDiscountPolicy(MesPdDiscountPolicySaveReqVO updateReqVO) {
        validateDiscountPolicyExists(updateReqVO.getId());
        MesPdDiscountPolicyDO entity = BeanUtils.toBean(updateReqVO, MesPdDiscountPolicyDO.class);
        discountPolicyMapper.updateById(entity);
    }
    @Override
    public void deleteDiscountPolicy(Long id) {
        validateDiscountPolicyExists(id);
        discountPolicyMapper.deleteById(id);
    }
    @Override
    public MesPdDiscountPolicyRespVO getDiscountPolicy(Long id) {
        MesPdDiscountPolicyDO entity = discountPolicyMapper.selectById(id);
        if (entity == null) {
            return null;
        }
        MesPdDiscountPolicyRespVO respVO = BeanUtils.toBean(entity, MesPdDiscountPolicyRespVO.class);
        fillDictName(respVO);
        return respVO;
    }
    @Override
    public PageResult<MesPdDiscountPolicyRespVO> getDiscountPolicyPage(MesPdDiscountPolicyPageReqVO pageReqVO) {
        PageResult<MesPdDiscountPolicyDO> page = discountPolicyMapper.selectPage(pageReqVO);
        PageResult<MesPdDiscountPolicyRespVO> result = BeanUtils.toBean(page, MesPdDiscountPolicyRespVO.class);
        result.getList().forEach(this::fillDictName);
        return result;
    }
    @Override
    public MesPdDiscountPolicyDO validateDiscountPolicyExists(Long id) {
        MesPdDiscountPolicyDO entity = discountPolicyMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_DISCOUNT_POLICY_NOT_EXISTS);
        }
        return entity;
    }
    private void fillDictName(MesPdDiscountPolicyRespVO respVO) {
        respVO.setPolicyTypeName(DictFrameworkUtils.parseDictDataLabel(
                DictTypeConstants.MES_PD_DISCOUNT_POLICY_TYPE, respVO.getPolicyType()));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdElectricityTypeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypeSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdElectricityTypeDO;
import java.util.List;
/**
 * MES ç”¨ç”µç±»åž‹åŸºç¡€ä¿¡æ¯ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdElectricityTypeService {
    Long createElectricityType(MesPdElectricityTypeSaveReqVO createReqVO);
    void updateElectricityType(MesPdElectricityTypeSaveReqVO updateReqVO);
    void deleteElectricityType(Long id);
    MesPdElectricityTypeRespVO getElectricityType(Long id);
    PageResult<MesPdElectricityTypeRespVO> getElectricityTypePage(MesPdElectricityTypePageReqVO pageReqVO);
    List<MesPdElectricityTypeDO> getElectricityTypeList();
    MesPdElectricityTypeDO validateElectricityTypeExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdElectricityTypeServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdElectricityTypeSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdElectricityTypeDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdElectricityTypeMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_ELECTRICITY_TYPE_NOT_EXISTS;
/**
 * MES ç”¨ç”µç±»åž‹åŸºç¡€ä¿¡æ¯ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdElectricityTypeServiceImpl implements MesPdElectricityTypeService {
    @Resource
    private MesPdElectricityTypeMapper electricityTypeMapper;
    @Override
    public Long createElectricityType(MesPdElectricityTypeSaveReqVO createReqVO) {
        MesPdElectricityTypeDO entity = BeanUtils.toBean(createReqVO, MesPdElectricityTypeDO.class);
        electricityTypeMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateElectricityType(MesPdElectricityTypeSaveReqVO updateReqVO) {
        validateElectricityTypeExists(updateReqVO.getId());
        MesPdElectricityTypeDO entity = BeanUtils.toBean(updateReqVO, MesPdElectricityTypeDO.class);
        electricityTypeMapper.updateById(entity);
    }
    @Override
    public void deleteElectricityType(Long id) {
        validateElectricityTypeExists(id);
        electricityTypeMapper.deleteById(id);
    }
    @Override
    public MesPdElectricityTypeRespVO getElectricityType(Long id) {
        MesPdElectricityTypeDO entity = electricityTypeMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, MesPdElectricityTypeRespVO.class);
    }
    @Override
    public PageResult<MesPdElectricityTypeRespVO> getElectricityTypePage(MesPdElectricityTypePageReqVO pageReqVO) {
        return BeanUtils.toBean(electricityTypeMapper.selectPage(pageReqVO), MesPdElectricityTypeRespVO.class);
    }
    @Override
    public List<MesPdElectricityTypeDO> getElectricityTypeList() {
        return electricityTypeMapper.selectList();
    }
    @Override
    public MesPdElectricityTypeDO validateElectricityTypeExists(Long id) {
        MesPdElectricityTypeDO entity = electricityTypeMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_ELECTRICITY_TYPE_NOT_EXISTS);
        }
        return entity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdPriceStandardService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdPriceStandardDO;
/**
 * MES å®šä»·æ ‡å‡† Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdPriceStandardService {
    Long createPriceStandard(MesPdPriceStandardSaveReqVO createReqVO);
    void updatePriceStandard(MesPdPriceStandardSaveReqVO updateReqVO);
    void deletePriceStandard(Long id);
    MesPdPriceStandardRespVO getPriceStandard(Long id);
    PageResult<MesPdPriceStandardRespVO> getPriceStandardPage(MesPdPriceStandardPageReqVO pageReqVO);
    MesPdPriceStandardDO validatePriceStandardExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdPriceStandardServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdPriceStandardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdPriceStandardDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdPriceStandardMapper;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_PRICE_STANDARD_NOT_EXISTS;
/**
 * MES å®šä»·æ ‡å‡† Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdPriceStandardServiceImpl implements MesPdPriceStandardService {
    @Resource
    private MesPdPriceStandardMapper priceStandardMapper;
    @Override
    public Long createPriceStandard(MesPdPriceStandardSaveReqVO createReqVO) {
        MesPdPriceStandardDO entity = BeanUtils.toBean(createReqVO, MesPdPriceStandardDO.class);
        priceStandardMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updatePriceStandard(MesPdPriceStandardSaveReqVO updateReqVO) {
        validatePriceStandardExists(updateReqVO.getId());
        MesPdPriceStandardDO entity = BeanUtils.toBean(updateReqVO, MesPdPriceStandardDO.class);
        priceStandardMapper.updateById(entity);
    }
    @Override
    public void deletePriceStandard(Long id) {
        validatePriceStandardExists(id);
        priceStandardMapper.deleteById(id);
    }
    @Override
    public MesPdPriceStandardRespVO getPriceStandard(Long id) {
        MesPdPriceStandardDO entity = priceStandardMapper.selectById(id);
        if (entity == null) {
            return null;
        }
        MesPdPriceStandardRespVO respVO = BeanUtils.toBean(entity, MesPdPriceStandardRespVO.class);
        fillDictName(respVO);
        return respVO;
    }
    @Override
    public PageResult<MesPdPriceStandardRespVO> getPriceStandardPage(MesPdPriceStandardPageReqVO pageReqVO) {
        PageResult<MesPdPriceStandardDO> page = priceStandardMapper.selectPage(pageReqVO);
        PageResult<MesPdPriceStandardRespVO> result = BeanUtils.toBean(page, MesPdPriceStandardRespVO.class);
        result.getList().forEach(this::fillDictName);
        return result;
    }
    @Override
    public MesPdPriceStandardDO validatePriceStandardExists(Long id) {
        MesPdPriceStandardDO entity = priceStandardMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_PRICE_STANDARD_NOT_EXISTS);
        }
        return entity;
    }
    private void fillDictName(MesPdPriceStandardRespVO respVO) {
        respVO.setObjectTypeName(DictFrameworkUtils.parseDictDataLabel(
                DictTypeConstants.MES_PD_PRICE_OBJECT_TYPE, respVO.getObjectType()));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdServicePackageService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackagePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackageRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackageSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdServicePackageDO;
import java.util.List;
/**
 * MES æœåŠ¡å¥—é¤åŸºç¡€ä¿¡æ¯ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdServicePackageService {
    Long createServicePackage(MesPdServicePackageSaveReqVO createReqVO);
    void updateServicePackage(MesPdServicePackageSaveReqVO updateReqVO);
    void deleteServicePackage(Long id);
    MesPdServicePackageRespVO getServicePackage(Long id);
    PageResult<MesPdServicePackageRespVO> getServicePackagePage(MesPdServicePackagePageReqVO pageReqVO);
    List<MesPdServicePackageDO> getServicePackageList();
    MesPdServicePackageDO validateServicePackageExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdServicePackageServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackagePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackageRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdServicePackageSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdServicePackageDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdServicePackageMapper;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_SERVICE_PACKAGE_NOT_EXISTS;
/**
 * MES æœåŠ¡å¥—é¤åŸºç¡€ä¿¡æ¯ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdServicePackageServiceImpl implements MesPdServicePackageService {
    @Resource
    private MesPdServicePackageMapper servicePackageMapper;
    @Resource
    @Lazy
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createServicePackage(MesPdServicePackageSaveReqVO createReqVO) {
        MesPdServicePackageDO entity = BeanUtils.toBean(createReqVO, MesPdServicePackageDO.class);
        entity.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PD_PACKAGE_CODE.getCode()));
        servicePackageMapper.insert(entity);
        return entity.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateServicePackage(MesPdServicePackageSaveReqVO updateReqVO) {
        MesPdServicePackageDO packageDO = validateServicePackageExists(updateReqVO.getId());
        MesPdServicePackageDO entity = BeanUtils.toBean(updateReqVO, MesPdServicePackageDO.class);
        entity.setCode(packageDO.getCode());
        servicePackageMapper.updateById(entity);
    }
    @Override
    public void deleteServicePackage(Long id) {
        validateServicePackageExists(id);
        servicePackageMapper.deleteById(id);
    }
    @Override
    public MesPdServicePackageRespVO getServicePackage(Long id) {
        MesPdServicePackageDO entity = servicePackageMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, MesPdServicePackageRespVO.class);
    }
    @Override
    public PageResult<MesPdServicePackageRespVO> getServicePackagePage(MesPdServicePackagePageReqVO pageReqVO) {
        return BeanUtils.toBean(servicePackageMapper.selectPage(pageReqVO), MesPdServicePackageRespVO.class);
    }
    @Override
    public List<MesPdServicePackageDO> getServicePackageList() {
        return servicePackageMapper.selectList();
    }
    @Override
    public MesPdServicePackageDO validateServicePackageExists(Long id) {
        MesPdServicePackageDO entity = servicePackageMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_SERVICE_PACKAGE_NOT_EXISTS);
        }
        return entity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdTieredPriceService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPricePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPriceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPriceSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdTieredPriceDO;
/**
 * MES é˜¶æ¢¯ç”µä»· Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdTieredPriceService {
    Long createTieredPrice(MesPdTieredPriceSaveReqVO createReqVO);
    void updateTieredPrice(MesPdTieredPriceSaveReqVO updateReqVO);
    void deleteTieredPrice(Long id);
    MesPdTieredPriceRespVO getTieredPrice(Long id);
    PageResult<MesPdTieredPriceRespVO> getTieredPricePage(MesPdTieredPricePageReqVO pageReqVO);
    MesPdTieredPriceDO validateTieredPriceExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdTieredPriceServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPricePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPriceRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdTieredPriceSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdTieredPriceDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdTieredPriceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_TIERED_PRICE_NOT_EXISTS;
/**
 * MES é˜¶æ¢¯ç”µä»· Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdTieredPriceServiceImpl implements MesPdTieredPriceService {
    @Resource
    private MesPdTieredPriceMapper tieredPriceMapper;
    @Override
    public Long createTieredPrice(MesPdTieredPriceSaveReqVO createReqVO) {
        MesPdTieredPriceDO entity = BeanUtils.toBean(createReqVO, MesPdTieredPriceDO.class);
        tieredPriceMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateTieredPrice(MesPdTieredPriceSaveReqVO updateReqVO) {
        validateTieredPriceExists(updateReqVO.getId());
        MesPdTieredPriceDO entity = BeanUtils.toBean(updateReqVO, MesPdTieredPriceDO.class);
        tieredPriceMapper.updateById(entity);
    }
    @Override
    public void deleteTieredPrice(Long id) {
        validateTieredPriceExists(id);
        tieredPriceMapper.deleteById(id);
    }
    @Override
    public MesPdTieredPriceRespVO getTieredPrice(Long id) {
        MesPdTieredPriceDO entity = tieredPriceMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, MesPdTieredPriceRespVO.class);
    }
    @Override
    public PageResult<MesPdTieredPriceRespVO> getTieredPricePage(MesPdTieredPricePageReqVO pageReqVO) {
        return BeanUtils.toBean(tieredPriceMapper.selectPage(pageReqVO), MesPdTieredPriceRespVO.class);
    }
    @Override
    public MesPdTieredPriceDO validateTieredPriceExists(Long id) {
        MesPdTieredPriceDO entity = tieredPriceMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_TIERED_PRICE_NOT_EXISTS);
        }
        return entity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdVoltageLevelService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdVoltageLevelDO;
import java.util.List;
/**
 * MES ç”µåŽ‹ç­‰çº§åŸºç¡€ä¿¡æ¯ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdVoltageLevelService {
    Long createVoltageLevel(MesPdVoltageLevelSaveReqVO createReqVO);
    void updateVoltageLevel(MesPdVoltageLevelSaveReqVO updateReqVO);
    void deleteVoltageLevel(Long id);
    MesPdVoltageLevelRespVO getVoltageLevel(Long id);
    PageResult<MesPdVoltageLevelRespVO> getVoltageLevelPage(MesPdVoltageLevelPageReqVO pageReqVO);
    List<MesPdVoltageLevelDO> getVoltageLevelList();
    MesPdVoltageLevelDO validateVoltageLevelExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/basedata/MesPdVoltageLevelServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.mes.service.pd.basedata;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.basedata.vo.MesPdVoltageLevelSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdVoltageLevelDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdVoltageLevelMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_VOLTAGE_LEVEL_NOT_EXISTS;
/**
 * MES ç”µåŽ‹ç­‰çº§åŸºç¡€ä¿¡æ¯ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesPdVoltageLevelServiceImpl implements MesPdVoltageLevelService {
    @Resource
    private MesPdVoltageLevelMapper voltageLevelMapper;
    @Override
    public Long createVoltageLevel(MesPdVoltageLevelSaveReqVO createReqVO) {
        MesPdVoltageLevelDO entity = BeanUtils.toBean(createReqVO, MesPdVoltageLevelDO.class);
        voltageLevelMapper.insert(entity);
        return entity.getId();
    }
    @Override
    public void updateVoltageLevel(MesPdVoltageLevelSaveReqVO updateReqVO) {
        validateVoltageLevelExists(updateReqVO.getId());
        MesPdVoltageLevelDO entity = BeanUtils.toBean(updateReqVO, MesPdVoltageLevelDO.class);
        voltageLevelMapper.updateById(entity);
    }
    @Override
    public void deleteVoltageLevel(Long id) {
        validateVoltageLevelExists(id);
        voltageLevelMapper.deleteById(id);
    }
    @Override
    public MesPdVoltageLevelRespVO getVoltageLevel(Long id) {
        MesPdVoltageLevelDO entity = voltageLevelMapper.selectById(id);
        return entity == null ? null : BeanUtils.toBean(entity, MesPdVoltageLevelRespVO.class);
    }
    @Override
    public PageResult<MesPdVoltageLevelRespVO> getVoltageLevelPage(MesPdVoltageLevelPageReqVO pageReqVO) {
        return BeanUtils.toBean(voltageLevelMapper.selectPage(pageReqVO), MesPdVoltageLevelRespVO.class);
    }
    @Override
    public List<MesPdVoltageLevelDO> getVoltageLevelList() {
        return voltageLevelMapper.selectList();
    }
    @Override
    public MesPdVoltageLevelDO validateVoltageLevelExists(Long id) {
        MesPdVoltageLevelDO entity = voltageLevelMapper.selectById(id);
        if (entity == null) {
            throw exception(PD_VOLTAGE_LEVEL_NOT_EXISTS);
        }
        return entity;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/ctc/ProductCtcService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.service.pd.ctc;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
/**
 * MES æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚æ•° Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface ProductCtcService {
    Long createCtc(ProductCtcSaveReqVO createReqVO);
    void updateCtc(ProductCtcSaveReqVO updateReqVO);
    void deleteCtc(Long id);
    ProductCtcDO getCtc(Long id);
    PageResult<ProductCtcDO> getCtcPage(ProductCtcPageReqVO pageReqVO);
    /**
     * ç»•包角度计算(已知纸带宽度,计算最外层绕包角度)
     */
    ProductCtcAngleCalcRespVO calcAngle(ProductCtcAngleCalcReqVO reqVO);
    /**
     * ç›´æµç”µé˜»è®¡ç®—(按温度换算)
     */
    ProductCtcResistanceCalcRespVO calcResistance(ProductCtcResistanceCalcReqVO reqVO);
    /**
     * ç›˜å…·å®¹é‡è®¡ç®—(每盘最大长度)
     */
    ProductCtcReelCalcRespVO calcReelCapacity(ProductCtcReelCalcReqVO reqVO);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/ctc/ProductCtcServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,175 @@
package cn.iocoder.yudao.module.mes.service.pd.ctc;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mes.controller.admin.pd.ctc.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.ctc.ProductCtcMapper;
import cn.iocoder.yudao.module.mes.service.pd.reel.ProductCtcReelService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_CTC_CALC_PARAM_INVALID;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_CTC_ITEM_DUPLICATE;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_CTC_NOT_EXISTS;
/**
 * MES æ¢ä½å¯¼çº¿äº§å“æŠ€æœ¯å‚æ•° Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class ProductCtcServiceImpl implements ProductCtcService {
    @Resource
    private ProductCtcMapper ctcMapper;
    @Resource
    private MdmItemApi mdmItemApi;
    @Resource
    private ProductCtcReelService reelService;
    @Override
    public Long createCtc(ProductCtcSaveReqVO createReqVO) {
        // æ ¡éªŒç‰©æ–™å­˜åœ¨
        mdmItemApi.validateItemExists(createReqVO.getItemId());
        // æ ¡éªŒç‰©æ–™å”¯ä¸€
        validateItemUnique(createReqVO.getItemId(), null);
        // æ’å…¥
        ProductCtcDO ctc = BeanUtils.toBean(createReqVO, ProductCtcDO.class);
        ctcMapper.insert(ctc);
        return ctc.getId();
    }
    @Override
    public void updateCtc(ProductCtcSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        ProductCtcDO ctc = validateCtcExists(updateReqVO.getId());
        // æ ¡éªŒç‰©æ–™å­˜åœ¨
        mdmItemApi.validateItemExists(updateReqVO.getItemId());
        // æ ¡éªŒç‰©æ–™å”¯ä¸€ï¼ˆæŽ’除自身)
        validateItemUnique(updateReqVO.getItemId(), updateReqVO.getId());
        // æ›´æ–°
        ProductCtcDO updateObj = BeanUtils.toBean(updateReqVO, ProductCtcDO.class);
        ctcMapper.updateById(updateObj);
    }
    @Override
    public void deleteCtc(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateCtcExists(id);
        // åˆ é™¤å…³è”盘具
        reelService.deleteByCtcId(id);
        // åˆ é™¤
        ctcMapper.deleteById(id);
    }
    @Override
    public ProductCtcDO getCtc(Long id) {
        return ctcMapper.selectById(id);
    }
    @Override
    public PageResult<ProductCtcDO> getCtcPage(ProductCtcPageReqVO pageReqVO) {
        return ctcMapper.selectPage(pageReqVO);
    }
    @Override
    public ProductCtcAngleCalcRespVO calcAngle(ProductCtcAngleCalcReqVO reqVO) {
        double a = reqVO.getConductorThickness().doubleValue();
        double b = reqVO.getConductorWidth().doubleValue();
        double r = reqVO.getCornerRadius().doubleValue();
        double t = reqVO.getInsulationThickness().doubleValue();
        double w = reqVO.getTapeWidth().doubleValue();
        double overlap = reqVO.getOverlapWidth().doubleValue();
        // å¯¼çº¿å‘¨é•¿ï¼šåœ†è§’矩形,厚度方向含绝缘,宽度方向不含
        double circumference = 2 * (a + b + 2 * t) - (8 - 2 * Math.PI) * r;
        // æœ‰æ•ˆçº¸å¸¦å®½åº¦ = çº¸å¸¦å®½åº¦ - é‡å å®½åº¦
        double effectiveWidth = w - overlap;
        if (circumference <= 0 || effectiveWidth <= 0 || effectiveWidth > circumference) {
            throw exception(PD_CTC_CALC_PARAM_INVALID);
        }
        double angle = Math.asin(effectiveWidth / circumference);
        double angleDeg = Math.toDegrees(angle);
        double pitch = effectiveWidth / Math.cos(angle);
        ProductCtcAngleCalcRespVO respVO = new ProductCtcAngleCalcRespVO();
        respVO.setCircumference(BigDecimal.valueOf(circumference).setScale(6, RoundingMode.HALF_UP));
        respVO.setWrappingAngle(BigDecimal.valueOf(angleDeg).setScale(6, RoundingMode.HALF_UP));
        respVO.setWrappingPitch(BigDecimal.valueOf(pitch).setScale(6, RoundingMode.HALF_UP));
        return respVO;
    }
    @Override
    public ProductCtcResistanceCalcRespVO calcResistance(ProductCtcResistanceCalcReqVO reqVO) {
        double thickness = reqVO.getConductorThickness().doubleValue();
        double width = reqVO.getConductorWidth().doubleValue();
        double temp = reqVO.getTemperature().doubleValue();
        // æè´¨å‚数:铜/铝(ρ20 å•位 Î©Â·mm²/m,α å•位 1/℃)
        double rho20;
        double alpha;
        if ("aluminum".equalsIgnoreCase(reqVO.getMaterial())) {
            rho20 = 0.0283;
            alpha = 0.00403;
        } else {
            rho20 = 0.0175;
            alpha = 0.00393;
        }
        double area = thickness * width;
        if (area <= 0) {
            throw exception(PD_CTC_CALC_PARAM_INVALID);
        }
        // R20 = Ï20 / A(Ω/m)
        double r20 = rho20 / area;
        // Rt = R20 Ã— (1 + Î± Ã— (t - 20))
        double rt = r20 * (1 + alpha * (temp - 20));
        ProductCtcResistanceCalcRespVO respVO = new ProductCtcResistanceCalcRespVO();
        respVO.setCrossSection(BigDecimal.valueOf(area).setScale(6, RoundingMode.HALF_UP));
        respVO.setResistance20cKm(BigDecimal.valueOf(r20 * 1000).setScale(6, RoundingMode.HALF_UP));
        respVO.setResistanceAtTemp(BigDecimal.valueOf(rt).setScale(6, RoundingMode.HALF_UP));
        respVO.setResistanceAtTempKm(BigDecimal.valueOf(rt * 1000).setScale(6, RoundingMode.HALF_UP));
        return respVO;
    }
    @Override
    public ProductCtcReelCalcRespVO calcReelCapacity(ProductCtcReelCalcReqVO reqVO) {
        double d1 = reqVO.getReelOuterDiameter().doubleValue();
        double d2 = reqVO.getReelInnerDiameter().doubleValue();
        double l2 = reqVO.getReelWidth().doubleValue();
        double ct = reqVO.getCtcThickness().doubleValue();
        double cw = reqVO.getCtcWidth().doubleValue();
        if (d1 <= d2 || l2 <= 0 || ct <= 0 || cw <= 0) {
            throw exception(PD_CTC_CALC_PARAM_INVALID);
        }
        // ç›˜å…·å®¹é‡å…¬å¼ï¼ˆå‚照需求文档盘具计算 sheet):
        // ((d1-60)+(d2+10)) Ã— INT(l2/(CTC宽+1)) Ã— (d1-d2-70) / (CTC高+0.2) / 1274
        double capacityM = ((d1 - 60) + (d2 + 10)) * Math.floor(l2 / (cw + 1))
                * (d1 - d2 - 70) / (ct + 0.2) / 1274;
        ProductCtcReelCalcRespVO respVO = new ProductCtcReelCalcRespVO();
        respVO.setCapacityMeter(BigDecimal.valueOf(capacityM).setScale(2, RoundingMode.HALF_UP));
        respVO.setCapacityKm(BigDecimal.valueOf(capacityM / 1000).setScale(4, RoundingMode.HALF_UP));
        return respVO;
    }
    private void validateItemUnique(Long itemId, Long excludeId) {
        ProductCtcDO ctc = ctcMapper.selectByItemId(itemId);
        if (ctc != null && !ctc.getId().equals(excludeId)) {
            throw exception(PD_CTC_ITEM_DUPLICATE);
        }
    }
    private ProductCtcDO validateCtcExists(Long id) {
        ProductCtcDO ctc = ctcMapper.selectById(id);
        if (ctc == null) {
            throw exception(PD_CTC_NOT_EXISTS);
        }
        return ctc;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/price/MesPdPriceService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.service.pd.price;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcRespVO;
/**
 * MES é˜¶æ¢¯ç”µä»·æµ‹ç®— Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdPriceService {
    /**
     * é˜¶æ¢¯ç”µä»·æµ‹ç®—:按用电量分档累计,叠加优惠策略
     *
     * @param reqVO æµ‹ç®—请求
     * @return æµ‹ç®—结果
     */
    MesPdPriceCalcRespVO calcPrice(MesPdPriceCalcReqVO reqVO);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/price/MesPdPriceServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
package cn.iocoder.yudao.module.mes.service.pd.price;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.price.vo.MesPdPriceCalcRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdDiscountPolicyDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdServicePackageDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdTieredPriceDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.basedata.MesPdVoltageLevelDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdDiscountPolicyMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.basedata.MesPdTieredPriceMapper;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdServicePackageService;
import cn.iocoder.yudao.module.mes.service.pd.basedata.MesPdVoltageLevelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_PRICE_CALC_OBJECT_NOT_CONFIGURED;
/**
 * MES é˜¶æ¢¯ç”µä»·æµ‹ç®— Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
@Slf4j
public class MesPdPriceServiceImpl implements MesPdPriceService {
    private static final Integer CALC_TYPE_PACKAGE = 1;
    private static final Integer CALC_TYPE_VOLTAGE = 2;
    @Resource
    private MesPdTieredPriceMapper tieredPriceMapper;
    @Resource
    private MesPdDiscountPolicyMapper discountPolicyMapper;
    @Resource
    private MesPdServicePackageService servicePackageService;
    @Resource
    private MesPdVoltageLevelService voltageLevelService;
    @Override
    public MesPdPriceCalcRespVO calcPrice(MesPdPriceCalcReqVO reqVO) {
        // 1. å®šä½é˜¶æ¢¯ç”µä»·æ¡£ä½
        List<MesPdTieredPriceDO> tiers = getTieredPrices(reqVO);
        if (CollUtil.isEmpty(tiers)) {
            throw exception(PD_PRICE_CALC_OBJECT_NOT_CONFIGURED);
        }
        // 2. åˆ†æ¡£ç´¯è®¡è®¡ç®—原价
        BigDecimal quantity = reqVO.getQuantity();
        BigDecimal originalPrice = BigDecimal.ZERO;
        List<MesPdPriceCalcDetailRespVO> details = new ArrayList<>();
        for (MesPdTieredPriceDO tier : tiers) {
            BigDecimal lower = tier.getStartValue();
            BigDecimal upper = tier.getEndValue();
            if (quantity.compareTo(lower) <= 0) {
                break;
            }
            BigDecimal upperBound = upper == null ? quantity : upper.min(quantity);
            BigDecimal tierQuantity = upperBound.subtract(lower);
            if (tierQuantity.signum() <= 0) {
                continue;
            }
            BigDecimal subtotal = tier.getPricePerUnit().multiply(tierQuantity);
            originalPrice = originalPrice.add(subtotal);
            MesPdPriceCalcDetailRespVO detail = new MesPdPriceCalcDetailRespVO();
            detail.setStartValue(lower);
            detail.setEndValue(upper);
            detail.setTierQuantity(tierQuantity);
            detail.setPricePerUnit(tier.getPricePerUnit());
            detail.setSubtotal(subtotal);
            details.add(detail);
            if (upper != null && quantity.compareTo(upper) <= 0) {
                break;
            }
        }
        // 3. å åŠ ä¼˜æƒ ç­–ç•¥ï¼ˆç›®æ ‡ä¸“å±ž + å…¨å±€ï¼Œå–最优)
        BigDecimal finalPrice = originalPrice;
        Integer targetType = Objects.equals(CALC_TYPE_PACKAGE, reqVO.getCalcType()) ? 1 : 2;
        Long targetId = Objects.equals(CALC_TYPE_PACKAGE, reqVO.getCalcType()) ? reqVO.getPackageId() : reqVO.getVoltageLevelId();
        List<MesPdDiscountPolicyDO> policies = discountPolicyMapper.selectListByTarget(targetType, targetId);
        LocalDateTime now = LocalDateTime.now();
        for (MesPdDiscountPolicyDO policy : policies) {
            if (policy.getStartDate() != null && now.isBefore(policy.getStartDate())) {
                continue;
            }
            if (policy.getEndDate() != null && now.isAfter(policy.getEndDate())) {
                continue;
            }
            if (policy.getPolicyValue() == null) {
                continue;
            }
            BigDecimal candidate;
            if (Objects.equals(policy.getPolicyType(), 1)) { // æŠ˜æ‰£
                candidate = originalPrice.multiply(policy.getPolicyValue());
            } else { // ç«‹å‡
                candidate = originalPrice.subtract(policy.getPolicyValue()).max(BigDecimal.ZERO);
            }
            finalPrice = finalPrice.min(candidate);
        }
        // 4. ç»„装返回
        MesPdPriceCalcRespVO respVO = new MesPdPriceCalcRespVO();
        respVO.setOriginalPrice(originalPrice);
        respVO.setFinalPrice(finalPrice);
        respVO.setDiscountAmount(originalPrice.subtract(finalPrice));
        respVO.setDetails(details);
        if (reqVO.getPackageId() != null) {
            MesPdServicePackageDO pkg = servicePackageService.validateServicePackageExists(reqVO.getPackageId());
            respVO.setPackageName(pkg.getName());
        }
        if (reqVO.getVoltageLevelId() != null) {
            MesPdVoltageLevelDO level = voltageLevelService.validateVoltageLevelExists(reqVO.getVoltageLevelId());
            respVO.setVoltageLevelName(level.getName());
        }
        return respVO;
    }
    private List<MesPdTieredPriceDO> getTieredPrices(MesPdPriceCalcReqVO reqVO) {
        if (Objects.equals(CALC_TYPE_PACKAGE, reqVO.getCalcType()) && reqVO.getPackageId() != null) {
            return tieredPriceMapper.selectListByPackageId(reqVO.getPackageId());
        }
        if (Objects.equals(CALC_TYPE_VOLTAGE, reqVO.getCalcType()) && reqVO.getVoltageLevelId() != null) {
            return tieredPriceMapper.selectListByVoltageLevelId(reqVO.getVoltageLevelId());
        }
        return null;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.mes.service.pd.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.product.MesPdProductDO;
/**
 * MES äº§å“æ¡£æ¡ˆ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface MesPdProductService {
    Long createProduct(MesPdProductSaveReqVO createReqVO);
    void updateProduct(MesPdProductSaveReqVO updateReqVO);
    void deleteProduct(Long id);
    MesPdProductRespVO getProduct(Long id);
    PageResult<MesPdProductRespVO> getProductPage(MesPdProductPageReqVO pageReqVO);
    /**
     * æäº¤å®¡æ‰¹ï¼ˆæœªæäº¤/驳回 -> å®¡æ‰¹ä¸­ï¼‰
     */
    void submitProduct(Long id);
    /**
     * å®¡æ‰¹ï¼ˆå®¡æ‰¹ä¸­ -> é€šè¿‡/驳回)
     *
     * @param id äº§å“æ¡£æ¡ˆç¼–号
     * @param pass æ˜¯å¦é€šè¿‡
     * @param auditRemark å®¡æ‰¹æ„è§
     */
    void auditProduct(Long id, Boolean pass, String auditRemark);
    MesPdProductDO validateProductExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,151 @@
package cn.iocoder.yudao.module.mes.service.pd.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.product.vo.MesPdProductSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.product.MesPdProductDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.product.MesPdProductMapper;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pd.MesPdAuditStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_PRODUCT_AUDIT_STATUS_INVALID;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_PRODUCT_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_PRODUCT_SUBMIT_DUPLICATE;
/**
 * MES äº§å“æ¡£æ¡ˆ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
@Slf4j
public class MesPdProductServiceImpl implements MesPdProductService {
    @Resource
    private MesPdProductMapper productMapper;
    @Resource
    @Lazy
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createProduct(MesPdProductSaveReqVO createReqVO) {
        MesPdProductDO product = BeanUtils.toBean(createReqVO, MesPdProductDO.class);
        product.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PD_PRODUCT_CODE.getCode()));
        product.setAuditStatus(MesPdAuditStatusEnum.DRAFT.getStatus());
        productMapper.insert(product);
        return product.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProduct(MesPdProductSaveReqVO updateReqVO) {
        MesPdProductDO product = validateProductExists(updateReqVO.getId());
        validateEditable(product);
        MesPdProductDO updateObj = BeanUtils.toBean(updateReqVO, MesPdProductDO.class);
        updateObj.setCode(product.getCode());
        productMapper.updateById(updateObj);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProduct(Long id) {
        MesPdProductDO product = validateProductExists(id);
        validateEditable(product);
        productMapper.deleteById(id);
    }
    @Override
    public MesPdProductRespVO getProduct(Long id) {
        MesPdProductDO product = productMapper.selectById(id);
        return product == null ? null : convertToRespVO(product);
    }
    @Override
    public PageResult<MesPdProductRespVO> getProductPage(MesPdProductPageReqVO pageReqVO) {
        PageResult<MesPdProductDO> page = productMapper.selectPage(pageReqVO);
        PageResult<MesPdProductRespVO> result = BeanUtils.toBean(page, MesPdProductRespVO.class);
        result.getList().forEach(this::fillDictName);
        return result;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitProduct(Long id) {
        MesPdProductDO product = validateProductExists(id);
        // ä»…未提交/驳回可提交审批
        if (!MesPdAuditStatusEnum.DRAFT.getStatus().equals(product.getAuditStatus())
                && !MesPdAuditStatusEnum.REJECT.getStatus().equals(product.getAuditStatus())) {
            throw exception(PD_PRODUCT_SUBMIT_DUPLICATE);
        }
        productMapper.updateById(MesPdProductDO.builder()
                .id(id)
                .auditStatus(MesPdAuditStatusEnum.PROCESS.getStatus())
                .auditRemark(null)
                .auditTime(null)
                .build());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditProduct(Long id, Boolean pass, String auditRemark) {
        MesPdProductDO product = validateProductExists(id);
        // ä»…审批中可审核
        if (!MesPdAuditStatusEnum.PROCESS.getStatus().equals(product.getAuditStatus())) {
            throw exception(PD_PRODUCT_AUDIT_STATUS_INVALID);
        }
        productMapper.updateById(MesPdProductDO.builder()
                .id(id)
                .auditStatus(pass ? MesPdAuditStatusEnum.APPROVE.getStatus() : MesPdAuditStatusEnum.REJECT.getStatus())
                .auditRemark(auditRemark)
                .auditTime(LocalDateTime.now())
                .build());
    }
    @Override
    public MesPdProductDO validateProductExists(Long id) {
        MesPdProductDO product = productMapper.selectById(id);
        if (product == null) {
            throw exception(PD_PRODUCT_NOT_EXISTS);
        }
        return product;
    }
    private void validateEditable(MesPdProductDO product) {
        // å®¡æ‰¹ä¸­/已通过不可修改或删除
        if (!MesPdAuditStatusEnum.DRAFT.getStatus().equals(product.getAuditStatus())
                && !MesPdAuditStatusEnum.REJECT.getStatus().equals(product.getAuditStatus())) {
            throw exception(PD_PRODUCT_AUDIT_STATUS_INVALID);
        }
    }
    private MesPdProductRespVO convertToRespVO(MesPdProductDO product) {
        MesPdProductRespVO respVO = BeanUtils.toBean(product, MesPdProductRespVO.class);
        fillDictName(respVO);
        return respVO;
    }
    private void fillDictName(MesPdProductRespVO respVO) {
        respVO.setArchiveTypeName(DictFrameworkUtils.parseDictDataLabel(
                DictTypeConstants.MES_PD_PRODUCT_ARCHIVE_TYPE, respVO.getArchiveType()));
        respVO.setAuditStatusName(DictFrameworkUtils.parseDictDataLabel(
                DictTypeConstants.MES_PD_PRODUCT_AUDIT_STATUS, respVO.getAuditStatus()));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/reel/ProductCtcReelService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.mes.service.pd.reel;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.reel.ProductCtcReelDO;
import java.util.List;
/**
 * MES æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®— Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface ProductCtcReelService {
    Long createReel(ProductCtcReelSaveReqVO createReqVO);
    void updateReel(ProductCtcReelSaveReqVO updateReqVO);
    void deleteReel(Long id);
    ProductCtcReelDO getReel(Long id);
    PageResult<ProductCtcReelDO> getReelPage(ProductCtcReelPageReqVO pageReqVO);
    List<ProductCtcReelDO> getReelListByCtcId(Long ctcId);
    void deleteByCtcId(Long ctcId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/reel/ProductCtcReelServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.mes.service.pd.reel;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.reel.vo.ProductCtcReelSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.ctc.ProductCtcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.reel.ProductCtcReelDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.reel.ProductCtcReelMapper;
import cn.iocoder.yudao.module.mes.service.pd.ctc.ProductCtcService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_CTC_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_CTC_REEL_NOT_EXISTS;
/**
 * MES æ¢ä½å¯¼çº¿ç›˜å…·è®¡ç®— Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class ProductCtcReelServiceImpl implements ProductCtcReelService {
    @Resource
    private ProductCtcReelMapper reelMapper;
    @Resource
    @Lazy
    private ProductCtcService ctcService;
    @Override
    public Long createReel(ProductCtcReelSaveReqVO createReqVO) {
        // æ ¡éªŒæ¢ä½å¯¼çº¿äº§å“å­˜åœ¨
        validateCtcExists(createReqVO.getCtcId());
        // æ’å…¥
        ProductCtcReelDO reel = BeanUtils.toBean(createReqVO, ProductCtcReelDO.class);
        reelMapper.insert(reel);
        return reel.getId();
    }
    @Override
    public void updateReel(ProductCtcReelSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateReelExists(updateReqVO.getId());
        // æ ¡éªŒæ¢ä½å¯¼çº¿äº§å“å­˜åœ¨
        validateCtcExists(updateReqVO.getCtcId());
        // æ›´æ–°
        ProductCtcReelDO updateObj = BeanUtils.toBean(updateReqVO, ProductCtcReelDO.class);
        reelMapper.updateById(updateObj);
    }
    @Override
    public void deleteReel(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateReelExists(id);
        // åˆ é™¤
        reelMapper.deleteById(id);
    }
    @Override
    public ProductCtcReelDO getReel(Long id) {
        return reelMapper.selectById(id);
    }
    @Override
    public PageResult<ProductCtcReelDO> getReelPage(ProductCtcReelPageReqVO pageReqVO) {
        return reelMapper.selectPage(pageReqVO);
    }
    @Override
    public List<ProductCtcReelDO> getReelListByCtcId(Long ctcId) {
        return reelMapper.selectListByCtcId(ctcId);
    }
    @Override
    public void deleteByCtcId(Long ctcId) {
        reelMapper.deleteByCtcId(ctcId);
    }
    private void validateCtcExists(Long ctcId) {
        ProductCtcDO ctc = ctcService.getCtc(ctcId);
        if (ctc == null) {
            throw exception(PD_CTC_NOT_EXISTS);
        }
    }
    private ProductCtcReelDO validateReelExists(Long id) {
        ProductCtcReelDO reel = reelMapper.selectById(id);
        if (reel == null) {
            throw exception(PD_CTC_REEL_NOT_EXISTS);
        }
        return reel;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/techconfirm/ProductTechConfirmService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.mes.service.pd.techconfirm;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.techconfirm.ProductTechConfirmDO;
/**
 * MES äº§å“æŠ€æœ¯ç¡®è®¤å‡½ Service æŽ¥å£
 *
 * @author è¶…级管理员
 */
public interface ProductTechConfirmService {
    Long createTechConfirm(ProductTechConfirmSaveReqVO createReqVO);
    void updateTechConfirm(ProductTechConfirmSaveReqVO updateReqVO);
    void deleteTechConfirm(Long id);
    ProductTechConfirmDO getTechConfirm(Long id);
    PageResult<ProductTechConfirmDO> getTechConfirmPage(ProductTechConfirmPageReqVO pageReqVO);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/techconfirm/ProductTechConfirmServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package cn.iocoder.yudao.module.mes.service.pd.techconfirm;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.techconfirm.vo.ProductTechConfirmSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.techconfirm.ProductTechConfirmDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.techconfirm.ProductTechConfirmMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_TECH_CONFIRM_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_TECH_CONFIRM_NO_DUPLICATE;
/**
 * MES äº§å“æŠ€æœ¯ç¡®è®¤å‡½ Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class ProductTechConfirmServiceImpl implements ProductTechConfirmService {
    @Resource
    private ProductTechConfirmMapper techConfirmMapper;
    @Override
    public Long createTechConfirm(ProductTechConfirmSaveReqVO createReqVO) {
        // æ ¡éªŒç¡®è®¤å‡½ç¼–号唯一
        validateConfirmNoUnique(createReqVO.getConfirmNo(), null);
        // æ’å…¥
        ProductTechConfirmDO techConfirm = BeanUtils.toBean(createReqVO, ProductTechConfirmDO.class);
        techConfirmMapper.insert(techConfirm);
        return techConfirm.getId();
    }
    @Override
    public void updateTechConfirm(ProductTechConfirmSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateTechConfirmExists(updateReqVO.getId());
        // æ ¡éªŒç¡®è®¤å‡½ç¼–号唯一(排除自身)
        validateConfirmNoUnique(updateReqVO.getConfirmNo(), updateReqVO.getId());
        // æ›´æ–°
        ProductTechConfirmDO updateObj = BeanUtils.toBean(updateReqVO, ProductTechConfirmDO.class);
        techConfirmMapper.updateById(updateObj);
    }
    @Override
    public void deleteTechConfirm(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateTechConfirmExists(id);
        // åˆ é™¤
        techConfirmMapper.deleteById(id);
    }
    @Override
    public ProductTechConfirmDO getTechConfirm(Long id) {
        return techConfirmMapper.selectById(id);
    }
    @Override
    public PageResult<ProductTechConfirmDO> getTechConfirmPage(ProductTechConfirmPageReqVO pageReqVO) {
        return techConfirmMapper.selectPage(pageReqVO);
    }
    private void validateConfirmNoUnique(String confirmNo, Long excludeId) {
        if (confirmNo == null) {
            return;
        }
        ProductTechConfirmDO techConfirm = techConfirmMapper.selectOne(
                ProductTechConfirmDO::getConfirmNo, confirmNo);
        if (techConfirm != null && !techConfirm.getId().equals(excludeId)) {
            throw exception(PD_TECH_CONFIRM_NO_DUPLICATE);
        }
    }
    private ProductTechConfirmDO validateTechConfirmExists(Long id) {
        ProductTechConfirmDO techConfirm = techConfirmMapper.selectById(id);
        if (techConfirm == null) {
            throw exception(PD_TECH_CONFIRM_NOT_EXISTS);
        }
        return techConfirm;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/emergencyplan/MesProEmergencyPlanService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.mes.service.pro.emergencyplan;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.emergencyplan.MesProEmergencyPlanDO;
import java.util.Collection;
/**
 * MES åº”急处置预案 Service æŽ¥å£
 *
 * @author xiaoyi
 */
public interface MesProEmergencyPlanService {
    Long createPlan(MesProEmergencyPlanSaveReqVO createReqVO);
    void updatePlan(MesProEmergencyPlanSaveReqVO updateReqVO);
    void deletePlan(Collection<Long> ids);
    MesProEmergencyPlanDO getPlan(Long id);
    PageResult<MesProEmergencyPlanDO> getPlanPage(MesProEmergencyPlanPageReqVO pageReqVO);
    void publishPlan(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/emergencyplan/MesProEmergencyPlanServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package cn.iocoder.yudao.module.mes.service.pro.emergencyplan;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.emergencyplan.vo.MesProEmergencyPlanSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.emergencyplan.MesProEmergencyPlanDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.emergencyplan.MesProEmergencyPlanMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_NOT_PREPARE;
/**
 * MES åº”急处置预案 Service å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
public class MesProEmergencyPlanServiceImpl implements MesProEmergencyPlanService {
    private static final Integer STATUS_DRAFT = 0;
    private static final Integer STATUS_PUBLISHED = 10;
    @Resource
    private MesProEmergencyPlanMapper planMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPlan(MesProEmergencyPlanSaveReqVO createReqVO) {
        String code = generateCode();
        if (planMapper.selectByCode(code) != null) {
            throw exception(PRO_WORK_ORDER_NOT_EXISTS);
        }
        MesProEmergencyPlanDO plan = BeanUtils.toBean(createReqVO, MesProEmergencyPlanDO.class, req -> req
                .setCode(code)
                .setStatus(STATUS_DRAFT));
        planMapper.insert(plan);
        return plan.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePlan(MesProEmergencyPlanSaveReqVO updateReqVO) {
        MesProEmergencyPlanDO oldPlan = validatePlanCanModify(updateReqVO.getId());
        MesProEmergencyPlanDO updateObj = BeanUtils.toBean(updateReqVO, MesProEmergencyPlanDO.class, req -> req
                .setCode(oldPlan.getCode())
                .setStatus(oldPlan.getStatus()));
        planMapper.updateById(updateObj);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePlan(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        for (Long id : ids) {
            validatePlanCanModify(id);
            planMapper.deleteById(id);
        }
    }
    @Override
    public MesProEmergencyPlanDO getPlan(Long id) {
        return planMapper.selectById(id);
    }
    @Override
    public PageResult<MesProEmergencyPlanDO> getPlanPage(MesProEmergencyPlanPageReqVO pageReqVO) {
        return planMapper.selectPage(pageReqVO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void publishPlan(Long id) {
        MesProEmergencyPlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        planMapper.updateById(new MesProEmergencyPlanDO().setId(id).setStatus(STATUS_PUBLISHED));
    }
    private String generateCode() {
        return "YJ" + DateUtil.format(java.time.LocalDateTime.now(), "yyyyMMddHHmmss")
                + String.format("%03d", (int) (Math.random() * 1000));
    }
    private MesProEmergencyPlanDO validatePlanExists(Long id) {
        MesProEmergencyPlanDO plan = planMapper.selectById(id);
        if (plan == null) {
            throw exception(PRO_WORK_ORDER_NOT_EXISTS);
        }
        return plan;
    }
    private MesProEmergencyPlanDO validatePlanCanModify(Long id) {
        MesProEmergencyPlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        return plan;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackService.java
@@ -6,6 +6,7 @@
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
@@ -155,5 +156,18 @@
     */
    MesProFeedbackBatchCreateRespVO batchCreateFeedback(@Valid MesProFeedbackBatchCreateReqVO reqVO);
    /**
     * ä½œä¸šæˆæžœæ±‡æ€»ç»Ÿè®¡
     *
     * <p>汇总指定时间范围内已批准报工的报工数量/合格数量,按工单类型、日期分组,
     * ç”¨äºŽå‘电、供电、检修等电力作业成果统计。
     *
     * @param beginDate     å¼€å§‹æ—¥æœŸï¼ˆå«ï¼Œå¯ç©ºï¼‰
     * @param endDate       ç»“束日期(含,可空)
     * @param workOrderType å·¥å•类型过滤(可空)
     * @return æ±‡æ€»ç»“æžœ
     */
    MesProFeedbackSummaryRespVO getFeedbackSummary(LocalDate beginDate, LocalDate endDate, Integer workOrderType);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -6,9 +6,11 @@
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.*;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
@@ -46,11 +48,17 @@
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -580,4 +588,105 @@
        return respVO;
    }
    @Override
    public MesProFeedbackSummaryRespVO getFeedbackSummary(LocalDate beginDate, LocalDate endDate,
                                                          Integer workOrderType) {
        // 1. æŸ¥è¯¢æœ‰æ•ˆæŠ¥å·¥ï¼ˆå·²æ‰¹å‡†ï¼šå¾…检验 + å·²å®Œæˆï¼‰
        LambdaQueryWrapperX<MesProFeedbackDO> query = new LambdaQueryWrapperX<MesProFeedbackDO>()
                .in(MesProFeedbackDO::getStatus, List.of(
                        MesProFeedbackStatusEnum.UNCHECK.getStatus(),
                        MesProFeedbackStatusEnum.FINISHED.getStatus()))
                .betweenIfPresent(MesProFeedbackDO::getFeedbackTime,
                        beginDate != null ? beginDate.atStartOfDay() : null,
                        endDate != null ? endDate.plusDays(1).atStartOfDay() : null);
        List<MesProFeedbackDO> feedbackList = feedbackMapper.selectList(query);
        if (CollUtil.isEmpty(feedbackList)) {
            MesProFeedbackSummaryRespVO empty = new MesProFeedbackSummaryRespVO();
            empty.setTotalFeedbackQuantity(BigDecimal.ZERO);
            empty.setTotalQualifiedQuantity(BigDecimal.ZERO);
            empty.setTotalFeedbackCount(0);
            empty.setByType(Collections.emptyList());
            empty.setByDate(Collections.emptyList());
            return empty;
        }
        // 2. åŠ è½½å·¥å•ä¿¡æ¯ï¼Œæ˜ å°„ å·¥å•ID -> å·¥å•类型
        Set<Long> workOrderIds = feedbackList.stream()
                .map(MesProFeedbackDO::getWorkOrderId).filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(workOrderIds);
        // 3. è¿‡æ»¤å·¥å•类型
        List<MesProFeedbackDO> filteredList = feedbackList.stream()
                .filter(feedback -> {
                    if (workOrderType == null) {
                        return true;
                    }
                    MesProWorkOrderDO workOrder = workOrderMap.get(feedback.getWorkOrderId());
                    return workOrder != null && Objects.equals(workOrder.getType(), workOrderType);
                })
                .collect(Collectors.toList());
        // 4. æ±‡æ€»
        BigDecimal totalFeedbackQuantity = BigDecimal.ZERO;
        BigDecimal totalQualifiedQuantity = BigDecimal.ZERO;
        int totalCount = 0;
        for (MesProFeedbackDO feedback : filteredList) {
            totalFeedbackQuantity = totalFeedbackQuantity.add(nullToZero(feedback.getFeedbackQuantity()));
            totalQualifiedQuantity = totalQualifiedQuantity.add(nullToZero(feedback.getQualifiedQuantity()));
            totalCount++;
        }
        // 5. æŒ‰å·¥å•类型分组
        Map<Integer, MesProFeedbackSummaryRespVO.TypeSummaryItem> typeMap = new LinkedHashMap<>();
        // 6. æŒ‰æ—¥æœŸåˆ†ç»„
        Map<LocalDate, MesProFeedbackSummaryRespVO.DateSummaryItem> dateMap = new LinkedHashMap<>();
        for (MesProFeedbackDO feedback : filteredList) {
            MesProWorkOrderDO workOrder = workOrderMap.get(feedback.getWorkOrderId());
            Integer type = workOrder != null ? workOrder.getType() : null;
            if (type != null) {
                MesProFeedbackSummaryRespVO.TypeSummaryItem item = typeMap.computeIfAbsent(type, key -> {
                    MesProFeedbackSummaryRespVO.TypeSummaryItem t = new MesProFeedbackSummaryRespVO.TypeSummaryItem();
                    t.setWorkOrderType(key);
                    t.setWorkOrderTypeName(MesProWorkOrderTypeEnum.valueOf(key) != null
                            ? MesProWorkOrderTypeEnum.valueOf(key).getName() : String.valueOf(key));
                    t.setFeedbackQuantity(BigDecimal.ZERO);
                    t.setQualifiedQuantity(BigDecimal.ZERO);
                    t.setFeedbackCount(0);
                    return t;
                });
                item.setFeedbackQuantity(item.getFeedbackQuantity().add(nullToZero(feedback.getFeedbackQuantity())));
                item.setQualifiedQuantity(item.getQualifiedQuantity().add(nullToZero(feedback.getQualifiedQuantity())));
                item.setFeedbackCount(item.getFeedbackCount() + 1);
            }
            if (feedback.getFeedbackTime() != null) {
                LocalDate date = feedback.getFeedbackTime().toLocalDate();
                MesProFeedbackSummaryRespVO.DateSummaryItem item = dateMap.computeIfAbsent(date, key -> {
                    MesProFeedbackSummaryRespVO.DateSummaryItem d = new MesProFeedbackSummaryRespVO.DateSummaryItem();
                    d.setDate(key);
                    d.setFeedbackQuantity(BigDecimal.ZERO);
                    d.setQualifiedQuantity(BigDecimal.ZERO);
                    d.setFeedbackCount(0);
                    return d;
                });
                item.setFeedbackQuantity(item.getFeedbackQuantity().add(nullToZero(feedback.getFeedbackQuantity())));
                item.setQualifiedQuantity(item.getQualifiedQuantity().add(nullToZero(feedback.getQualifiedQuantity())));
                item.setFeedbackCount(item.getFeedbackCount() + 1);
            }
        }
        // 7. ç»„装响应
        MesProFeedbackSummaryRespVO respVO = new MesProFeedbackSummaryRespVO();
        respVO.setTotalFeedbackQuantity(totalFeedbackQuantity);
        respVO.setTotalQualifiedQuantity(totalQualifiedQuantity);
        respVO.setTotalFeedbackCount(totalCount);
        respVO.setByType(new ArrayList<>(typeMap.values()));
        respVO.setByDate(new ArrayList<>(dateMap.values()));
        return respVO;
    }
    private static BigDecimal nullToZero(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package cn.iocoder.yudao.module.mes.service.pro.maintenanceplan;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanItemDO;
import java.util.Collection;
import java.util.List;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’ Service æŽ¥å£
 *
 * @author xiaoyi
 */
public interface MesProMaintenancePlanService {
    /**
     * åˆ›å»ºç”µåŠ›ä½œä¸šè®¡åˆ’
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createPlan(MesProMaintenancePlanSaveReqVO createReqVO);
    /**
     * æ›´æ–°ç”µåŠ›ä½œä¸šè®¡åˆ’
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updatePlan(MesProMaintenancePlanSaveReqVO updateReqVO);
    /**
     * åˆ é™¤ç”µåŠ›ä½œä¸šè®¡åˆ’
     *
     * @param ids ç¼–号数组
     */
    void deletePlan(Collection<Long> ids);
    /**
     * èŽ·å¾—ç”µåŠ›ä½œä¸šè®¡åˆ’
     *
     * @param id ç¼–号
     * @return ç”µåŠ›ä½œä¸šè®¡åˆ’
     */
    MesProMaintenancePlanDO getPlan(Long id);
    /**
     * èŽ·å¾—ç”µåŠ›ä½œä¸šè®¡åˆ’æ˜Žç»†åˆ—è¡¨
     *
     * @param planId è®¡åˆ’编号
     * @return æ˜Žç»†åˆ—表
     */
    List<MesProMaintenancePlanItemDO> getPlanItemListByPlanId(Long planId);
    /**
     * æŒ‰è®¡åˆ’编号集合获得明细列表
     *
     * @param planIds è®¡åˆ’编号集合
     * @return æ˜Žç»†åˆ—表
     */
    List<MesProMaintenancePlanItemDO> getPlanItemListByPlanIds(Collection<Long> planIds);
    /**
     * èŽ·å¾—ç”µåŠ›ä½œä¸šè®¡åˆ’åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return åˆ†é¡µç»“æžœ
     */
    PageResult<MesProMaintenancePlanDO> getPlanPage(MesProMaintenancePlanPageReqVO pageReqVO);
    /**
     * ä¸‹å‘电力作业计划(草稿 -> å·²ä¸‹å‘)
     *
     * @param id ç¼–号
     */
    void issuePlan(Long id);
    /**
     * å®Œæˆç”µåŠ›ä½œä¸šè®¡åˆ’ï¼ˆå·²ä¸‹å‘ -> å·²å®Œæˆï¼‰
     *
     * @param id ç¼–号
     */
    void finishPlan(Long id);
    /**
     * æ ¹æ®è®¡åˆ’备件/物资需求生成 ERP é‡‡è´­è®¡åˆ’
     *
     * @param id ç”µåŠ›ä½œä¸šè®¡åˆ’ç¼–å·
     * @return é‡‡è´­è®¡åˆ’编号
     */
    Long generatePurchasePlan(Long id);
    /**
     * ç”Ÿæˆç”Ÿäº§å·¥å•(运维/发电/检修类)
     *
     * @param id ç”µåŠ›ä½œä¸šè®¡åˆ’ç¼–å·
     * @return å·¥å•编号
     */
    Long generateWorkOrder(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,288 @@
package cn.iocoder.yudao.module.mes.service.pro.maintenanceplan;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchasePlanApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchasePlanCreateReqDTO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.maintenanceplan.MesProMaintenancePlanItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.maintenanceplan.MesProMaintenancePlanItemMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.maintenanceplan.MesProMaintenancePlanMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum;
import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES ç”µåŠ›ä½œä¸šè®¡åˆ’ Service å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
@Slf4j
public class MesProMaintenancePlanServiceImpl implements MesProMaintenancePlanService {
    /**
     * çŠ¶æ€ï¼šè‰ç¨¿
     */
    private static final Integer STATUS_DRAFT = 0;
    /**
     * çŠ¶æ€ï¼šå·²ä¸‹å‘
     */
    private static final Integer STATUS_ISSUED = 10;
    /**
     * çŠ¶æ€ï¼šå·²å®Œæˆ
     */
    private static final Integer STATUS_FINISHED = 20;
    /**
     * çŠ¶æ€ï¼šå·²å–æ¶ˆ
     */
    private static final Integer STATUS_CANCELLED = 30;
    @Resource
    private MesProMaintenancePlanMapper planMapper;
    @Resource
    private MesProMaintenancePlanItemMapper planItemMapper;
    @Resource
    private MesCalTeamService teamService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private ErpPurchasePlanApi erpPurchasePlanApi;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPlan(MesProMaintenancePlanSaveReqVO createReqVO) {
        // 1. ç”Ÿæˆè®¡åˆ’编码
        String code = generatePlanCode();
        if (planMapper.selectByCode(code) != null) {
            throw exception(MD_ITEM_CODE_DUPLICATE);
        }
        // 2. æ ¡éªŒç­ç»„并回填
        fillTeamInfo(createReqVO);
        // 3. æ’入计划
        MesProMaintenancePlanDO plan = BeanUtils.toBean(createReqVO, MesProMaintenancePlanDO.class, req -> req
                .setCode(code)
                .setStatus(STATUS_DRAFT));
        planMapper.insert(plan);
        // 4. æ’入明细
        insertPlanItems(plan.getId(), createReqVO.getItems());
        return plan.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePlan(MesProMaintenancePlanSaveReqVO updateReqVO) {
        // 1. æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        MesProMaintenancePlanDO oldPlan = validatePlanCanModify(updateReqVO.getId());
        fillTeamInfo(updateReqVO);
        // 2. æ›´æ–°ä¸»è¡¨
        MesProMaintenancePlanDO updateObj = BeanUtils.toBean(updateReqVO, MesProMaintenancePlanDO.class, req -> req
                .setCode(oldPlan.getCode())
                .setStatus(oldPlan.getStatus()));
        planMapper.updateById(updateObj);
        // 3. æ›´æ–°æ˜Žç»†ï¼šå…ˆåˆ åŽæ’
        planItemMapper.deleteByPlanId(updateReqVO.getId());
        insertPlanItems(updateReqVO.getId(), updateReqVO.getItems());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePlan(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        for (Long id : ids) {
            validatePlanCanModify(id);
            planItemMapper.deleteByPlanId(id);
            planMapper.deleteById(id);
        }
    }
    @Override
    public MesProMaintenancePlanDO getPlan(Long id) {
        return planMapper.selectById(id);
    }
    @Override
    public List<MesProMaintenancePlanItemDO> getPlanItemListByPlanId(Long planId) {
        return planItemMapper.selectListByPlanId(planId);
    }
    @Override
    public List<MesProMaintenancePlanItemDO> getPlanItemListByPlanIds(Collection<Long> planIds) {
        return planItemMapper.selectListByPlanIds(planIds);
    }
    @Override
    public PageResult<MesProMaintenancePlanDO> getPlanPage(MesProMaintenancePlanPageReqVO pageReqVO) {
        return planMapper.selectPage(pageReqVO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issuePlan(Long id) {
        MesProMaintenancePlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        planMapper.updateById(new MesProMaintenancePlanDO().setId(id).setStatus(STATUS_ISSUED));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void finishPlan(Long id) {
        MesProMaintenancePlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_ISSUED)) {
            throw exception(PRO_WORK_ORDER_NOT_CONFIRMED);
        }
        planMapper.updateById(new MesProMaintenancePlanDO().setId(id).setStatus(STATUS_FINISHED));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generatePurchasePlan(Long id) {
        // 1. æ ¡éªŒè®¡åˆ’存在且已下发
        MesProMaintenancePlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_ISSUED)) {
            throw exception(PRO_WORK_ORDER_NOT_CONFIRMED);
        }
        // 2. èŽ·å–æ˜Žç»†
        List<MesProMaintenancePlanItemDO> planItems = planItemMapper.selectListByPlanId(id);
        if (CollUtil.isEmpty(planItems)) {
            throw exception(PRO_WORK_ORDER_NOT_EXISTS);
        }
        // 3. ç»„装采购计划 DTO(MES ç‰©æ–™ -> MDM ç‰©æ–™ï¼‰
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
                planItems.stream().map(MesProMaintenancePlanItemDO::getItemId).collect(Collectors.toSet()));
        List<ErpPurchasePlanCreateReqDTO.Item> items = planItems.stream().map(item -> {
            ErpPurchasePlanCreateReqDTO.Item dtoItem = new ErpPurchasePlanCreateReqDTO.Item();
            MesMdItemDO mesItem = itemMap.get(item.getItemId());
            dtoItem.setProductId(mesItem != null ? mesItem.getMdmItemId() : item.getItemId());
            dtoItem.setProductUnitId(mesItem != null ? mesItem.getUnitMeasureId() : null);
            dtoItem.setCount(item.getQuantity());
            dtoItem.setRemark(item.getRemark());
            return dtoItem;
        }).collect(Collectors.toList());
        // 4. è°ƒç”¨ ERP ç”Ÿæˆé‡‡è´­è®¡åˆ’
        ErpPurchasePlanCreateReqDTO createReqDTO = new ErpPurchasePlanCreateReqDTO();
        createReqDTO.setName("作业计划采购-" + plan.getName());
        createReqDTO.setDemandSource(2); // è¿ç»´/生产计划
        createReqDTO.setPlanDate(plan.getPlanDate());
        createReqDTO.setRemark("来源计划: " + plan.getCode());
        createReqDTO.setItems(items);
        return erpPurchasePlanApi.createPurchasePlan(createReqDTO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long generateWorkOrder(Long id) {
        // 1. æ ¡éªŒè®¡åˆ’存在且已下发
        MesProMaintenancePlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_ISSUED)) {
            throw exception(PRO_WORK_ORDER_NOT_CONFIRMED);
        }
        // 2. å·²ç”Ÿæˆè¿‡å·¥å•则直接返回
        if (plan.getWorkOrderId() != null) {
            return plan.getWorkOrderId();
        }
        // 3. ç»„装工单
        MesProWorkOrderSaveReqVO workOrderReq = new MesProWorkOrderSaveReqVO();
        workOrderReq.setName(plan.getName());
        workOrderReq.setType(convertPlanTypeToWorkOrderType(plan.getPlanType()));
        workOrderReq.setOrderSourceType(MesProWorkOrderSourceTypeEnum.MAINTENANCE_PLAN.getType());
        workOrderReq.setOrderSourceCode(plan.getCode());
        workOrderReq.setQuantity(BigDecimal.ONE);
        workOrderReq.setRequestDate(plan.getPlanDate() != null ? plan.getPlanDate() : java.time.LocalDateTime.now());
        workOrderReq.setRemark("来自电力作业计划: " + plan.getCode());
        Long workOrderId = workOrderService.createWorkOrder(workOrderReq);
        // 4. å›žå†™è®¡åˆ’
        MesProMaintenancePlanDO updateObj = new MesProMaintenancePlanDO();
        updateObj.setId(id);
        updateObj.setWorkOrderId(workOrderId);
        planMapper.updateById(updateObj);
        return workOrderId;
    }
    /**
     * è®¡åˆ’类型 -> å·¥å•类型映射
     */
    private Integer convertPlanTypeToWorkOrderType(Integer planType) {
        return switch (planType) {
            case 1 -> MesProWorkOrderTypeEnum.POWER_OPERATION.getType(); // ç”µç½‘运行 -> ç”µåŠ›è¿ç»´
            case 2 -> MesProWorkOrderTypeEnum.POWER_GENERATION.getType(); // å‘电
            case 3 -> MesProWorkOrderTypeEnum.POWER_OPERATION.getType(); // å˜ç”µè¿ç»´ -> ç”µåŠ›è¿ç»´
            case 4 -> MesProWorkOrderTypeEnum.POWER_MAINTENANCE.getType(); // çº¿è·¯æ£€ä¿® -> æ£€ä¿®
            default -> MesProWorkOrderTypeEnum.POWER_OPERATION.getType();
        };
    }
    private void insertPlanItems(Long planId, List<MesProMaintenancePlanSaveReqVO.Item> items) {
        if (CollUtil.isEmpty(items)) {
            return;
        }
        planItemMapper.insertBatch(items.stream().map(item ->
                BeanUtils.toBean(item, MesProMaintenancePlanItemDO.class).setPlanId(planId))
                .collect(Collectors.toList()));
    }
    private void fillTeamInfo(MesProMaintenancePlanSaveReqVO reqVO) {
        if (reqVO.getTeamId() == null) {
            return;
        }
        MesCalTeamDO team = teamService.validateTeamExists(reqVO.getTeamId());
        if (team != null) {
            reqVO.setTeamName(team.getName());
        }
    }
    private String generatePlanCode() {
        return "MP" + DateUtil.format(java.time.LocalDateTime.now(), "yyyyMMddHHmmss")
                + String.format("%03d", (int) (Math.random() * 1000));
    }
    private MesProMaintenancePlanDO validatePlanExists(Long id) {
        MesProMaintenancePlanDO plan = planMapper.selectById(id);
        if (plan == null) {
            throw exception(PRO_WORK_ORDER_NOT_EXISTS);
        }
        return plan;
    }
    private MesProMaintenancePlanDO validatePlanCanModify(Long id) {
        MesProMaintenancePlanDO plan = validatePlanExists(id);
        if (!Objects.equals(plan.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        return plan;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -18,6 +18,8 @@
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum;
import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService;
import cn.iocoder.yudao.module.mes.service.cal.team.MesCalTeamService;
import cn.iocoder.yudao.module.mes.dal.dataobject.cal.team.MesCalTeamDO;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
@@ -108,6 +110,8 @@
    @Resource
    @Lazy
    private MesDvMachineryService machineryService;
    @Resource
    private MesCalTeamService teamService;
    @Override
    public Long createTask(MesProTaskSaveReqVO createReqVO) {
@@ -116,6 +120,7 @@
        workstationService.validateWorkstationExists(createReqVO.getWorkstationId());
        routeService.validateRouteExists(createReqVO.getRouteId());
        processService.validateProcessExistsAndEnable(createReqVO.getProcessId());
        fillTeamInfo(createReqVO);
        // 2. è‡ªåŠ¨ç¡®å®š itemId:优先前端传入 â†’ å·¥è‰ºè·¯çº¿å·¥åºçš„产出产品 â†’ å·¥å•成品
        Long itemId = createReqVO.getItemId();
@@ -161,6 +166,7 @@
        if (updateReqVO.getProcessId() != null) {
            processService.validateProcessExistsAndEnable(updateReqVO.getProcessId());
        }
        fillTeamInfo(updateReqVO);
        MesMdItemDO item;
        if (updateReqVO.getItemId() != null) {
            item = itemService.validateItemExists(updateReqVO.getItemId());
@@ -176,6 +182,19 @@
    }
    /**
     * æ ¡éªŒç­ç»„存在并回填班组名称
     */
    private void fillTeamInfo(MesProTaskSaveReqVO reqVO) {
        if (reqVO.getTeamId() == null) {
            return;
        }
        MesCalTeamDO team = teamService.validateTeamExists(reqVO.getTeamId());
        if (team != null) {
            reqVO.setTeamName(team.getName());
        }
    }
    /**
     * æž„建任务名称,格式:"产品名【数量】单位"
     */
    private String buildTaskName(MesMdItemDO item, BigDecimal quantity) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java
@@ -24,7 +24,10 @@
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -69,11 +72,17 @@
    @Resource
    private CrmCustomerApi customerApi;
    @Resource
    @Lazy // é¿å… erpSaleOrderApiImpl -> ... -> mesProWorkOrderServiceImpl çš„循环依赖
    private ErpSaleOrderApi saleOrderApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createWorkOrder(MesProWorkOrderSaveReqVO createReqVO) {
        // 1. æ ¡éªŒæ•°æ®
        validateWorkOrderSaveData(null, createReqVO);
        // 1.1 å…³è”营销订单信息回填
        fillSaleOrderInfo(createReqVO);
        // 2.1 è®¾ç½®é»˜è®¤å€¼
        if (createReqVO.getParentId() == null) {
@@ -84,8 +93,10 @@
        workOrder.setStatus(MesProWorkOrderStatusEnum.PREPARE.getStatus());
        workOrderMapper.insert(workOrder);
        // 3. è‡ªåŠ¨ç”Ÿæˆ BOM:根据产品 BOM ç”Ÿæˆå·¥å• BOM
        // 3. è‡ªåŠ¨ç”Ÿæˆ BOM:根据产品 BOM ç”Ÿæˆå·¥å• BOM(无产品的运维/发电/检修类工单跳过)
        if (createReqVO.getProductId() != null) {
        workOrderBomService.generateWorkOrderBom(workOrder.getId(), createReqVO, false);
        }
        // 4. å·¥åºç”±å‰ç«¯é€‰æ‹©äº§å“åŽå¸¦å…¥ï¼Œä¸å†è‡ªåŠ¨ç”Ÿæˆ
@@ -105,11 +116,13 @@
        }
        // 1.2 æ ¡éªŒæ•°æ®
        validateWorkOrderSaveData(updateReqVO.getId(), updateReqVO);
        // 1.3 å…³è”营销订单信息回填
        fillSaleOrderInfo(updateReqVO);
        // 2. åˆ¤æ–­äº§å“æˆ–数量是否变更,如果变更则重新生成 BOM(updated=true ä¼šå…ˆæ¸…理旧数据)
        // 2. åˆ¤æ–­äº§å“æˆ–数量是否变更,如果变更则重新生成 BOM(updated=true ä¼šå…ˆæ¸…理旧数据;无产品的运维类工单跳过)
        boolean productChanged = ObjUtil.notEqual(oldWorkOrder.getProductId(), updateReqVO.getProductId());
        boolean quantityChanged = oldWorkOrder.getQuantity().compareTo(updateReqVO.getQuantity()) != 0;
        if (productChanged || quantityChanged) {
        if ((productChanged || quantityChanged) && updateReqVO.getProductId() != null) {
            workOrderBomService.generateWorkOrderBom(updateReqVO.getId(), updateReqVO, true);
        }
        // 2.1 å·¥åºç”±å‰ç«¯ç®¡ç†ï¼Œäº§å“å˜æ›´æ—¶å‰ç«¯ä¼šé‡æ–°èŽ·å–å·¥è‰ºè·¯çº¿å·¥åº
@@ -245,12 +258,14 @@
    private void validateWorkOrderSaveData(Long id, MesProWorkOrderSaveReqVO reqVO) {
        // 1. æ ¡éªŒç¼–码唯一
        validateWorkOrderCodeUnique(id, reqVO.getCode());
        // 2. æ ¡éªŒäº§å“å­˜åœ¨
        // 2. äº§å“éžç©ºæ—¶æ ¡éªŒäº§å“å­˜åœ¨ï¼ˆè¿ç»´/发电/检修类工单可无产品)
        if (reqVO.getProductId() != null) {
        itemService.validateItemExists(reqVO.getProductId());
        // 3. æ ¡éªŒæ‰¹æ¬¡é…ç½®ï¼ˆå¦‚果产品有 clientFlag=true,则 clientId å¿…填)
        MesMdItemBatchConfigDO batchConfig = itemBatchConfigService.getItemBatchConfigByItemId(reqVO.getProductId());
        if (batchConfig != null && Boolean.TRUE.equals(batchConfig.getClientFlag()) && reqVO.getClientId() == null) {
            throw exception(MD_CLIENT_NOT_EXISTS);
            }
        }
    }
@@ -267,6 +282,23 @@
        }
    }
    /**
     * å…³è”营销订单(ERP é”€å”®è®¢å•):校验存在性并回填单号、客户
     */
    private void fillSaleOrderInfo(MesProWorkOrderSaveReqVO reqVO) {
        if (reqVO.getSaleOrderId() == null) {
            return;
        }
        ErpSaleOrderRespDTO saleOrder = saleOrderApi.getSaleOrder(reqVO.getSaleOrderId()).getCheckedData();
        if (saleOrder == null) {
            throw exception(PRO_WORK_ORDER_SALE_ORDER_NOT_EXISTS);
        }
        reqVO.setSaleOrderNo(saleOrder.getNo());
        if (reqVO.getClientId() == null && saleOrder.getCustomerId() != null) {
            reqVO.setClientId(saleOrder.getCustomerId());
        }
    }
    @Override
    public void updateProducedQuantity(Long id, BigDecimal incrQuantityProduced) {
        // æ ¡éªŒå·¥å•存在
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/outsourcetest/MesQcOutsourceTestService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.mes.service.qc.outsourcetest;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest.MesQcOutsourceTestDO;
import java.util.Collection;
/**
 * MES å§”外检测/试验单 Service æŽ¥å£
 *
 * @author xiaoyi
 */
public interface MesQcOutsourceTestService {
    /**
     * åˆ›å»ºå§”外检测/试验单
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createTest(MesQcOutsourceTestSaveReqVO createReqVO);
    /**
     * æ›´æ–°å§”外检测/试验单
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateTest(MesQcOutsourceTestSaveReqVO updateReqVO);
    /**
     * åˆ é™¤å§”外检测/试验单
     *
     * @param ids ç¼–号数组
     */
    void deleteTest(Collection<Long> ids);
    /**
     * èŽ·å¾—å§”å¤–æ£€æµ‹/试验单
     *
     * @param id ç¼–号
     * @return å§”外检测/试验单
     */
    MesQcOutsourceTestDO getTest(Long id);
    /**
     * èŽ·å¾—å§”å¤–æ£€æµ‹/试验单分页
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return åˆ†é¡µç»“æžœ
     */
    PageResult<MesQcOutsourceTestDO> getTestPage(MesQcOutsourceTestPageReqVO pageReqVO);
    /**
     * æäº¤å§”外检测/试验单(草稿 -> å·²æäº¤ï¼‰
     *
     * @param id ç¼–号
     */
    void submitTest(Long id);
    /**
     * å®Œæˆå§”外检测/试验单(已提交 -> å·²å®Œæˆï¼‰
     *
     * @param id ç¼–号
     */
    void finishTest(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/outsourcetest/MesQcOutsourceTestServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,158 @@
package cn.iocoder.yudao.module.mes.service.qc.outsourcetest;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.outsourcetest.vo.MesQcOutsourceTestSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.outsourcetest.MesQcOutsourceTestDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.outsourcetest.MesQcOutsourceTestMapper;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES å§”外检测/试验单 Service å®žçŽ°ç±»
 *
 * @author xiaoyi
 */
@Service
@Validated
@Slf4j
public class MesQcOutsourceTestServiceImpl implements MesQcOutsourceTestService {
    /**
     * é™„件关联业务类型
     */
    private static final String ATTACHMENT_BIZ_TYPE = "file";
    /**
     * é™„件关联业务记录类型
     */
    private static final String ATTACHMENT_RECORD_TYPE = "mes_qc_outsource_test";
    /**
     * çŠ¶æ€ï¼šè‰ç¨¿
     */
    private static final Integer STATUS_DRAFT = 0;
    /**
     * çŠ¶æ€ï¼šå·²æäº¤
     */
    private static final Integer STATUS_SUBMITTED = 10;
    /**
     * çŠ¶æ€ï¼šå·²å®Œæˆ
     */
    private static final Integer STATUS_FINISHED = 20;
    @Resource
    private MesQcOutsourceTestMapper testMapper;
    @Resource
    private StorageAttachmentApi storageAttachmentApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createTest(MesQcOutsourceTestSaveReqVO createReqVO) {
        // 1. ç”Ÿæˆå•号
        String code = generateCode();
        if (testMapper.selectByCode(code) != null) {
            throw exception(PRO_WORK_ORDER_CODE_DUPLICATE);
        }
        // 2. æ’å…¥
        MesQcOutsourceTestDO test = BeanUtils.toBean(createReqVO, MesQcOutsourceTestDO.class, req -> req
                .setCode(code)
                .setStatus(STATUS_DRAFT));
        testMapper.insert(test);
        // 3. ç»‘定附件
        storageAttachmentApi.bindAttachments(ATTACHMENT_BIZ_TYPE, ATTACHMENT_RECORD_TYPE,
                test.getId(), createReqVO.getBlobIds());
        return test.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTest(MesQcOutsourceTestSaveReqVO updateReqVO) {
        // 1. æ ¡éªŒå­˜åœ¨ + è‰ç¨¿
        MesQcOutsourceTestDO oldTest = validateTestCanModify(updateReqVO.getId());
        // 2. æ›´æ–°
        MesQcOutsourceTestDO updateObj = BeanUtils.toBean(updateReqVO, MesQcOutsourceTestDO.class, req -> req
                .setCode(oldTest.getCode())
                .setStatus(oldTest.getStatus()));
        testMapper.updateById(updateObj);
        // 3. é‡æ–°ç»‘定附件
        storageAttachmentApi.bindAttachments(ATTACHMENT_BIZ_TYPE, ATTACHMENT_RECORD_TYPE,
                updateReqVO.getId(), updateReqVO.getBlobIds());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTest(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        for (Long id : ids) {
            validateTestCanModify(id);
            testMapper.deleteById(id);
        }
    }
    @Override
    public MesQcOutsourceTestDO getTest(Long id) {
        return testMapper.selectById(id);
    }
    @Override
    public PageResult<MesQcOutsourceTestDO> getTestPage(MesQcOutsourceTestPageReqVO pageReqVO) {
        return testMapper.selectPage(pageReqVO);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitTest(Long id) {
        MesQcOutsourceTestDO test = validateTestExists(id);
        if (!Objects.equals(test.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        testMapper.updateById(new MesQcOutsourceTestDO().setId(id).setStatus(STATUS_SUBMITTED));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void finishTest(Long id) {
        MesQcOutsourceTestDO test = validateTestExists(id);
        if (!Objects.equals(test.getStatus(), STATUS_SUBMITTED)) {
            throw exception(PRO_WORK_ORDER_NOT_CONFIRMED);
        }
        testMapper.updateById(new MesQcOutsourceTestDO().setId(id).setStatus(STATUS_FINISHED));
    }
    private String generateCode() {
        return "WT" + DateUtil.format(java.time.LocalDateTime.now(), "yyyyMMddHHmmss")
                + String.format("%03d", (int) (Math.random() * 1000));
    }
    private MesQcOutsourceTestDO validateTestExists(Long id) {
        MesQcOutsourceTestDO test = testMapper.selectById(id);
        if (test == null) {
            throw exception(PRO_WORK_ORDER_NOT_EXISTS);
        }
        return test;
    }
    private MesQcOutsourceTestDO validateTestCanModify(Long id) {
        MesQcOutsourceTestDO test = validateTestExists(id);
        if (!Objects.equals(test.getStatus(), STATUS_DRAFT)) {
            throw exception(PRO_WORK_ORDER_NOT_PREPARE);
        }
        return test;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
@@ -18,6 +18,7 @@
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
@@ -102,6 +103,9 @@
        // 3. æ’入主表
        MesWmProductIssueDO issue = BeanUtils.toBean(createReqVO, MesWmProductIssueDO.class);
        issue.setStatus(MesWmProductIssueStatusEnum.PREPARE.getStatus());
        if (issue.getIssueType() == null) {
            issue.setIssueType(MesWmProductIssueTypeEnum.NORMAL.getType());
        }
        issueMapper.insert(issue);
        return issue.getId();
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskLineService.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.mes.service.wm.stocktaking.task;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.result.MesWmStockTakingTaskResultSaveReqVO;
@@ -114,4 +116,16 @@
     */
    void updateStockTakingTaskLineTakingQuantity(Long id, BigDecimal takingQuantity);
    /**
     * Excel å¯¼å…¥å®žç›˜æ•°é‡
     *
     * <p>按 ç‰©æ–™ç¼–码 + æ‰¹æ¬¡å· åŒ¹é…ç›˜ç‚¹ä»»åŠ¡è¡Œï¼Œå†™å…¥å®žç›˜æ•°é‡ã€‚
     *
     * @param taskId ç›˜ç‚¹ä»»åŠ¡ç¼–å·
     * @param list   å¯¼å…¥æ•°æ®
     * @return å¯¼å…¥ç»“æžœ
     */
    MesWmStockTakingTaskLineImportRespVO importTakingQuantity(Long taskId,
                                                              List<MesWmStockTakingTaskLineImportExcelVO> list);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stocktaking/task/MesWmStockTakingTaskLineServiceImpl.java
@@ -7,6 +7,8 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.line.MesWmStockTakingTaskLineImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.stocktaking.task.vo.result.MesWmStockTakingTaskResultSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stocktaking.plan.MesWmStockTakingPlanParamDO;
@@ -50,6 +52,8 @@
    private MesWmMaterialStockService materialStockService;
    @Resource
    private MesWmStockTakingTaskService stockTakingTaskService;
    @Resource
    private cn.iocoder.yudao.module.mdm.api.item.MdmItemApi mdmItemApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -230,4 +234,58 @@
        return param != null ? param.getValueId() : null;
    }
    @Override
    public MesWmStockTakingTaskLineImportRespVO importTakingQuantity(Long taskId,
                                                                    List<MesWmStockTakingTaskLineImportExcelVO> list) {
        MesWmStockTakingTaskLineImportRespVO respVO = new MesWmStockTakingTaskLineImportRespVO();
        respVO.setSuccessCount(0);
        respVO.setFailureCount(0);
        respVO.setFailureMessages(new java.util.ArrayList<>());
        if (CollUtil.isEmpty(list)) {
            return respVO;
        }
        // 1. èŽ·å–ç›˜ç‚¹ä»»åŠ¡è¡Œ
        List<MesWmStockTakingTaskLineDO> lines = stockTakingTaskLineMapper.selectListByTaskId(taskId);
        if (CollUtil.isEmpty(lines)) {
            throw exception(cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_STOCK_TAKING_TASK_LINE_NOT_EXISTS);
        }
        // 2. æž„建 ç‰©æ–™ç¼–码 -> ç‰©æ–™ID æ˜ å°„
        java.util.Set<Long> itemIds = lines.stream().map(MesWmStockTakingTaskLineDO::getItemId)
                .filter(Objects::nonNull).collect(java.util.stream.Collectors.toSet());
        java.util.Map<Long, cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO> itemMap =
                mdmItemApi.getItemMap(itemIds);
        java.util.Map<String, Long> codeToItemIdMap = new java.util.HashMap<>();
        itemMap.values().forEach(item -> {
            if (item.getCode() != null) {
                codeToItemIdMap.put(item.getCode(), item.getId());
            }
        });
        // 3. é€è¡ŒåŒ¹é…å¹¶å†™å…¥å®žç›˜æ•°é‡
        for (MesWmStockTakingTaskLineImportExcelVO row : list) {
            Long itemId = codeToItemIdMap.get(row.getItemCode());
            if (itemId == null) {
                respVO.setFailureCount(respVO.getFailureCount() + 1);
                respVO.getFailureMessages().add("物料编码不存在: " + row.getItemCode());
                continue;
            }
            MesWmStockTakingTaskLineDO matched = lines.stream()
                    .filter(line -> Objects.equals(line.getItemId(), itemId)
                            && Objects.equals(line.getBatchCode(), row.getBatchCode()))
                    .findFirst().orElse(null);
            if (matched == null) {
                respVO.setFailureCount(respVO.getFailureCount() + 1);
                respVO.getFailureMessages().add("未找到对应盘点行: ç‰©æ–™=" + row.getItemCode() + " æ‰¹æ¬¡=" + row.getBatchCode());
                continue;
            }
            if (row.getTakingQuantity() == null) {
                respVO.setFailureCount(respVO.getFailureCount() + 1);
                respVO.getFailureMessages().add("实盘数量为空: ç‰©æ–™=" + row.getItemCode());
                continue;
            }
            updateStockTakingTaskLineTakingQuantity(matched.getId(), row.getTakingQuantity());
            respVO.setSuccessCount(respVO.getSuccessCount() + 1);
        }
        return respVO;
    }
}
yudao-server/src/main/resources/application-local.yaml
@@ -75,7 +75,7 @@
      host: localhost # åœ°å€
      port: 6379 # ç«¯å£
      database: 0 # æ•°æ®åº“索引
#      password: # å¯†ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯
      password: 123456
--- #################### å®šæ—¶ä»»åŠ¡ç›¸å…³é…ç½® ####################
@@ -198,3 +198,10 @@
    compress: true # æ˜¯å¦åŽ‹ç¼©å›¾ç‰‡
    need-compress-size: 10MB # è§¦å‘压缩的文件大小阈值
    compress-quality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
# MES æ•°é‡‡ï¼ˆè®¾å¤‡æ•°æ®é‡‡é›†ï¼‰é…ç½®ï¼Œå¤–部数采服务地址按实际部署填写
yudao:
  mes:
    telemetry:
      base-url: http://106.227.91.252:3100 # æ•°é‡‡æœåŠ¡åœ°å€
      path: /api/jxjs/mesTb/getDeviceDetails