diff --git a/README.md b/README.md index 37e604ec..37bbe587 100644 --- a/README.md +++ b/README.md @@ -1,146 +1 @@ -## 平台简介 -[![Gitee Repo star](https://gitee.com/KonBAI-Q/klp-flowable-plus/badge/star.svg?theme=dark)](https://gitee.com/KonBAI-Q/klp-flowable-plus/stargazers) -[![GitHub Repo stars](https://img.shields.io/github/stars/KonBAI-Q/KLP?style=social)](https://github.com/KonBAI-Q/KLP/stargazers) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/KonBAI-Q/klp-flowable-plus/blob/master/LICENSE) -[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com) -[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]() -[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() -[![JDK-11](`https://img.shields.io/badge/JDK-11-green.svg)]() - -- 本项目基于 [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) 进行二次开发,采用 `Flowable` 扩展工作流应用场景,支持在线表单设计和丰富的工作流程设计能力。 -- 本项目主要针对`Flowable`工作流场景开发,脚手架功能同步更新 [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) 项目。 -- 采用`MIT开源协议`,完全免费给个人及企业使用。 -- 项目处于开发阶段,工作流流程还存在不足。因此,目前仅推荐用于学习、毕业设计等个人使用。 - -## 赞助商 -[![驰骋BPM](https://gitee.com/KonBAI-Q/klp-flowable-plus-vuepress/raw/master/imgs/chicheng-logo.png)](http://ccflow.org/?frm=KonBAI) - -## 参考文档 -- 项目文档:[RuoYi-Flowable-Plus开发文档](http://rfp-doc.konbai.work) -- 项目文档(备用):[RuoYi-Flowable-Plus开发文档](http://159.75.158.189:81/) -- 脚手架文档:[RuoYi-Vue-Plus文档](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages) - -## 项目地址 -- Gitee: -- GitHub: - -## 在线演示 -演示服务不限制CURD操作,希望大家按需使用,不要恶意添加脏数据或对服务器进行攻击等操作。(将不定期清理数据) - -[KLP 在线演示](http://159.75.158.189/) - -| | 账号 | 密码 | -|---------------- | ----- | -------- | -| 超管账户 | admin | admin123 | -| 监控中心(未运行) | klp | 123456 | -| 任务调度中心 | admin | 123456 | -| 数据监控中心 | klp | 123456 | - -## 技术交流群 - -交流1群 🈵️ [![加入QQ群](https://img.shields.io/badge/QQ群-1007207992-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027\&k=PYDZa1tA)
-交流2群 🈵️ [![加入QQ群](https://img.shields.io/badge/QQ群-725502135-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027&k=J4zeZaKo)
-交流3群 🈵️ [![加入QQ群](https://img.shields.io/badge/QQ群-860980043-blue.svg?style=flat)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NfqIsFMASOvIC6yHYwY6bnaSfdgcD1La&authKey=SeFDA4oFkb%2FkdvnI%2FJ3aJTJZkyzDaz8v8gybpzUATAilnKSCmyKhCE6R2jkXc5e2&noverify=0&group_code=860980043)
-交流4群 [![加入QQ群](https://img.shields.io/badge/QQ群-683510042-blue.svg?style=flat)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=RBXhJKfZT0GSjEPa0CcViGmP_mnVE82j&authKey=J9shEDSoCujDRJO9wcpqzsbvCQskcEvo0idGd54I0uk735K90HhA0v5ywEkUdxK3&noverify=0&group_code=683510042)
- -## 参与开源 -- 如遇到问题,欢迎提交到 [issues](https://gitee.com/KonBAI-Q/klp-flowable-plus/issues)(请按模版进行填写信息)。 -- 欢迎提交 [PR](https://gitee.com/KonBAI-Q/klp-flowable-plus/pulls) ,注意请提交到 `develop` 开发分支 统一测试发版。 - -## 特别鸣谢 -- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) -- [RuoYi-flowable](https://gitee.com/tony2y/RuoYi-flowable) -- [bpmn-process-designer](https://gitee.com/MiyueSC/bpmn-process-designer) - -## 支持项目 -如果项目对你有帮助,请给项目点个Star。也可以通过下方二维码请作者喝一杯奶茶! -![输入图片说明](http://qiniu-flowable.konbai.work/Collection-Code.jpg) - -## 友情链接 -- [玩转RuoYi-Cloud-Plus - Flowable基础](https://blog.csdn.net/zhaozhiqiang1981/article/details/129240406):文档包含Flowable基础知识、项目使用说明、源码解析等。(新人必看) -- [基于若依的Flowable工作流实战](https://space.bilibili.com/400188320/channel/collectiondetail?sid=1002899):Flowable视频学习专栏,项目基本覆盖了Flowable的方方面面,也拓展了很多为了达到生产级别项目而附加的表结构,工具类等知识点! - -## 推荐图书 -- 大家在使用本项目时,推荐结合贺波老师的书[《深入Flowable流程引擎:核心原理与高阶实战》](https://item.jd.com/14804836.html)学习。这本书得到了Flowable创始人Tijs Rademakers亲笔作序推荐,对系统学习和深入掌握Flowable的用法非常有帮助。 - -![深入Flowable流程引擎:核心原理与高阶实战](https://foruda.gitee.com/images/1727508315476163030/4e083d99_5096840.jpeg) - -- 大家在使用本项目时,推荐结合贺波老师的书[《深入Activiti流程引擎:核心原理与高阶实战》](https://item.m.jd.com/product/13928958.html?gx=RnAomTM2bmCImZxDqYAkVCoIHuIYVqc),这本书对系统学习和深入掌握Activiti/Flowable的用法非常有帮助。 - -![深入Activiti流程引擎:核心原理与高阶实战](https://foruda.gitee.com/images/1727508299212519153/0791b5ac_5096840.jpeg) - -## 演示图例 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 登录页面 - 登录页面 - - 用户管理 - 用户管理 -
- 流程分类 - 流程分类 - - 流程表单 - 流程表单 -
- 流程定义 - 流程定义 - - 流程发起 - 流程发起 -
- 表单设计 - 表单设计 - - 流程设计 - 流程设计 -
- 发起流程 - 发起流程 - - 待办任务 - 代办任务 -
- 任务办理 - 任务办理 - - 流转记录 - 流转记录 -
- 流程跟踪 - 流程跟踪 - - 流程完结 - 流程完结 -
+下面我需要你写前端手机端uniapp web管理端 \ No newline at end of file diff --git a/klp-admin/src/main/resources/application-prod.yml b/klp-admin/src/main/resources/application-prod.yml index ac0a944c..cceed40d 100644 --- a/klp-admin/src/main/resources/application-prod.yml +++ b/klp-admin/src/main/resources/application-prod.yml @@ -58,9 +58,9 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://140.143.206.120:13306/klp-oa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://140.143.206.120:3306/klp-oa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: klp - password: KeLunPu@123 + password: KeLunPu123@ # 从库数据源 slave: lazy: true diff --git a/klp-wms/src/main/java/com/klp/domain/vo/WmsMaterialCoilVo.java b/klp-wms/src/main/java/com/klp/domain/vo/WmsMaterialCoilVo.java index f5ea3392..fd0ff220 100644 --- a/klp-wms/src/main/java/com/klp/domain/vo/WmsMaterialCoilVo.java +++ b/klp-wms/src/main/java/com/klp/domain/vo/WmsMaterialCoilVo.java @@ -6,6 +6,7 @@ import com.klp.common.annotation.ExcelDictFormat; import com.klp.common.convert.ExcelDictConvert; import lombok.Data; import java.math.BigDecimal; +import java.util.List; /** @@ -151,6 +152,11 @@ public class WmsMaterialCoilVo { * 产品信息(当itemType为product时) */ private Object product; // 产品VO待定义 + + /** + * BOM列表(原材料对应的BOM项目信息) + */ + private List bomItemList; } diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java index 2f85baa7..12403de6 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsMaterialCoilServiceImpl.java @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.klp.common.helper.LoginHelper; import com.klp.common.utils.StringUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,6 +18,10 @@ import com.klp.domain.vo.WmsMaterialCoilVo; import com.klp.domain.vo.WmsGenerateRecordVo; import com.klp.domain.vo.WmsWarehouseVo; import com.klp.domain.vo.WmsRawMaterialVo; +import com.klp.domain.vo.WmsProductBomVo; +import com.klp.domain.vo.WmsBomItemVo; +import com.klp.domain.bo.WmsProductBomBo; +import com.klp.domain.bo.WmsBomItemBo; import com.klp.domain.WmsMaterialCoil; import com.klp.domain.WmsStock; import com.klp.domain.bo.WmsStockBo; @@ -28,6 +33,8 @@ import com.klp.service.IWmsStockService; import com.klp.service.IWmsGenerateRecordService; import com.klp.service.IWmsWarehouseService; import com.klp.service.IWmsRawMaterialService; +import com.klp.service.IWmsProductBomService; +import com.klp.service.IWmsBomItemService; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; @@ -55,6 +62,8 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { private final IWmsGenerateRecordService generateRecordService; private final IWmsWarehouseService warehouseService; private final IWmsRawMaterialService rawMaterialService; + private final IWmsProductBomService productBomService; + private final IWmsBomItemService bomItemService; /** * 查询钢卷物料表 @@ -66,12 +75,28 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { return null; } + // 如果查询到的是历史数据,尝试查找对应的当前数据 + if (vo.getDataType() != null && vo.getDataType() == 0) { + // 根据入场钢卷号查找当前数据 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(WmsMaterialCoil::getEnterCoilNo, vo.getEnterCoilNo()) + .eq(WmsMaterialCoil::getDataType, 1) // 查找当前数据 + .orderByDesc(WmsMaterialCoil::getCreateTime); // 按创建时间倒序,获取最新的 + + List currentDataList = baseMapper.selectVoList(lqw); + if (!currentDataList.isEmpty()) { + // 如果找到当前数据,返回最新的当前数据 + vo = currentDataList.get(0); + } + // 如果没有找到当前数据,仍然返回历史数据供查看 + } + // 查询关联对象 fillRelatedObjects(vo); - + return vo; } - + /** * 填充关联对象信息 */ @@ -81,25 +106,33 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { WmsWarehouseVo warehouse = warehouseService.queryById(vo.getWarehouseId()); vo.setWarehouse(warehouse); } - + // 查询下一库区信息 if (vo.getNextWarehouseId() != null) { WmsWarehouseVo nextWarehouse = warehouseService.queryById(vo.getNextWarehouseId()); vo.setNextWarehouse(nextWarehouse); } - + // 查询二维码信息 if (vo.getQrcodeRecordId() != null) { WmsGenerateRecordVo qrcodeRecord = generateRecordService.queryById(vo.getQrcodeRecordId()); vo.setQrcodeRecord(qrcodeRecord); } - + // 查询原材料信息(当itemType为raw_material时) if ("raw_material".equals(vo.getItemType()) && vo.getItemId() != null) { WmsRawMaterialVo rawMaterial = rawMaterialService.queryById(vo.getItemId()); vo.setRawMaterial(rawMaterial); + + // 查询原材料对应的BOM信息(通过bomId查询BomItem列表) + if (rawMaterial != null && rawMaterial.getBomId() != null) { + WmsBomItemBo bomItemBo = new WmsBomItemBo(); + bomItemBo.setBomId(rawMaterial.getBomId()); + List bomItemList = bomItemService.queryList(bomItemBo); + vo.setBomItemList(bomItemList); + } } - + // 查询产品信息(当itemType为product时) // TODO: 当产品VO定义后,添加产品查询逻辑 if ("product".equals(vo.getItemType()) && vo.getItemId() != null) { @@ -147,13 +180,13 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { // 1. 生成二维码 Long qrcodeRecordId = generateQrcodeForInsert(bo); bo.setQrcodeRecordId(qrcodeRecordId); - + // 2. 如果warehouseId为空,查找或创建stock if (bo.getWarehouseId() == null) { Long warehouseId = findOrCreateStock(bo); bo.setWarehouseId(warehouseId); } - + // 3. 插入钢卷数据 WmsMaterialCoil add = BeanUtil.toBean(bo, WmsMaterialCoil.class); add.setDataType(1); // 新增的钢卷默认为当前数据 @@ -166,7 +199,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } return flag; } - + /** * 生成二维码(新增) */ @@ -174,18 +207,18 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { try { Map contentMap = new HashMap<>(); String currentCoilNo = bo.getCurrentCoilNo() != null ? bo.getCurrentCoilNo() : bo.getEnterCoilNo(); - + contentMap.put("enter_coil_no", bo.getEnterCoilNo()); // 入场钢卷号(唯一不变) contentMap.put("current_coil_no", currentCoilNo); // 当前钢卷号(可变) contentMap.put("coil_id", "null"); // 钢卷ID(新增时暂时为null,插入后更新) - + // 创建steps数组 List> steps = new ArrayList<>(); Map step1 = new HashMap<>(); step1.put("step", 1); step1.put("action", "新增"); step1.put("current_coil_no", currentCoilNo); - + // 判断是合卷还是分卷 if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) { // 合卷:父编号字符串用逗号分隔 @@ -199,27 +232,27 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { // 默认:当前钢卷号 step1.put("operation", "新增"); } - + steps.add(step1); contentMap.put("steps", steps); - + ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); - + WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(bo.getEnterCoilNo()); // 使用入场钢卷号作为编号 recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); - + WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成二维码失败: " + e.getMessage()); } } - + /** * 查找或创建stock */ @@ -227,13 +260,13 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { if (bo.getItemType() == null || bo.getItemId() == null) { throw new RuntimeException("物品类型和物品ID不能为空"); } - + // 查询是否存在相同的stock WmsStockBo stockBo = new WmsStockBo(); stockBo.setItemType(bo.getItemType()); stockBo.setItemId(bo.getItemId()); List stockList = stockService.queryList(stockBo); - + if (!stockList.isEmpty()) { // 如果找到相同的stock,返回第一个的warehouseId return stockList.get(0).getWarehouseId(); @@ -279,20 +312,20 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { if (bo.getCoilId() == null) { throw new RuntimeException("钢卷ID不能为空"); } - + // 查询原钢卷是否存在 WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId()); if (oldCoil == null) { throw new RuntimeException("钢卷不存在"); } - + // 直接更新钢卷属性 WmsMaterialCoil updateCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); validEntityBeforeSave(updateCoil); - + // 使用MyBatis-Plus的updateById方法直接更新 boolean flag = baseMapper.updateById(updateCoil) > 0; - + return flag; } @@ -305,21 +338,36 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { if (oldCoil == null) { throw new RuntimeException("原钢卷不存在"); } - + // 1. 更新二维码,添加操作记录 updateQrcodeContent(oldCoil.getQrcodeRecordId(), bo); - + // 2. 将原数据更新为历史数据(data_type=0) LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(WmsMaterialCoil::getCoilId, bo.getCoilId()) .set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据 baseMapper.update(null, updateWrapper); - + // 3. 插入一条新的当前数据(data_type=1) WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); newCoil.setCoilId(null); // 清空ID,让数据库自动生成新的ID newCoil.setDataType(1); // 设置为当前数据 newCoil.setQrcodeRecordId(oldCoil.getQrcodeRecordId()); // 继承二维码ID + + // 确保关键字段不丢失 + if (newCoil.getEnterCoilNo() == null) { + newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); + } + if (newCoil.getSupplierCoilNo() == null) { + newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); // 保留厂家原料卷号 + } + if (newCoil.getItemType() == null) { + newCoil.setItemType(oldCoil.getItemType()); + } + if (newCoil.getItemId() == null) { + newCoil.setItemId(oldCoil.getItemId()); + } + validEntityBeforeSave(newCoil); boolean flag = baseMapper.insert(newCoil) > 0; if (flag) { @@ -340,11 +388,11 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { throw new RuntimeException("原钢卷不存在"); } } - + // 判断是分卷还是合卷 boolean isSplit = false; boolean isMerge = false; - + // 检查bo本身是否为合卷 if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) { isMerge = true; @@ -357,7 +405,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } } } - + // 1. 将原数据更新为历史数据(data_type=0) // 注意:合卷时bo的coilId可能为空,因为bo是合卷后的新钢卷 if (bo.getCoilId() != null) { @@ -366,42 +414,51 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { .set(WmsMaterialCoil::getDataType, 0); // 设置为历史数据 baseMapper.update(null, updateWrapper); } - + // 2. 插入多条新的当前数据(data_type=1) List newCoils = new ArrayList<>(); List allNewCoilNos = new ArrayList<>(); - + // 收集所有新钢卷号 for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { allNewCoilNos.add(newCoilBo.getCurrentCoilNo()); } - + if (isSplit) { // 分卷:将bo作为被分卷的原始对象,newCoils中的对象作为分卷后产生的新钢卷 if (oldCoil == null) { throw new RuntimeException("分卷操作需要原钢卷信息"); } - + // 1. 将原始钢卷更新为历史数据(已在上面完成) - - // 2. 为每个分卷后的子钢卷生成独立的二维码并插入数据库 - for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { - WmsMaterialCoil newCoil = BeanUtil.toBean(newCoilBo, WmsMaterialCoil.class); - newCoil.setCoilId(null); - newCoil.setDataType(1); - newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); - - // 为每个子钢卷生成独立二维码 - Long newQrcodeId = generateQrcodeForSplit(oldCoil, newCoilBo, allNewCoilNos); - newCoil.setQrcodeRecordId(newQrcodeId); - - validEntityBeforeSave(newCoil); - baseMapper.insert(newCoil); - newCoils.add(newCoil); - - // 更新二维码内容中的coilId - updateQrcodeCoilId(newQrcodeId, newCoil.getCoilId()); - } + + // 2. 为每个分卷后的子钢卷生成独立的二维码并插入数据库 + for (WmsMaterialCoilBo newCoilBo : bo.getNewCoils()) { + WmsMaterialCoil newCoil = BeanUtil.toBean(newCoilBo, WmsMaterialCoil.class); + newCoil.setCoilId(null); + newCoil.setDataType(1); + // 继承原钢卷的基本信息 + newCoil.setEnterCoilNo(oldCoil.getEnterCoilNo()); + newCoil.setSupplierCoilNo(oldCoil.getSupplierCoilNo()); // 保留厂家原料卷号 + newCoil.setItemType(oldCoil.getItemType()); + newCoil.setItemId(oldCoil.getItemId()); + newCoil.setTeam(oldCoil.getTeam()); + // 如果没有指定库区,使用原库区 + if (newCoil.getWarehouseId() == null) { + newCoil.setWarehouseId(oldCoil.getWarehouseId()); + } + + // 为每个子钢卷生成独立二维码 + Long newQrcodeId = generateQrcodeForSplit(oldCoil, newCoilBo, allNewCoilNos); + newCoil.setQrcodeRecordId(newQrcodeId); + + validEntityBeforeSave(newCoil); + baseMapper.insert(newCoil); + newCoils.add(newCoil); + + // 更新二维码内容中的coilId + updateQrcodeCoilId(newQrcodeId, newCoil.getCoilId()); + } } else if (isMerge) { // 合卷:将bo作为合卷后的新钢卷,newCoils中的对象作为参与合卷的原始钢卷 // 1. 将参与合卷的原始钢卷更新为历史数据 @@ -413,35 +470,50 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { baseMapper.update(null, originalUpdateWrapper); } } - + // 2. 生成合卷后的新钢卷二维码 Long mergedQrcodeId = generateQrcodeForMerge(bo, bo.getNewCoils()); - + // 3. 插入合卷后的新钢卷 WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class); newCoil.setCoilId(null); newCoil.setDataType(1); - // 合卷后的钢卷使用自己的enterCoilNo,如果没有则从参与合卷的原始钢卷中获取 - if (newCoil.getEnterCoilNo() == null && !bo.getNewCoils().isEmpty()) { - // 从第一个参与合卷的原始钢卷获取enterCoilNo + + // 从第一个参与合卷的原始钢卷获取基本信息 + if (!bo.getNewCoils().isEmpty()) { WmsMaterialCoil firstOriginalCoil = baseMapper.selectById(bo.getNewCoils().get(0).getCoilId()); if (firstOriginalCoil != null) { - newCoil.setEnterCoilNo(firstOriginalCoil.getEnterCoilNo()); + // 继承基本信息 + if (newCoil.getEnterCoilNo() == null) { + newCoil.setEnterCoilNo(firstOriginalCoil.getEnterCoilNo()); + } + if (newCoil.getSupplierCoilNo() == null) { + newCoil.setSupplierCoilNo(firstOriginalCoil.getSupplierCoilNo()); // 保留厂家原料卷号 + } + if (newCoil.getItemType() == null) { + newCoil.setItemType(firstOriginalCoil.getItemType()); + } + if (newCoil.getItemId() == null) { + newCoil.setItemId(firstOriginalCoil.getItemId()); + } + if (newCoil.getTeam() == null) { + newCoil.setTeam(firstOriginalCoil.getTeam()); + } } } newCoil.setQrcodeRecordId(mergedQrcodeId); - + validEntityBeforeSave(newCoil); baseMapper.insert(newCoil); newCoils.add(newCoil); - + // 更新二维码内容中的coilId updateQrcodeCoilId(mergedQrcodeId, newCoil.getCoilId()); } - + return true; } - + /** * 为分卷生成新二维码(每个子钢卷一个) */ @@ -451,7 +523,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { contentMap.put("enter_coil_no", oldCoil.getEnterCoilNo()); contentMap.put("current_coil_no", newCoilBo.getCurrentCoilNo()); contentMap.put("coil_id", "null"); // 钢卷ID(分卷时暂时为null,插入后更新) - + // 复制原钢卷的历史steps List> steps = new ArrayList<>(); if (oldCoil.getQrcodeRecordId() != null) { @@ -467,7 +539,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } } } - + // 添加分卷步骤 Map splitStep = new HashMap<>(); splitStep.put("step", steps.size() + 1); @@ -477,27 +549,28 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { splitStep.put("old_coil_id", String.valueOf(oldCoil.getCoilId())); splitStep.put("new_current_coil_nos", String.join(",", allNewCoilNos)); splitStep.put("child_coils", allNewCoilNos); + splitStep.put("operator", LoginHelper.getUsername()); // 操作者 steps.add(splitStep); - + contentMap.put("steps", steps); - + ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); - + WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(oldCoil.getEnterCoilNo() + "-" + newCoilBo.getCurrentCoilNo()); recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); - + WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { throw new RuntimeException("生成分卷二维码失败: " + e.getMessage()); } } - + /** * 为合卷生成新二维码(合并多个父钢卷的二维码信息) */ @@ -506,7 +579,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { if (mergedCoilBo == null) { throw new RuntimeException("合卷后的钢卷数据不能为空"); } - + Map contentMap = new HashMap<>(); // 获取enterCoilNo,优先使用mergedCoilBo的,如果没有则从原始钢卷中获取 String enterCoilNo = mergedCoilBo.getEnterCoilNo(); @@ -519,10 +592,10 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { contentMap.put("enter_coil_no", enterCoilNo); contentMap.put("current_coil_no", mergedCoilBo.getCurrentCoilNo()); contentMap.put("coil_id", "null"); // 钢卷ID(合卷时暂时为null,插入后更新) - + // 合并所有参与合卷的原始钢卷的历史steps List> steps = new ArrayList<>(); - + // 从参与合卷的原始钢卷中获取二维码信息并合并 if (originalCoils != null && !originalCoils.isEmpty()) { for (WmsMaterialCoilBo originalCoilBo : originalCoils) { @@ -545,13 +618,13 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } } } - + // 添加合卷步骤 Map mergeStep = new HashMap<>(); mergeStep.put("step", steps.size() + 1); mergeStep.put("action", "更新"); mergeStep.put("operation", "合卷"); - + // 收集参与合卷的原始钢卷号和ID List originalCoilNos = new ArrayList<>(); List originalCoilIds = new ArrayList<>(); @@ -568,20 +641,23 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { mergeStep.put("parent_coil_nos", String.join(",", originalCoilNos)); mergeStep.put("parent_coil_ids", String.join(",", originalCoilIds)); mergeStep.put("new_current_coil_no", mergedCoilBo.getCurrentCoilNo()); + mergeStep.put("operator", LoginHelper.getUsername()); // 操作者 steps.add(mergeStep); - + contentMap.put("steps", steps); - + // 将父钢卷ID也存储到顶层,方便快速查询 + contentMap.put("parent_coil_ids", String.join(",", originalCoilIds)); + ObjectMapper objectMapper = new ObjectMapper(); String contentJson = objectMapper.writeValueAsString(contentMap); - + WmsGenerateRecordBo recordBo = new WmsGenerateRecordBo(); recordBo.setContent(contentJson); recordBo.setSerialNumber(enterCoilNo + "-" + mergedCoilBo.getCurrentCoilNo()); recordBo.setQrcodeType(0L); recordBo.setIsEnabled(0L); recordBo.setSize(200L); - + WmsGenerateRecordVo record = generateRecordService.insertByBo(recordBo); return record.getRecordId(); } catch (Exception e) { @@ -598,14 +674,14 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { if (record == null) { throw new RuntimeException("二维码记录不存在"); } - + // 解析现有content ObjectMapper objectMapper = new ObjectMapper(); Map contentMap = objectMapper.readValue(record.getContent(), Map.class); - + // 更新coilId contentMap.put("coil_id", String.valueOf(coilId)); - + // 更新二维码记录 String newContentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo(); @@ -616,7 +692,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { throw new RuntimeException("更新二维码coilId失败: " + e.getMessage()); } } - + /** * 更新二维码内容(单个更新) */ @@ -624,24 +700,24 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { try { // 获取原钢卷信息 WmsMaterialCoil oldCoil = baseMapper.selectById(bo.getCoilId()); - + // 获取原二维码记录 WmsGenerateRecordVo oldRecord = generateRecordService.queryById(qrcodeRecordId); if (oldRecord == null) { throw new RuntimeException("二维码记录不存在"); } - + // 解析现有content ObjectMapper objectMapper = new ObjectMapper(); Map contentMap = objectMapper.readValue(oldRecord.getContent(), Map.class); - + // 获取现有steps @SuppressWarnings("unchecked") List> steps = (List>) contentMap.get("steps"); if (steps == null) { steps = new ArrayList<>(); } - + // 添加新的step,记录钢卷号的变化 Map newStep = new HashMap<>(); newStep.put("step", steps.size() + 1); @@ -649,7 +725,8 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { newStep.put("old_current_coil_no", oldCoil.getCurrentCoilNo()); // 原当前钢卷号 newStep.put("new_current_coil_no", bo.getCurrentCoilNo()); // 新当前钢卷号 newStep.put("coil_id", String.valueOf(bo.getCoilId())); // 钢卷ID - + newStep.put("operator", LoginHelper.getUsername()); // 操作者 + // 判断操作类型 if (bo.getHasMergeSplit() != null && bo.getHasMergeSplit() == 2) { newStep.put("operation", "合卷"); @@ -660,12 +737,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } else { newStep.put("operation", "更新"); } - + steps.add(newStep); contentMap.put("steps", steps); // 更新当前钢卷号到最外层(方便快速查看) contentMap.put("current_coil_no", bo.getCurrentCoilNo()); - + // 更新二维码记录 String newContentJson = objectMapper.writeValueAsString(contentMap); WmsGenerateRecordBo updateBo = new WmsGenerateRecordBo(); @@ -676,7 +753,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { throw new RuntimeException("更新二维码失败: " + e.getMessage()); } } - + /** * 保存前的数据校验 @@ -707,89 +784,184 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { @Override public Map queryTrace(String enterCoilNo, String currentCoilNo) { try { - // 1. 根据入场钢卷号查询二维码记录 + // 1. 查询所有相关的二维码记录(包括分卷后的独立二维码) + List allQrRecords = new ArrayList<>(); + + // 首先查询主二维码(以入场钢卷号为序列号的) WmsGenerateRecordBo qrBo = new WmsGenerateRecordBo(); qrBo.setSerialNumber(enterCoilNo); - List qrRecords = generateRecordService.queryList(qrBo); + List mainQrRecords = generateRecordService.queryList(qrBo); + allQrRecords.addAll(mainQrRecords); - if (qrRecords.isEmpty()) { + // 然后查询所有以该入场钢卷号开头的二维码(分卷后的二维码) + WmsGenerateRecordBo splitQrBo = new WmsGenerateRecordBo(); + List allRecords = generateRecordService.queryList(splitQrBo); + for (WmsGenerateRecordVo record : allRecords) { + if (record.getSerialNumber() != null && + record.getSerialNumber().startsWith(enterCoilNo + "-") && + !allQrRecords.contains(record)) { + allQrRecords.add(record); + } + } + + if (allQrRecords.isEmpty()) { throw new RuntimeException("未找到对应的二维码记录"); } + + // 2. 合并所有二维码的steps信息,去重并重新编号 + Map> uniqueSteps = new HashMap<>(); // 用于去重 + Set allCoilNos = new HashSet<>(); - // 取第一个(应该只有一个) - WmsGenerateRecordVo qrRecord = qrRecords.get(0); - - // 2. 解析二维码content中的steps - ObjectMapper objectMapper = new ObjectMapper(); - @SuppressWarnings("unchecked") - Map contentMap = objectMapper.readValue(qrRecord.getContent(), Map.class); - - @SuppressWarnings("unchecked") - List> steps = (List>) contentMap.get("steps"); - - // 3. 从steps中提取所有钢卷号 - Set coilNos = new HashSet<>(); - if (steps != null) { - for (Map step : steps) { - // 提取各种可能的钢卷号字段 - extractCoilNo(step, "current_coil_no", coilNos); - extractCoilNo(step, "new_current_coil_no", coilNos); - extractCoilNo(step, "old_current_coil_no", coilNos); - extractCoilNo(step, "new_current_coil_nos", coilNos); - extractCoilNo(step, "merged_from", coilNos); - extractCoilNo(step, "parent_coil_nos", coilNos); - // 提取钢卷ID字段 - extractCoilId(step, "coil_id", coilNos); - extractCoilId(step, "old_coil_id", coilNos); - extractCoilId(step, "parent_coil_ids", coilNos); + for (WmsGenerateRecordVo qrRecord : allQrRecords) { + ObjectMapper objectMapper = new ObjectMapper(); + @SuppressWarnings("unchecked") + Map contentMap = objectMapper.readValue(qrRecord.getContent(), Map.class); + + @SuppressWarnings("unchecked") + List> steps = (List>) contentMap.get("steps"); + + if (steps != null) { + for (Map step : steps) { + // 创建唯一标识:操作类型 + 相关钢卷号 + String stepKey = createStepKey(step); + + // 如果是新的步骤,添加到唯一步骤集合中 + if (!uniqueSteps.containsKey(stepKey)) { + Map uniqueStep = new HashMap<>(step); + uniqueStep.put("qrcode_serial", qrRecord.getSerialNumber()); + uniqueStep.put("qrcode_id", qrRecord.getRecordId()); + uniqueSteps.put(stepKey, uniqueStep); + } + + // 提取钢卷号 + extractCoilNo(step, "current_coil_no", allCoilNos); + extractCoilNo(step, "new_current_coil_no", allCoilNos); + extractCoilNo(step, "old_current_coil_no", allCoilNos); + extractCoilNo(step, "new_current_coil_nos", allCoilNos); + extractCoilNo(step, "merged_from", allCoilNos); + extractCoilNo(step, "parent_coil_nos", allCoilNos); + extractCoilId(step, "coil_id", allCoilNos); + extractCoilId(step, "old_coil_id", allCoilNos); + extractCoilId(step, "parent_coil_ids", allCoilNos); + } } } - // 4. 如果指定了当前钢卷号,过滤出相关的钢卷号 - Set filteredCoilNos = coilNos; + // 转换为列表并按原始步骤号排序(保持时间顺序) + List> allSteps = new ArrayList<>(uniqueSteps.values()); + + // 按原始步骤号排序,保持实际操作的时间顺序 + allSteps.sort((a, b) -> { + Integer stepA = (Integer) a.get("step"); + Integer stepB = (Integer) b.get("step"); + if (stepA == null) stepA = 0; + if (stepB == null) stepB = 0; + return stepA.compareTo(stepB); + }); + + // 重新编号(保持连续性) + for (int i = 0; i < allSteps.size(); i++) { + allSteps.get(i).put("display_step", i + 1); + // 保留原始步骤号用于调试 + allSteps.get(i).put("original_step", allSteps.get(i).get("step")); + } + + // 3. 如果指定了当前钢卷号,过滤出相关的钢卷号 + Set filteredCoilNos = allCoilNos; if (currentCoilNo != null && !currentCoilNo.trim().isEmpty()) { - final String filterValue = currentCoilNo; // 用于lambda的final变量 - filteredCoilNos = coilNos.stream() + final String filterValue = currentCoilNo; + filteredCoilNos = allCoilNos.stream() .filter(coilNo -> coilNo.contains(filterValue)) .collect(Collectors.toSet()); } - - // 5. 根据提取的钢卷号反向查询数据库 + + // 4. 根据提取的钢卷号反向查询数据库 List result = new ArrayList<>(); if (!filteredCoilNos.isEmpty()) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo); - + // 查询包含提取出的钢卷号的记录 - final Set finalCoilNos = filteredCoilNos; // 用于lambda的final变量 + final Set finalCoilNos = filteredCoilNos; lqw.and(wrapper -> { int count = 0; for (String coilNo : finalCoilNos) { if (count == 0) { - wrapper.like(WmsMaterialCoil::getCurrentCoilNo, coilNo); + wrapper.eq(WmsMaterialCoil::getCurrentCoilNo, coilNo); } else { - wrapper.or().like(WmsMaterialCoil::getCurrentCoilNo, coilNo); + wrapper.or().eq(WmsMaterialCoil::getCurrentCoilNo, coilNo); } count++; } }); - + lqw.orderByAsc(WmsMaterialCoil::getCreateTime); result = baseMapper.selectVoList(lqw); + + // 填充每个记录的关联对象信息(如库区) + for (WmsMaterialCoilVo vo : result) { + fillRelatedObjects(vo); + } } - // 6. 构建返回结果 + // 如果没有找到记录,尝试查询所有相关的钢卷(包括历史数据) + if (result.isEmpty()) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(WmsMaterialCoil::getEnterCoilNo, enterCoilNo); + lqw.orderByAsc(WmsMaterialCoil::getCreateTime); + result = baseMapper.selectVoList(lqw); + + // 填充每个记录的关联对象信息 + for (WmsMaterialCoilVo vo : result) { + fillRelatedObjects(vo); + } + } + + // 5. 构建返回结果 Map resultMap = new HashMap<>(); - resultMap.put("qrcode", qrRecord); - resultMap.put("steps", steps); - resultMap.put("records", result); - + resultMap.put("qrcode", allQrRecords.get(0)); // 主二维码 + resultMap.put("all_qrcodes", allQrRecords); // 所有相关二维码 + resultMap.put("steps", allSteps); // 所有步骤 + resultMap.put("records", result); // 所有钢卷记录 + return resultMap; } catch (Exception e) { throw new RuntimeException("溯源查询失败: " + e.getMessage()); } } + + /** + * 创建步骤唯一标识 + */ + private String createStepKey(Map step) { + StringBuilder keyBuilder = new StringBuilder(); + + // 使用操作类型和主要标识符创建唯一key + String operation = (String) step.get("operation"); + keyBuilder.append(operation).append("-"); + + // 根据操作类型使用不同的标识符 + if ("分卷".equals(operation)) { + // 分卷:使用原钢卷号 + 分卷列表 + keyBuilder.append(step.get("old_current_coil_no")).append("->"); + keyBuilder.append(step.get("new_current_coil_nos")); + } else if ("合卷".equals(operation)) { + // 合卷:使用父钢卷列表 + 新钢卷号 + keyBuilder.append(step.get("parent_coil_nos")).append("->"); + keyBuilder.append(step.get("new_current_coil_no")); + } else if ("新增".equals(operation)) { + // 新增:使用当前钢卷号 + keyBuilder.append(step.get("current_coil_no")); + } else { + // 其他更新:使用原钢卷号 -> 新钢卷号 + keyBuilder.append(step.get("old_current_coil_no")).append("->"); + keyBuilder.append(step.get("new_current_coil_no")); + } + + return keyBuilder.toString(); + } + /** * 从step中提取钢卷号 */ @@ -808,7 +980,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService { } } } - + /** * 从step中提取钢卷ID */ diff --git a/script/sql/mysql/stock.sql b/script/sql/mysql/stock.sql index 4c2d2ba8..35b43501 100644 --- a/script/sql/mysql/stock.sql +++ b/script/sql/mysql/stock.sql @@ -1,5 +1,6 @@ DROP TABLE IF EXISTS `wms_stock`; +DROP TABLE IF EXISTS `wms_material_coil`; create table wms_stock (