From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingChurukuServiceImpl.java |  279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 279 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingChurukuServiceImpl.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingChurukuServiceImpl.java
new file mode 100644
index 0000000..ad3e5c0
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingChurukuServiceImpl.java
@@ -0,0 +1,279 @@
+package jnpf.limsService.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import jnpf.audit.sdk.annotation.AuditLog;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import jnpf.base.UserInfo;
+import jnpf.exception.DataException;
+import jnpf.limsEntity.LimsWendingxingChurukuEntity;
+import jnpf.limsEntity.LimsWendingxingChurukuSubmitParam;
+import jnpf.limsEntity.LimsWendingxingChurukuZixiangEntity;
+import jnpf.limsMapper.LimsWendingxingChurukuMapper;
+import jnpf.limsMapper.LimsWendingxingChurukuZixiangMapper;
+import jnpf.limsService.LimsWendingxingChurukuService;
+import jnpf.util.RandomUtil;
+import jnpf.util.UserProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+@Service
+public class LimsWendingxingChurukuServiceImpl implements LimsWendingxingChurukuService {
+
+    private static final String LEIXING_RUKU = "ruku";
+    private static final String LEIXING_CHUKU = "chuku";
+    private static final String ZERO = "0";
+
+    @Autowired
+    private LimsWendingxingChurukuMapper churukuMapper;
+
+    @Autowired
+    private LimsWendingxingChurukuZixiangMapper zixiangMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    @AuditLog(action = "CREATE", label = "绋冲畾鎬у嚭鍏ュ簱鎻愪氦", bizType = "WENDINGXING",
+            bizCodeExpr = "#result['f_id']", targetTable = "lims_wendingxing_churuku",
+            targetIdExpr = "#result != null ? #result['f_id'] : null", diff = true,
+            entityClass = LimsWendingxingChurukuEntity.class)
+    public Map<String, Object> submit(LimsWendingxingChurukuSubmitParam param) {
+        validateParam(param);
+        UserInfo userInfo = UserProvider.getUser();
+        if (userInfo == null || !StringUtils.hasText(userInfo.getUserId())) {
+            throw new DataException("褰撳墠鏈櫥褰�");
+        }
+
+        Date now = new Date();
+        String churukuId = RandomUtil.uuId();
+        LimsWendingxingChurukuEntity churuku = buildChuruku(param, churukuId, userInfo, now);
+        if (churukuMapper.insert(churuku) != 1) {
+            throw new DataException("绋冲畾鎬у嚭鍏ュ簱涓昏〃淇濆瓨澶辫触");
+        }
+
+        Map<String, BigDecimal> balanceCache = new HashMap<>();
+        for (int i = 0; i < param.getZixiang().size(); i++) {
+            LimsWendingxingChurukuZixiangEntity zixiang = param.getZixiang().get(i);
+            saveZixiang(zixiang, i, churukuId, param.getBeizhu(), userInfo, now, balanceCache);
+        }
+
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("f_id", churukuId);
+        result.put("zixiang_count", param.getZixiang().size());
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    @AuditLog(action = "BALANCE_SYNC", label = "鍚屾绋冲畾鎬у嚭鍏ュ簱缁撳瓨", bizType = "WENDINGXING",
+            bizCodeExpr = "#result > 0 ? #flowId : ''", targetTable = "lims_wendingxing_churuku_zixiang",
+            targetIdExpr = "T(jnpf.limsService.support.LimsAuditTargetIds).firstWendingxingChurukuItemByFlow(#flowId, #flowTaskId)",
+            diff = true, entityClass = LimsWendingxingChurukuZixiangEntity.class)
+    public int recalculateByFlow(String flowId, String flowTaskId) {
+        LimsWendingxingChurukuEntity churuku = getChurukuByFlow(flowId, flowTaskId);
+
+        LambdaQueryWrapper<LimsWendingxingChurukuZixiangEntity> currentQuery = new LambdaQueryWrapper<>();
+        currentQuery.eq(LimsWendingxingChurukuZixiangEntity::getForeignId, churuku.getId())
+                .and(wrapper -> wrapper.isNull(LimsWendingxingChurukuZixiangEntity::getDeleteMark)
+                        .or().ne(LimsWendingxingChurukuZixiangEntity::getDeleteMark, 1))
+                .orderByAsc(LimsWendingxingChurukuZixiangEntity::getCreatorTime)
+                .orderByAsc(LimsWendingxingChurukuZixiangEntity::getId);
+        List<LimsWendingxingChurukuZixiangEntity> currentRows = zixiangMapper.selectList(currentQuery);
+        if (currentRows.isEmpty()) {
+            throw new DataException("鏈壘鍒板搴旂殑绋冲畾鎬у嚭鍏ュ簱瀛愰」鏁版嵁");
+        }
+
+        Map<String, BigDecimal> balanceCache = new HashMap<>();
+        for (int i = 0; i < currentRows.size(); i++) {
+            recalculateZixiang(currentRows.get(i), i, churuku.getId(), balanceCache);
+        }
+        return currentRows.size();
+    }
+
+    private LimsWendingxingChurukuEntity getChurukuByFlow(String flowId, String flowTaskId) {
+        LambdaQueryWrapper<LimsWendingxingChurukuEntity> query = new LambdaQueryWrapper<>();
+        query.eq(LimsWendingxingChurukuEntity::getFlowId, flowId)
+                .eq(LimsWendingxingChurukuEntity::getFlowTaskId, flowTaskId)
+                .and(wrapper -> wrapper.isNull(LimsWendingxingChurukuEntity::getDeleteMark)
+                        .or().ne(LimsWendingxingChurukuEntity::getDeleteMark, 1));
+        LimsWendingxingChurukuEntity churuku = churukuMapper.selectOne(query);
+        if (churuku == null) {
+            throw new DataException("鏈壘鍒板搴旂殑绋冲畾鎬у嚭鍏ュ簱鏁版嵁");
+        }
+        return churuku;
+    }
+
+    private void recalculateZixiang(LimsWendingxingChurukuZixiangEntity row, int index,
+                                    String currentForeignId, Map<String, BigDecimal> balanceCache) {
+        String zhouqiId = trim(row.getJihuaZhouqiId());
+        if (!StringUtils.hasText(zhouqiId)) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑璁″垝鍛ㄦ湡ID(jihua_zhouqi_id)涓嶈兘涓虹┖");
+        }
+        String leixing = trim(row.getChurukuLeixing()).toLowerCase(Locale.ROOT);
+        if (!LEIXING_RUKU.equals(leixing) && !LEIXING_CHUKU.equals(leixing)) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑鍑哄叆搴撶被鍨嬪繀椤讳负ruku鎴朿huku");
+        }
+        BigDecimal shuliang = parseQuantity(row.getChurukuShuliang(), index, "鍑哄叆搴撴暟閲�");
+        if (shuliang.signum() < 0) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑鍑哄叆搴撴暟閲忎笉鑳藉皬浜�0");
+        }
+
+        BigDecimal previousBalance = balanceCache.get(zhouqiId);
+        if (previousBalance == null) {
+            previousBalance = getLatestBalance(zhouqiId, currentForeignId);
+        }
+        BigDecimal currentBalance = LEIXING_RUKU.equals(leixing)
+                ? previousBalance.add(shuliang) : previousBalance.subtract(shuliang);
+        if (currentBalance.signum() < 0) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤瑰嚭搴撳悗缁撳瓨閲忓皬浜�0锛屽綋鍓嶇粨瀛橀噺涓�"
+                    + format(previousBalance) + "锛屽嚭搴撴暟閲忎负" + format(shuliang));
+        }
+
+        String shouruliang = LEIXING_RUKU.equals(leixing) ? format(shuliang) : ZERO;
+        String fachuliang = LEIXING_CHUKU.equals(leixing) ? format(shuliang) : ZERO;
+        LambdaUpdateWrapper<LimsWendingxingChurukuZixiangEntity> update = new LambdaUpdateWrapper<>();
+        update.eq(LimsWendingxingChurukuZixiangEntity::getId, row.getId())
+                .eq(LimsWendingxingChurukuZixiangEntity::getForeignId, currentForeignId)
+                .set(LimsWendingxingChurukuZixiangEntity::getShouruliang, shouruliang)
+                .set(LimsWendingxingChurukuZixiangEntity::getFachuliang, fachuliang)
+                .set(LimsWendingxingChurukuZixiangEntity::getJiecunliang, format(currentBalance));
+        if (zixiangMapper.update(null, update) != 1) {
+            throw new DataException("绗�" + (index + 1) + "鏉$ǔ瀹氭�у嚭鍏ュ簱瀛愰」鏇存柊澶辫触");
+        }
+        balanceCache.put(zhouqiId, currentBalance);
+    }
+
+    private LimsWendingxingChurukuEntity buildChuruku(LimsWendingxingChurukuSubmitParam param,
+                                                        String churukuId, UserInfo userInfo, Date now) {
+        LimsWendingxingChurukuEntity entity = new LimsWendingxingChurukuEntity();
+        entity.setId(churukuId);
+        entity.setShenqingRen(param.getShenqingRen());
+        entity.setShenqingRiqi(param.getShenqingRiqi());
+        entity.setBeizhu(param.getBeizhu());
+        entity.setBizSign(param.getBizSign());
+        entity.setTenantId("0");
+        entity.setCreatorUserId(userInfo.getUserId());
+        entity.setCreatorTime(now);
+        return entity;
+    }
+
+    private void saveZixiang(LimsWendingxingChurukuZixiangEntity entity, int index,
+                             String churukuId, String beizhu, UserInfo userInfo, Date now,
+                             Map<String, BigDecimal> balanceCache) {
+        if (entity == null) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤逛笉鑳戒负绌�");
+        }
+        String zhouqiId = trim(entity.getJihuaZhouqiId());
+        if (!StringUtils.hasText(zhouqiId)) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑璁″垝鍛ㄦ湡ID(jihua_zhouqi_id)涓嶈兘涓虹┖");
+        }
+        String leixing = trim(entity.getChurukuLeixing()).toLowerCase(Locale.ROOT);
+        if (!LEIXING_RUKU.equals(leixing) && !LEIXING_CHUKU.equals(leixing)) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑鍑哄叆搴撶被鍨嬪繀椤讳负ruku鎴朿huku");
+        }
+        BigDecimal shuliang = parseQuantity(entity.getChurukuShuliang(), index, "鍑哄叆搴撴暟閲�");
+        if (shuliang.signum() < 0) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑鍑哄叆搴撴暟閲忎笉鑳藉皬浜�0");
+        }
+
+        BigDecimal previousBalance = balanceCache.get(zhouqiId);
+        if (previousBalance == null) {
+            previousBalance = getLatestBalance(zhouqiId);
+        }
+        BigDecimal currentBalance = LEIXING_RUKU.equals(leixing)
+                ? previousBalance.add(shuliang) : previousBalance.subtract(shuliang);
+        if (currentBalance.signum() < 0) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤瑰嚭搴撳悗缁撳瓨閲忓皬浜�0锛屽綋鍓嶇粨瀛橀噺涓�"
+                    + format(previousBalance) + "锛屽嚭搴撴暟閲忎负" + format(shuliang));
+        }
+
+        entity.setId(RandomUtil.uuId());
+        entity.setJihuaZhouqiId(zhouqiId);
+        entity.setChurukuLeixing(leixing);
+        entity.setChurukuShuliang(format(shuliang));
+        entity.setShouruliang(LEIXING_RUKU.equals(leixing) ? format(shuliang) : ZERO);
+        entity.setFachuliang(LEIXING_CHUKU.equals(leixing) ? format(shuliang) : ZERO);
+        entity.setJiecunliang(format(currentBalance));
+        entity.setForeignId(churukuId);
+        entity.setBeizhu(beizhu);
+        entity.setTenantId(userInfo.getTenantId());
+        entity.setCreatorUserId(userInfo.getUserId());
+        entity.setCreatorTime(now);
+        if (zixiangMapper.insert(entity) != 1) {
+            throw new DataException("绗�" + (index + 1) + "鏉$ǔ瀹氭�у嚭鍏ュ簱瀛愰」淇濆瓨澶辫触");
+        }
+        balanceCache.put(zhouqiId, currentBalance);
+    }
+
+    private BigDecimal getLatestBalance(String zhouqiId) {
+        return getLatestBalance(zhouqiId, null);
+    }
+
+    private BigDecimal getLatestBalance(String zhouqiId, String excludedForeignId) {
+        LambdaQueryWrapper<LimsWendingxingChurukuZixiangEntity> query = new LambdaQueryWrapper<>();
+        query.eq(LimsWendingxingChurukuZixiangEntity::getJihuaZhouqiId, zhouqiId)
+                .and(wrapper -> wrapper.isNull(LimsWendingxingChurukuZixiangEntity::getDeleteMark)
+                        .or().ne(LimsWendingxingChurukuZixiangEntity::getDeleteMark, 1));
+        if (StringUtils.hasText(excludedForeignId)) {
+            query.ne(LimsWendingxingChurukuZixiangEntity::getForeignId, excludedForeignId);
+        }
+        query.orderByDesc(LimsWendingxingChurukuZixiangEntity::getCreatorTime)
+                .orderByDesc(LimsWendingxingChurukuZixiangEntity::getId);
+        List<LimsWendingxingChurukuZixiangEntity> rows = zixiangMapper
+                .selectPage(new Page<>(1, 1, false), query)
+                .getRecords();
+        if (rows.isEmpty()) {
+            return BigDecimal.ZERO;
+        }
+        LimsWendingxingChurukuZixiangEntity row = rows.get(0);
+        return parseBalance(row.getJiecunliang(), zhouqiId);
+    }
+
+    private BigDecimal parseBalance(String value, String zhouqiId) {
+        if (!StringUtils.hasText(value)) {
+            return BigDecimal.ZERO;
+        }
+        try {
+            return new BigDecimal(value.trim());
+        } catch (NumberFormatException e) {
+            throw new DataException("璁″垝鍛ㄦ湡" + zhouqiId + "鐨勬渶鏂扮粨瀛橀噺鏍煎紡涓嶆纭細" + value);
+        }
+    }
+
+    private BigDecimal parseQuantity(String value, int index, String fieldName) {
+        if (!StringUtils.hasText(value)) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑" + fieldName + "(churuku_shuliang)涓嶈兘涓虹┖");
+        }
+        try {
+            return new BigDecimal(value.trim());
+        } catch (NumberFormatException e) {
+            throw new DataException("绗�" + (index + 1) + "鏉″瓙椤圭殑" + fieldName + "鏍煎紡涓嶆纭細" + value);
+        }
+    }
+
+    private void validateParam(LimsWendingxingChurukuSubmitParam param) {
+        if (param == null) {
+            throw new DataException("鎻愪氦鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (param.getZixiang() == null || param.getZixiang().isEmpty()) {
+            throw new DataException("鍑哄叆搴撳瓙椤�(zixiang)涓嶈兘涓虹┖");
+        }
+    }
+
+    private String trim(String value) {
+        return value == null ? "" : value.trim();
+    }
+
+    private String format(BigDecimal value) {
+        return value.stripTrailingZeros().toPlainString();
+    }
+}

--
Gitblit v1.8.0