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,94 @@
package com.klp.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.domain.DrMillProcessRecipe;
import com.klp.domain.DrMillProcessRecipeVersion;
import com.klp.service.IDrMillProcessRecipeService;
import com.klp.service.IDrMillProcessRecipeVersionService;
import com.klp.service.impl.DrRecipeSyncService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
@RequiredArgsConstructor
@RestController
@RequestMapping("/dr/mill/recipe")
public class DrMillProcessRecipeController extends BaseController {
private final IDrMillProcessRecipeService recipeService;
private final IDrMillProcessRecipeVersionService versionService;
private final DrRecipeSyncService syncService;
// ─── 方案 ────────────────────────────────────────────────
@GetMapping("/list")
public R<List<DrMillProcessRecipe>> list(DrMillProcessRecipe query) {
List<DrMillProcessRecipe> list = recipeService.selectList(query);
// 每次查询时补齐缺失的 L3 工艺规程(幂等,已存在则跳过)
syncService.syncRecipesToSpec(list);
return R.ok(list);
}
@GetMapping("/{id}")
public R<DrMillProcessRecipe> detail(@PathVariable Long id) {
return R.ok(recipeService.selectDetailById(id));
}
@PostMapping
public R<Long> add(@RequestBody DrMillProcessRecipe recipe) {
recipeService.save(recipe);
// 新增方案后立即同步 L3 工艺规程
syncService.syncRecipesToSpec(Collections.singletonList(recipe));
return R.ok(recipe.getRecipeId());
}
@PutMapping
public R<Void> edit(@RequestBody DrMillProcessRecipe recipe) {
recipeService.update(recipe);
return R.ok();
}
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
recipeService.deleteByIds(ids);
return R.ok();
}
// ─── 版本 ────────────────────────────────────────────────
@GetMapping("/version/list/{recipeId}")
public R<List<DrMillProcessRecipeVersion>> versionList(@PathVariable Long recipeId) {
return R.ok(versionService.listByRecipeId(recipeId));
}
@GetMapping("/version/{versionId}")
public R<DrMillProcessRecipeVersion> versionDetail(@PathVariable Long versionId) {
return R.ok(versionService.getDetailById(versionId));
}
@PostMapping("/version")
public R<Long> addVersion(@RequestBody DrMillProcessRecipeVersion version) {
return R.ok(versionService.save(version));
}
@PutMapping("/version")
public R<Void> editVersion(@RequestBody DrMillProcessRecipeVersion version) {
versionService.update(version);
return R.ok();
}
@PutMapping("/version/activate/{versionId}")
public R<Void> activate(@PathVariable Long versionId) {
versionService.activate(versionId);
return R.ok();
}
@DeleteMapping("/version/{versionId}")
public R<Void> deleteVersion(@PathVariable Long versionId) {
versionService.deleteById(versionId);
return R.ok();
}
}

View File

@@ -0,0 +1,83 @@
package com.klp.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.domain.DrMillProductionPlan;
import com.klp.service.IDrMillProductionPlanService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 双机架生产计划管理
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/dr/mill/plan")
public class DrMillProductionPlanController extends BaseController {
private final IDrMillProductionPlanService planService;
@GetMapping("/list")
public R<List<DrMillProductionPlan>> list(DrMillProductionPlan query) {
return R.ok(planService.selectList(query));
}
/** 实绩分页查询(按创建时间倒序,支持卷号/状态/时间范围过滤) */
@GetMapping("/actual/page")
public TableDataInfo<DrMillProductionPlan> actualPage(DrMillProductionPlan query) {
return planService.selectPageList(query);
}
@GetMapping("/{id}")
public R<DrMillProductionPlan> detail(@PathVariable Long id) {
return R.ok(planService.selectById(id));
}
/**
* 通过待操作ID查询绑定计划含道次供录入页快捷写入
* planNo = "DR" + actionId
*/
@GetMapping("/byAction/{actionId}")
public R<DrMillProductionPlan> getByActionId(@PathVariable Long actionId) {
return R.ok(planService.selectByActionId(actionId));
}
@PostMapping
public R<Void> add(@RequestBody DrMillProductionPlan plan) {
planService.insert(plan);
return R.ok();
}
@PutMapping
public R<Void> edit(@RequestBody DrMillProductionPlan plan) {
planService.update(plan);
return R.ok();
}
@DeleteMapping("/{id}")
public R<Void> remove(@PathVariable Long id) {
planService.deleteById(id);
return R.ok();
}
@PutMapping("/moveUp/{id}")
public R<Void> moveUp(@PathVariable Long id) {
planService.moveUp(id);
return R.ok();
}
@PutMapping("/moveDown/{id}")
public R<Void> moveDown(@PathVariable Long id) {
planService.moveDown(id);
return R.ok();
}
@PutMapping("/finish/{id}")
public R<Void> finish(@PathVariable Long id) {
planService.finish(id);
return R.ok();
}
}

