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 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 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 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 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 currentRows = zixiangMapper.selectList(currentQuery); if (currentRows.isEmpty()) { throw new DataException("未找到对应的稳定性出入库子项数据"); } Map 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 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 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或chuku"); } 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 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 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或chuku"); } 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 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 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(); } }