1完成酸轧轧辊调整

2完成双机架工艺规格串联
3完成双机架计划串联
4完成双机架wip快捷录入检索
5完成双机架实绩串联
This commit is contained in:
2026-05-19 17:13:37 +08:00
parent 417783e64a
commit 53a180787b
46 changed files with 5592 additions and 231 deletions

View File

@@ -0,0 +1,65 @@
package com.klp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.klp.domain.DrMillProcessRecipeVersion;
import com.klp.mapper.DrMillProcessPassMapper;
import com.klp.mapper.DrMillProcessRecipeVersionMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 双机架历史道次版本迁移
* 将所有 version_id IS NULL 的道次归入对应方案的 V1.0 版本
*/
@Slf4j
@DS("double-rack")
@RequiredArgsConstructor
@Service
public class DrMigrationService {
private final DrMillProcessPassMapper passMapper;
private final DrMillProcessRecipeVersionMapper versionMapper;
@Transactional(rollbackFor = Exception.class)
public void migrateOrphanPassesToV1() {
List<Long> recipeIds = passMapper.selectDistinctRecipeIdsWithOrphanPasses();
if (recipeIds.isEmpty()) {
log.info("[DR迁移] 无需迁移:所有道次已绑定版本");
return;
}
int newVersionCount = 0;
int migratedPassCount = 0;
for (Long recipeId : recipeIds) {
// 幂等V1.0 已存在则复用,避免重复执行时重复创建
DrMillProcessRecipeVersion v1 = versionMapper.selectV1ByRecipeId(recipeId);
if (v1 == null) {
v1 = new DrMillProcessRecipeVersion();
v1.setRecipeId(recipeId);
v1.setVersionCode("V1.0");
v1.setIsActive(1); // 默认激活
v1.setStatus("1"); // 已发布
v1.setCreateBy("system");
v1.setUpdateBy("system");
v1.setRemark("系统启动时自动迁移历史数据");
versionMapper.insert(v1);
newVersionCount++;
log.info("[DR迁移] 方案 {} 新建 V1.0versionId={}", recipeId, v1.getVersionId());
} else {
log.info("[DR迁移] 方案 {} 已有 V1.0versionId={}),直接复用", recipeId, v1.getVersionId());
}
int rows = passMapper.updateVersionIdForOrphans(recipeId, v1.getVersionId());
migratedPassCount += rows;
log.info("[DR迁移] 方案 {} 迁移 {} 条道次 → versionId={}", recipeId, rows, v1.getVersionId());
}
log.info("[DR迁移] 完成:共处理方案 {} 个,新建版本 {} 个,迁移道次 {} 条",
recipeIds.size(), newVersionCount, migratedPassCount);
}
}

View File