View File

@@ -616,6 +616,16 @@ public class WmsMaterialCoilController extends BaseController {
}
}
/**
* 根据入场钢卷号或当前钢卷号查询钢卷信息,供双机架计划绑定使用
*
* @param coilNo 入场钢卷号或当前钢卷号
*/
@GetMapping("/queryByCoilNo")
public R<com.klp.domain.vo.WmsMaterialCoilVo> queryByCoilNo(@NotBlank(message = "钢卷号不能为空") @RequestParam String coilNo) {
return R.ok(iWmsMaterialCoilService.queryByCoilNo(coilNo));
}
/**
* 冷硬卷切边统计

View File

@@ -0,0 +1,33 @@
package com.klp.domain;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/** 双机架工艺方案道次(对应 double-rack.mill_process_pass */
@Data
public class DrMillProcessPass {
private Long passId;
private Long recipeId;
/** 所属版本 ID */
private Long versionId;
private Integer passNo;
private BigDecimal inThick;
private BigDecimal outThick;
private BigDecimal width;
private BigDecimal rollForce;
private BigDecimal inTension;
private BigDecimal outTension;
private BigDecimal maxSpeed;
private BigDecimal inUnitTension;
private BigDecimal outUnitTension;
private BigDecimal reduction;
private BigDecimal totalReduction;
private String delFlag;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
private String remark;
}

View File

@@ -0,0 +1,31 @@
package com.klp.domain;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/** 双机架工艺方案主表(对应 double-rack.mill_process_recipe */
@Data
public class DrMillProcessRecipe {
private Long recipeId;
private String recipeNo;
private String alloyNo;
private Integer passCount;
private BigDecimal inThick;
private BigDecimal outThick;
private BigDecimal outWidth;
/** 0-正常 1-停用 */
private String status;
/** 0-存在 2-删除 */
private String delFlag;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
private String remark;
/** 关联道次(非数据库字段) */
private List<DrMillProcessPass> passList;
}

View File

@@ -0,0 +1,28 @@
package com.klp.domain;
import lombok.Data;
import java.util.Date;
import java.util.List;
/** 双机架工艺方案版本double-rack.mill_process_recipe_version */
@Data
public class DrMillProcessRecipeVersion {
private Long versionId;
private Long recipeId;
/** 版本号,如 V1.0 */
private String versionCode;
/** 1=已激活 0=未激活 */
private Integer isActive;
/** 0=草稿 1=已发布 */
private String status;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
private String remark;
private String delFlag;
/** 关联道次(非数据库字段) */
private List<DrMillProcessPass> passList;
}

View File

