From f3c1f61dbd9d67a237dbf4d108fb27e3eb9adc67 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Fri, 14 Aug 2026 16:17:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E7=B4=A7=E6=97=A5=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E6=8E=92=E7=A8=8B=E4=B8=8E=E7=A7=9F=E6=88=B7=E7=AD=96?= =?UTF-8?q?=E7=95=A5=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../specs/2026-08-14-lms-daily-plan-design.md | 6 +-- .../admin/dailyplan/vo/DailyPlanRespVO.java | 4 -- .../dailyplan/DailyPlanStrategyMapper.java | 3 ++ .../DailyPlanScheduleServiceImpl.java | 7 ++- .../dailyplan/DailyPlanServiceImpl.java | 34 +++++++++---- .../DailyPlanStrategyInitializer.java | 51 +++++++++++++++++++ .../mapper/dailyplan/DailyPlanMapper.xml | 4 +- .../dailyplan/DailyPlanStrategyMapper.xml | 8 +++ 8 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanStrategyInitializer.java diff --git a/docs/superpowers/specs/2026-08-14-lms-daily-plan-design.md b/docs/superpowers/specs/2026-08-14-lms-daily-plan-design.md index 7cec148c..342cc2f4 100644 --- a/docs/superpowers/specs/2026-08-14-lms-daily-plan-design.md +++ b/docs/superpowers/specs/2026-08-14-lms-daily-plan-design.md @@ -76,7 +76,7 @@ - 备货策略。 - 套管策略。 -每条配置保存业务类型、当前模式、修改人、修改时间和版本号。当前模式包括“顺序模式”和“权重轮转模式”。数据库唯一约束保证同一租户、同一业务类型只有一条有效配置。 +每条配置保存业务类型、当前模式、修改人、修改时间和版本号。当前模式包括“顺序模式”和“权重轮转模式”。数据库唯一约束保证同一租户、同一业务类型只有一条有效配置;查询或调度时若当前租户尚无配置,则并发安全地懒初始化备货、套管两类默认顺序策略,已逻辑删除的历史配置不自动恢复或重复插入。 ### 操作记录表 `lms_daily_plan_operation_log` @@ -135,7 +135,7 @@ ERP 推送不允许直接修改顺序、权重、当前完成数量或作业状 ## 人工创建和维护 -人工创建字段包括计划日期、人工订单号、管芯物料、计划数量、装箱信息、顺序和权重。人工订单号允许手填;未填写时使用 LMS 日计划编号作为订单号。 +人工创建字段包括计划日期、人工订单号、管芯物料、计划数量、装箱信息、顺序和权重。人工订单号统一去除首尾空白;未填写、全空白或修改时清空,均使用 LMS 日计划编号作为订单号。 只有未开始的人工来源日计划允许编辑基本信息。ERP 来源计划通过 ERP 幂等推送维护,管理页面只读。开始订单时 ERP 和人工来源均允许再次确认计划数量,并可填写追加数量;计划数量、累计追加数量和开始状态在同一事务中原子更新,开始成功后进入日计划作业页面。 @@ -238,7 +238,7 @@ ERP 推送不允许直接修改顺序、权重、当前完成数量或作业状 - 未开始订单不能执行备货、套管、追加和结束作业操作。 - 已结束订单不能取消、恢复、追加或重新开始。 - 完成数量不能为负数,也不能超过需求总量。 -- 顺序号和权重必须为正整数。 +- 顺序号和权重必须为正整数,且只允许对订单状态为“进行中”的计划修改;批量排序任一计划状态不符时整批回滚。 - 取消和恢复未填写原因时由参数校验直接拒绝。 - 非法状态流转返回业务异常,不做静默幂等成功。 - 请求失败沿用全局错误提示,不在前端吞掉异常。 diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/controller/admin/dailyplan/vo/DailyPlanRespVO.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/controller/admin/dailyplan/vo/DailyPlanRespVO.java index 0cdb948d..5a5e91da 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/controller/admin/dailyplan/vo/DailyPlanRespVO.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/controller/admin/dailyplan/vo/DailyPlanRespVO.java @@ -1,10 +1,7 @@ package cn.code.nl.module.lms.controller.admin.dailyplan.vo; -import cn.code.nl.module.system.api.user.AdminUserApi; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; -import org.dromara.core.trans.anno.Trans; -import org.dromara.core.trans.constant.TransType; import org.dromara.core.trans.vo.VO; import java.time.LocalDate; @@ -47,7 +44,6 @@ public class DailyPlanRespVO implements VO { private Integer version; private String creator; private LocalDateTime createTime; - @Trans(type = TransType.AUTO_TRANS, key = AdminUserApi.PREFIX, fields = "nickname", ref = "updaterName") private String updater; private String updaterName; private LocalDateTime updateTime; diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java index dba85c16..a0a9ca14 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java @@ -16,6 +16,9 @@ public interface DailyPlanStrategyMapper extends BaseMapperX candidates, String operator) { - DailyPlanStrategyDO strategy = dailyPlanStrategyMapper.selectByStrategyType(type.getCode()); - if (strategy == null) { - throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); - } + DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type); if (candidates.isEmpty()) { return null; } diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanServiceImpl.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanServiceImpl.java index 1e4ab7a8..83390c3e 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanServiceImpl.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanServiceImpl.java @@ -93,6 +93,8 @@ public class DailyPlanServiceImpl implements DailyPlanService { @Resource private DailyPlanOperationLogMapper dailyPlanOperationLogMapper; @Resource + private DailyPlanStrategyInitializer strategyInitializer; + @Resource private CodeGenApi codeGenApi; @Resource private AdminUserApi adminUserApi; @@ -207,6 +209,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { for (DailyPlanDO plan : page.getList()) { result.add(convertPlan(plan)); } + resolvePlanUpdaterNames(result); return new PageResult<>(result, page.getTotal()); } @@ -222,6 +225,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { for (DailyPlanDO plan : page.getList()) { result.add(convertPlan(plan)); } + resolvePlanUpdaterNames(result); return new PageResult<>(result, page.getTotal()); } @@ -232,7 +236,9 @@ public class DailyPlanServiceImpl implements DailyPlanService { if (plan == null) { throw exception(DAILY_PLAN_NOT_EXISTS); } - return convertPlan(plan); + DailyPlanRespVO result = convertPlan(plan); + resolvePlanUpdaterNames(List.of(result)); + return result; } /** 查询日计划操作日志。 */ @@ -273,9 +279,12 @@ public class DailyPlanServiceImpl implements DailyPlanService { if (ERP_OPERATOR.equalsIgnoreCase(operator)) { return "ERP"; } + if ("SCHEDULER".equalsIgnoreCase(operator) || "系统调度".equals(operator)) { + return "系统调度"; + } Long operatorId = parseOperatorId(operator); if (operatorId == null) { - return "系统"; + return StrUtil.blankToDefault(operator, "系统"); } AdminUserRespDTO user = userMap.get(operatorId); return user == null ? "未知用户" : user.getNickname(); @@ -297,10 +306,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { public List getStrategies() { List result = new ArrayList<>(2); for (DailyPlanStrategyTypeEnum type : DailyPlanStrategyTypeEnum.values()) { - DailyPlanStrategyDO strategy = dailyPlanStrategyMapper.selectByStrategyType(type.getCode()); - if (strategy == null) { - throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); - } + DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type); result.add(convertStrategy(strategy)); } return result; @@ -316,7 +322,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { plan.setDailyPlanId(IdWorker.getId()); plan.setPlanCode(planCode); plan.setSourceType(DailyPlanSourceTypeEnum.MANUAL.getCode()); - plan.setManualOrderCode(StrUtil.isEmpty(reqVO.getManualOrderCode()) ? planCode : reqVO.getManualOrderCode()); + plan.setManualOrderCode(StrUtil.blankToDefault(StrUtil.trim(reqVO.getManualOrderCode()), planCode)); setEditableFields(plan, reqVO); plan.setCreator(userId); plan.setUpdater(userId); @@ -353,7 +359,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { DailyPlanDO update = new DailyPlanDO(); update.setDailyPlanId(reqVO.getDailyPlanId()); update.setVersion(reqVO.getVersion()); - update.setManualOrderCode(reqVO.getManualOrderCode()); + update.setManualOrderCode(StrUtil.blankToDefault(StrUtil.trim(reqVO.getManualOrderCode()), current.getPlanCode())); setEditableFields(update, reqVO); update.setUpdater(String.valueOf(getLoginUserId())); checkAffected(dailyPlanMapper.updateNotStartedPlan(update)); @@ -528,7 +534,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { throw exception(DAILY_PLAN_STATUS_OPERATION_NOT_ALLOWED, item.getId(), "重复修改排序"); } DailyPlanDO current = validateVersion(item.getId(), item.getVersion()); - validateNotFinished(current, "修改排序"); + validateOrderStatus(current, DailyPlanOrderStatusEnum.IN_PROGRESS, "修改排序"); checkAffected(dailyPlanMapper.updateSortWithVersion(current.getDailyPlanId(), item.getSortSeq(), item.getVersion(), String.valueOf(getLoginUserId()))); insertLog(current.getDailyPlanId(), TARGET_SORT, TYPE_UPDATE, String.valueOf(current.getSortSeq()), @@ -541,7 +547,7 @@ public class DailyPlanServiceImpl implements DailyPlanService { @Transactional(rollbackFor = Exception.class) public void updateWeight(DailyPlanWeightUpdateReqVO reqVO) { DailyPlanDO current = validateVersion(reqVO.getId(), reqVO.getVersion()); - validateNotFinished(current, "修改权重"); + validateOrderStatus(current, DailyPlanOrderStatusEnum.IN_PROGRESS, "修改权重"); checkAffected(dailyPlanMapper.updateWeightWithVersion(current.getDailyPlanId(), reqVO.getWeight(), reqVO.getVersion(), String.valueOf(getLoginUserId()))); insertLog(current.getDailyPlanId(), TARGET_WEIGHT, TYPE_UPDATE, String.valueOf(current.getWeight()), @@ -722,6 +728,14 @@ public class DailyPlanServiceImpl implements DailyPlanService { checkAffected(dailyPlanOperationLogMapper.insertOperationLog(log)); } + /** 批量解析列表修改人,数字用户走用户服务,系统标识保留可靠的显示名称。 */ + private void resolvePlanUpdaterNames(List plans) { + Set userIds = plans.stream().map(DailyPlanRespVO::getUpdater) + .map(DailyPlanServiceImpl::parseOperatorId).filter(Objects::nonNull).collect(Collectors.toSet()); + Map userMap = userIds.isEmpty() ? Map.of() : adminUserApi.getUserMap(userIds); + plans.forEach(plan -> plan.setUpdaterName(resolveOperatorName(plan.getUpdater(), userMap))); + } + /** 将日计划数据对象逐字段转换为响应对象。 */ private DailyPlanRespVO convertPlan(DailyPlanDO plan) { DailyPlanRespVO response = new DailyPlanRespVO(); diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanStrategyInitializer.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanStrategyInitializer.java new file mode 100644 index 00000000..5a5be1de --- /dev/null +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanStrategyInitializer.java @@ -0,0 +1,51 @@ +package cn.code.nl.module.lms.service.dailyplan; + +import cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanStrategyDO; +import cn.code.nl.module.lms.dal.mysql.dailyplan.DailyPlanStrategyMapper; +import cn.code.nl.module.lms.enums.DailyPlanStrategyModeEnum; +import cn.code.nl.module.lms.enums.DailyPlanStrategyTypeEnum; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import jakarta.annotation.Resource; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Component; + +import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STRATEGY_NOT_EXISTS; + +/** 按当前租户幂等补齐默认日计划策略。 */ +@Component +public class DailyPlanStrategyInitializer { + + private static final String SYSTEM_OPERATOR = "SCHEDULER"; + + @Resource + private DailyPlanStrategyMapper strategyMapper; + + public DailyPlanStrategyDO getOrCreate(DailyPlanStrategyTypeEnum type) { + DailyPlanStrategyDO current = strategyMapper.selectByStrategyType(type.getCode()); + if (current != null) { + return current; + } + // 唯一键不包含 deleted;存在已删除记录时不能另插一条,也不能擅自恢复历史配置。 + if (strategyMapper.selectAnyByStrategyType(type.getCode()) != null) { + throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); + } + DailyPlanStrategyDO strategy = new DailyPlanStrategyDO(); + strategy.setStrategyId(IdWorker.getId()); + strategy.setStrategyType(type.getCode()); + strategy.setStrategyMode(DailyPlanStrategyModeEnum.SEQUENCE.getCode()); + strategy.setVersion(0); + strategy.setCreator(SYSTEM_OPERATOR); + strategy.setUpdater(SYSTEM_OPERATOR); + try { + strategyMapper.insert(strategy); + return strategy; + } catch (DuplicateKeyException ignored) { + DailyPlanStrategyDO concurrent = strategyMapper.selectByStrategyType(type.getCode()); + if (concurrent != null) { + return concurrent; + } + throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); + } + } +} diff --git a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanMapper.xml b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanMapper.xml index a2c7987f..4b8920ce 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanMapper.xml +++ b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanMapper.xml @@ -263,13 +263,13 @@ UPDATE lms_daily_plan SET sort_seq = #{sortSeq}, updater = #{updater}, version = version + 1 - WHERE daily_plan_id = #{dailyPlanId} AND version = #{version} AND deleted = b'0' + WHERE daily_plan_id = #{dailyPlanId} AND version = #{version} AND order_status = 1 AND deleted = b'0' UPDATE lms_daily_plan SET weight = #{weight}, updater = #{updater}, version = version + 1 - WHERE daily_plan_id = #{dailyPlanId} AND version = #{version} AND deleted = b'0' + WHERE daily_plan_id = #{dailyPlanId} AND version = #{version} AND order_status = 1 AND deleted = b'0' diff --git a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml index ede33a17..5010fab7 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml +++ b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml @@ -15,6 +15,14 @@ AND deleted = b'0' + + UPDATE lms_daily_plan_strategy SET strategy_mode = #{strategyMode},