From d7cda8aec488caa2623547893d633063a09888e0 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 16 六月 2026 09:39:47 +0800
Subject: [PATCH] ai数据自动生成

---
 src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java |   81 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
index 4f83c66..777438f 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -164,6 +164,9 @@
         purchaseLedger.setApprovalStatus(1);
         // 3. 鏂板鎴栨洿鏂颁富琛�
         if (purchaseLedger.getId() == null) {
+            if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) {
+                purchaseLedger.setPurchaseContractNumber(generatePurchaseContractNo(purchaseLedger.getEntryDate()));
+            }
             purchaseLedgerMapper.insert(purchaseLedger);
         } else {
             // 鍒犻櫎閲囪喘瀹℃牳锛岄噸鏂版彁浜�
@@ -1073,4 +1076,82 @@
         }
         return sb.toString();
     }
+
+    private static final String PURCHASE_LOCK_PREFIX = "purchase_contract_no:";
+    private static final long PURCHASE_LOCK_WAIT_TIMEOUT = 10;
+    private static final long PURCHASE_LOCK_EXPIRE_TIME = 30;
+
+    private String generatePurchaseContractNo(Date entryDate) {
+        LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now();
+        String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
+        String lockKey = PURCHASE_LOCK_PREFIX + datePart;
+        String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime();
+
+        try {
+            long startWaitTime = System.currentTimeMillis();
+            while (System.currentTimeMillis() - startWaitTime < PURCHASE_LOCK_WAIT_TIMEOUT * 1000) {
+                Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, PURCHASE_LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
+                if (Boolean.TRUE.equals(locked)) {
+                    break;
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    throw new RuntimeException("鑾峰彇閿佹椂琚腑鏂�", e);
+                }
+            }
+
+            if (Boolean.FALSE.equals(redisTemplate.hasKey(lockKey))) {
+                throw new RuntimeException("鑾峰彇閲囪喘鍚堝悓缂栧彿鐢熸垚閿佸け璐ワ細瓒呮椂");
+            }
+
+            String prefix = "CG-" + datePart + "-";
+            List<PurchaseLedger> existingList = purchaseLedgerMapper.selectList(
+                    new LambdaQueryWrapper<PurchaseLedger>()
+                            .likeRight(PurchaseLedger::getPurchaseContractNumber, prefix)
+                            .select(PurchaseLedger::getPurchaseContractNumber));
+            List<Integer> existingSequences = existingList.stream()
+                    .map(PurchaseLedger::getPurchaseContractNumber)
+                    .filter(Objects::nonNull)
+                    .map(no -> {
+                        int lastDash = no.lastIndexOf('-');
+                        if (lastDash >= 0) {
+                            try {
+                                return Integer.parseInt(no.substring(lastDash + 1));
+                            } catch (NumberFormatException e) {
+                                return 0;
+                            }
+                        }
+                        return 0;
+                    })
+                    .collect(Collectors.toList());
+            int nextSequence = findFirstMissingSequence(existingSequences);
+
+            return prefix + String.format("%03d", nextSequence);
+        } finally {
+            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
+            redisTemplate.execute(
+                    new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
+                    Collections.singletonList(lockKey),
+                    lockValue
+            );
+        }
+    }
+
+    private int findFirstMissingSequence(List<Integer> sequences) {
+        if (sequences.isEmpty()) {
+            return 1;
+        }
+        sequences.sort(Integer::compareTo);
+        int next = 1;
+        for (int seq : sequences) {
+            if (seq == next) {
+                next++;
+            } else if (seq > next) {
+                break;
+            }
+        }
+        return next;
+    }
 }

--
Gitblit v1.9.3