@@ -0,0 +1,75 @@
package com.klp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.klp.common.helper.LoginHelper;
import com.klp.domain.DrMillProcessRecipe;
import com.klp.domain.DrMillProcessRecipeVersion;
import com.klp.mapper.DrMillProcessPassMapper;
import com.klp.mapper.DrMillProcessRecipeMapper;
import com.klp.mapper.DrMillProcessRecipeVersionMapper;
import com.klp.service.IDrMillProcessRecipeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@DS("double-rack")
@RequiredArgsConstructor
@Service
public class DrMillProcessRecipeServiceImpl implements IDrMillProcessRecipeService {
private final DrMillProcessRecipeMapper recipeMapper;
private final DrMillProcessPassMapper passMapper;
private final DrMillProcessRecipeVersionMapper versionMapper;
@Override
public List<DrMillProcessRecipe> selectList(DrMillProcessRecipe query) {
return recipeMapper.selectList(query);
}
@Override
public DrMillProcessRecipe selectDetailById(Long id) {
DrMillProcessRecipe recipe = recipeMapper.selectById(id);
if (recipe != null) {
// 优先取激活版本的道次;无激活版本则返回空列表,避免多版本混合
DrMillProcessRecipeVersion active = versionMapper.selectActiveByRecipeId(id);
if (active != null) {
recipe.setPassList(passMapper.selectByVersionId(active.getVersionId()));
}
}
return recipe;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int save(DrMillProcessRecipe recipe) {
String user = LoginHelper.getUsername();
recipe.setCreateBy(user);
recipe.setUpdateBy(user);
recipeMapper.insert(recipe);
// 道次现在通过版本接口管理,不再直接挂在方案上
return 1;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int update(DrMillProcessRecipe recipe) {
recipe.setUpdateBy(LoginHelper.getUsername());
recipeMapper.update(recipe);
// 道次通过版本接口管理,不在此处操作
return 1;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteByIds(Long[] ids) {
for (Long id : ids) {
// 级联删版本下的道次,再删版本,再删方案
passMapper.deleteByRecipeId(id);
versionMapper.deleteByRecipeIds(new Long[]{id});
}
return recipeMapper.deleteBatchByIds(ids);
}
}

View File

@@ -0,0 +1,86 @@
package com.klp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.klp.common.helper.LoginHelper;
import com.klp.domain.DrMillProcessPass;
import com.klp.domain.DrMillProcessRecipeVersion;
import com.klp.mapper.DrMillProcessPassMapper;
import com.klp.mapper.DrMillProcessRecipeVersionMapper;
import com.klp.service.IDrMillProcessRecipeVersionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@DS("double-rack")
@RequiredArgsConstructor
@Service
public class DrMillProcessRecipeVersionServiceImpl implements IDrMillProcessRecipeVersionService {
private final DrMillProcessRecipeVersionMapper versionMapper;
private final DrMillProcessPassMapper passMapper;
@Override
public List<DrMillProcessRecipeVersion> listByRecipeId(Long recipeId) {
return versionMapper.selectByRecipeId(recipeId);
}
@Override
public DrMillProcessRecipeVersion getDetailById(Long versionId) {
DrMillProcessRecipeVersion v = versionMapper.selectById(versionId);
if (v != null) {
v.setPassList(passMapper.selectByVersionId(versionId));
}
return v;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long save(DrMillProcessRecipeVersion version) {
String user = LoginHelper.getUsername();
version.setCreateBy(user);
version.setUpdateBy(user);
versionMapper.insert(version);
savePassList(version);
return version.getVersionId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(DrMillProcessRecipeVersion version) {
version.setUpdateBy(LoginHelper.getUsername());
versionMapper.update(version);
passMapper.deleteByVersionId(version.getVersionId());
savePassList(version);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void activate(Long versionId) {
DrMillProcessRecipeVersion v = versionMapper.selectById(versionId);
if (v == null) throw new RuntimeException("版本不存在");
versionMapper.deactivateOthers(v.getRecipeId(), versionId);
versionMapper.activate(versionId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(Long versionId) {
passMapper.deleteByVersionId(versionId);
versionMapper.deleteById(versionId);
}
private void savePassList(DrMillProcessRecipeVersion version) {
List<DrMillProcessPass> list = version.getPassList();
if (list == null || list.isEmpty()) return;
String user = LoginHelper.getUsername();
for (DrMillProcessPass p : list) {
p.setRecipeId(version.getRecipeId());
p.setVersionId(version.getVersionId());
p.setCreateBy(user);
p.setUpdateBy(user);
}
passMapper.insertBatch(list);
}
}

View File

@@ -0,0 +1,118 @@
package com.klp.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.helper.LoginHelper;
import com.klp.domain.DrMillProductionPlan;
import com.klp.mapper.DrMillProcessPassMapper;
import com.klp.mapper.DrMillProductionPlanMapper;
import com.klp.service.IDrMillProductionPlanService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@DS("double-rack")
@RequiredArgsConstructor
@Service
public class DrMillProductionPlanServiceImpl implements IDrMillProductionPlanService {
private final DrMillProductionPlanMapper planMapper;
private final DrMillProcessPassMapper passMapper;
@Override
public List<DrMillProductionPlan> selectList(DrMillProductionPlan query) {
return planMapper.selectList(query);
}
@Override
public TableDataInfo<DrMillProductionPlan> selectPageList(DrMillProductionPlan query) {
if (query.getPageNum() == null || query.getPageNum() < 1) query.setPageNum(1);
if (query.getPageSize() == null || query.getPageSize() < 1) query.setPageSize(50);
long total = planMapper.countPageList(query);
List<DrMillProductionPlan> rows = planMapper.selectPageList(query);
TableDataInfo<DrMillProductionPlan> data = TableDataInfo.build(rows);
data.setTotal(total);
return data;
}
@Override
public DrMillProductionPlan selectById(Long id) {
return planMapper.selectById(id);
}
@Override
public DrMillProductionPlan selectByActionId(Long actionId) {
String planNo = "DR" + actionId;
DrMillProductionPlan plan = planMapper.selectByPlanNo(planNo);
if (plan != null && plan.getRecipeId() != null) {
plan.setPassList(passMapper.selectByRecipeId(plan.getRecipeId()));
}
return plan;
}
@Override
public int insert(DrMillProductionPlan plan) {
String user = LoginHelper.getUsername();
plan.setCreateBy(user);
plan.setUpdateBy(user);
List<DrMillProductionPlan> all = planMapper.selectList(new DrMillProductionPlan());
plan.setSortNo(all.size() + 1);
if (plan.getPlanNo() == null || plan.getPlanNo().isEmpty()) {
plan.setPlanNo("DR" + System.currentTimeMillis());
}
return planMapper.insert(plan);
}
@Override
public int update(DrMillProductionPlan plan) {
plan.setUpdateBy(LoginHelper.getUsername());
return planMapper.update(plan);
}
@Override
public int deleteById(Long id) {
return planMapper.deleteById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int moveUp(Long id) {
DrMillProductionPlan cur = planMapper.selectById(id);
if (cur == null || cur.getSortNo() <= 1) return 0;
List<DrMillProductionPlan> all = planMapper.selectList(new DrMillProductionPlan());
DrMillProductionPlan prev = all.stream()
.filter(p -> p.getSortNo() == cur.getSortNo() - 1)
.findFirst().orElse(null);
if (prev == null) return 0;
planMapper.updateSortNo(cur.getPlanId(), prev.getSortNo());
planMapper.updateSortNo(prev.getPlanId(), cur.getSortNo());
return 1;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int moveDown(Long id) {
DrMillProductionPlan cur = planMapper.selectById(id);
if (cur == null) return 0;
List<DrMillProductionPlan> all = planMapper.selectList(new DrMillProductionPlan());
DrMillProductionPlan next = all.stream()
.filter(p -> p.getSortNo() == cur.getSortNo() + 1)
.findFirst().orElse(null);
if (next == null) return 0;
planMapper.updateSortNo(cur.getPlanId(), next.getSortNo());
planMapper.updateSortNo(next.getPlanId(), cur.getSortNo());
return 1;
}
@Override
public int finish(Long id) {
DrMillProductionPlan plan = planMapper.selectById(id);
if (plan == null) return 0;
plan.setUpdateBy(LoginHelper.getUsername());
plan.setPlanStatus("2");
plan.setProdStatus("Done");
return planMapper.update(plan);
}
}

View File

@@ -0,0 +1,96 @@
package com.klp.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.klp.domain.DrMillProcessRecipe;
import com.klp.domain.WmsProcessSpec;
import com.klp.domain.WmsProductionLine;
import com.klp.mapper.WmsProcessSpecMapper;
import com.klp.mapper.WmsProductionLineMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
/**
* 双机架工艺方案 → L3规程wms_process_spec同步服务
* <p>
* 注意:此 Service 不标注 @DS默认走 master 数据源,
* 供 Controller 在 double-rack 查询完方案列表后调用,
* 将缺失的 wms_process_spec 记录自动补齐。
* </p>
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class DrRecipeSyncService {
/** 双机架产线编号,与 wms_production_line.line_code 对应 */
private static final String DR_LINE_CODE = "DR";
private static final String DR_LINE_NAME = "双机架轧机";
/** spec_code 前缀,区分同名方案号属于哪条产线 */
private static final String SPEC_CODE_PREFIX = "DR-";
private final WmsProcessSpecMapper specMapper;
private final WmsProductionLineMapper lineMapper;
/**
* 检查并补充 wms_process_spec
* 对列表中每条方案,若在 L3 规程表中不存在则自动新增。
*/
@Transactional(rollbackFor = Exception.class)
public void syncRecipesToSpec(List<DrMillProcessRecipe> recipes) {
if (recipes == null || recipes.isEmpty()) return;
Long lineId = getOrCreateDrLineId();
int created = 0;
for (DrMillProcessRecipe recipe : recipes) {
String specCode = SPEC_CODE_PREFIX + recipe.getRecipeNo();
LambdaQueryWrapper<WmsProcessSpec> qw = Wrappers.lambdaQuery();
qw.eq(WmsProcessSpec::getSpecCode, specCode);
if (specMapper.selectCount(qw) == 0) {
WmsProcessSpec spec = new WmsProcessSpec();
spec.setSpecCode(specCode);
spec.setSpecName(recipe.getRecipeNo());
spec.setSpecType("PROCESS");
spec.setLineId(lineId);
spec.setIsEnabled(1);
spec.setRemark("由双机架工艺方案自动同步");
specMapper.insert(spec);
created++;
log.info("[DR同步] 新增 wms_process_spec: specCode={}, recipeId={}",
specCode, recipe.getRecipeId());
}
}
if (created > 0) {
log.info("[DR同步] 本次共补充 {} 条 L3 工艺规程", created);
}
}
/**
* 查找或创建双机架产线记录,返回 line_id。
*/
private Long getOrCreateDrLineId() {
LambdaQueryWrapper<WmsProductionLine> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsProductionLine::getLineCode, DR_LINE_CODE);
WmsProductionLine line = lineMapper.selectOne(lqw);
if (line == null) {
line = new WmsProductionLine();
line.setLineCode(DR_LINE_CODE);
line.setLineName(DR_LINE_NAME);
line.setCapacity(BigDecimal.ZERO);
line.setUnit("t");
line.setIsEnabled(1);
line.setRemark("双机架轧机产线,系统自动创建");
lineMapper.insert(line);
log.info("[DR同步] 新建 wms_production_line: lineCode={}, lineId={}",
DR_LINE_CODE, line.getLineId());
}
return line.getLineId();
}
}

View File

@@ -19,8 +19,12 @@ import com.klp.domain.vo.TheoryCycleRegressionResultVo;
import com.klp.domain.vo.TheoryCycleRegressionVo;
import com.klp.domain.vo.WmsCoilPendingActionVo;
import com.klp.domain.vo.WmsCoilPendingActionIdCoilVo;
import com.klp.domain.DrMillProductionPlan;
import com.klp.domain.WmsRawMaterial;
import com.klp.mapper.DrMillProductionPlanMapper;
import com.klp.mapper.WmsCoilPendingActionMapper;
import com.klp.mapper.WmsMaterialCoilMapper;
import com.klp.mapper.WmsRawMaterialMapper;
import com.klp.service.IWmsCoilPendingActionService;
import com.klp.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
@@ -52,8 +56,14 @@ public class WmsCoilPendingActionServiceImpl implements IWmsCoilPendingActionSer
private final ISysUserService userService;
private final WmsMaterialCoilMapper materialCoilMapper;
private final WmsRawMaterialMapper rawMaterialMapper;
private final DrMillProductionPlanMapper drPlanMapper;
private final StringRedisTemplate stringRedisTemplate;
/** 双机架工序 / 修复的 actionType */
private static final int ACTION_TYPE_DR_NORMAL = 504;
private static final int ACTION_TYPE_DR_REPAIR = 524;
private static final String REDIS_KEY_IDEAL_CYCLE = "oee:ideal-cycle-time";
/**
@@ -256,10 +266,99 @@ public class WmsCoilPendingActionServiceImpl implements IWmsCoilPendingActionSer
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setActionId(add.getActionId());
// 双机架工序/修复:同步在 double-rack 数据库创建生产计划
if (ACTION_TYPE_DR_NORMAL == add.getActionType() || ACTION_TYPE_DR_REPAIR == add.getActionType()) {
autoCreateDrPlan(add);
}
}
return flag;
}
/**
* 自动创建双机架生产计划(写入 double-rack 数据源)。
* 同时查询钢卷库wms_material_coil和原材料库wms_raw_material
* 将完整的钢卷/原料规格信息写入计划。
* planNo = "DR" + actionId保证唯一且可追溯。
*/
@com.baomidou.dynamic.datasource.annotation.DS("double-rack")
private void autoCreateDrPlan(WmsCoilPendingAction action) {
try {
DrMillProductionPlan plan = new DrMillProductionPlan();
plan.setPlanNo("DR" + action.getActionId());
if (action.getCoilId() != null) {
// ① 查钢卷库
WmsMaterialCoil coil = materialCoilMapper.selectById(action.getCoilId());
if (coil != null) {
plan.setInMatNo(coil.getEnterCoilNo() != null ? coil.getEnterCoilNo() : coil.getCurrentCoilNo());
plan.setEnterCoilNo(coil.getEnterCoilNo());
plan.setCurrentCoilNo(coil.getCurrentCoilNo());
// 实测厚度(优先)
if (coil.getActualThickness() != null) {
try { plan.setInMatThick(new java.math.BigDecimal(coil.getActualThickness())); } catch (Exception ignored) {}
}
// 实测宽度(优先)
plan.setInMatWidth(coil.getActualWidth());
// 净重 > 毛重
plan.setInMatWeight(coil.getNetWeight() != null ? coil.getNetWeight() : coil.getGrossWeight());
// 长度
plan.setInMatLength(coil.getLength());
// ② 查原材料库itemType='raw_material' 时通过 itemId 关联)
if ("raw_material".equals(coil.getItemType()) && coil.getItemId() != null) {
WmsRawMaterial rm = rawMaterialMapper.selectById(coil.getItemId());
if (rm != null) {
// 钢种/合金号
plan.setAlloyNo(rm.getSteelGrade());
// 标称厚度(实测没有时用标称补齐)
if (plan.getInMatThick() == null && rm.getThickness() != null) {
plan.setInMatThick(rm.getThickness());
}
// 标称宽度(实测没有时用标称补齐)
if (plan.getInMatWidth() == null && rm.getWidth() != null) {
plan.setInMatWidth(rm.getWidth());
}
// 卷重WMS 净重没有时用原材料卷重补齐)
if (plan.getInMatWeight() == null && rm.getCoilWeight() != null) {
plan.setInMatWeight(rm.getCoilWeight());
}
// 目标冷轧厚度 / 宽度写入出口目标厚度
if (rm.getTargetColdThickness() != null) {
plan.setOutThick(rm.getTargetColdThickness());
}
}
}
}
} else if (action.getCurrentCoilNo() != null) {
plan.setInMatNo(action.getCurrentCoilNo());
plan.setCurrentCoilNo(action.getCurrentCoilNo());
}
// 修复工序在备注中标记
if (ACTION_TYPE_DR_REPAIR == action.getActionType()) {
plan.setRemark("[修复] " + (action.getRemark() != null ? action.getRemark() : ""));
} else {
plan.setRemark(action.getRemark());
}
String user = LoginHelper.getUsername();
plan.setCreateBy(user);
plan.setUpdateBy(user);
plan.setPlanStatus("0");
plan.setProdStatus("Idle");
// 排到队列末尾
List<DrMillProductionPlan> all = drPlanMapper.selectList(new DrMillProductionPlan());
plan.setSortNo(all.size() + 1);
drPlanMapper.insert(plan);
} catch (Exception e) {
org.slf4j.LoggerFactory.getLogger(getClass())
.error("[双机架] 自动创建生产计划失败, actionId={}", action.getActionId(), e);
}
}
/**
* 修改钢卷待操作
*/

View File

@@ -1356,12 +1356,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
}
result = updateBySingle(bo, qrcodeStepType); // 返回新钢卷ID字符串
}
// 如果有关联的操作记录ID调用完成接口
if (bo.getActionId() != null && bo.getActionId() > 0) {
coilPendingActionService.completeAction(bo.getActionId(), result);
}
return result;
}
@@ -5743,5 +5743,15 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
return baseMapper.update(null, updateWrapper) > 0;
}
@Override
public WmsMaterialCoilVo queryByCoilNo(String coilNo) {
LambdaQueryWrapper<WmsMaterialCoil> lqw = Wrappers.lambdaQuery();
lqw.eq(WmsMaterialCoil::getEnterCoilNo, coilNo)
.or()
.eq(WmsMaterialCoil::getCurrentCoilNo, coilNo);
lqw.last("LIMIT 1");
return baseMapper.selectVoOne(lqw);
}
}