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/LimsWendingxingFenxiServiceImpl.java |  311 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 311 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingFenxiServiceImpl.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingFenxiServiceImpl.java
new file mode 100644
index 0000000..ba8190f
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/impl/LimsWendingxingFenxiServiceImpl.java
@@ -0,0 +1,311 @@
+package jnpf.limsService.impl;
+
+import jnpf.base.UserInfo;
+import jnpf.exception.DataException;
+import jnpf.limsEntity.JianyanStatus;
+import jnpf.limsEntity.LimsWendingxingFenxiBatchParam;
+import jnpf.limsEntity.LimsWendingxingFenxiCompletenessRecordVo;
+import jnpf.limsEntity.LimsWendingxingFenxiCompletenessVo;
+import jnpf.limsEntity.LimsWendingxingFenxiDataVo;
+import jnpf.limsEntity.LimsWendingxingFenxiExpectedPeriodVo;
+import jnpf.limsEntity.LimsWendingxingFenxiOptionVo;
+import jnpf.limsEntity.LimsWendingxingFenxiOptionsVo;
+import jnpf.limsEntity.LimsWendingxingFenxiParam;
+import jnpf.limsEntity.LimsWendingxingFenxiRowVo;
+import jnpf.limsMapper.LimsWendingxingFenxiMapper;
+import jnpf.limsService.LimsWendingxingFenxiService;
+import jnpf.limsService.support.LimsWendingxingFenxiSupport;
+import jnpf.util.UserProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Service
+public class LimsWendingxingFenxiServiceImpl implements LimsWendingxingFenxiService {
+
+    @Autowired
+    private LimsWendingxingFenxiMapper fenxiMapper;
+
+    @Override
+    public LimsWendingxingFenxiOptionsVo listOptions(LimsWendingxingFenxiParam source) {
+        LimsWendingxingFenxiParam param = normalize(source);
+        String tenantId = currentTenantId();
+        LimsWendingxingFenxiOptionsVo result = new LimsWendingxingFenxiOptionsVo();
+        result.setFenleiList(fenxiMapper.selectCategories(tenantId));
+        if (!StringUtils.hasText(param.getFenleiBianma())) {
+            return result;
+        }
+        result.setYangpinList(fenxiMapper.selectProducts(tenantId, param.getFenleiBianma()));
+        if (!StringUtils.hasText(param.getYangpinId())) {
+            return result;
+        }
+        result.setXiangmuList(fenxiMapper.selectItems(
+                tenantId, param.getFenleiBianma(), param.getYangpinId()));
+        if (param.getXiangmuIds().isEmpty()) {
+            return result;
+        }
+        result.setJihuaList(fenxiMapper.selectPlans(
+                tenantId, param.getYangpinId(), param.getXiangmuIds()));
+        result.setPihaoList(fenxiMapper.selectBatches(
+                tenantId, param.getYangpinId(), param.getXiangmuIds(), param.getJihuaIds()));
+        return result;
+    }
+
+    @Override
+    public LimsWendingxingFenxiDataVo listData(LimsWendingxingFenxiParam source) {
+        validateRawFilters(source);
+        LimsWendingxingFenxiParam param = normalize(source);
+        validateRequired(param);
+        String tenantId = currentTenantId();
+        validateAuthorizedOptions(tenantId, param);
+
+        List<LimsWendingxingFenxiRowVo> rows = fenxiMapper.selectRows(tenantId, param);
+        for (LimsWendingxingFenxiRowVo row : rows) {
+            normalizeRow(row);
+        }
+        rows.sort(rowComparator());
+
+        LimsWendingxingFenxiDataVo result = new LimsWendingxingFenxiDataVo();
+        result.setRows(rows);
+        List<LimsWendingxingFenxiCompletenessRecordVo> completenessRecords =
+                fenxiMapper.selectCompleteness(tenantId, param);
+        result.setCompleteness(buildCompleteness(completenessRecords));
+        result.setExpectedPeriods(buildExpectedPeriods(completenessRecords));
+        return result;
+    }
+
+    private void validateRawFilters(LimsWendingxingFenxiParam source) {
+        if (source == null) {
+            return;
+        }
+        validateRawIds(source.getXiangmuIds(), "妫�楠岄」鐩�");
+        validateRawIds(source.getJihuaIds(), "绋冲畾鎬ц�冨療缂栧彿");
+        Set<String> batchKeys = new HashSet<>();
+        if (source.getPihaoKeys() == null) {
+            return;
+        }
+        for (LimsWendingxingFenxiBatchParam batch : source.getPihaoKeys()) {
+            if (batch == null || !StringUtils.hasText(batch.getJihuaId())
+                    || !StringUtils.hasText(batch.getPihao())) {
+                throw new DataException("鎵瑰彿绛涢�夐」涓嶅畬鏁�");
+            }
+            String key = batch.getJihuaId().trim() + "::" + batch.getPihao().trim();
+            if (!batchKeys.add(key)) {
+                throw new DataException("鎵瑰彿绛涢�夐」涓嶈兘閲嶅");
+            }
+        }
+    }
+
+    private void validateRawIds(List<String> values, String label) {
+        if (values == null) {
+            return;
+        }
+        Set<String> unique = new HashSet<>();
+        for (String value : values) {
+            if (!StringUtils.hasText(value)) {
+                throw new DataException(label + "绛涢�夐」涓嶈兘涓虹┖");
+            }
+            if (!unique.add(value.trim())) {
+                throw new DataException(label + "绛涢�夐」涓嶈兘閲嶅");
+            }
+        }
+    }
+
+    private void validateRequired(LimsWendingxingFenxiParam param) {
+        if (!StringUtils.hasText(param.getFenleiBianma())) {
+            throw new DataException("鍒嗙被涓嶈兘涓虹┖");
+        }
+        if (!StringUtils.hasText(param.getYangpinId())) {
+            throw new DataException("鍝佸悕涓嶈兘涓虹┖");
+        }
+        if (param.getXiangmuIds().isEmpty()) {
+            throw new DataException("妫�楠岄」鐩笉鑳戒负绌�");
+        }
+    }
+
+    private void validateAuthorizedOptions(String tenantId, LimsWendingxingFenxiParam param) {
+        requireContains(fenxiMapper.selectCategories(tenantId), param.getFenleiBianma(), "鍒嗙被");
+        requireContains(fenxiMapper.selectProducts(tenantId, param.getFenleiBianma()), param.getYangpinId(), "鍝佸悕");
+        requireContainsAll(fenxiMapper.selectItems(
+                tenantId, param.getFenleiBianma(), param.getYangpinId()), param.getXiangmuIds(), "妫�楠岄」鐩�");
+
+        List<LimsWendingxingFenxiOptionVo> planOptions = fenxiMapper.selectPlans(
+                tenantId, param.getYangpinId(), param.getXiangmuIds());
+        requireContainsAll(planOptions, param.getJihuaIds(), "绋冲畾鎬ц�冨療缂栧彿");
+
+        List<LimsWendingxingFenxiOptionVo> batchOptions = fenxiMapper.selectBatches(
+                tenantId, param.getYangpinId(), param.getXiangmuIds(), param.getJihuaIds());
+        Set<String> allowedBatchKeys = optionKeys(batchOptions);
+        for (LimsWendingxingFenxiBatchParam batch : param.getPihaoKeys()) {
+            if (!allowedBatchKeys.contains(batchKey(batch))) {
+                throw new DataException("鎵瑰彿涓嶅睘浜庡綋鍓嶇瓫閫夎寖鍥�");
+            }
+        }
+    }
+
+    private void requireContains(List<LimsWendingxingFenxiOptionVo> options,
+                                 String value, String label) {
+        if (!optionKeys(options).contains(value)) {
+            throw new DataException(label + "涓嶅睘浜庡綋鍓嶇鎴锋垨绛涢�夎寖鍥�");
+        }
+    }
+
+    private void requireContainsAll(List<LimsWendingxingFenxiOptionVo> options,
+                                    List<String> values, String label) {
+        Set<String> allowed = optionKeys(options);
+        for (String value : values) {
+            if (!allowed.contains(value)) {
+                throw new DataException(label + "涓嶅睘浜庡綋鍓嶇鎴锋垨绛涢�夎寖鍥�");
+            }
+        }
+    }
+
+    private Set<String> optionKeys(List<LimsWendingxingFenxiOptionVo> options) {
+        Set<String> result = new HashSet<>();
+        for (LimsWendingxingFenxiOptionVo option : options) {
+            if (StringUtils.hasText(option.getKey())) {
+                result.add(option.getKey());
+            } else if (StringUtils.hasText(option.getId())) {
+                result.add(option.getId());
+            }
+        }
+        return result;
+    }
+
+    private void normalizeRow(LimsWendingxingFenxiRowVo row) {
+        BigDecimal value = LimsWendingxingFenxiSupport.strictDecimal(row.getRawResult());
+        BigDecimal lower = LimsWendingxingFenxiSupport.strictDecimal(row.getXiaxian());
+        BigDecimal upper = LimsWendingxingFenxiSupport.strictDecimal(row.getShangxian());
+        row.setNumericResult(value);
+        row.setXiaxianValue(lower);
+        row.setShangxianValue(upper);
+        try {
+            row.setZhouqiOrder(LimsWendingxingFenxiSupport.periodOrder(
+                    new BigDecimal(row.getZhouqi()), row.getZhouqiDanwei()));
+        } catch (RuntimeException ignored) {
+            row.setZhouqiOrder(Double.MAX_VALUE);
+        }
+        row.setExceptionType(LimsWendingxingFenxiSupport.exceptionType(
+                row.getYichangDiaochaLeixing(), row.getJianyanJielun(), value, lower, upper));
+        row.setBatchKey(safe(row.getJihuaId()) + "::" + safe(row.getPihao()));
+    }
+
+    private Comparator<LimsWendingxingFenxiRowVo> rowComparator() {
+        Comparator<String> strings = Comparator.nullsLast(String::compareTo);
+        Comparator<Date> dates = Comparator.nullsLast(Date::compareTo);
+        return Comparator.comparing(LimsWendingxingFenxiRowVo::getXiangmuMingcheng, strings)
+                .thenComparing(LimsWendingxingFenxiRowVo::getXiangmuBianma, strings)
+                .thenComparing(LimsWendingxingFenxiRowVo::getJihuaBianhao, strings)
+                .thenComparing(LimsWendingxingFenxiRowVo::getJihuaId, strings)
+                .thenComparing(LimsWendingxingFenxiRowVo::getPihao, strings)
+                .thenComparing(LimsWendingxingFenxiRowVo::getZhouqiOrder,
+                        Comparator.nullsLast(Double::compareTo))
+                .thenComparing(LimsWendingxingFenxiRowVo::getJieguoLuruRiqi, dates)
+                .thenComparing(LimsWendingxingFenxiRowVo::getXiangmuId, strings);
+    }
+
+    private LimsWendingxingFenxiCompletenessVo buildCompleteness(
+            List<LimsWendingxingFenxiCompletenessRecordVo> records) {
+        Map<String, Integer> states = new LinkedHashMap<>();
+        for (LimsWendingxingFenxiCompletenessRecordVo record : records) {
+            int state = record.getJianyanZhuangtai() == null ? 0
+                    : JianyanStatus.AUDITED.getCode().equals(record.getJianyanZhuangtai()) ? 2 : 1;
+            states.merge(record.getExpectedKey(), state, Math::max);
+        }
+        LimsWendingxingFenxiCompletenessVo result = new LimsWendingxingFenxiCompletenessVo();
+        result.setExpectedCount(states.size());
+        for (Integer state : states.values()) {
+            if (state == 2) {
+                result.setAuditedCount(result.getAuditedCount() + 1);
+            } else if (state == 1) {
+                result.setPendingCount(result.getPendingCount() + 1);
+            } else {
+                result.setMissingCount(result.getMissingCount() + 1);
+            }
+        }
+        return result;
+    }
+
+    private List<LimsWendingxingFenxiExpectedPeriodVo> buildExpectedPeriods(
+            List<LimsWendingxingFenxiCompletenessRecordVo> records) {
+        Map<String, LimsWendingxingFenxiExpectedPeriodVo> periods = new LinkedHashMap<>();
+        for (LimsWendingxingFenxiCompletenessRecordVo record : records) {
+            String key = safe(record.getXiangmuId()) + "::" + safe(record.getZhouqi())
+                    + "::" + safe(record.getZhouqiDanwei());
+            if (periods.containsKey(key)) {
+                continue;
+            }
+            LimsWendingxingFenxiExpectedPeriodVo period =
+                    new LimsWendingxingFenxiExpectedPeriodVo();
+            period.setXiangmuId(record.getXiangmuId());
+            period.setZhouqi(record.getZhouqi());
+            period.setZhouqiDanwei(record.getZhouqiDanwei());
+            try {
+                period.setZhouqiOrder(LimsWendingxingFenxiSupport.periodOrder(
+                        new BigDecimal(record.getZhouqi()), record.getZhouqiDanwei()));
+            } catch (RuntimeException ignored) {
+                period.setZhouqiOrder(Double.MAX_VALUE);
+            }
+            periods.put(key, period);
+        }
+        List<LimsWendingxingFenxiExpectedPeriodVo> result =
+                new ArrayList<>(periods.values());
+        result.sort(Comparator.comparing(
+                LimsWendingxingFenxiExpectedPeriodVo::getZhouqiOrder,
+                Comparator.nullsLast(Double::compareTo)));
+        return result;
+    }
+
+    private LimsWendingxingFenxiParam normalize(LimsWendingxingFenxiParam source) {
+        LimsWendingxingFenxiParam result = source == null
+                ? new LimsWendingxingFenxiParam() : source;
+        result.setFenleiBianma(trimToNull(result.getFenleiBianma()));
+        result.setYangpinId(trimToNull(result.getYangpinId()));
+        result.setXiangmuIds(LimsWendingxingFenxiSupport.normalizeIds(result.getXiangmuIds()));
+        result.setJihuaIds(LimsWendingxingFenxiSupport.normalizeIds(result.getJihuaIds()));
+
+        Map<String, LimsWendingxingFenxiBatchParam> batches = new LinkedHashMap<>();
+        if (result.getPihaoKeys() != null) {
+            for (LimsWendingxingFenxiBatchParam batch : result.getPihaoKeys()) {
+                if (batch == null) {
+                    continue;
+                }
+                batch.setJihuaId(trimToNull(batch.getJihuaId()));
+                batch.setPihao(trimToNull(batch.getPihao()));
+                if (batch.getJihuaId() != null && batch.getPihao() != null) {
+                    batches.putIfAbsent(batchKey(batch), batch);
+                }
+            }
+        }
+        result.setPihaoKeys(new ArrayList<>(batches.values()));
+        return result;
+    }
+
+    private String batchKey(LimsWendingxingFenxiBatchParam batch) {
+        return safe(batch.getJihuaId()) + "::" + safe(batch.getPihao());
+    }
+
+    private String safe(String value) {
+        return value == null ? "" : value;
+    }
+
+    private String trimToNull(String value) {
+        return StringUtils.hasText(value) ? value.trim() : null;
+    }
+
+    private String currentTenantId() {
+        UserInfo user = UserProvider.getUser();
+        return user == null || !StringUtils.hasText(user.getTenantId())
+                ? "0" : user.getTenantId();
+    }
+}

--
Gitblit v1.8.0