| 2026-05-24 | yuan | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | yuan | ![]() |
| 2026-05-24 | yuan | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | yuan | ![]() |
| 2026-05-24 | yuan | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | gongchunyi | ![]() |
| 2026-05-24 | yuan | ![]() |
doc/20260523_ÐÂÔöÈë¿â¼Ç¼±íµÄÔ¤¾¯ÊýÁ¿.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,2 @@ alter table stock_in_record add warn_num decimal(16, 4) null comment 'é¢è¦æ°é'; src/main/java/com/ruoyi/inspectiontask/service/impl/TimingTaskScheduler.java
@@ -35,6 +35,17 @@ // è·åç°æè§¦åå¨å¹¶è½¬æ¢ä¸º CronTrigger Trigger oldTrigger = scheduler.getTrigger(triggerKey); if (oldTrigger == null) { JobKey jobKey = new JobKey("timingTask_" + task.getId()); JobDetail jobDetail = scheduler.getJobDetail(jobKey); if (jobDetail != null) { Trigger trigger = buildJobTrigger(task, jobDetail); scheduler.scheduleJob(trigger); } else { scheduleTimingTask(task); } return; } if (!(oldTrigger instanceof CronTrigger)) { throw new SchedulerException("Existing trigger is not a CronTrigger"); } @@ -144,18 +155,13 @@ // 使ç¨switchç¡®ä¿æ¡ä»¶äºæ¥ String frequencyType = task.getFrequencyType().toUpperCase(); // ç»ä¸è½¬ä¸ºå¤§åæ¯è¾ switch (frequencyType) { case "DAILY": return convertDailyToCron(task.getFrequencyDetail()); case "WEEKLY": return convertWeeklyToCron(task.getFrequencyDetail()); case "MONTHLY": return convertMonthlyToCron(task.getFrequencyDetail()); case "QUARTERLY": return convertQuarterlyToCron(task.getFrequencyDetail()); default: throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + task.getFrequencyType()); } return switch (frequencyType) { case "DAILY" -> convertDailyToCron(task.getFrequencyDetail()); case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail()); case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail()); case "QUARTERLY" -> convertQuarterlyToCron(task.getFrequencyDetail()); default -> throw new IllegalArgumentException("䏿¯æçé¢çç±»å: " + task.getFrequencyType()); }; } // æ¯æ¥ä»»å¡è½¬æ¢ src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsDetailsVo.java
@@ -24,10 +24,10 @@ @Schema(description = "ååéé¢") private BigDecimal contractAmount; @Schema(description = "仿¬¾éé¢") private BigDecimal paymentAmount; @Schema(description = "å·²å ¥åºéé¢") private BigDecimal shippedAmount; @Schema(description = "åºä»éé¢") private BigDecimal payableAmount; @Schema(description = "æªå ¥åºéé¢") private BigDecimal unshippedAmount; } src/main/java/com/ruoyi/purchase/vo/SupplierTransactionsVo.java
@@ -16,15 +16,12 @@ private String supplierName; @Schema(description = "ååæ»éé¢") //该ä¾åºåéè´åå累计éé¢ private BigDecimal contractAmounts; @Schema(description = "仿¬¾éé¢") //该ä¾åºåéè´ä»æ¬¾ç´¯è®¡éé¢ private BigDecimal paymentAmount; @Schema(description = "å·²å ¥åºéé¢") private BigDecimal shippedAmount; @Schema(description = "åºä»éé¢") //该ä¾åºåéè´åºä»ç´¯è®¡éé¢=è´¢å¡(å ¥åº-éè´§) private BigDecimal payableAmount; @Schema(description = "æªå ¥åºéé¢") private BigDecimal unshippedAmount; } src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
@@ -24,10 +24,10 @@ @Schema(description = "ååéé¢") private BigDecimal contractAmount; @Schema(description = "æ¶æ¬¾éé¢") private BigDecimal receiptPaymentAmount; @Schema(description = "å·²åºåºéé¢") private BigDecimal shippedAmount; @Schema(description = "åºæ¶éé¢") private BigDecimal receiptableAmount; @Schema(description = "æªåºåºéé¢") private BigDecimal unshippedAmount; } src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
@@ -16,15 +16,12 @@ private String customerName; @Schema(description = "ååæ»éé¢") //该客æ·éå®åå累计éé¢ private BigDecimal contractAmounts; @Schema(description = "æ¶æ¬¾éé¢") //该客æ·é宿¶æ¬¾ç´¯è®¡éé¢ private BigDecimal receiptPaymentAmount; @Schema(description = "å·²åºåºéé¢") private BigDecimal shippedAmount; @Schema(description = "åºæ¶éé¢") //该客æ·éå®åºæ¶ç´¯è®¡éé¢=è´¢å¡(åºåº-éè´§) private BigDecimal receiptableAmount; @Schema(description = "æªåºåºéé¢") private BigDecimal unshippedAmount; } src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -47,7 +47,6 @@ private String remark; @Schema(description = "é¢è¦æ°é") @TableField(exist = false) private BigDecimal warnNum; @Schema(description = "ç±»å 0åæ ¼å ¥åº 1ä¸åæ ¼å ¥åº") src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -69,9 +69,56 @@ throw new BaseException("è¯¥å ¥åºè®°å½ä¸åå¨,æ æ³æ´æ°!!!"); } // è®°å½ä¿®æ¹åç batch_no String oldBatchNo = stockInRecord.getBatchNo(); String newBatchNo = stockInRecordDto.getBatchNo(); String[] ignoreProperties = {"id", "inbound_batches"};//æé¤id屿§ BeanUtils.copyProperties(stockInRecordDto, stockInRecord, ignoreProperties); return stockInRecordMapper.updateById(stockInRecord); int result = stockInRecordMapper.updateById(stockInRecord); // 妿 batch_no åçååï¼éè¦åæ¥æ´æ°å ³è表 if (newBatchNo != null && !newBatchNo.equals(oldBatchNo)) { updateRelatedBatchNo(stockInRecord, oldBatchNo, newBatchNo); } return result; } /** * åæ¥æ´æ°å ³è表ç batch_no * @param stockInRecord å ¥åºè®°å½ * @param oldBatchNo ä¿®æ¹åçæ¹å· * @param newBatchNo ä¿®æ¹åçæ¹å· */ private void updateRelatedBatchNo(StockInRecord stockInRecord, String oldBatchNo, String newBatchNo) { // 1. æ´æ° stock_inventory 表ï¼åæ ¼åºåï¼ LambdaQueryWrapper<StockInventory> inventoryEq = new LambdaQueryWrapper<StockInventory>() .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId()); if (StringUtils.isEmpty(oldBatchNo)) { inventoryEq.isNull(StockInventory::getBatchNo); } else { inventoryEq.eq(StockInventory::getBatchNo, oldBatchNo); } StockInventory stockInventory = stockInventoryMapper.selectOne(inventoryEq); if (stockInventory != null) { stockInventory.setBatchNo(newBatchNo); stockInventoryMapper.updateById(stockInventory); } // 2. æ´æ° stock_uninventory 表ï¼ä¸åæ ¼åºåï¼ LambdaQueryWrapper<StockUninventory> uninventoryEq = new LambdaQueryWrapper<StockUninventory>() .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId()); if (StringUtils.isEmpty(oldBatchNo)) { uninventoryEq.isNull(StockUninventory::getBatchNo); } else { uninventoryEq.eq(StockUninventory::getBatchNo, oldBatchNo); } StockUninventory stockUninventory = stockUninventoryMapper.selectOne(uninventoryEq); if (stockUninventory != null) { stockUninventory.setBatchNo(newBatchNo); stockUninventoryMapper.updateById(stockUninventory); } } @Override src/main/resources/mapper/basic/CustomerMapper.xml
@@ -9,9 +9,9 @@ <select id="listPage" resultType="com.ruoyi.basic.vo.CustomerVo"> select c.*, u.user_name usage_user_name, u.nick_name as usage_user_name, ( select group_concat(u2.user_name separator ', ') select group_concat(u2.nick_name separator ', ') from customer_user cu left join sys_user u2 on cu.user_id = u2.user_id where cu.customer_id = c.id @@ -107,14 +107,14 @@ </if> </where> </select> <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo"> select T1.customer_id, c.customer_name, T1.contractAmounts, IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount, IFNULL(T3.outboundAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount IFNULL(T3.outboundAmount, 0) AS shippedAmount, GREATEST(T1.contractAmounts - IFNULL(T3.outboundAmount, 0), 0) AS unshippedAmount from (select customer_id, sum(contract_amount) as contractAmounts from sales_ledger group by customer_id) T1 left join (select customer_id, sum(collection_amount) as receiptPaymentAmount from account_sales_collection group by customer_id) T2 on T1.customer_id = T2.customer_id left join ( SELECT sl.customer_id, @@ -128,16 +128,6 @@ and slp.type = 1 group by sl.customer_id ) T3 on T3.customer_id=T1.customer_id left join ( select sl.customer_id, sum(rm.refund_amount) as returnAmount from return_management rm left join shipping_info si on rm.shipping_id = si.id left join sales_ledger sl on si.sales_ledger_id = sl.id where rm.status=1 group by sl.customer_id ) T4 on T4.customer_id=T1.customer_id left join customer c on T1.customer_id = c.id <where> <if test="customerName!=null and customerName!=''"> @@ -145,27 +135,16 @@ </if> </where> </select> <select id="customewTransactionsDetails" resultType="com.ruoyi.sales.vo.CustomerTransactionsDetailsVo"> select sl.id salesLedgerId, sl.sales_contract_no, sl.execution_date, sl.contract_amount, IFNULL(T1.receiptPaymentAmount, 0) AS receiptPaymentAmount, IFNULL(T2.outboundAmount, 0) - IFNULL(T3.returnAmount, 0) AS receiptableAmount IFNULL(T2.outboundAmount, 0) AS shippedAmount, GREATEST(sl.contract_amount - IFNULL(T2.outboundAmount, 0), 0) AS unshippedAmount from sales_ledger sl left join ( select sl.id, sum(ascc.collection_amount) as receiptPaymentAmount from account_sales_collection ascc left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0 left join shipping_info s on sor.record_id = s.id LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id WHERE sor.record_type='13' and sor.approval_status=1 group by sl.id )T1 on T1.id = sl.id left join ( SELECT sl.id, @@ -179,15 +158,6 @@ and slp.type = 1 group by sl.id )T2 on T2.id = sl.id left join ( select sl.id, sum(rm.refund_amount) as returnAmount from return_management rm left join shipping_info si on rm.shipping_id = si.id left join sales_ledger sl on si.sales_ledger_id = sl.id where rm.status=1 group by sl.id )T3 on T3.id = sl.id where sl.customer_id = #{customerId} </select> </mapper> src/main/resources/mapper/basic/SupplierManageMapper.xml
@@ -68,95 +68,69 @@ </if> </where> </select> <select id="supplierTransactions" resultType="com.ruoyi.purchase.vo.SupplierTransactionsVo"> select T1.supplier_id, SELECT T1.supplier_id, sm.supplier_name, T1.contractAmounts, IFNULL(T2.paymentAmount, 0) AS paymentAmount, IFNULL(T3.InboundAmount, 0) - IFNULL(T4.returnAmount, 0) AS payableAmount from (select supplier_id, sum(contract_amount) as contractAmounts from purchase_ledger group by supplier_id) T1 left join (select supplier_id, sum(payment_amount) as paymentAmount from account_purchase_payment group by supplier_id) T2 on T1.supplier_id = T2.supplier_id left join ( SELECT pl.supplier_id, sum(sir.stock_in_num * slp.tax_inclusive_unit_price) AS InboundAmount FROM stock_in_record sir -- 10 ç±»åæå ³èè´¨æ£è¡¨ LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id -- å¨æå ³èéè´ï¼èªå¨éé 7 å 10ï¼ LEFT JOIN purchase_ledger pl ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) -- 产åå ³èä¸å¨ LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id -- æ¡ä»¶ WHERE sir.approval_status = 1 AND slp.type = 2 AND sir.record_type IN ('7','10') group by pl.supplier_id ) T3 on T3.supplier_id=T1.supplier_id left join ( select supplier_id, sum(total_amount) as returnAmount from purchase_return_orders pro group by supplier_id ) T4 on T4.supplier_id=T1.supplier_id left join supplier_manage sm on T1.supplier_id = sm.id IFNULL(T3.InboundAmount, 0) AS shippedAmount, T1.contractAmounts - IFNULL(T3.InboundAmount, 0) AS unshippedAmount FROM (SELECT supplier_id, SUM(contract_amount) AS contractAmounts FROM purchase_ledger GROUP BY supplier_id) T1 LEFT JOIN ( SELECT t.supplier_id, SUM(t.inbound_amount) AS InboundAmount FROM ( SELECT sir.stock_in_num * slp.tax_inclusive_unit_price AS inbound_amount, pl.supplier_id FROM stock_in_record sir INNER JOIN sales_ledger_product slp ON slp.id = sir.record_id INNER JOIN purchase_ledger pl ON pl.id = slp.sales_ledger_id WHERE sir.approval_status = 1 AND sir.record_type = 7 AND slp.type = 2 UNION ALL SELECT sir.stock_in_num * slp.tax_inclusive_unit_price AS inbound_amount, pl.supplier_id FROM stock_in_record sir INNER JOIN quality_inspect qi ON qi.id = sir.record_id INNER JOIN purchase_ledger pl ON pl.id = qi.purchase_ledger_id INNER JOIN sales_ledger_product slp ON slp.sales_ledger_id = pl.id AND slp.product_model_id = sir.product_model_id WHERE sir.approval_status = 1 AND sir.record_type = 10 AND slp.type = 2 ) t GROUP BY t.supplier_id ) T3 ON T3.supplier_id = T1.supplier_id LEFT JOIN supplier_manage sm ON T1.supplier_id = sm.id <where> <if test="supplierName!=null and supplierName!=''"> AND sm.supplier_name LIKE CONCAT('%',#{supplierName},'%') </if> </where> </select> <select id="supplierTransactionsDetails" resultType="com.ruoyi.purchase.vo.SupplierTransactionsDetailsVo"> select pl.id purchaseLedgerId, SELECT pl.id purchaseLedgerId, pl.purchase_contract_number, pl.execution_date, pl.contract_amount, IFNULL(T1.paymentAmount, 0) AS paymentAmount, IFNULL(T2.InboundAmount, 0) - IFNULL(T3.returnAmount, 0) AS payableAmount from purchase_ledger pl left join ( select pl.id, sum(app.payment_amount) as paymentAmount from account_purchase_payment app left join account_payment_application apa on app.account_payment_application_id = apa.id left join stock_in_record sir on FIND_IN_SET(sir.id, apa.stock_in_record_ids) > 0 -- 10 ç±»åæå ³èè´¨æ£è¡¨ LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id -- å¨æå ³èéè´ï¼èªå¨éé 7 å 10ï¼ LEFT JOIN purchase_ledger pl ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) WHERE sir.approval_status = 1 AND sir.record_type IN ('7','10') group by pl.id )T1 on T1.id = pl.id left join ( SELECT pl.id, sum(sir.stock_in_num * slp.tax_inclusive_unit_price) AS InboundAmount FROM stock_in_record sir -- 10 ç±»åæå ³èè´¨æ£è¡¨ LEFT JOIN quality_inspect qi ON sir.record_type = 10 AND sir.record_id = qi.id -- å¨æå ³èéè´ï¼èªå¨éé 7 å 10ï¼ LEFT JOIN purchase_ledger pl ON pl.id = IF(sir.record_type = 7, sir.record_id, qi.purchase_ledger_id) -- 产åå ³èä¸å¨ LEFT JOIN sales_ledger_product slp ON pl.id = slp.sales_ledger_id -- æ¡ä»¶ WHERE sir.approval_status = 1 AND slp.type = 2 AND sir.record_type IN ('7','10') group by pl.id )T2 on T2.id = pl.id left join ( select pl.id, sum(pro.total_amount) as returnAmount from purchase_return_orders pro left join purchase_ledger pl on pro.purchase_ledger_id = pl.id group by pl.id )T3 on T3.id = pl.id where pl.supplier_id = #{supplierId} IFNULL(T2.InboundAmount, 0) AS shippedAmount, pl.contract_amount - IFNULL(T2.InboundAmount, 0) AS unshippedAmount FROM purchase_ledger pl LEFT JOIN ( SELECT t.sales_ledger_id, SUM(t.inbound_amount) AS InboundAmount FROM ( SELECT sir.stock_in_num * slp.tax_inclusive_unit_price AS inbound_amount, slp.sales_ledger_id FROM stock_in_record sir INNER JOIN sales_ledger_product slp ON slp.id = sir.record_id WHERE sir.approval_status = 1 AND sir.record_type = 7 AND slp.type = 2 UNION ALL SELECT sir.stock_in_num * slp.tax_inclusive_unit_price AS inbound_amount, slp.sales_ledger_id FROM stock_in_record sir INNER JOIN quality_inspect qi ON qi.id = sir.record_id INNER JOIN purchase_ledger pl2 ON pl2.id = qi.purchase_ledger_id INNER JOIN sales_ledger_product slp ON slp.sales_ledger_id = pl2.id AND slp.product_model_id = sir.product_model_id WHERE sir.approval_status = 1 AND sir.record_type = 10 AND slp.type = 2 ) t GROUP BY t.sales_ledger_id ) T2 ON T2.sales_ledger_id = pl.id WHERE pl.supplier_id = #{supplierId} </select> </mapper> src/main/resources/mapper/device/DeviceMaintenanceMapper.xml
@@ -26,31 +26,41 @@ left join device_ledger dl on dm.device_ledger_id = dl.id left join sys_user su on dm.create_user = su.user_id <where> 1 = 1 <if test="deviceMaintenanceDto.deviceName != null"> and dl.device_name like concat('%',#{deviceMaintenanceDto.deviceName},'%') <if test="deviceMaintenanceDto.deviceName != null and deviceMaintenanceDto.deviceName != ''"> and dl.device_name like concat('%', #{deviceMaintenanceDto.deviceName}, '%') </if> <if test="deviceMaintenanceDto.deviceModel != null"> and dl.device_model like concat('%',#{deviceMaintenanceDto.deviceModel},'%') <if test="deviceMaintenanceDto.deviceModel != null and deviceMaintenanceDto.deviceModel != ''"> and dl.device_model like concat('%', #{deviceMaintenanceDto.deviceModel}, '%') </if> <if test="deviceMaintenanceDto.status != null"> and dm.status = #{deviceMaintenanceDto.status} </if> <if test="deviceMaintenanceDto.maintenanceActuallyName != null"> and dm.maintenance_actually_name like concat('%',#{deviceMaintenanceDto.maintenanceActuallyName},'%') <if test="deviceMaintenanceDto.maintenanceActuallyName != null and deviceMaintenanceDto.maintenanceActuallyName != ''"> and dm.maintenance_actually_name like concat('%', #{deviceMaintenanceDto.maintenanceActuallyName}, '%') </if> <if test="deviceMaintenanceDto.maintenancePlanTime != null"> and dm.maintenance_plan_time like concat('%',#{deviceMaintenanceDto.maintenancePlanTime},'%') and dm.maintenance_plan_time = #{deviceMaintenanceDto.maintenancePlanTime} </if> <if test="deviceMaintenanceDto.maintenanceActuallyTime != null"> and dm.maintenance_actually_time like concat('%',#{deviceMaintenanceDto.maintenanceActuallyTime},'%') </if> <if test="deviceMaintenanceDto.maintenanceActuallyTime != null"> and dm.maintenance_actually_time >= str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d') and dm.maintenance_actually_time < date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day) and dm.maintenance_actually_time >= str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d') and dm.maintenance_actually_time < date_add(str_to_date(#{deviceMaintenanceDto.maintenanceActuallyTime}, '%Y-%m-%d'), interval 1 day) </if> </where> order by <!-- å¾ ä¿å »(0)ä¼å æå¨ä¸é¢ï¼å·²å®ç»(1)å¨ä¸é¢ --> dm.status asc, case <!-- å½ç¶ææ¯ 0ï¼å¾ ä¿å »ï¼æ¶ï¼æè®¡åæ¶é´ååºï¼å³æ¶é´æè¿çåå卿ä¸é¢ --> when dm.status = 0 then dm.maintenance_plan_time end asc, case <!-- å½ç¶ææ¯ 1ï¼å·²å®ç»ï¼æ¶ï¼æå®é ä¿å »æ¶é´éåºï¼æè¿åä¿å »å®çååå¨å·²å®ç»éææå --> when dm.status = 1 then dm.maintenance_actually_time end desc </select> <select id="detailById" resultType="com.ruoyi.device.vo.DeviceMaintenanceVo"> select dm.id, dm.device_ledger_id, src/main/resources/mapper/stock/StockInRecordMapper.xml
@@ -132,7 +132,7 @@ and p.id in (select id from product_tree) </if> </where> order by sir.id desc order by sir.create_time desc </select> <select id="listStockInRecordExportData" resultType="com.ruoyi.stock.execl.StockInRecordExportData"> SELECT @@ -159,7 +159,7 @@ and sir.record_type = #{params.recordType} </if> </where> order by sir.id desc order by sir.create_time desc </select> <select id="listPageAccountPurchase" resultType="com.ruoyi.account.bean.vo.purchase.PurchaseInboundVo"> SELECT src/main/resources/mapper/stock/StockOutRecordMapper.xml
@@ -62,7 +62,7 @@ and p.id in (select id from product_tree) </if> </where> order by sor.id desc order by sor.create_time desc </select> <select id="listStockOutRecordExportData" resultType="com.ruoyi.stock.execl.StockOutRecordExportData"> SELECT src/test/java/com/ruoyi/approve/service/impl/ApproveProcessIdModifyTest.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,117 @@ package com.ruoyi.approve.service.impl; import com.ruoyi.approve.mapper.ApproveNodeMapper; import com.ruoyi.approve.mapper.ApproveProcessMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @SpringBootTest public class ApproveProcessIdModifyTest { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private ApproveProcessMapper approveProcessMapper; @Autowired private ApproveNodeMapper approveNodeMapper; /** * ä¿®æ¹å®¡æ¹æµç¨ç¼å· */ @Test void testModifyApproveId() { Map<String, String> modifyMap = new LinkedHashMap<>(); modifyMap.put("20260523020", "2026-05-09"); modifyMap.put("20260523019", "2026-04-29"); modifyMap.put("20260523018", "2026-04-16"); modifyMap.put("20260523009", "2026-05-07"); modifyMap.put("20260523008", "2026-04-11"); modifyMap.put("20260523007", "2026-04-10"); modifyMap.put("20260523006", "2026-04-07"); modifyMap.put("20260523003", "2026-04-07"); for (Map.Entry<String, String> entry : modifyMap.entrySet()) { processOne(entry.getKey(), entry.getValue()); } System.out.println("å ¨é¨å®æï¼"); } private void processOne(String oldApproveId, String targetDateStr) { LocalDate targetDate = LocalDate.parse(targetDateStr); String datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); String maxId = jdbcTemplate.queryForObject( "SELECT MAX(approve_id) FROM approve_process WHERE approve_id LIKE ?", String.class, datePrefix + "%"); long nextSeq = 1; if (maxId != null && maxId.length() >= 11) { nextSeq = Long.parseLong(maxId.substring(8)) + 1; } String newApproveId = datePrefix + String.format("%03d", nextSeq); System.out.println("æ§æ¹å·: " + oldApproveId + " -> æ°æ¹å·: " + newApproveId); List<Map<String, Object>> processList = jdbcTemplate.queryForList( "SELECT * FROM approve_process WHERE approve_id = ?", oldApproveId); if (processList.isEmpty()) { System.out.println(" â æªæ¾å°è®°å½ï¼è·³è¿"); return; } Map<String, Object> process = processList.get(0); Timestamp newApproveTime = replaceDate(process.get("approve_time"), targetDate); Timestamp newApproveOverTime = replaceDate(process.get("approve_over_time"), targetDate); Timestamp newCreateTime = replaceDate(process.get("create_time"), targetDate); jdbcTemplate.update( "UPDATE approve_process SET approve_id = ?, approve_time = ?, approve_over_time = ?, create_time = ? WHERE approve_id = ?", newApproveId, newApproveTime, newApproveOverTime, newCreateTime, oldApproveId); System.out.println(" â approve_process å·²æ´æ°"); List<Map<String, Object>> nodeList = jdbcTemplate.queryForList( "SELECT * FROM approve_node WHERE approve_process_id = ?", oldApproveId); if (!nodeList.isEmpty()) { Map<String, Object> node = nodeList.get(0); Timestamp newNodeTime = replaceDate(node.get("approve_node_time"), targetDate); Timestamp newNodeCreateTime = replaceDate(node.get("create_time"), targetDate); Timestamp newNodeUpdateTime = replaceDate(node.get("update_time"), targetDate); jdbcTemplate.update( "UPDATE approve_node SET approve_process_id = ?, approve_node_time = ?, create_time = ?, update_time = ? WHERE approve_process_id = ?", newApproveId, newNodeTime, newNodeCreateTime, newNodeUpdateTime, oldApproveId); System.out.println(" â approve_node å·²æ´æ° (" + nodeList.size() + " æ¡)"); } else { System.out.println(" - approve_node æ è®°å½"); } } private static Timestamp replaceDate(Object dateObj, LocalDate targetDate) { if (dateObj == null) return null; LocalDateTime ldt; if (dateObj instanceof Timestamp ts) { ldt = ts.toLocalDateTime(); } else if (dateObj instanceof LocalDateTime dt) { ldt = dt; } else { return null; } return Timestamp.valueOf(LocalDateTime.of(targetDate, ldt.toLocalTime())); } } src/test/java/com/ruoyi/device/service/impl/MaintenanceTaskJobTest.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,448 @@ package com.ruoyi.device.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.device.mapper.MaintenanceTaskMapper; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.MaintenanceTask; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; import java.time.LocalDate; import java.time.DayOfWeek; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * 设å¤ä¿å »ä»»å¡æµè¯ç±» * * æ¥è¯¢æ°æ®åºä¸çä¿å »ä»»å¡ï¼æ ¹æ®ç»è®°æ¥æã颿¬¡ãå¼å§æ¥æä¸æ¶é´æ¥çæä¿å »è®°å½ * ä»ç»è®°æ¥æå°ä»å¤©ï¼æé¢æ¬¡çæææç¬¦åçè®°å½ */ @SpringBootTest public class MaintenanceTaskJobTest { @Autowired private MaintenanceTaskMapper maintenanceTaskMapper; @Autowired private DeviceMaintenanceServiceImpl deviceMaintenanceService; @Autowired private JdbcTemplate jdbcTemplate; /** * æµè¯ï¼ææ¥æåæ£çæä¿å »è®°å½ * ä»ç»è®°æ¥æå°ä»å¤©ï¼ææ¥æé¡ºåºï¼æ¯å¤©çææè®¾å¤ä¿å »è®°å½ä¸èµ·çæ */ @Test void testGenerateOneByOne() { // æ¥è¯¢ææå¯ç¨çä¿å »ä»»å¡ List<MaintenanceTask> tasks = maintenanceTaskMapper.selectList( new LambdaQueryWrapper<MaintenanceTask>() .eq(MaintenanceTask::getIsActive, 1) .eq(MaintenanceTask::getDeleted, 0) ); if (tasks.isEmpty()) { System.out.println("=== æ²¡ææ¾å°ä¿å »ä»»å¡ ==="); return; } // æ¾åºææ©åææçç»è®°æ¥æ LocalDate earliestDate = tasks.stream() .map(MaintenanceTask::getRegistrationDate) .filter(Objects::nonNull) .min(LocalDate::compareTo) .orElse(LocalDate.now()); LocalDate latestDate = LocalDate.now(); System.out.println("=== ç»è®°æ¥æèå´: " + earliestDate + " è³ " + latestDate + " ==="); System.out.println("=== å ±æ¾å° " + tasks.size() + " 个ä¿å »ä»»å¡ ===\n"); // ææ¥æé¡ºåºçæï¼æ¯å¤©çæä¸æ¬¡ LocalDate currentDate = earliestDate; int totalRecords = 0; while (!currentDate.isAfter(latestDate)) { final LocalDate date = currentDate; final List<DeviceMaintenance> recordsToSave = new ArrayList<>(); // æ¾åºå½å¤©éè¦ä¿å »çææä»»å¡ for (MaintenanceTask task : tasks) { LocalDate startDate = task.getRegistrationDate() != null ? task.getRegistrationDate() : LocalDate.now(); if (date.isBefore(startDate)) { continue; // è·³è¿ç»è®°æ¥æä¹åçæ¥æ } // æ£æ¥è¯¥ä»»å¡ç颿¬¡æ¯å¦å¹é å½å¤© if (isExecutionDate(task.getFrequencyType(), task.getFrequencyDetail(), startDate, date)) { LocalTime time = getExecutionTime(task.getFrequencyDetail()); LocalDateTime executionDateTime = LocalDateTime.of(date, time); DeviceMaintenance record = createMaintenanceRecord(task, executionDateTime); recordsToSave.add(record); } } // 妿å½å¤©æè®°å½ï¼æ¹éä¿å if (!recordsToSave.isEmpty()) { for (DeviceMaintenance record : recordsToSave) { try { deviceMaintenanceService.save(record); System.out.println(" â è®¡åæ¥æ: " + date + " | 设å¤: " + record.getDeviceName() + " | è®°å½ID: " + record.getId()); } catch (Exception e) { System.out.println(" â è®¡åæ¥æ: " + date + " | 设å¤: " + record.getDeviceName() + " | 失败: " + e.getMessage()); } } totalRecords += recordsToSave.size(); System.out.println(" === æ¥æ " + date + " å ±çæ " + recordsToSave.size() + " æ¡è®°å½ ===\n"); } currentDate = currentDate.plusDays(1); } System.out.println("=== æ§è¡å®æ: å ±çæ " + totalRecords + " æ¡è®°å½ ==="); } /** * æ£æ¥æå®æ¥ææ¯å¦ç¬¦åä»»å¡çæ§è¡é¢æ¬¡ */ private boolean isExecutionDate(String frequencyType, String frequencyDetail, LocalDate startDate, LocalDate checkDate) { if (checkDate.isBefore(startDate)) { return false; } switch (frequencyType) { case "DAILY": return true; // æ¯å¤©é½éè¦æ§è¡ case "WEEKLY": return isWeeklyMatch(frequencyDetail, checkDate); case "MONTHLY": return isMonthlyMatch(frequencyDetail, checkDate); case "QUARTERLY": return isQuarterlyMatch(frequencyDetail, checkDate); default: return false; } } private boolean isWeeklyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; DayOfWeek targetDay = parseDayOfWeek(dayOfWeekStr); return date.getDayOfWeek() == targetDay; } private boolean isMonthlyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); int targetDay = Integer.parseInt(parts[0]); return date.getDayOfMonth() == targetDay; } private boolean isQuarterlyMatch(String detail, LocalDate date) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); // å£åº¦ä¸ç第å 个æ int targetDay = Integer.parseInt(parts[1]); int currentMonth = date.getMonthValue(); int monthInQuarter = (currentMonth - 1) % 3 + 1; return monthInQuarter == quarterMonth && date.getDayOfMonth() == targetDay; } private LocalTime getExecutionTime(String frequencyDetail) { String[] parts = frequencyDetail.split(","); if (parts.length >= 2 && (parts.length == 2 || parts.length == 3)) { return LocalTime.parse(parts[parts.length - 1]); } return LocalTime.of(9, 0); // é»è®¤æ¶é´ } /** * æµè¯ï¼æ ¹æ®ææä¿å »ä»»å¡çæè®¾å¤ä¿å »è®°å½ * æé¡ºåºå¤çæ¯ä¸ªä»»å¡ï¼ä»ç»è®°æ¥æå°ä»å¤©ï¼æé¢æ¬¡ä¾æ¬¡çæææç¬¦åçè®°å½ */ @Test void testGenerateMaintenanceRecordForAllTasks() { // æ¥è¯¢ææå¯ç¨çä¿å »ä»»å¡ï¼æIDæåº List<MaintenanceTask> tasks = maintenanceTaskMapper.selectList( new LambdaQueryWrapper<MaintenanceTask>() .eq(MaintenanceTask::getIsActive, 1) .eq(MaintenanceTask::getDeleted, 0) .orderByAsc(MaintenanceTask::getId) ); System.out.println("=== å ±æ¾å° " + tasks.size() + " 个ä¿å »ä»»å¡ ===\n"); int totalRecords = 0; for (MaintenanceTask task : tasks) { try { // æé¡ºåºçæè¯¥ä»»å¡ææç¬¦å颿¬¡çè®°å½ int recordCount = generateRecordsForTaskSequentially(task); totalRecords += recordCount; System.out.println("â ä»»å¡ID: " + task.getId() + " | " + task.getTaskName() + " | ç»è®°æ¥æ: " + task.getRegistrationDate() + " | çæè®°å½æ°: " + recordCount); } catch (Exception e) { System.out.println("â ä»»å¡ID: " + task.getId() + " | " + task.getTaskName() + " | 失败: " + e.getMessage()); } } System.out.println("\n=== æ§è¡å®æ: å ±çæ " + totalRecords + " æ¡è®°å½ ==="); } /** * 为åä¸ªä»»å¡æé¡ºåºçæææç¬¦å颿¬¡çè®°å½ */ private int generateRecordsForTaskSequentially(MaintenanceTask task) { LocalDate startDate = task.getRegistrationDate(); if (startDate == null) { startDate = LocalDate.now(); } LocalDate endDate = LocalDate.now(); // æ ¹æ®é¢æ¬¡è·åææéè¦æ§è¡çæ¥æ List<LocalDateTime> executionDates = getExecutionDates(task.getFrequencyType(), task.getFrequencyDetail(), startDate, endDate); if (executionDates.isEmpty()) { return 0; } int count = 0; for (LocalDateTime executionDate : executionDates) { try { // æé¡ºåºçæä¿å »è®°å½ DeviceMaintenance record = createMaintenanceRecord(task, executionDate); deviceMaintenanceService.save(record); count++; } catch (Exception e) { System.out.println(" â æ¥æ: " + executionDate + " | 失败: " + e.getMessage()); } } // æ´æ°ä»»å¡ç䏿¬¡æ§è¡æ¶é´ if (count > 0) { // first_execution = ç¬¬ä¸æ¡è®°å½çæ¥æ = last_execution_time LocalDateTime firstExecution = executionDates.get(0); // next_execution = æå䏿¡è®°å½çä¸ä¸æ¬¡æ§è¡æ¥æ LocalDateTime lastExecution = executionDates.get(executionDates.size() - 1); LocalDateTime nextExecution = calculateNextExecutionTime(task.getFrequencyType(), task.getFrequencyDetail(), lastExecution); updateTaskExecutionTime(task.getId(), firstExecution, nextExecution); } return count; } /** * è·åæå®æ¥æèå´å ææç¬¦å颿¬¡çæ§è¡æ¶é´ */ private List<LocalDateTime> getExecutionDates(String frequencyType, String frequencyDetail, LocalDate startDate, LocalDate endDate) { List<LocalDateTime> dates = new ArrayList<>(); switch (frequencyType) { case "DAILY": dates.addAll(getDailyDates(startDate, endDate, frequencyDetail)); break; case "WEEKLY": dates.addAll(getWeeklyDates(startDate, endDate, frequencyDetail)); break; case "MONTHLY": dates.addAll(getMonthlyDates(startDate, endDate, frequencyDetail)); break; case "QUARTERLY": dates.addAll(getQuarterlyDates(startDate, endDate, frequencyDetail)); break; } return dates; } private List<LocalDateTime> getDailyDates(LocalDate startDate, LocalDate endDate, String timeStr) { List<LocalDateTime> dates = new ArrayList<>(); LocalTime time = LocalTime.parse(timeStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusDays(1); } return dates; } private List<LocalDateTime> getMonthlyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new ArrayList<>(); String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); // ä»ç»è®°æ¥ææå¨ææ¾ç¬¬ä¸ä¸ªç¬¦åæ¡ä»¶çæ¥æ int actualDay = Math.min(dayOfMonth, startDate.lengthOfMonth()); LocalDate current = startDate.withDayOfMonth(actualDay); // 妿è¿ä¸ªæ¥æå¨ç»è®°æ¥æä¹åï¼å¾åæ¨ä¸ä¸ªæ if (current.isBefore(startDate)) { current = current.plusMonths(1); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(1); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } return dates; } private List<LocalDateTime> getWeeklyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new ArrayList<>(); String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; LocalTime time = LocalTime.parse(parts[1]); java.time.DayOfWeek targetDay = parseDayOfWeek(dayOfWeekStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { if (current.getDayOfWeek() == targetDay) { dates.add(LocalDateTime.of(current, time)); } current = current.plusDays(1); } return dates; } private List<LocalDateTime> getQuarterlyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new ArrayList<>(); String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonth = startDate.getMonthValue(); int targetMonth = ((currentMonth - 1) / 3) * 3 + quarterMonth; int yearAdjust = 0; if (targetMonth > 12) { targetMonth -= 12; yearAdjust = 1; } int actualDay = Math.min(dayOfMonth, YearMonth.of(startDate.getYear() + yearAdjust, targetMonth).lengthOfMonth()); LocalDate current = startDate.withYear(startDate.getYear() + yearAdjust) .withMonth(targetMonth) .withDayOfMonth(actualDay); while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(3); actualDay = Math.min(dayOfMonth, current.lengthOfMonth()); current = current.withDayOfMonth(actualDay); } return dates; } private java.time.DayOfWeek parseDayOfWeek(String dayOfWeekStr) { switch (dayOfWeekStr.toUpperCase()) { case "MON": return java.time.DayOfWeek.MONDAY; case "TUE": return java.time.DayOfWeek.TUESDAY; case "WED": return java.time.DayOfWeek.WEDNESDAY; case "THU": return java.time.DayOfWeek.THURSDAY; case "FRI": return java.time.DayOfWeek.FRIDAY; case "SAT": return java.time.DayOfWeek.SATURDAY; case "SUN": return java.time.DayOfWeek.SUNDAY; default: throw new IllegalArgumentException("æ æçææå : " + dayOfWeekStr); } } /** * å建ä¿å »è®°å½ */ private DeviceMaintenance createMaintenanceRecord(MaintenanceTask task, LocalDateTime executionDate) { DeviceMaintenance record = new DeviceMaintenance(); record.setDeviceName(task.getTaskName()); record.setMaintenanceTaskId(task.getId()); record.setDeviceLedgerId(task.getTaskId()); record.setMaintenancePlanTime(executionDate); record.setMaintenanceActuallyName(task.getMaintenancePerson()); record.setFrequencyType(task.getFrequencyType()); record.setFrequencyDetail(task.getFrequencyDetail()); record.setTenantId(task.getTenantId()); record.setStatus(0); // å¾ ä¿å » record.setDeviceModel(task.getDeviceModel()); record.setMachineryCategory(task.getMachineryCategory()); record.setCreateUser(Integer.parseInt(task.getRegistrantId().toString())); record.setUpdateTime(executionDate); record.setCreateTime(executionDate); record.setUpdateUser(Integer.parseInt(task.getRegistrantId().toString())); return record; } /** * æ´æ°ä»»å¡çæ§è¡æ¶é´ */ private void updateTaskExecutionTime(Long taskId, LocalDateTime lastExecutionTime, LocalDateTime nextExecutionTime) { String sql = "UPDATE maintenance_task SET last_execution_time = ?, next_execution_time = ? WHERE id = ?"; jdbcTemplate.update(sql, lastExecutionTime, nextExecutionTime, taskId); } /** * 计ç®ä¸æ¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { return switch (frequencyType) { case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime); case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime); case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime); case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime); default -> throw new IllegalArgumentException("䏿¯æç颿¬¡ç±»å: " + frequencyType); }; } private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { LocalTime executionTime = LocalTime.parse(timeStr); LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime); return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1); } private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); return current.plusMonths(1) .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth())) .with(time); } private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) { return current.plusWeeks(1); } private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1; YearMonth targetYearMonth; if (currentMonthInQuarter < quarterMonth) { targetYearMonth = YearMonth.from(current).plusMonths(quarterMonth - currentMonthInQuarter); } else { targetYearMonth = YearMonth.from(current).plusMonths(3 - currentMonthInQuarter + quarterMonth); } int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth()); return LocalDateTime.of(targetYearMonth.getYear(), targetYearMonth.getMonthValue(), adjustedDay, time.getHour(), time.getMinute()); } } src/test/java/com/ruoyi/inspectiontask/service/impl/TimingTaskJobTest.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,339 @@ package com.ruoyi.inspectiontask.service.impl; import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper; import com.ruoyi.inspectiontask.pojo.InspectionTask; import com.ruoyi.inspectiontask.pojo.TimingTask; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; import java.time.LocalDate; import java.util.HashSet; import java.util.List; import java.util.Set; /** * 设å¤å·¡æ£å®æ¶ä»»å¡æµè¯ç±» * * æ¥è¯¢æ°æ®åºä¸çå·¡æ£ä»»å¡ï¼æ ¹æ®ç»è®°æ¥æã颿¬¡ãå¼å§æ¥æä¸æ¶é´æ¥çæå·¡æ£è®°å½ * å¹¶æ´æ° timing_task 表çæåæ§è¡æ¶é´å䏿¬¡æ§è¡æ¶é´ */ @SpringBootTest public class TimingTaskJobTest { @Autowired private InspectionTaskMapper inspectionTaskMapper; @Autowired private JdbcTemplate jdbcTemplate; /** * æµè¯ï¼æ ¹æ®ææå·¡æ£ä»»å¡çæå·¡æ£è®°å½ * ä»ç»è®°æ¥æå°ä»å¤©ï¼æé¢æ¬¡æ¯å¨çæä¸æ¡è®°å½ */ @Test void testGenerateInspectionRecordForAllTasks() { // æ¥è¯¢ææå¯ç¨çå·¡æ£ä»»å¡ String sql = "SELECT * FROM timing_task WHERE is_enabled = 1 AND deleted = 0"; List<TimingTask> tasks = jdbcTemplate.query(sql, (rs, rowNum) -> { TimingTask task = new TimingTask(); task.setId(rs.getLong("id")); task.setTaskName(rs.getString("task_name")); task.setInspectionProject(rs.getString("inspection_project")); task.setTaskId(rs.getInt("task_id")); task.setInspectorIds(rs.getString("inspector_ids")); task.setInspectionLocation(rs.getString("inspection_location")); task.setFrequencyType(rs.getString("frequency_type")); task.setFrequencyDetail(rs.getString("frequency_detail")); task.setRemarks(rs.getString("remarks")); task.setRegistrantId(rs.getLong("registrant_id")); task.setRegistrant(rs.getString("registrant")); task.setTenantId(rs.getLong("tenant_id")); // è·åç»è®°æ¥æ java.sql.Date regDate = rs.getDate("registration_date"); if (regDate != null) { task.setRegistrationDate(regDate.toLocalDate()); } return task; }); System.out.println("=== å ±æ¾å° " + tasks.size() + " 个巡æ£ä»»å¡ ===\n"); int totalRecords = 0; for (TimingTask task : tasks) { try { // çæææç¬¦å颿¬¡çè®°å½ int recordCount = generateRecordsForTask(task); totalRecords += recordCount; System.out.println("â ä»»å¡ID: " + task.getId() + " | " + task.getTaskName() + " | ç»è®°æ¥æ: " + task.getRegistrationDate() + " | çæè®°å½æ°: " + recordCount); } catch (Exception e) { System.out.println("â ä»»å¡ID: " + task.getId() + " | " + task.getTaskName() + " | 失败: " + e.getMessage()); } } System.out.println("\n=== æ§è¡å®æ: å ±çæ " + totalRecords + " æ¡è®°å½ ==="); } /** * 为å个任å¡çæææç¬¦å颿¬¡çè®°å½ */ private int generateRecordsForTask(TimingTask task) { LocalDate startDate = task.getRegistrationDate(); if (startDate == null) { startDate = LocalDate.now(); } LocalDate endDate = LocalDate.now(); // æ ¹æ®é¢æ¬¡è·åææéè¦æ§è¡çæ¥æ List<LocalDateTime> executionDates = getExecutionDates(task.getFrequencyType(), task.getFrequencyDetail(), startDate, endDate); int count = 0; for (LocalDateTime executionDate : executionDates) { try { // çæå·¡æ£è®°å½ InspectionTask record = createInspectionRecord(task, executionDate); inspectionTaskMapper.insert(record); // æ´æ°ä»»å¡ç䏿¬¡æ§è¡æ¶é´ updateTaskLastExecutionTime(task.getId(), executionDate); count++; } catch (Exception e) { System.out.println(" â æ¥æ: " + executionDate + " | 失败: " + e.getMessage()); } } // æ´æ°ä»»å¡ç䏿¬¡æ§è¡æ¶é´ if (count > 0) { LocalDateTime lastExecution = executionDates.get(executionDates.size() - 1); LocalDateTime nextExecution = calculateNextExecutionTime(task.getFrequencyType(), task.getFrequencyDetail(), lastExecution); updateTaskNextExecutionTime(task.getId(), nextExecution); } return count; } /** * è·åæå®æ¥æèå´å ææç¬¦å颿¬¡çæ§è¡æ¶é´ */ private List<LocalDateTime> getExecutionDates(String frequencyType, String frequencyDetail, LocalDate startDate, LocalDate endDate) { List<LocalDateTime> dates = new java.util.ArrayList<>(); switch (frequencyType) { case "DAILY": dates.addAll(getDailyDates(startDate, endDate, frequencyDetail)); break; case "WEEKLY": dates.addAll(getWeeklyDates(startDate, endDate, frequencyDetail)); break; case "MONTHLY": dates.addAll(getMonthlyDates(startDate, endDate, frequencyDetail)); break; case "QUARTERLY": dates.addAll(getQuarterlyDates(startDate, endDate, frequencyDetail)); break; } return dates; } private List<LocalDateTime> getDailyDates(LocalDate startDate, LocalDate endDate, String timeStr) { List<LocalDateTime> dates = new java.util.ArrayList<>(); LocalTime time = LocalTime.parse(timeStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusDays(1); } return dates; } private List<LocalDateTime> getWeeklyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new java.util.ArrayList<>(); String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; LocalTime time = LocalTime.parse(parts[1]); Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr); LocalDate current = startDate; while (!current.isAfter(endDate)) { if (targetDays.contains(current.getDayOfWeek())) { dates.add(LocalDateTime.of(current, time)); } current = current.plusDays(1); } return dates; } private List<LocalDateTime> getMonthlyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new java.util.ArrayList<>(); String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); LocalDate current = startDate.plusMonths(0).withDayOfMonth(Math.min(dayOfMonth, startDate.lengthOfMonth())); while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(1).withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).lengthOfMonth())); } return dates; } private List<LocalDateTime> getQuarterlyDates(LocalDate startDate, LocalDate endDate, String detail) { List<LocalDateTime> dates = new java.util.ArrayList<>(); String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonth = startDate.getMonthValue(); int targetMonth = ((currentMonth - 1) / 3) * 3 + quarterMonth; int yearAdjust = 0; if (targetMonth > 12) { targetMonth -= 12; yearAdjust = 1; } LocalDate current = startDate.withYear(startDate.getYear() + yearAdjust) .withMonth(targetMonth) .withDayOfMonth(Math.min(dayOfMonth, YearMonth.of(startDate.getYear() + yearAdjust, targetMonth).lengthOfMonth())); while (!current.isAfter(endDate)) { dates.add(LocalDateTime.of(current, time)); current = current.plusMonths(3).withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(3).lengthOfMonth())); } return dates; } private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) { Set<DayOfWeek> days = new HashSet<>(); String[] dayStrs = dayOfWeekStr.split("\\|"); for (String dayStr : dayStrs) { switch (dayStr.toUpperCase()) { case "MON": days.add(DayOfWeek.MONDAY); break; case "TUE": days.add(DayOfWeek.TUESDAY); break; case "WED": days.add(DayOfWeek.WEDNESDAY); break; case "THU": days.add(DayOfWeek.THURSDAY); break; case "FRI": days.add(DayOfWeek.FRIDAY); break; case "SAT": days.add(DayOfWeek.SATURDAY); break; case "SUN": days.add(DayOfWeek.SUNDAY); break; } } return days; } /** * 计ç®ä¸æ¬¡æ§è¡æ¶é´ */ private LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { return switch (frequencyType) { case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime); case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime); case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime); case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime); default -> throw new IllegalArgumentException("䏿¯æç颿¬¡ç±»å: " + frequencyType); }; } private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) { LocalTime executionTime = LocalTime.parse(timeStr); LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime); return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1); } private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int dayOfMonth = Integer.parseInt(parts[0]); LocalTime time = LocalTime.parse(parts[1]); return current.plusMonths(1) .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth())) .with(time); } private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); String dayOfWeekStr = parts[0]; LocalTime time = LocalTime.parse(parts[1]); Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr); LocalDateTime nextTime = current; while (true) { nextTime = nextTime.plusDays(1); if (targetDays.contains(nextTime.getDayOfWeek())) { return LocalDateTime.of(nextTime.toLocalDate(), time); } if (nextTime.isAfter(current.plusYears(1))) { throw new RuntimeException("æ æ³æ¾å°ä¸æ¬¡æ§è¡æ¶é´"); } } } private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) { String[] parts = detail.split(","); int quarterMonth = Integer.parseInt(parts[0]); int dayOfMonth = Integer.parseInt(parts[1]); LocalTime time = LocalTime.parse(parts[2]); int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1; YearMonth targetYearMonth; if (currentMonthInQuarter < quarterMonth) { targetYearMonth = YearMonth.from(current).plusMonths(quarterMonth - currentMonthInQuarter); } else { targetYearMonth = YearMonth.from(current).plusMonths(3 - currentMonthInQuarter + quarterMonth); } int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth()); return LocalDateTime.of(targetYearMonth.getYear(), targetYearMonth.getMonthValue(), adjustedDay, time.getHour(), time.getMinute()); } /** * å建巡æ£è®°å½ */ private InspectionTask createInspectionRecord(TimingTask timingTask, LocalDateTime executionDate) { InspectionTask inspectionTask = new InspectionTask(); inspectionTask.setTaskName(timingTask.getTaskName()); inspectionTask.setInspectionProject(timingTask.getInspectionProject()); inspectionTask.setTaskId(timingTask.getTaskId()); inspectionTask.setInspectorId(timingTask.getInspectorIds()); inspectionTask.setInspectionLocation(timingTask.getInspectionLocation()); inspectionTask.setRemarks("èªå¨çæèªå®æ¶ä»»å¡ID: " + timingTask.getId() + "ï¼" + timingTask.getRemarks()); inspectionTask.setRegistrantId(timingTask.getRegistrantId()); inspectionTask.setFrequencyType(timingTask.getFrequencyType()); inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail()); inspectionTask.setTenantId(timingTask.getTenantId()); // 设置ç»è®°æ¥æä¸ºæ§è¡æ¥æ inspectionTask.setCreateTime(executionDate); inspectionTask.setUpdateTime(executionDate); return inspectionTask; } /** * æ´æ°ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private void updateTaskLastExecutionTime(Long taskId, LocalDateTime lastExecutionTime) { String updateSql = "UPDATE timing_task SET last_execution_time = ? WHERE id = ?"; jdbcTemplate.update(updateSql, lastExecutionTime, taskId); } /** * æ´æ°ä»»å¡ç䏿¬¡æ§è¡æ¶é´ */ private void updateTaskNextExecutionTime(Long taskId, LocalDateTime nextExecutionTime) { String updateSql = "UPDATE timing_task SET next_execution_time = ? WHERE id = ?"; jdbcTemplate.update(updateSql, nextExecutionTime, taskId); } } src/test/java/com/ruoyi/stock/service/impl/StockOutRecordBatchUpdateTest.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,254 @@ package com.ruoyi.stock.service.impl; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionOrderPickMapper; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderPick; import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper; import com.ruoyi.purchase.pojo.PurchaseReturnOrders; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.ShippingInfoMapper; import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.pojo.ShippingInfo; import com.ruoyi.stock.mapper.StockOutRecordMapper; import com.ruoyi.stock.pojo.StockOutRecord; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @SpringBootTest public class StockOutRecordBatchUpdateTest { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private StockOutRecordMapper stockOutRecordMapper; @Autowired private PurchaseReturnOrdersMapper purchaseReturnOrdersMapper; @Autowired private ShippingInfoMapper shippingInfoMapper; @Autowired private SalesLedgerMapper salesLedgerMapper; @Autowired private ProductionOrderPickMapper productionOrderPickMapper; @Autowired private ProductionOrderMapper productionOrderMapper; /** * æ´æ°åºåºåæ¶é´ */ @Test void testUpdateStockOutRecords() { List<StockOutRecord> allRecords = stockOutRecordMapper.selectList(null); System.out.println("æ»è®°å½æ°: " + allRecords.size()); List<RecordWithDate> recordWithDates = new ArrayList<>(); for (StockOutRecord record : allRecords) { String type = record.getRecordType(); if (type == null) continue; switch (type) { case "1": case "10": recordWithDates.add(resolveType1or10(record, type)); break; case "3": System.out.println("ç±»å3ï¼ç产æ¥å·¥-åºåºï¼é¢ç忝ï¼å½åæ æ°æ®ï¼è·³è¿"); break; case "8": System.out.println("ç±»å8ï¼éå®-åºåºï¼é¢ç忝ï¼å½åæ æ°æ®ï¼è·³è¿"); break; case "9": recordWithDates.add(resolveType9(record)); break; case "13": recordWithDates.add(resolveType13(record)); break; case "14": recordWithDates.add(resolveType14or15(record)); break; case "15": recordWithDates.add(resolveType14or15(record)); break; default: System.out.println("æªç¥ç±»å " + type + "ï¼è·³è¿ ID=" + record.getId()); } } Map<LocalDate, List<RecordWithDate>> byDate = recordWithDates.stream() .collect(Collectors.groupingBy( rwd -> rwd.dateTime.toLocalDate(), LinkedHashMap::new, Collectors.toList() )); int totalUpdated = 0; for (Map.Entry<LocalDate, List<RecordWithDate>> dateEntry : byDate.entrySet()) { LocalDate date = dateEntry.getKey(); List<RecordWithDate> records = dateEntry.getValue(); records.sort(Comparator.comparing(rwd -> rwd.dateTime)); assignRandomTimes(records, date); records.sort(Comparator.comparing(rwd -> rwd.dateTime)); int seq = 1; for (RecordWithDate rwd : records) { StockOutRecord record = rwd.record; String type = record.getRecordType(); String batchNo = "CK" + date.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + String.format("%03d", seq++); LocalDateTime createTime = rwd.dateTime; jdbcTemplate.update( "UPDATE stock_out_record SET outbound_batches = ?, create_time = ?, update_time = ? WHERE id = ?", batchNo, createTime, createTime, record.getId()); System.out.println("ID=" + record.getId() + " type=" + type + " batch=" + batchNo + " time=" + createTime); totalUpdated++; } } System.out.println("å ¨é¨å®æï¼å ±æ´æ° " + totalUpdated + " æ¡è®°å½"); } private void assignRandomTimes(List<RecordWithDate> records, LocalDate date) { List<RecordWithDate> individual = new ArrayList<>(); Map<Long, List<RecordWithDate>> grouped = new LinkedHashMap<>(); for (RecordWithDate rwd : records) { String type = rwd.record.getRecordType(); if (!"1".equals(type) && !"9".equals(type) && !"10".equals(type) && !"13".equals(type) && !"14".equals(type) && !"15".equals(type)) { continue; } if (("14".equals(type) || "15".equals(type)) && rwd.record.getRecordId() != null) { grouped.computeIfAbsent(rwd.record.getRecordId(), k -> new ArrayList<>()).add(rwd); } else { individual.add(rwd); } } List<TimeSlot> slots = new ArrayList<>(); for (RecordWithDate rwd : individual) { slots.add(new TimeSlot(rwd.record.getId(), List.of(rwd))); } for (Map.Entry<Long, List<RecordWithDate>> entry : grouped.entrySet()) { long minId = entry.getValue().stream() .mapToLong(rwd -> rwd.record.getId()) .min().orElse(0); slots.add(new TimeSlot(minId, entry.getValue())); } if (slots.isEmpty()) return; slots.sort(Comparator.comparingLong(s -> s.sortKey)); int totalMinutes = 480; for (int i = 0; i < slots.size(); i++) { int offsetMinutes = (int) ((long) i * totalMinutes / slots.size()); int jitter = (int) (Math.random() * 6 - 3); offsetMinutes = Math.max(0, Math.min(479, offsetMinutes + jitter)); LocalTime time; if (offsetMinutes < 240) { time = LocalTime.of(8, 0).plusMinutes(offsetMinutes); } else { time = LocalTime.of(14, 0).plusMinutes(offsetMinutes - 240); } LocalDateTime dt = LocalDateTime.of(date, time); for (RecordWithDate rwd : slots.get(i).members) { rwd.dateTime = dt; } } } private static class TimeSlot { final long sortKey; final List<RecordWithDate> members; TimeSlot(long sortKey, List<RecordWithDate> members) { this.sortKey = sortKey; this.members = members; } } private RecordWithDate resolveType1or10(StockOutRecord record, String type) { LocalDate date = record.getCreateTime() != null ? record.getCreateTime().toLocalDate() : LocalDate.now(); return new RecordWithDate(record, LocalDateTime.of(date, LocalTime.of(8, 0))); } private RecordWithDate resolveType9(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { PurchaseReturnOrders pro = purchaseReturnOrdersMapper.selectById(recordId); if (pro != null && pro.getPreparedAt() != null) { return new RecordWithDate(record, LocalDateTime.of(pro.getPreparedAt(), LocalTime.of(8, 0))); } } return fallbackDateTime(record); } private RecordWithDate resolveType13(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { ShippingInfo shippingInfo = shippingInfoMapper.selectById(recordId); if (shippingInfo != null && shippingInfo.getSalesLedgerId() != null) { SalesLedger salesLedger = salesLedgerMapper.selectById(shippingInfo.getSalesLedgerId()); if (salesLedger != null && salesLedger.getDeliveryDate() != null) { return new RecordWithDate(record, LocalDateTime.of(salesLedger.getDeliveryDate(), LocalTime.of(8, 0))); } } } return fallbackDateTime(record); } private RecordWithDate resolveType14or15(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { ProductionOrderPick pick = productionOrderPickMapper.selectById(recordId); if (pick != null && pick.getProductionOrderId() != null) { ProductionOrder order = productionOrderMapper.selectById(pick.getProductionOrderId()); if (order != null && order.getStartTime() != null) { return new RecordWithDate(record, LocalDateTime.of(order.getStartTime().toLocalDate(), LocalTime.of(8, 0))); } } } return fallbackDateTime(record); } private RecordWithDate fallbackDateTime(StockOutRecord record) { return record.getCreateTime() != null ? new RecordWithDate(record, record.getCreateTime()) : new RecordWithDate(record, LocalDateTime.now()); } private static class RecordWithDate { final StockOutRecord record; LocalDateTime dateTime; RecordWithDate(StockOutRecord record, LocalDateTime dateTime) { this.record = record; this.dateTime = dateTime; } } }