@@ -0,0 +1,66 @@
package com.klp.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 双机架生产计划(对应 double-rack.mill_production_plan
* enterCoilNo / currentCoilNo 用于绑定三级 WMS 钢卷数据。
*/
@Data
public class DrMillProductionPlan {
private Long planId;
private String planNo;
/** 0-待生产 1-生产中 2-完成 3-撤销 */
private String planStatus;
/** Idle / Rolling / NextCoil / Done */
private String prodStatus;
private Integer sortNo;
private String inMatNo;
private String alloyNo;
private BigDecimal inMatThick;
private BigDecimal inMatWidth;
private BigDecimal inMatWeight;
private BigDecimal inMatLength;
private BigDecimal inMatId;
private BigDecimal inMatOd;
private BigDecimal outThick;
private Integer passCount;
private Long recipeId;
private String recipeNo;
/** 绑定的工艺版本 ID */
private Long versionId;
/** 关联三级 WMS 入场钢卷号 */
private String enterCoilNo;
/** 关联三级 WMS 当前钢卷号 */
private String currentCoilNo;
/** 绑定方案的道次列表非DB字段按需填充 */
private List<DrMillProcessPass> passList;
private String delFlag;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
private String remark;
// ── 非 DB 查询字段(分页 + 时间范围) ──────────────
@TableField(exist = false)
private Integer pageNum;
@TableField(exist = false)
private Integer pageSize;
@TableField(exist = false)
private String createStartTime;
@TableField(exist = false)
private String createEndTime;
public int getOffset() {
int num = (pageNum != null && pageNum > 0) ? pageNum : 1;
int size = (pageSize != null && pageSize > 0) ? pageSize : 50;
return (num - 1) * size;
}
}

View File

@@ -0,0 +1,21 @@
package com.klp.mapper;
import com.klp.domain.DrMillProcessPass;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface DrMillProcessPassMapper {
List<DrMillProcessPass> selectByVersionId(Long versionId);
List<DrMillProcessPass> selectByRecipeId(Long recipeId);
int insertBatch(List<DrMillProcessPass> list);
int deleteByVersionId(Long versionId);
int deleteByRecipeId(Long recipeId);
/** 查出所有 version_id 为空的道次所属 recipe_id去重 */
List<Long> selectDistinctRecipeIdsWithOrphanPasses();
/** 将指定方案下所有 version_id 为空的道次批量设置 version_id */
int updateVersionIdForOrphans(@Param("recipeId") Long recipeId,
@Param("versionId") Long versionId);
}

View File

@@ -0,0 +1,14 @@
package com.klp.mapper;
import com.klp.domain.DrMillProcessRecipe;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DrMillProcessRecipeMapper {
List<DrMillProcessRecipe> selectList(DrMillProcessRecipe query);
DrMillProcessRecipe selectById(Long recipeId);
int insert(DrMillProcessRecipe recipe);
int update(DrMillProcessRecipe recipe);
int deleteBatchByIds(Long[] ids);
}

View File

@@ -0,0 +1,24 @@
package com.klp.mapper;
import com.klp.domain.DrMillProcessRecipeVersion;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface DrMillProcessRecipeVersionMapper {
List<DrMillProcessRecipeVersion> selectByRecipeId(Long recipeId);
/** 查询指定方案已有的 V1.0 版本(用于迁移幂等判断) */
DrMillProcessRecipeVersion selectV1ByRecipeId(@Param("recipeId") Long recipeId);
/** 查询指定方案当前激活版本 */
DrMillProcessRecipeVersion selectActiveByRecipeId(@Param("recipeId") Long recipeId);
DrMillProcessRecipeVersion selectById(Long versionId);
int insert(DrMillProcessRecipeVersion version);
int update(DrMillProcessRecipeVersion version);
int deleteById(Long versionId);
int deleteByRecipeIds(@Param("ids") Long[] ids);
/** 将同方案其他版本置为未激活 */
int deactivateOthers(@Param("recipeId") Long recipeId, @Param("versionId") Long versionId);
/** 激活指定版本 */
int activate(Long versionId);
}

View File

@@ -0,0 +1,21 @@
package com.klp.mapper;
import com.klp.domain.DrMillProductionPlan;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface DrMillProductionPlanMapper {
List<DrMillProductionPlan> selectList(DrMillProductionPlan query);
/** 分页查询(配合 PageHelper 或手动 LIMIT/OFFSET */
List<DrMillProductionPlan> selectPageList(DrMillProductionPlan query);
long countPageList(DrMillProductionPlan query);
DrMillProductionPlan selectById(Long planId);
DrMillProductionPlan selectByPlanNo(@Param("planNo") String planNo);
int insert(DrMillProductionPlan plan);
int update(DrMillProductionPlan plan);
int deleteById(Long planId);
int updateSortNo(@Param("planId") Long planId, @Param("sortNo") int sortNo);
int countByStatus(@Param("status") String status);
}

View File

@@ -0,0 +1,12 @@
package com.klp.service;
import com.klp.domain.DrMillProcessRecipe;
import java.util.List;
public interface IDrMillProcessRecipeService {
List<DrMillProcessRecipe> selectList(DrMillProcessRecipe query);
DrMillProcessRecipe selectDetailById(Long id);
int save(DrMillProcessRecipe recipe);
int update(DrMillProcessRecipe recipe);
int deleteByIds(Long[] ids);
}

View File

@@ -0,0 +1,13 @@
package com.klp.service;
import com.klp.domain.DrMillProcessRecipeVersion;
import java.util.List;
public interface IDrMillProcessRecipeVersionService {
List<DrMillProcessRecipeVersion> listByRecipeId(Long recipeId);
DrMillProcessRecipeVersion getDetailById(Long versionId);
Long save(DrMillProcessRecipeVersion version);
void update(DrMillProcessRecipeVersion version);
void activate(Long versionId);
void deleteById(Long versionId);
}

View File

@@ -0,0 +1,20 @@
package com.klp.service;
import com.klp.common.core.page.TableDataInfo;
import com.klp.domain.DrMillProductionPlan;
import java.util.List;
public interface IDrMillProductionPlanService {
List<DrMillProductionPlan> selectList(DrMillProductionPlan query);
/** 分页查询实绩(按创建时间倒序) */
TableDataInfo<DrMillProductionPlan> selectPageList(DrMillProductionPlan query);
DrMillProductionPlan selectById(Long id);
/** 通过待操作ID查计划planNo = "DR" + actionId并附带道次列表 */
DrMillProductionPlan selectByActionId(Long actionId);
int insert(DrMillProductionPlan plan);
int update(DrMillProductionPlan plan);
int deleteById(Long id);
int moveUp(Long id);
int moveDown(Long id);
int finish(Long id);
}

View File

@@ -349,5 +349,10 @@ public interface IWmsMaterialCoilService {
* @param response HTTP响应对象
*/
void exportAbnormalReport(WmsMaterialCoilBo bo, HttpServletResponse response);
/**
* 根据入场钢卷号或当前钢卷号查询钢卷,供双机架计划绑定使用
*/
com.klp.domain.vo.WmsMaterialCoilVo queryByCoilNo(String coilNo);
}

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);
}
}

View File

@@ -0,0 +1,33 @@
package com.klp.task;
import com.klp.service.impl.DrMigrationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 应用启动后自动执行双机架历史道次版本迁移。
* 逻辑幂等:若已迁移完毕则无任何 DB 写操作,仅打印一条 info 日志。
*/
@Slf4j
@Order(100)
@Component
@RequiredArgsConstructor
public class DrMigrationRunner implements ApplicationRunner {
private final DrMigrationService migrationService;
@Override
public void run(ApplicationArguments args) {
log.info("[DR迁移] 开始检查历史道次版本迁移...");
try {
migrationService.migrateOrphanPassesToV1();
} catch (Exception e) {
// 迁移失败不阻断服务启动,只记录错误
log.error("[DR迁移] 执行异常,请人工检查 mill_process_pass 表", e);
}
}
}