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 rows = fenxiMapper.selectRows(tenantId, param); for (LimsWendingxingFenxiRowVo row : rows) { normalizeRow(row); } rows.sort(rowComparator()); LimsWendingxingFenxiDataVo result = new LimsWendingxingFenxiDataVo(); result.setRows(rows); List 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 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 values, String label) { if (values == null) { return; } Set 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 planOptions = fenxiMapper.selectPlans( tenantId, param.getYangpinId(), param.getXiangmuIds()); requireContainsAll(planOptions, param.getJihuaIds(), "稳定性考察编号"); List batchOptions = fenxiMapper.selectBatches( tenantId, param.getYangpinId(), param.getXiangmuIds(), param.getJihuaIds()); Set allowedBatchKeys = optionKeys(batchOptions); for (LimsWendingxingFenxiBatchParam batch : param.getPihaoKeys()) { if (!allowedBatchKeys.contains(batchKey(batch))) { throw new DataException("批号不属于当前筛选范围"); } } } private void requireContains(List options, String value, String label) { if (!optionKeys(options).contains(value)) { throw new DataException(label + "不属于当前租户或筛选范围"); } } private void requireContainsAll(List options, List values, String label) { Set allowed = optionKeys(options); for (String value : values) { if (!allowed.contains(value)) { throw new DataException(label + "不属于当前租户或筛选范围"); } } } private Set optionKeys(List options) { Set 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 rowComparator() { Comparator strings = Comparator.nullsLast(String::compareTo); Comparator 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 records) { Map 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 buildExpectedPeriods( List records) { Map 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 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 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(); } }