贴皮排单分页接口异常修复

This commit is contained in:
gaoqr
2026-08-14 17:34:58 +08:00
parent 87407366c2
commit a45fc887f6
3 changed files with 79 additions and 9 deletions
@@ -1,6 +1,7 @@
package com.cf.imes.module.executor.controller.admin.veneer.vo;
import com.cf.imes.module.executor.controller.admin.plan.vo.PlanOrderRespVO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -30,6 +31,10 @@ public class VeneerPlanPageRespVO {
@Schema(description = "贴皮信息,格式为材质-颜色-厚度mm")
private String veneerInfo;
@JsonIgnore
@Schema(hidden = true)
private Long sourceVeneerId;
@Schema(description = "排单关联的订单主键ID集合,JSON数组格式")
private String orderNos;
@@ -35,12 +35,14 @@ import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderPlateVeneerDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderVeneerGoodsDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.OrderVeneerPlanDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlannedPlateCountDO;
import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerPlanStatisticsDO;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.OrderVeneerPlanBatchMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.OrderVeneerGoodsMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerPlanMapper;
import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper;
import com.cf.imes.module.executor.enums.VeneerPlanStatusEnum;
import com.cf.imes.module.executor.enums.VeneerPlateProcessStatusEnum;
import jakarta.annotation.Resource;
@@ -130,9 +132,19 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
VeneerPlanStatusEnum.NOCUTTING.getStatus(),
VeneerPlanStatusEnum.OPTIMIZED.getStatus());
/** 新增贴皮允许覆盖全部排单阶段,已结束开料的排单新增后重新进入开料中。 */
private static final List<Integer> PLAN_ADD_PLATE_STATUSES = List.of(
VeneerPlanStatusEnum.NOCUTTING.getStatus(),
VeneerPlanStatusEnum.OPTIMIZED.getStatus(),
VeneerPlanStatusEnum.OPENING.getStatus(),
VeneerPlanStatusEnum.OPENED.getStatus());
@Resource
private VeneerPlanMapper veneerPlanMapper;
@Resource
private VeneerMapper veneerMapper;
@Resource
private OrderVeneerPlanBatchMapper orderVeneerPlanBatchMapper;
@@ -351,7 +363,7 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
@Transactional(rollbackFor = Exception.class)
public void addPlanPlate(VeneerPlanAddPlateReqVO req) {
Long planId = req == null ? null : req.getPlanId();
OrderVeneerPlanDO plan = validatePlanStatuses(planId, PLAN_EDITABLE_STATUSES);
OrderVeneerPlanDO plan = validatePlanStatuses(planId, PLAN_ADD_PLATE_STATUSES);
List<Long> veneerIds = normalizePlanVeneerIds(
req == null ? null : req.getVeneerIds());
@@ -383,7 +395,9 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
}
}
refreshPlanStatistics(plan);
// 与普通订单排单一致:开料中允许追加,已结束开料追加后恢复为开料中。
refreshPlanStatistics(plan, PLAN_ADD_PLATE_STATUSES,
VeneerPlanStatusEnum.OPENED.getStatus().equals(plan.getStatus()));
refreshAffectedPlannedVeneerPlateCount(veneers);
}
@@ -456,10 +470,46 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
IPage<VeneerPlanPageRespVO> page = veneerPlanMapper.selectPlanPage(
new PageDTO<>(req.getPageNo(), req.getPageSize()),
req);
fillPlanVeneerInfo(page.getRecords());
fillPlanOrderList(page.getRecords());
return new PageResult<>(page.getRecords(), page.getTotal());
}
/** 分页完成后按来源贴皮ID批量补充贴皮信息,避免MyCat执行跨分片JOIN及全局排序。 */
private void fillPlanVeneerInfo(List<VeneerPlanPageRespVO> plans) {
if (CollUtil.isEmpty(plans)) {
return;
}
List<Long> veneerIds = plans.stream()
.map(VeneerPlanPageRespVO::getSourceVeneerId)
.filter(Objects::nonNull)
.distinct()
.toList();
Map<Long, VeneerDO> veneerMap = new HashMap<>(veneerIds.size());
for (List<Long> batch : CollUtil.split(veneerIds, PLAN_STAT_BATCH_SIZE)) {
for (VeneerDO veneer : veneerMapper.selectBatchIds(batch)) {
veneerMap.put(veneer.getId(), veneer);
}
}
for (VeneerPlanPageRespVO plan : plans) {
VeneerDO veneer = veneerMap.get(plan.getSourceVeneerId());
if (veneer == null) {
continue;
}
List<String> parts = new ArrayList<>(3);
if (StrUtil.isNotBlank(veneer.getMaterial())) {
parts.add(veneer.getMaterial());
}
if (StrUtil.isNotBlank(veneer.getColor())) {
parts.add(veneer.getColor());
}
if (veneer.getThickness() != null) {
parts.add(veneer.getThickness().stripTrailingZeros().toPlainString() + "mm");
}
plan.setVeneerInfo(String.join("-", parts));
}
}
@Override
public List<VeneerPlanPlateRespVO> getPlanPlateList(Long planId) {
if (planId == null) {
@@ -708,12 +758,19 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
* @param plan 更新前读取的贴皮排单
*/
private void refreshPlanStatistics(OrderVeneerPlanDO plan) {
refreshPlanStatistics(plan, PLAN_EDITABLE_STATUSES, false);
}
/** 按指定允许状态重新统计排单,并可将已结束排单重新打开为开料中。 */
private void refreshPlanStatistics(OrderVeneerPlanDO plan,
List<Integer> expectedStatuses,
boolean reopenCutting) {
VeneerPlanStatisticsDO statistics = veneerPlanMapper.selectPlanStatistics(plan.getId());
if (statistics == null || statistics.getVeneerNum() == null
|| statistics.getVeneerArea() == null || statistics.getVeneerNum() <= 0) {
throw new ServiceException(VENEER_PLAN_PLATE_STATUS_CHANGED);
}
updatePlanStatistics(plan, statistics);
updatePlanStatistics(plan, statistics, expectedStatuses, reopenCutting);
}
/**
@@ -724,14 +781,26 @@ public class VeneerPlanServiceImpl implements VeneerPlanService {
*/
private void updatePlanStatistics(
OrderVeneerPlanDO plan, VeneerPlanStatisticsDO statistics) {
updatePlanStatistics(plan, statistics, PLAN_EDITABLE_STATUSES, false);
}
/** 使用指定状态范围及version乐观锁覆盖贴皮排单汇总信息。 */
private void updatePlanStatistics(OrderVeneerPlanDO plan,
VeneerPlanStatisticsDO statistics,
List<Integer> expectedStatuses,
boolean reopenCutting) {
// 订单集合是读取快照,以当前排单剩余贴皮为准重新生成,避免新增或移除后数据漂移。
String orderNos = JSON.toJSONString(
veneerPlanMapper.selectPlanOrderIdList(plan.getId()));
int version = plan.getVersion();
int updated = veneerPlanMapper.update(new LambdaUpdateWrapper<OrderVeneerPlanDO>()
.eq(OrderVeneerPlanDO::getId, plan.getId())
.in(OrderVeneerPlanDO::getStatus, PLAN_EDITABLE_STATUSES)
.in(OrderVeneerPlanDO::getStatus, expectedStatuses)
.eq(OrderVeneerPlanDO::getVersion, version)
.set(reopenCutting, OrderVeneerPlanDO::getStatus,
VeneerPlanStatusEnum.OPENING.getStatus())
.set(reopenCutting, OrderVeneerPlanDO::getProduceTime, null)
.set(reopenCutting, OrderVeneerPlanDO::getFinishTime, null)
.set(OrderVeneerPlanDO::getVeneerNum, statistics.getVeneerNum())
.set(OrderVeneerPlanDO::getVeneerArea, statistics.getVeneerArea())
.set(OrderVeneerPlanDO::getOrderNos, orderNos)
@@ -17,8 +17,7 @@
ovp.sort,
ovp.process_id AS processId,
ovp.process_name AS processName,
CONCAT_WS('-', NULLIF(v.material, ''), NULLIF(v.color, ''),
CONCAT(CAST(v.thickness AS CHAR), 'mm')) AS veneerInfo,
ovp.source_veneer_id AS sourceVeneerId,
ovp.order_nos AS orderNos,
ovp.status,
ovp.is_scheduled AS isScheduled,
@@ -34,9 +33,6 @@
ovp.create_time AS createTime,
ovp.produce_time AS produceTime
FROM order_veneer_plan ovp
JOIN veneers v ON v.id = ovp.source_veneer_id
AND v.organ_id = ovp.organ_id
AND v.deleted = false
WHERE ovp.deleted = false
<if test="req.id != null and req.id != ''">
AND CAST(ovp.id AS CHAR) LIKE CONCAT('%', #{req.id}, '%')