Compare commits

...

75 Commits

Author SHA1 Message Date
75a2623d8b config: 更新生产环境和预发布环境页面标题
- 将生产环境页面标题从"科伦普冷轧涂镀数智运营一体化平台"改为"MES一体化平台"
- 将预发布环境页面标题从"科伦普冷轧涂镀数智运营一体化平台"改为"MES一体化平台"
2026-03-09 16:58:25 +08:00
5d046be15b docs(app): 更新应用名称为MES一体化平台
- 将VUE_APP_TITLE从"科伦普冷轧涂镀数智运营一体化平台"更改为"MES一体化平台"
- 修改Greeting组件中的平台描述为"欢迎使用MES数智一体化平台"
- 将favicon链接从png格式更改为ico格式
- 替换index.vue中的关于页面内容为MES平台相关介绍
- 更新登录页面中的平台描述和公司信息
- 修改侧边栏Logo组件中的平台标题为"MES一体化平台"
- 注释掉dashboard demo组件中的全屏功能代码
2026-03-09 16:27:47 +08:00
砂糖
fef2f132d0 feat(wms): 添加冷硬卷板切边统计和类别宽度统计功能
- 新增冷硬卷板切边统计页面和API接口
- 新增类别宽度统计页面和API接口
- 修改发货单页面,调整列显示
- 扩展统计预览功能,支持多种统计类型展示
2026-03-09 16:07:07 +08:00
砂糖
1038b17a66 feat(标签打印): 添加镀铬卷标签类型支持
新增镀铬卷(DuGeTag)标签组件,包含对应的样式和布局
在do.vue和LabelRender/index.vue中增加镀铬卷类型判断逻辑
调整标签尺寸配置以适配镀铬卷标签规格
2026-03-09 15:24:04 +08:00
砂糖
bd5e0ac5e9 style(ZincRawTag): 调整标签样式和二维码大小
增加标签容器的内边距和值单元格的字体加粗
设置二维码组件的大小为80px
2026-03-09 14:52:58 +08:00
896c6b89ca Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-09 14:10:23 +08:00
a8e94ac34d feat(WmsMaterialCoilController): 更新统计接口URL路径
- 将trimStatistics接口路径从/trimStatistics修改为/statistics/trimStatistics
- 将categoryWidthStatistics接口路径从/categoryWidthStatistics修改为/statistics/categoryWidthStatistics
- 统一统计类接口的URL命名空间,增强API路径的一致性
2026-03-09 14:10:14 +08:00
砂糖
f389576c92 feat(标签打印): 增加镀锌原料标签类型并优化标签选择逻辑
- 在LabelRender组件中新增类型5的标签尺寸配置
- 修改ZincRawTag组件中下工序默认值为'镀锌卷'
- 在do.vue中新增标签尺寸映射并实现根据物料类型和仓库自动选择标签类型
2026-03-09 13:24:16 +08:00
砂糖
a2b90f3b72 Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-09 13:06:33 +08:00
砂糖
693edbb543 feat(wms): 新增热轧原料库存透视功能及相关组件
添加热轧原料库存透视表API接口
创建透视表展示组件HotZhaRaw.vue和Perspective.vue
实现数据持久化功能及左右布局管理界面
优化锌卷标签显示样式及字段名称
2026-03-09 13:06:30 +08:00
194c481e12 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-09 10:46:54 +08:00
0050af7677 feat(wms): 添加钢卷类别统计——汇总统计
- 新增 CategoryWidthRawVo 和 CategoryWidthStatisticsVo 数据传输对象
- 在 IWmsMaterialCoilService 中添加 getCategoryWidthStatistics 方法定义
- 实现 WmsMaterialCoilController 的 categoryWidthStatistics 接口
- 添加 WmsMaterialCoilMapper 的 selectCategoryWidthStatistics 查询方法
- 实现 WmsMaterialCoilServiceImpl 中的 getCategoryWidthStatistics 业务逻辑
- 在 XML 映射文件中添加类别宽度统计的 SQL 查询语句
2026-03-09 10:46:22 +08:00
砂糖
d3da84f65e fix: 修复产线能力查询参数错误及调整分页默认值
修复产线能力查询时lineId参数传递错误的问题,增加参数校验
调整分页组件默认的pageSizes选项,增加10的选项
修改物料线圈查询状态参数为0
优化线圈吞吐记录展示方式,改为行点击触发
移除用餐记录中不必要的部门字典依赖
2026-03-09 10:34:05 +08:00
砂糖
c532d7f753 feat(标签打印): 添加镀锌原料标签组件及显示逻辑
新增 ZincRawTag 组件用于显示镀锌原料标签,并在特定仓库条件下自动选择该标签类型。标签包含冷卷号、热卷号、规格等字段,并支持二维码显示和打印适配。
2026-03-09 10:33:17 +08:00
7736ac3311 完成排产(测试过了) 2026-03-08 16:02:44 +08:00
b660ddcc3e refactor(WmsMaterialCoilService): 移除废弃的厚度分组统计逻辑
- 删除了基于修剪需求的厚度分组统计代码
- 移除了 CoilTrimStatisticsVo 和 TrimWidthStatisticsVo 的构建逻辑
- 保留了净边和毛边列表的获取功能
- 简化了数据处理流程并提高代码可读性
2026-03-07 15:57:42 +08:00
e6c588af63 feat(wms): 添加钢卷生产统计汇总持久化功能
- 创建了WmsCoilStatisticsSummary实体类用于数据持久化
- 定义了IWmsCoilStatisticsSummaryService服务接口及其实现
- 开发了WmsCoilStatisticsSummaryController控制器提供REST API
- 实现了数据查询、新增、修改、删除等基本CRUD操作
- 集成了分页查询和导出Excel功能
- 添加了数据验证和业务逻辑处理机制
- 配置了MyBatis映射文件和数据库表结构映射
2026-03-07 15:43:54 +08:00
7a78ad3eb7 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-07 14:37:25 +08:00
db379748f2 feat(wms): 新增第三个报表统计——冷硬卷统计
- 在IWmsMaterialCoilService中添加getCoilTrimStatistics方法
- 在WmsMaterialCoilController中新增trimStatistics接口
- 在WmsMaterialCoilMapper中添加selectCoilTrimStatistics查询方法
- 在WmsMaterialCoilMapper.xml中实现切边统计数据SQL查询
- 在WmsMaterialCoilServiceImpl中实现切边统计业务逻辑
- 新增CoilTrimRawVo、CoilTrimStatisticsVo和TrimWidthStatisticsVo数据传输对象
- 按厚度、宽度、切边要求(净边/毛边)统计钢卷数量和重量
- 实现数据分组和排序功能,支持前端展示需求
2026-03-07 14:37:12 +08:00
砂糖
44013170f5 feat(wms/coil): 添加业务目的和关联订单功能
在基础面板中添加业务目的下拉选择和关联订单开关功能,并创建两个新的视图组件分别展示这些功能。同时更新字典数据以支持业务目的选项。
2026-03-07 14:18:30 +08:00
e5acc06e1c feat(WmsRawMaterialMapper): 添加原材料查询过滤条件
- 在左连接中增加data_type和status条件过滤
- 添加原材料名称模糊匹配查询功能
- 优化查询逻辑以提高数据筛选准确性
2026-03-07 11:39:37 +08:00
71159a496b feat(domain): 添加材料卷材业务用途和订单关联字段
- 在 WmsMaterialCoil 实体类中新增 businessPurpose 和 isRelatedToOrder 字段
- 在 WmsMaterialCoilBo 业务对象中同步添加对应字段
- 在 WmsMaterialCoilVo 视图对象中添加相应属性定义
- 更新 MyBatis 映射文件中的查询字段和条件过滤逻辑
- 实现在查询条件中支持按业务用途和订单关联状态进行筛选
2026-03-07 11:28:39 +08:00
141d0a1c4e Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-07 10:29:46 +08:00
e5147564ce feat(wms): 历史钢卷不允许占用实际库区
- 实现历史钢卷(dataType=0)禁止修改实际库区的业务规则
- 当原钢卷为历史钢卷时阻止实际库区ID的更新操作
- 添加置空(-1)判断避免误操作历史钢卷库区占用
- 抛出运行时异常提示历史钢卷不允许占用实际库区
2026-03-07 10:29:36 +08:00
砂糖
273d7c44b7 feat(wms/coil): 新增按名称查询钢卷功能并优化产品信息展示
添加镀锌卷、热轧卷、冷轧卷、冷硬卷的按名称查询页面组件
在产品信息和原材料信息展示中增加镀层信息显示
移除base.vue中未使用的BomInfoMini组件
优化DrillDownTable.vue的代码结构
新增ByName.vue作为通用查询面板
2026-03-07 10:24:28 +08:00
砂糖
4fdddac389 feat(wms): 新增员工报餐日历页面
添加员工报餐日历功能页面,包含月份切换、餐型和部门筛选功能,展示每日报餐人数统计
2026-03-06 17:22:18 +08:00
砂糖
3afac814c4 Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-06 15:23:18 +08:00
砂糖
cbfea329f0 fix(wms): 修复钢卷入库时库区操作记录未正确创建的问题
当钢卷实际库区存在时,确保正确创建库区操作记录并添加入库备注
2026-03-06 15:23:12 +08:00
5b0e35ce03 fix(wms): 修复钢卷发货出库操作日志记录逻辑
- 调整操作日志记录位置,确保仅在仓库更新成功时才记录日志
- 添加oldActualWarehouseId非空检查条件
- 优化代码执行流程,避免无效的日志记录操作
2026-03-06 14:57:01 +08:00
砂糖
e3727abebf fix(wms): 调整运单表格列宽以匹配Web端显示
修改Excel导出和页面表格的列宽配置,使两者显示一致。主要调整了第2列和第5列的宽度。
2026-03-06 14:41:48 +08:00
砂糖
a024be0858 feat(wms): 添加钢卷吞吐记录功能及优化日志查询
- 新增根据钢卷ID查询操作记录的API接口
- 在钢卷管理页面添加吞吐记录查看功能
- 优化日志表格组件,支持按业务类型和出入库类型筛选
- 增加日志删除功能
- 调整ActualWarehouseSelect组件支持清除输入
2026-03-06 13:04:17 +08:00
bc99d985f5 feat(wms): 添加按钢卷ID查询操作记录功能
- 在 IWmsCoilWarehouseOperationLogService 中新增 queryByCoilId 方法
- 在 WmsCoilWarehouseOperationLogController 中新增 /byCoilId 接口
- 在 WmsCoilWarehouseOperationLogServiceImpl 中实现按钢卷ID查询逻辑
- 实现关联查询钢卷信息和库区信息的功能
- 支持按操作类型、出入库类型、时间范围进行筛选
- 将原有仓库ID查询接口的参数改为可选参数
2026-03-06 11:41:07 +08:00
1f6b3a9ace fix(wms): 修复钢卷库区绑定清理逻辑
- 移除注释掉的库区绑定清理代码
- 将库区绑定清理逻辑移到新记录插入后执行
- 添加对实际库区ID为-1的条件判断避免无效操作
- 修复clearActualWarehouseBinding方法中的空指针检查逻辑
2026-03-06 11:17:30 +08:00
7bb102a17f fix(wms): 优化仓库操作日志查询和钢卷库区绑定逻辑
- 修改查询条件,当operationType和inOutType为空时不添加到查询条件中
- 简化钢卷库区变更的日志记录逻辑,移除-1置空的特殊处理分支
- 注释掉实际库区ID为-1时清空钢卷库区绑定的相关代码
- 移除加工操作中对-1值的特殊处理,统一入库操作逻辑
- 在合卷操作中增加对实际库区ID不等于-1的判断条件
2026-03-06 11:00:36 +08:00
78a7bb4123 feat(wms): 扩展钢卷仓库操作日志查询功能
- 在服务接口中增加操作类型和出入库类型参数
- 在控制器层添加相应的请求参数映射
- 在服务实现类中修改查询方法以支持动态过滤条件
- 更新查询逻辑将固定值替换为动态传入的操作类型和出入库类型
- 保持原有的时间范围查询功能并增强其灵活性
2026-03-06 10:21:02 +08:00
0edeb429c6 feat(wms): 添加退货操作的加工日志记录功能
- 在退货流程中增加加工日志记录逻辑
- 当老的实际库区存在时自动添加加工出库记录
- 使用operationType=2标识退货操作类型
- 记录退货操作的仓库作业日志信息
2026-03-06 10:17:33 +08:00
f9c192a5b4 feat(wms): 添加钢卷操作日志记录功能
- 在简单更新操作中添加调拨日志记录,区分入库和出库操作
- 在单个更新操作中添加加工日志记录,处理钢卷ID变化的情况
- 在分卷操作中添加加工日志记录,为原钢卷和子钢卷分别记录出入库
- 在合卷操作中添加加工日志记录,为参与合卷的钢卷记录出入库
- 在单步分卷完成操作中添加加工日志记录,确保完整流程追踪
- 根据实际库区变化判断是否需要记录操作日志,避免重复记录
2026-03-06 10:09:49 +08:00
88741bc062 feat(project): 添加 klp-aps 模块并配置依赖关系
- 在父项目 pom.xml 中添加 klp-aps 模块依赖
- 新增 klp-aps 子模块的 pom.xml 配置文件
- 在主 pom.xml 中将 klp-aps 添加到模块列表
- 配置 klp-aps 模块继承父项目的版本信息
- 添加 lombok 和 klp-common 作为 klp-aps 的依赖项
2026-03-05 18:13:19 +08:00
17e6f793c7 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-05 17:28:27 +08:00
砂糖
f6c60226ba refactor(wms): 移除钢卷号重复校验并替换部门选择组件
移除多个组件中关于钢卷号重复的远程校验逻辑,改为本地校验
将部门选择从DictSelect替换为el-select组件,直接从API获取部门数据
2026-03-05 17:28:13 +08:00
b257afdb6b refactor(WmsMaterialCoilService): 移除钢卷号重复检查逻辑和步骤标识创建方法
- 删除了 createStepKey 方法,移除了基于操作类型的步骤唯一标识生成功能
- 注释掉了钢卷回滚操作中的钢卷号重复检查逻辑
- 移除了分卷、合卷、新增等操作的重复性验证代码
- 简化了子钢卷创建时的重复性检查流程
2026-03-05 17:26:20 +08:00
砂糖
9e24368d4c feat(仓库管理): 新增钢卷库区操作日志功能
添加钢卷库区操作日志记录功能,包括:
1. 在入库操作时自动记录日志
2. 新增日志查询API接口
3. 实现日志查看页面和表格组件
4. 处理拒签和删除操作时的日志清理
2026-03-05 17:19:48 +08:00
428e94d4f9 refactor(WmsCoilWarehouseOperationLogService): 优化仓库ID获取逻辑
- 添加空值检查避免空指针异常
- 修改查询策略减少数据库访问次数
- 使用流式处理和分组收集子仓库ID
- 实现队列遍历算法提高查询效率
- 优化内存使用减少不必要的对象创建
2026-03-05 16:36:44 +08:00
4a11ae2079 feat(wms): 实现钢卷列表按发货状态排序功能
- 在WmsMaterialCoilBo中新增statusFirst字段用于控制排序逻辑
- 在WmsDeliveryWaybillDetailController中设置statusFirst为true以启用新排序
- 在WmsMaterialCoilServiceImpl中实现按发货状态排序的SQL逻辑
- 未发货钢卷(status=0)将排在已发货钢卷之前显示
- 保持原有的仓库ID和创建时间排序规则作为备选方案
2026-03-05 16:30:18 +08:00
e8b40d2fa3 refactor(wms): 将仓库操作日志服务中的仓库实体替换为实际仓库实体
- 替换 WmsWarehouse 为 WmsActualWarehouse 相关类
- 更新 WmsActualWarehouseMapper 的依赖注入
- 修改查询条件中仓库ID字段的映射关系
- 调整子仓库ID获取逻辑以适配新的实体结构
- 更新VO类中仓库属性的数据类型定义
2026-03-05 15:38:50 +08:00
f30fd06dc4 更新sql 2026-03-05 15:31:38 +08:00
86812a655b refactor(wms): 移除未使用的实体映射器依赖
- 注释掉 WmsDeliveryPlanMapper 的注入
- 注释掉 WmsProductMapper 的注入
- 注释掉 WmsRawMaterialMapper 的注入
- 保持其他依赖注入不变
- 减少类的不必要的依赖项
2026-03-05 13:56:43 +08:00
0e6357253a feat(wms): 添加钢卷库区操作日志记录功能
- 在WmsMaterialCoilServiceImpl中注入WmsCoilWarehouseOperationLogMapper
- 在钢卷发货出库操作后添加操作日志记录功能
- 新增recordWarehouseOperationLog方法用于记录库区操作日志
- 创建CoilWarehouseOperationLog注解用于标记需要记录操作日志的方法
- 实现CoilWarehouseOperationLogAspect切面自动记录操作日志
- 支持通过注解配置操作类型、出入库方向和备注信息
- 提供参数名映射功能自动获取钢卷ID和库区ID参数值
2026-03-05 13:55:38 +08:00
612fd8bf12 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-05 13:07:10 +08:00
625ef6330b feat(wms): 添加钢卷库区操作记录的查询和删除功能
- 新增根据钢卷ID、操作类型和出入库类型删除操作记录的方法
- 新增根据二级库区ID和时间范围查询操作记录的功能
- 在WmsCoilWarehouseOperationLogVo中添加创建时间和关联对象字段
- 实现递归查询子库区ID的功能支持范围查询
- 添加钢卷和库区信息的关联查询和映射
- 在控制器中暴露新的API接口供前端调用
2026-03-05 13:07:01 +08:00
砂糖
3e343f5a07 Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-05 11:15:36 +08:00
砂糖
15e59c10da fix(wms): 修正合卷操作状态查询参数并添加绑定钢卷列表功能
将actionStatus从0改为-1以正确查询不为2的记录
添加listBoundCoil接口用于查询已绑定发货的钢卷列表
在基础面板中显示单据状态标签并优化仓库查询逻辑
2026-03-05 11:15:34 +08:00
04de7e267c feat(wms): 添加钢卷库区操作记录管理功能
- 创建钢卷库区操作记录实体类WmsCoilWarehouseOperationLog
- 定义服务接口IWmsCoilWarehouseOperationLogService及其实现
- 实现控制器WmsCoilWarehouseOperationLogController提供CRUD操作
- 添加数据传输对象WmsCoilWarehouseOperationLogBo和WmsCoilWarehouseOperationLogVo
- 配置MyBatis映射器WmsCoilWarehouseOperationLogMapper及XML映射文件
- 实现出入库操作类型和方向的业务字段定义
- 集成分页查询、导出Excel等功能
2026-03-05 11:12:44 +08:00
ffc42d110a fix(wms): 修复库区状态更新逻辑
- 修改实际库区启用状态更新方法的参数传递
- 将旧库区ID设置为null以避免不必要的状态变更
- 确保只有新库区ID有效时才执行状态更新操作
2026-03-05 10:05:46 +08:00
dfd2ba15d9 feat(delivery): 添加查询已绑定钢卷列表功能
- 在 IWmsDeliveryWaybillDetailService 中新增 getBoundCoilIds 方法
- 在 WmsDeliveryWaybillDetailController 中新增 boundCoilList 接口
- 实现 WmsDeliveryWaybillDetailServiceImpl 的 getBoundCoilIds 查询逻辑
- 集成 WmsMaterialCoilService 查询已发货绑定的钢卷信息
- 添加钢卷 ID 去重处理确保数据准确性
- 支持分页查询返回 TableDataInfo 格式数据
2026-03-05 09:57:00 +08:00
28839275d2 fix(wms): 修复待处理操作查询状态条件判断逻辑
- 修改了 action_status 查询条件的处理方式
- 添加了对 action_status 为 -1 的特殊处理逻辑
- 当 action_status 为 -1 时执行不等于 2 的查询条件
- 其他情况继续使用相等查询条件
- 修复了原有的状态过滤异常问题
2026-03-05 09:55:44 +08:00
0b5fe00d94 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-05 09:36:00 +08:00
5c6a3c4981 refactor(WmsMaterialCoilService): 简化库区状态更新条件并优化代码注释
- 移除库区ID比较条件,仅检查新库区ID是否为空
- 更新实际库区启用状态的方法调用逻辑
- 添加关于退货操作的详细注释说明
- 优化代码可读性和维护性
2026-03-05 09:35:51 +08:00
砂糖
8659847e96 fix(wms报表): 为待办动作列表添加actionStatus参数
统一为所有报表页面的待办动作列表请求添加actionStatus=2参数,确保查询结果的一致性
2026-03-05 09:34:30 +08:00
砂糖
721c48d6a4 style(wms): 调整表单和表格的布局及样式
- 减少表单标签宽度和表格列宽
- 移除出口长度列
- 调整主内容区网格布局宽度
- 优化样式细节
2026-03-04 21:47:35 +08:00
砂糖
d601dc8320 Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-04 17:29:54 +08:00
砂糖
ee05f031a4 fix(wms): 注释掉加载历史记录的方法调用
避免在加载钢卷信息时不必要的历史记录查询,提高页面响应速度
2026-03-04 17:29:49 +08:00
2bfecfbbb2 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-04 16:54:43 +08:00
f6681b73af fix(wms): 修复钢卷合并拆分回滚时二维码状态管理问题
- 在合卷回滚操作中恢复原始钢卷的二维码为生效状态并更新相关记录
- 在拆卷回滚操作中恢复母卷的二维码为生效状态并更新相关记录
- 修改updateQrcodeForMergeRollback方法参数以正确传递恢复的钢卷ID
- 更新合卷回滚步骤中的钢卷ID记录逻辑以准确追踪操作流程
- 在钢卷拆分操作中将母卷的二维码标记为失效状态确保数据一致性
2026-03-04 16:54:32 +08:00
砂糖
03bf7f7398 Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-04 16:15:16 +08:00
砂糖
341606c051 feat(wms): 新增发货单据相关展示列及状态标签
在基础面板组件中添加发货计划、发货单据和发货状态的展示列
新增showWaybill属性控制相关列的显示
2026-03-04 16:15:13 +08:00
8b47e91598 refactor(wms): 修改钢卷溯源查询方法参数和优化查询逻辑
- 将溯源查询方法参数从入场钢卷号改为钢卷ID
- 通过钢卷ID直接获取钢卷信息和关联的二维码记录
- 移除原有的复杂二维码查询逻辑,简化为直接通过钢卷ID获取
- 优化钢卷记录查询方式,移除重复和不必要的查询步骤
- 简化溯源步骤处理逻辑,移除重复步骤合并功能
- 调整返回结果结构,精简二维码相关信息
- 优化数据库查询性能,使用更直接的关联查询方式
2026-03-04 16:09:32 +08:00
砂糖
f26960e4c7 fix(wms): 将材料卷追踪参数从enterCoilNo改为coilId
使用coilId作为追踪参数更准确,避免使用已弃用的enterCoilNo字段
2026-03-04 15:35:58 +08:00
8d9613a350 Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-04 15:05:01 +08:00
181f0726d8 fix(wms): 解决钢卷回滚操作中的独占状态检查问题
- 添加最后一步操作信息解析功能,用于检查独占状态
- 实现分卷操作的独占状态验证,防止母卷正在进行分卷时回滚
- 修复分卷回滚时子钢卷ID匹配逻辑
- 优化二维码内容解析,支持多步骤操作回滚
- 添加分卷回滚前的子钢卷有效性检查
2026-03-04 15:04:51 +08:00
砂糖
9ed926e4ce Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X 2026-03-04 14:02:28 +08:00
砂糖
261f55dded feat(钢卷管理): 在多个页面添加当前钢卷号显示组件
为提升用户体验,在钢卷分切、合并、录入等页面统一添加current-coil-no组件,用于显示当前钢卷号信息
2026-03-04 14:02:25 +08:00
砂糖
a4b77f9654 feat(发货单明细): 添加实际库区显示并优化数据加载逻辑
修改发货单明细表格,新增实际库区列显示。同时将数据加载逻辑改为异步方式,并添加卷号对应的实际库区信息查询功能。移除数量列的显示以简化界面。
2026-03-04 13:55:43 +08:00
66bb295acb Merge remote-tracking branch 'origin/0.8.X' into 0.8.X 2026-03-04 11:01:41 +08:00
8a540096f5 feat(wms): 添加分卷操作时间记录功能
- 在分卷步骤中添加 create_time 字段记录操作时间
- 使用 DateUtils.getNowDate() 获取当前时间戳
- 确保分卷流程中的时间信息完整记录
2026-03-04 11:01:30 +08:00
220 changed files with 18127 additions and 1831 deletions

View File

@@ -119,6 +119,10 @@
<groupId>com.klp</groupId>
<artifactId>klp-reader</artifactId>
</dependency>
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-aps</artifactId>
</dependency>
<dependency>

25
klp-aps/pom.xml Normal file
View File

@@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.klp</groupId>
<artifactId>klp-oa</artifactId>
<version>0.8.3</version>
</parent>
<artifactId>klp-aps</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-common</artifactId>
</dependency>
<dependency>
<groupId>com.klp</groupId>
<artifactId>klp-wms</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,93 @@
package com.klp.aps.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.aps.domain.bo.ApsCalendarBo;
import com.klp.aps.domain.vo.ApsCalendarVo;
import com.klp.aps.service.ApsCalendarService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 工厂日历Controller
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/calendar")
public class ApsCalendarController extends BaseController {
private final ApsCalendarService apsCalendarService;
/**
* 查询工厂日历列表
*/
@GetMapping("/list")
public TableDataInfo<ApsCalendarVo> list(ApsCalendarBo bo, PageQuery pageQuery) {
return apsCalendarService.queryPageList(bo, pageQuery);
}
/**
* 导出工厂日历列表
*/
@Log(title = "工厂日历", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ApsCalendarBo bo, HttpServletResponse response) {
List<ApsCalendarVo> list = apsCalendarService.queryList(bo);
ExcelUtil.exportExcel(list, "工厂日历", ApsCalendarVo.class, response);
}
/**
* 获取工厂日历详细信息
*/
@GetMapping("/{calendarId}")
public R<ApsCalendarVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long calendarId) {
return R.ok(apsCalendarService.queryById(calendarId));
}
/**
* 新增工厂日历
*/
@Log(title = "工厂日历", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ApsCalendarBo bo) {
return toAjax(apsCalendarService.insertByBo(bo));
}
/**
* 修改工厂日历
*/
@Log(title = "工厂日历", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ApsCalendarBo bo) {
return toAjax(apsCalendarService.updateByBo(bo));
}
/**
* 删除工厂日历
*/
@Log(title = "工厂日历", businessType = BusinessType.DELETE)
@DeleteMapping("/{calendarIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] calendarIds) {
return toAjax(apsCalendarService.deleteWithValidByIds(Arrays.asList(calendarIds), true));
}
}

View File

@@ -0,0 +1,103 @@
package com.klp.aps.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.aps.domain.bo.ApsCalendarShiftBo;
import com.klp.aps.domain.vo.ApsCalendarShiftVo;
import com.klp.aps.service.ApsCalendarShiftService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 日历班次配置Controller
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/calendar-shift")
public class ApsCalendarShiftController extends BaseController {
private final ApsCalendarShiftService apsCalendarShiftService;
/**
* 查询日历班次配置列表
*/
@GetMapping("/list")
public TableDataInfo<ApsCalendarShiftVo> list(ApsCalendarShiftBo bo, PageQuery pageQuery) {
return apsCalendarShiftService.queryPageList(bo, pageQuery);
}
/**
* 导出日历班次配置列表
*/
@Log(title = "日历班次配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ApsCalendarShiftBo bo, HttpServletResponse response) {
List<ApsCalendarShiftVo> list = apsCalendarShiftService.queryList(bo);
ExcelUtil.exportExcel(list, "日历班次配置", ApsCalendarShiftVo.class, response);
}
/**
* 获取日历班次配置详细信息
*/
@GetMapping("/{configId}")
public R<ApsCalendarShiftVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long configId) {
return R.ok(apsCalendarShiftService.queryById(configId));
}
/**
* 新增日历班次配置
*/
@Log(title = "日历班次配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ApsCalendarShiftBo bo) {
return toAjax(apsCalendarShiftService.insertByBo(bo));
}
/**
* 修改日历班次配置
*/
@Log(title = "日历班次配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ApsCalendarShiftBo bo) {
return toAjax(apsCalendarShiftService.updateByBo(bo));
}
/**
* 删除日历班次配置
*/
@Log(title = "日历班次配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] configIds) {
return toAjax(apsCalendarShiftService.deleteWithValidByIds(Arrays.asList(configIds), true));
}
/**
* 一键生成全年工作日日历
*/
@Log(title = "日历班次配置", businessType = BusinessType.INSERT)
@PostMapping("/generate-workday-year/{year}")
public R<Integer> generateWorkdayYear(@PathVariable Integer year,
@RequestParam(value = "overwriteExisting", required = false, defaultValue = "false") Boolean overwriteExisting) {
return R.ok(apsCalendarShiftService.generateWorkdayForYear(year, overwriteExisting));
}
}

View File

@@ -0,0 +1,45 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsGanttQueryReq;
import com.klp.aps.domain.vo.ApsFactoryCalendarRespVo;
import com.klp.aps.domain.vo.ApsGanttItemVo;
import com.klp.aps.service.ApsGanttService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* APS 甘特图查询MVP
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps")
public class ApsGanttController extends BaseController {
private final ApsGanttService apsGanttService;
/**
* GET /aps/gantt
* 参数lineId/queryStart/queryEnd/planId/orderId
*/
@GetMapping("/gantt")
public R<List<ApsGanttItemVo>> gantt(@Validated ApsGanttQueryReq req) {
return R.ok(apsGanttService.selectGanttItems(req));
}
/**
* 工厂日历聚合接口(减轻前端计算压力)
*/
@GetMapping("/factory-calendar")
public R<ApsFactoryCalendarRespVo> factoryCalendar(@Validated ApsGanttQueryReq req) {
return R.ok(apsGanttService.queryFactoryCalendar(req));
}
}

View File

@@ -0,0 +1,93 @@
package com.klp.aps.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.aps.domain.bo.ApsLineCapabilityBo;
import com.klp.aps.domain.vo.ApsLineCapabilityVo;
import com.klp.aps.service.ApsLineCapabilityService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 产线能力Controller
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/line-capability")
public class ApsLineCapabilityController extends BaseController {
private final ApsLineCapabilityService apsLineCapabilityService;
/**
* 查询产线能力列表
*/
@GetMapping("/list")
public TableDataInfo<ApsLineCapabilityVo> list(ApsLineCapabilityBo bo, PageQuery pageQuery) {
return apsLineCapabilityService.queryPageList(bo, pageQuery);
}
/**
* 导出产线能力列表
*/
@Log(title = "产线能力", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ApsLineCapabilityBo bo, HttpServletResponse response) {
List<ApsLineCapabilityVo> list = apsLineCapabilityService.queryList(bo);
ExcelUtil.exportExcel(list, "产线能力", ApsLineCapabilityVo.class, response);
}
/**
* 获取产线能力详细信息
*/
@GetMapping("/{capabilityId}")
public R<ApsLineCapabilityVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long capabilityId) {
return R.ok(apsLineCapabilityService.queryById(capabilityId));
}
/**
* 新增产线能力
*/
@Log(title = "产线能力", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ApsLineCapabilityBo bo) {
return toAjax(apsLineCapabilityService.insertByBo(bo));
}
/**
* 修改产线能力
*/
@Log(title = "产线能力", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ApsLineCapabilityBo bo) {
return toAjax(apsLineCapabilityService.updateByBo(bo));
}
/**
* 删除产线能力
*/
@Log(title = "产线能力", businessType = BusinessType.DELETE)
@DeleteMapping("/{capabilityIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] capabilityIds) {
return toAjax(apsLineCapabilityService.deleteWithValidByIds(Arrays.asList(capabilityIds), true));
}
}

View File

@@ -0,0 +1,44 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsLockReq;
import com.klp.aps.service.ApsLockService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* APS 锁定/解锁
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps")
public class ApsLockController extends BaseController {
private final ApsLockService apsLockService;
/**
* POST /aps/lock
*/
@PostMapping("/lock")
public R<Long> lock(@Validated @RequestBody ApsLockReq req) {
Long lockId = apsLockService.createLock(req, getUsername());
return R.ok(lockId);
}
/**
* POST /aps/lock/release/{lockId}
*/
@PostMapping("/lock/release/{lockId}")
public R<Void> release(@PathVariable Long lockId) {
apsLockService.releaseLock(lockId, getUsername());
return R.ok();
}
}

View File

@@ -0,0 +1,34 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsRescheduleReq;
import com.klp.aps.service.ApsOperationService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* APS 工序级操作(重排等)
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/operation")
public class ApsOperationController extends BaseController {
private final ApsOperationService apsOperationService;
/**
* POST /aps/operation/reschedule
*/
@PostMapping("/reschedule")
public R<Void> reschedule(@Validated @RequestBody ApsRescheduleReq req) {
apsOperationService.reschedule(req, getUsername());
return R.ok();
}
}

View File

@@ -0,0 +1,48 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsConvertFromCrmReq;
import com.klp.aps.domain.dto.ApsConvertFromCrmResp;
import com.klp.aps.domain.dto.ApsConvertFromProductReq;
import com.klp.aps.domain.dto.ApsConvertFromProductsReq;
import com.klp.aps.service.ApsPlanService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/order")
public class ApsOrderController extends BaseController {
private final ApsPlanService apsPlanService;
@PostMapping("/convert-from-crm")
public R<ApsConvertFromCrmResp> convertFromCrm(@Validated @RequestBody ApsConvertFromCrmReq req) {
Long wmsOrderId = apsPlanService.convertFromCrmOrderWithItems(req.getCrmOrderId(), req.getCrmItemIds(), getUsername());
return R.ok(new ApsConvertFromCrmResp(wmsOrderId, null));
}
@PostMapping("/convert-from-product")
public R<ApsConvertFromCrmResp> convertFromProduct(@Validated @RequestBody ApsConvertFromProductReq req) {
Long wmsOrderId = apsPlanService.convertFromProduct(
req.getProductId(),
req.getQuantity(),
req.getProductName(),
req.getRemark(),
getUsername()
);
return R.ok(new ApsConvertFromCrmResp(wmsOrderId, null));
}
@PostMapping("/convert-from-products")
public R<ApsConvertFromCrmResp> convertFromProducts(@Validated @RequestBody ApsConvertFromProductsReq req) {
Long wmsOrderId = apsPlanService.convertFromProducts(req.getItems(), req.getRemark(), getUsername());
return R.ok(new ApsConvertFromCrmResp(wmsOrderId, null));
}
}

View File

@@ -0,0 +1,55 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsAutoScheduleReq;
import com.klp.aps.domain.dto.ApsPlanCreateReq;
import com.klp.aps.service.ApsAutoScheduleService;
import com.klp.aps.service.ApsPlanService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* APS 计划MVP
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/plan")
public class ApsPlanController extends BaseController {
private final ApsPlanService apsPlanService;
private final ApsAutoScheduleService apsAutoScheduleService;
/**
* POST /aps/plan/create
* 创建计划头与明细,返回 planId
*/
@PostMapping("/create")
public R<Long> create(@Validated @RequestBody ApsPlanCreateReq req) {
Long planId = apsPlanService.createPlan(req, getUsername());
return R.ok(planId);
}
/**
* POST /aps/plan/auto-schedule
* 自动排程生成 operation + change_log并更新 plan 状态
*/
@PostMapping("/auto-schedule")
public R<Void> autoSchedule(@Validated @RequestBody ApsAutoScheduleReq req) {
apsAutoScheduleService.autoSchedule(req, getUsername());
return R.ok();
}
/**
* POST /aps/plan/publish/{planId}
* 仅允许已排产(plan.status=1) 的计划发布,状态流转为 2已发布/生产中)
*/
@PostMapping("/publish/{planId}")
public R<Void> publish(@PathVariable Long planId) {
apsPlanService.publishPlan(planId, getUsername());
return R.ok();
}
}

View File

@@ -0,0 +1,55 @@
package com.klp.aps.controller;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.R;
import com.klp.aps.domain.dto.ApsScheduleSheetQueryReq;
import com.klp.aps.domain.dto.ApsScheduleSheetSupplementSaveReq;
import com.klp.aps.domain.vo.ApsScheduleSheetResp;
import com.klp.aps.service.ApsScheduleSheetService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
/**
* 统一排产表(查询 + 导出)
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps")
public class ApsScheduleSheetController extends BaseController {
private final ApsScheduleSheetService apsScheduleSheetService;
/**
* GET /aps/schedule-sheet
*/
@GetMapping("/schedule-sheet")
public R<ApsScheduleSheetResp> query(@Validated ApsScheduleSheetQueryReq req) {
return R.ok(apsScheduleSheetService.query(req));
}
/**
* GET /aps/schedule-sheet/export
*/
@GetMapping("/schedule-sheet/export")
public void export(@Validated ApsScheduleSheetQueryReq req, HttpServletResponse response) {
apsScheduleSheetService.exportExcel(req, response);
}
/**
* POST /aps/schedule-sheet/supplement/save
*/
@PostMapping("/schedule-sheet/supplement/save")
public R<Void> saveSupplement(@Validated @RequestBody ApsScheduleSheetSupplementSaveReq req) {
apsScheduleSheetService.saveSupplement(req, getUsername());
return R.ok();
}
}

View File

@@ -0,0 +1,93 @@
package com.klp.aps.controller;
import com.klp.common.annotation.Log;
import com.klp.common.annotation.RepeatSubmit;
import com.klp.common.core.controller.BaseController;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.domain.R;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.aps.domain.bo.ApsShiftTemplateBo;
import com.klp.aps.domain.vo.ApsShiftTemplateVo;
import com.klp.aps.service.ApsShiftTemplateService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
* 班次模板Controller
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/aps/shift-template")
public class ApsShiftTemplateController extends BaseController {
private final ApsShiftTemplateService apsShiftTemplateService;
/**
* 查询班次模板列表
*/
@GetMapping("/list")
public TableDataInfo<ApsShiftTemplateVo> list(ApsShiftTemplateBo bo, PageQuery pageQuery) {
return apsShiftTemplateService.queryPageList(bo, pageQuery);
}
/**
* 导出班次模板列表
*/
@Log(title = "班次模板", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ApsShiftTemplateBo bo, HttpServletResponse response) {
List<ApsShiftTemplateVo> list = apsShiftTemplateService.queryList(bo);
ExcelUtil.exportExcel(list, "班次模板", ApsShiftTemplateVo.class, response);
}
/**
* 获取班次模板详细信息
*/
@GetMapping("/{shiftId}")
public R<ApsShiftTemplateVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long shiftId) {
return R.ok(apsShiftTemplateService.queryById(shiftId));
}
/**
* 新增班次模板
*/
@Log(title = "班次模板", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ApsShiftTemplateBo bo) {
return toAjax(apsShiftTemplateService.insertByBo(bo));
}
/**
* 修改班次模板
*/
@Log(title = "班次模板", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ApsShiftTemplateBo bo) {
return toAjax(apsShiftTemplateService.updateByBo(bo));
}
/**
* 删除班次模板
*/
@Log(title = "班次模板", businessType = BusinessType.DELETE)
@DeleteMapping("/{shiftIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] shiftIds) {
return toAjax(apsShiftTemplateService.deleteWithValidByIds(Arrays.asList(shiftIds), true));
}
}

View File

@@ -0,0 +1,30 @@
package com.klp.aps.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
/**
* 工厂日历业务对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ApsCalendarBo extends BaseEntity {
private Long calendarId;
@NotNull(message = "日期不能为空", groups = {AddGroup.class, EditGroup.class})
private LocalDate calendarDate;
@NotNull(message = "日历类型不能为空", groups = {AddGroup.class, EditGroup.class})
private Integer calendarType;
private String factoryCode;
private String remark;
}

View File

@@ -0,0 +1,38 @@
package com.klp.aps.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 日历班次配置业务对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ApsCalendarShiftBo extends BaseEntity {
private Long configId;
@NotNull(message = "日期不能为空", groups = {AddGroup.class, EditGroup.class})
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate calendarDate;
@NotNull(message = "产线ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long lineId;
@NotNull(message = "班次ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long shiftId;
private BigDecimal plannedHours;
private Integer status;
private String remark;
}

View File

@@ -0,0 +1,38 @@
package com.klp.aps.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 产线能力业务对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ApsLineCapabilityBo extends BaseEntity {
private Long capabilityId;
@NotNull(message = "产线ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long lineId;
private Long productId;
private Long processId;
@NotNull(message = "每小时产能不能为空", groups = {AddGroup.class, EditGroup.class})
private BigDecimal capacityPerHour;
private Integer setupMinutes;
private Integer priority;
private Integer isEnabled;
private String remark;
}

View File

@@ -0,0 +1,40 @@
package com.klp.aps.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalTime;
/**
* 班次模板业务对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ApsShiftTemplateBo extends BaseEntity {
private Long shiftId;
@NotBlank(message = "班次编码不能为空", groups = {AddGroup.class, EditGroup.class})
private String shiftCode;
@NotBlank(message = "班次名称不能为空", groups = {AddGroup.class, EditGroup.class})
private String shiftName;
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
private LocalTime startTime;
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
private LocalTime endTime;
private Integer crossDay;
private BigDecimal efficiencyRate;
private String remark;
}

View File

@@ -0,0 +1,22 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 自动排程请求
*/
@Data
public class ApsAutoScheduleReq {
/** 必填计划ID */
@NotNull(message = "planId 不能为空")
private Long planId;
/** 是否清理旧排程(默认 true仅删除未锁定记录 */
private Boolean clearOld;
/** 备注/预留 */
private String remark;
}

View File

@@ -0,0 +1,18 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.List;
@Data
public class ApsConvertFromCrmReq {
@NotBlank(message = "crmOrderId 不能为空")
private String crmOrderId;
/**
* 可选:指定转换的 CRM 明细 itemId 列表;为空则转换该订单全部明细
*/
private List<String> crmItemIds;
}

View File

@@ -0,0 +1,12 @@
package com.klp.aps.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ApsConvertFromCrmResp {
private Long wmsOrderId;
private String wmsOrderCode;
}

View File

@@ -0,0 +1,22 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Data
public class ApsConvertFromProductReq {
@NotNull(message = "productId 不能为空")
private Long productId;
@NotNull(message = "quantity 不能为空")
@DecimalMin(value = "0.0001", message = "quantity 必须大于 0")
private BigDecimal quantity;
private String productName;
private String remark;
}

View File

@@ -0,0 +1,17 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
public class ApsConvertFromProductsReq {
@Valid
@NotEmpty(message = "items 不能为空")
private List<ApsConvertFromProductReq> items;
private String remark;
}

View File

@@ -0,0 +1,33 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* 甘特查询参数
*/
@Data
public class ApsGanttQueryReq {
/** 可空:不传则查全部产线 */
private Long lineId;
/** 必填:查询开始时间 */
@NotNull(message = "queryStart 不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime queryStart;
/** 必填:查询结束时间 */
@NotNull(message = "queryEnd 不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime queryEnd;
/** 可空:按计划过滤 */
private Long planId;
/** 可空:按订单过滤 */
private Long orderId;
}

View File

@@ -0,0 +1,30 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 排产锁定请求
*/
@Data
public class ApsLockReq {
/**
* 锁定类型1计划 2产线时间窗 3工序
*/
private Integer lockType;
private Long planId;
private Long lineId;
private Long operationId;
private LocalDateTime lockStartTime;
private LocalDateTime lockEndTime;
private String lockReason;
}

View File

@@ -0,0 +1,38 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* 创建计划请求
*/
@Data
public class ApsPlanCreateReq {
/** 必填订单ID */
@NotNull(message = "orderId 不能为空")
private Long orderId;
/** 可空:排产计划编号,不传则后端生成 */
private String planCode;
/** 可空:版本,不传默认 V1 */
private String version;
/** 可空优先级0低 1中 2高 3VIP */
private Integer priority;
/** 可空目标产线ID不传则按默认产线 */
private Long lineId;
/** 可空:计划开始时间 */
private LocalDateTime startDate;
/** 可空:计划结束时间 */
private LocalDateTime endDate;
/** 可空:备注 */
private String remark;
}

View File

@@ -0,0 +1,34 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* 单工序重排请求
*/
@Data
public class ApsRescheduleReq {
/** 必填工序排程ID */
@NotNull(message = "operationId 不能为空")
private Long operationId;
/** 必填目标产线ID */
@NotNull(message = "targetLineId 不能为空")
private Long targetLineId;
/** 必填:目标开始时间 */
@NotNull(message = "targetStartTime 不能为空")
private LocalDateTime targetStartTime;
/** 必填:目标结束时间 */
@NotNull(message = "targetEndTime 不能为空")
private LocalDateTime targetEndTime;
/** 必填:重排原因 */
@NotNull(message = "reason 不能为空")
private String reason;
}

View File

@@ -0,0 +1,33 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* 统一排产表查询参数MVP
*/
@Data
public class ApsScheduleSheetQueryReq {
/** 可空:计划查询关键字(支持 planId 或 planCode */
private String planId;
/** 可空:订单查询关键字(支持 orderId 或 orderCode */
private String orderId;
/** 可空产线ID不传查全部产线 */
private Long lineId;
/** 必填:查询开始时间 */
@NotNull(message = "queryStart 不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime queryStart;
/** 必填:查询结束时间 */
@NotNull(message = "queryEnd 不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime queryEnd;
}

View File

@@ -0,0 +1,31 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
@Data
public class ApsScheduleSheetSupplementSaveReq {
@Valid
@NotEmpty(message = "rows 不能为空")
private List<Row> rows;
@Data
public static class Row {
@NotNull(message = "operationId 不能为空")
private Long operationId;
private Long rawMaterialId;
private String rawCoilNos;
private BigDecimal rawNetWeight;
private String rawPackaging;
private String rawEdgeReq;
private String rawCoatingType;
private String rawLocation;
}
}

View File

@@ -0,0 +1,20 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApsWmsOrderCreateReq {
private String orderCode;
private Long customerId;
private String customerName;
private String salesManager;
private Integer orderStatus;
private String remark;
private BigDecimal taxAmount;
private BigDecimal noTaxAmount;
private String createBy;
private String updateBy;
}

View File

@@ -0,0 +1,21 @@
package com.klp.aps.domain.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApsWmsOrderDetailCreateReq {
private Long orderId;
private Long productId;
private BigDecimal quantity;
private String unit;
private String remark;
private BigDecimal taxPrice;
private BigDecimal noTaxPrice;
private Long groupId;
private Integer delFlag;
private String createBy;
private String updateBy;
}

View File

@@ -0,0 +1,30 @@
package com.klp.aps.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDate;
/**
* 工厂日历实体 wms_calendar
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_calendar")
public class ApsCalendarEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "calendar_id", type = IdType.AUTO)
private Long calendarId;
private LocalDate calendarDate;
private Integer calendarType;
private String factoryCode;
private String remark;
}

View File

@@ -0,0 +1,35 @@
package com.klp.aps.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 日历班次配置实体 wms_calendar_shift
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_calendar_shift")
public class ApsCalendarShiftEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "config_id", type = IdType.AUTO)
private Long configId;
private LocalDate calendarDate;
private Long lineId;
private Long shiftId;
private BigDecimal plannedHours;
private Integer status;
private String remark;
}

View File

@@ -0,0 +1,38 @@
package com.klp.aps.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* 产线能力实体 wms_line_capability
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_line_capability")
public class ApsLineCapabilityEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "capability_id", type = IdType.AUTO)
private Long capabilityId;
private Long lineId;
private Long productId;
private Long processId;
private BigDecimal capacityPerHour;
private Integer setupMinutes;
private Integer priority;
private Integer isEnabled;
private String remark;
}

View File

@@ -0,0 +1,18 @@
package com.klp.aps.domain.entity;
import lombok.Data;
/**
* wms_schedule_change_logAPS 自动排程审计)
*/
@Data
public class ApsScheduleChangeLogEntity {
private Long logId;
private Long operationId;
private String changeType;
private String beforeValue;
private String afterValue;
private String changeReason;
private String changeBy;
}

View File

@@ -0,0 +1,24 @@
package com.klp.aps.domain.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* wms_schedule_lock
*/
@Data
public class ApsScheduleLockEntity {
private Long lockId;
private Integer lockType;
private Long planId;
private Long lineId;
private Long operationId;
private LocalDateTime lockStartTime;
private LocalDateTime lockEndTime;
private String lockReason;
private Integer status;
private String createBy;
private String updateBy;
}

View File

@@ -0,0 +1,32 @@
package com.klp.aps.domain.entity;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* wms_schedule_operationAPS 自动排程落库)
*/
@Data
public class ApsScheduleOperationEntity {
private Long operationId;
private Long planId;
private Long detailId;
private Long orderId;
private Long orderDetailId;
private Long productId;
private Long processId;
private Long lineId;
private Integer sequenceNo;
private BigDecimal planQty;
private LocalDateTime startTime;
private LocalDateTime endTime;
private Integer setupMinutes;
private Integer status;
private Integer lockedFlag;
private String remark;
private String createBy;
private String updateBy;
}

View File

@@ -0,0 +1,26 @@
package com.klp.aps.domain.entity;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* wms_schedule_plan_detail仅用于 APS MVP 插入)
*/
@Data
public class ApsSchedulePlanDetailEntity {
private Long detailId;
private Long planId;
private Long lineId;
private Long taskId;
private Long productId;
private BigDecimal quantity;
private LocalDate startDate;
private LocalDate endDate;
private String remark;
private Integer delFlag;
private String createBy;
private String updateBy;
}

View File

@@ -0,0 +1,25 @@
package com.klp.aps.domain.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* wms_schedule_plan仅用于 APS MVP 插入/回填主键)
*/
@Data
public class ApsSchedulePlanEntity {
private Long planId;
private String planCode;
private String version;
private Long orderId;
private Integer status;
private String remark;
private Integer delFlag;
private String createBy;
private String updateBy;
private Integer priority;
private LocalDateTime startDate;
private LocalDateTime endDate;
}

View File

@@ -0,0 +1,37 @@
package com.klp.aps.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalTime;
/**
* 班次模板实体 wms_shift_template
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("wms_shift_template")
public class ApsShiftTemplateEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "shift_id", type = IdType.AUTO)
private Long shiftId;
private String shiftCode;
private String shiftName;
private LocalTime startTime;
private LocalTime endTime;
private Integer crossDay;
private BigDecimal efficiencyRate;
private String remark;
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApsCrmOrderItemRow {
private String itemId;
private String orderId;
private String productType;
private Long productNum;
private BigDecimal contractPrice;
private String remark;
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApsCrmOrderRow {
private String orderId;
private String orderCode;
private String customerId;
private String salesman;
private BigDecimal orderAmount;
private String remark;
}

View File

@@ -0,0 +1,17 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
/**
* 产线能力wms_line_capability最小行
*/
@Data
public class ApsLineCapabilityRow {
private Long lineId;
private BigDecimal capacityPerHour;
private Integer setupMinutes;
private Integer priority;
}

View File

@@ -0,0 +1,9 @@
package com.klp.aps.domain.row;
import lombok.Data;
@Data
public class ApsLineMetaRow {
private Long lineId;
private String lineName;
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
/**
* wms_order_detail 最小取数行
*/
@Data
public class ApsOrderDetailRow {
private Long detailId;
private Long productId;
private BigDecimal quantity;
}

View File

@@ -0,0 +1,17 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
/**
* 计划明细最小行(用于自动排程)
*/
@Data
public class ApsPlanDetailRow {
private Long detailId;
private Long taskId;
private Long lineId;
private Long productId;
private BigDecimal quantity;
}

View File

@@ -0,0 +1,11 @@
package com.klp.aps.domain.row;
import lombok.Data;
@Data
public class ApsProductMetaRow {
private Long productId;
private String productName;
private String specification;
private String material;
}

View File

@@ -0,0 +1,13 @@
package com.klp.aps.domain.row;
import lombok.Data;
/**
* 产品工艺路线wms_product_processe最小行
*/
@Data
public class ApsProductProcessRow {
private Long processId;
private Integer processSequence;
}

View File

@@ -0,0 +1,20 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.time.LocalDateTime;
/**
* wms_schedule_plan 最小行(用于自动排程)
*/
@Data
public class ApsSchedulePlanRow {
private Long planId;
private String planCode;
private String version;
private Long orderId;
private Integer status;
private LocalDateTime startDate;
private LocalDateTime endDate;
}

View File

@@ -0,0 +1,23 @@
package com.klp.aps.domain.row;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
/**
* 日历班次时段wms_calendar_shift + wms_shift_template
*/
@Data
public class ApsShiftSlotRow {
private LocalDate calendarDate;
private Long lineId;
private Long shiftId;
private BigDecimal plannedHours;
private LocalTime startTime;
private LocalTime endTime;
private Integer crossDay;
private BigDecimal efficiencyRate;
}

View File

@@ -0,0 +1,10 @@
package com.klp.aps.domain.row;
import lombok.Data;
@Data
public class ApsWmsOrderRow {
private Long orderId;
private String orderCode;
}

View File

@@ -0,0 +1,46 @@
package com.klp.aps.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 日历班次配置视图对象
*/
@Data
@ExcelIgnoreUnannotated
public class ApsCalendarShiftVo {
@ExcelProperty(value = "主键")
private Long configId;
@ExcelProperty(value = "日期")
private LocalDate calendarDate;
@ExcelProperty(value = "产线ID")
private Long lineId;
@ExcelProperty(value = "产线名称")
private String lineName;
@ExcelProperty(value = "班次ID")
private Long shiftId;
@ExcelProperty(value = "班次名称")
private String shiftName;
@ExcelProperty(value = "计划工时")
private BigDecimal plannedHours;
@ExcelProperty(value = "状态")
private Integer status;
@ExcelProperty(value = "状态名称")
private String statusName;
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,33 @@
package com.klp.aps.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDate;
/**
* 工厂日历视图对象
*/
@Data
@ExcelIgnoreUnannotated
public class ApsCalendarVo {
@ExcelProperty(value = "主键")
private Long calendarId;
@ExcelProperty(value = "日期")
private LocalDate calendarDate;
@ExcelProperty(value = "日历类型")
private Integer calendarType;
@ExcelProperty(value = "日历类型名称")
private String calendarTypeName;
@ExcelProperty(value = "工厂编码")
private String factoryCode;
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.domain.vo;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ApsFactoryCalendarDayVo {
private Integer taskCount = 0;
private Integer shiftCount = 0;
private Integer lineCount = 0;
/** 1=工作日,0=休息日,null=未知 */
private Integer dayStatus;
private List<ApsFactoryCalendarTaskLiteVo> tasks = new ArrayList<>();
}

View File

@@ -0,0 +1,15 @@
package com.klp.aps.domain.vo;
import lombok.Data;
@Data
public class ApsFactoryCalendarOverviewVo {
private Integer taskCount;
private Integer activeLineCount;
private Integer totalLineCount;
private Integer shiftCount;
private String totalHours;
private String peakDayText;
private String busiestLineText;
private String busiestLineShiftText;
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.domain.vo;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class ApsFactoryCalendarRespVo {
private ApsFactoryCalendarOverviewVo overview;
private List<String> dateList;
private Map<Long, String> lineNameMap;
private Map<Long, String> shiftNameMap;
private Map<String, ApsFactoryCalendarDayVo> lineDayMap;
private Map<String, ApsFactoryCalendarDayVo> lineShiftDayMap;
}

View File

@@ -0,0 +1,9 @@
package com.klp.aps.domain.vo;
import lombok.Data;
@Data
public class ApsFactoryCalendarTaskLiteVo {
private String label;
private String title;
}

View File

@@ -0,0 +1,33 @@
package com.klp.aps.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 甘特图条目返回
*/
@Data
public class ApsGanttItemVo {
private Long operationId;
private Long planId;
private String planCode;
private Long orderId;
private Long orderDetailId;
private Long productId;
private String productName;
private String material;
private String specification;
private Long processId;
private String processName;
private Long lineId;
private String lineName;
private Integer sequenceNo;
private BigDecimal planQty;
private LocalDateTime startTime;
private LocalDateTime endTime;
private Integer status;
private Integer lockedFlag;
}

View File

@@ -0,0 +1,54 @@
package com.klp.aps.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 产线能力视图对象
*/
@Data
@ExcelIgnoreUnannotated
public class ApsLineCapabilityVo {
@ExcelProperty(value = "主键")
private Long capabilityId;
@ExcelProperty(value = "产线ID")
private Long lineId;
@ExcelProperty(value = "产线名称")
private String lineName;
@ExcelProperty(value = "产品ID")
private Long productId;
@ExcelProperty(value = "产品名称")
private String productName;
@ExcelProperty(value = "工序ID")
private Long processId;
@ExcelProperty(value = "工序名称")
private String processName;
@ExcelProperty(value = "每小时产能")
private BigDecimal capacityPerHour;
@ExcelProperty(value = "换产时间(分钟)")
private Integer setupMinutes;
@ExcelProperty(value = "优先级")
private Integer priority;
@ExcelProperty(value = "是否启用")
private Integer isEnabled;
@ExcelProperty(value = "是否启用名称")
private String isEnabledName;
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,30 @@
package com.klp.aps.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* 统一排产表返回
*/
@Data
public class ApsScheduleSheetResp {
/** 表头元信息(可扩展:计划号/日期/机组等) */
private Map<String, Object> header;
/** 明细行 */
private List<ApsScheduleSheetRowVo> rows;
/** 合计 */
private Summary summary;
@Data
public static class Summary {
private Integer totalCount;
private BigDecimal totalPlanQty;
private BigDecimal totalRawNetWeight;
}
}

View File

@@ -0,0 +1,53 @@
package com.klp.aps.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* 统一排产表行(前端统一展示用)
*
* 说明:字段是“统一超集”,不同机组可只展示子集,但表头保持一致。
*/
@Data
public class ApsScheduleSheetRowVo {
/** 排程主键 */
private Long operationId;
/** 产线 */
private Long lineId;
private String lineName;
/** 计划/订单 */
private Long planId;
private String planCode;
private Long orderId;
private String orderCode;
/** 销售信息 */
private String salesman;
private String customerName;
/** 产品/工序 */
private Long productId;
private String productName;
private String material;
private String specification;
private Long processId;
private String processName;
/** 排程数量/时间 */
private BigDecimal planQty;
private String startTime;
private String endTime;
/** 原料钢卷(来自 wms_material_coil 的展示字段MVP按 productId 兜底关联) */
private Long rawMaterialId; // 回填钢卷ID
private String rawCoilNos; // current_coil_no 逗号分隔
private String rawLocation; // 实际库位名称
private BigDecimal rawNetWeight; // 合计净重(吨/或 kg按库表单位展示
private String rawPackaging; // packaging_requirement
private String rawEdgeReq; // trimming_requirement
private String rawCoatingType; // coating_type
}

View File

@@ -0,0 +1,43 @@
package com.klp.aps.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalTime;
/**
* 班次模板视图对象
*/
@Data
@ExcelIgnoreUnannotated
public class ApsShiftTemplateVo {
@ExcelProperty(value = "主键")
private Long shiftId;
@ExcelProperty(value = "班次编码")
private String shiftCode;
@ExcelProperty(value = "班次名称")
private String shiftName;
@ExcelProperty(value = "开始时间")
private LocalTime startTime;
@ExcelProperty(value = "结束时间")
private LocalTime endTime;
@ExcelProperty(value = "跨天标识")
private Integer crossDay;
@ExcelProperty(value = "跨天标识名称")
private String crossDayName;
@ExcelProperty(value = "效率系数")
private BigDecimal efficiencyRate;
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,64 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.entity.ApsScheduleChangeLogEntity;
import com.klp.aps.domain.entity.ApsScheduleOperationEntity;
import com.klp.aps.domain.row.ApsLineCapabilityRow;
import com.klp.aps.domain.row.ApsLineMetaRow;
import com.klp.aps.domain.row.ApsOrderDetailRow;
import com.klp.aps.domain.row.ApsPlanDetailRow;
import com.klp.aps.domain.row.ApsProductMetaRow;
import com.klp.aps.domain.row.ApsSchedulePlanRow;
import com.klp.aps.domain.row.ApsShiftSlotRow;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.List;
public interface ApsAutoScheduleMapper {
ApsSchedulePlanRow selectPlanByIdForUpdate(@Param("planId") Long planId);
List<ApsOrderDetailRow> selectOrderDetailsByOrderId(@Param("orderId") Long orderId);
List<ApsPlanDetailRow> selectPlanDetailsByPlanId(@Param("planId") Long planId);
List<ApsLineCapabilityRow> selectLineCandidatesByProductAndLine(@Param("productId") Long productId,
@Param("lineId") Long lineId);
ApsLineMetaRow selectLineMetaById(@Param("lineId") Long lineId);
ApsProductMetaRow selectProductMetaById(@Param("productId") Long productId);
List<ApsShiftSlotRow> selectAvailableShifts(@Param("lineId") Long lineId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);
Long selectPlanDetailId(@Param("planId") Long planId, @Param("taskId") Long taskId);
int deleteUnlockedOperationsByPlanId(@Param("planId") Long planId);
/**
* 返回冲突记录的 end_time若无冲突返回 null
*/
LocalDateTime selectFirstConflictEndTime(@Param("lineId") Long lineId,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("excludeOperationId") Long excludeOperationId);
/**
* 返回命中锁定的 lock_end_time若未命中返回 null
*/
LocalDateTime selectFirstHitLockEndTime(@Param("lineId") Long lineId,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("planId") Long planId,
@Param("excludeOperationId") Long excludeOperationId);
int insertOperation(ApsScheduleOperationEntity op);
int insertChangeLog(ApsScheduleChangeLogEntity log);
int updatePlanStatus(@Param("planId") Long planId, @Param("status") Integer status, @Param("updateBy") String updateBy);
}

View File

@@ -0,0 +1,27 @@
package com.klp.aps.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.klp.aps.domain.entity.ApsCalendarEntity;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
import java.util.List;
/**
* 工厂日历Mapper接口
*/
public interface ApsCalendarMapper extends BaseMapper<ApsCalendarEntity> {
/**
* 查询工厂日历列表
*/
List<ApsCalendarEntity> selectCalendarList(@Param("calendarDate") LocalDate calendarDate,
@Param("calendarType") Integer calendarType,
@Param("factoryCode") String factoryCode);
/**
* 根据日期和工厂编码查询
*/
ApsCalendarEntity selectByDateAndFactory(@Param("calendarDate") LocalDate calendarDate,
@Param("factoryCode") String factoryCode);
}

View File

@@ -0,0 +1,23 @@
package com.klp.aps.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.klp.aps.domain.entity.ApsCalendarShiftEntity;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
import java.util.List;
/**
* 日历班次配置Mapper接口
*/
public interface ApsCalendarShiftMapper extends BaseMapper<ApsCalendarShiftEntity> {
/**
* 根据日期、产线和班次查询
*/
ApsCalendarShiftEntity selectByDateLineAndShift(@Param("calendarDate") LocalDate calendarDate,
@Param("lineId") Long lineId,
@Param("shiftId") Long shiftId);
List<Long> selectDistinctLineIds();
}

View File

@@ -0,0 +1,11 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.dto.ApsGanttQueryReq;
import com.klp.aps.domain.vo.ApsGanttItemVo;
import java.util.List;
public interface ApsGanttMapper {
List<ApsGanttItemVo> selectGanttItems(ApsGanttQueryReq req);
}

View File

@@ -0,0 +1,18 @@
package com.klp.aps.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.klp.aps.domain.entity.ApsLineCapabilityEntity;
import org.apache.ibatis.annotations.Param;
/**
* 产线能力Mapper接口
*/
public interface ApsLineCapabilityMapper extends BaseMapper<ApsLineCapabilityEntity> {
/**
* 根据产线、产品、工序查询
*/
ApsLineCapabilityEntity selectByLineProductAndProcess(@Param("lineId") Long lineId,
@Param("productId") Long productId,
@Param("processId") Long processId);
}

View File

@@ -0,0 +1,14 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.entity.ApsScheduleLockEntity;
import org.apache.ibatis.annotations.Param;
public interface ApsLockMapper {
int insertLock(ApsScheduleLockEntity lock);
int updateLockStatus(@Param("lockId") Long lockId,
@Param("status") Integer status,
@Param("updateBy") String updateBy);
}

View File

@@ -0,0 +1,15 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.entity.ApsScheduleChangeLogEntity;
import com.klp.aps.domain.entity.ApsScheduleOperationEntity;
import org.apache.ibatis.annotations.Param;
public interface ApsOperationMapper {
ApsScheduleOperationEntity selectByIdForUpdate(@Param("operationId") Long operationId);
int updateSlot(ApsScheduleOperationEntity op);
int insertChangeLog(ApsScheduleChangeLogEntity log);
}

View File

@@ -0,0 +1,35 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.dto.ApsWmsOrderCreateReq;
import com.klp.aps.domain.dto.ApsWmsOrderDetailCreateReq;
import com.klp.aps.domain.entity.ApsSchedulePlanDetailEntity;
import com.klp.aps.domain.entity.ApsSchedulePlanEntity;
import com.klp.aps.domain.row.ApsCrmOrderItemRow;
import com.klp.aps.domain.row.ApsCrmOrderRow;
import com.klp.aps.domain.row.ApsOrderDetailRow;
import com.klp.aps.domain.row.ApsWmsOrderRow;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ApsPlanMapper {
List<ApsOrderDetailRow> selectOrderDetailsByOrderId(@Param("orderId") Long orderId);
ApsCrmOrderRow selectCrmOrderById(@Param("crmOrderId") String crmOrderId);
List<ApsCrmOrderItemRow> selectCrmOrderItemsByOrderId(@Param("crmOrderId") String crmOrderId);
ApsWmsOrderRow selectWmsOrderByCode(@Param("orderCode") String orderCode);
int insertWmsOrder(ApsWmsOrderCreateReq req);
int insertWmsOrderDetail(ApsWmsOrderDetailCreateReq req);
Long selectAnyProductionLineId();
int insertSchedulePlan(ApsSchedulePlanEntity plan);
int insertSchedulePlanDetail(ApsSchedulePlanDetailEntity detail);
}

View File

@@ -0,0 +1,18 @@
package com.klp.aps.mapper;
import com.klp.aps.domain.dto.ApsScheduleSheetQueryReq;
import com.klp.aps.domain.vo.ApsScheduleSheetRowVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ApsScheduleSheetMapper {
List<ApsScheduleSheetRowVo> selectSheetRows(ApsScheduleSheetQueryReq req);
int deleteOperationCoils(@Param("operationId") Long operationId, @Param("updateBy") String updateBy);
int insertOperationCoil(@Param("operationId") Long operationId, @Param("coilId") Long coilId, @Param("createBy") String createBy);
int updateOperationSupplement(@Param("operationId") Long operationId, @Param("remarkJson") String remarkJson, @Param("updateBy") String updateBy);
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.klp.aps.domain.entity.ApsShiftTemplateEntity;
import org.apache.ibatis.annotations.Param;
/**
* 班次模板Mapper接口
*/
public interface ApsShiftTemplateMapper extends BaseMapper<ApsShiftTemplateEntity> {
/**
* 根据班次编码查询
*/
ApsShiftTemplateEntity selectByShiftCode(@Param("shiftCode") String shiftCode);
}

View File

@@ -0,0 +1,8 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsAutoScheduleReq;
public interface ApsAutoScheduleService {
void autoSchedule(ApsAutoScheduleReq req, String operator);
}

View File

@@ -0,0 +1,45 @@
package com.klp.aps.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.aps.domain.bo.ApsCalendarBo;
import com.klp.aps.domain.vo.ApsCalendarVo;
import java.util.Collection;
import java.util.List;
/**
* 工厂日历Service接口
*/
public interface ApsCalendarService {
/**
* 查询工厂日历
*/
ApsCalendarVo queryById(Long calendarId);
/**
* 查询工厂日历列表
*/
TableDataInfo<ApsCalendarVo> queryPageList(ApsCalendarBo bo, PageQuery pageQuery);
/**
* 查询工厂日历列表
*/
List<ApsCalendarVo> queryList(ApsCalendarBo bo);
/**
* 新增工厂日历
*/
Boolean insertByBo(ApsCalendarBo bo);
/**
* 修改工厂日历
*/
Boolean updateByBo(ApsCalendarBo bo);
/**
* 校验并批量删除工厂日历
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@@ -0,0 +1,52 @@
package com.klp.aps.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.aps.domain.bo.ApsCalendarShiftBo;
import com.klp.aps.domain.vo.ApsCalendarShiftVo;
import java.util.Collection;
import java.util.List;
/**
* 日历班次配置Service接口
*/
public interface ApsCalendarShiftService {
/**
* 查询日历班次配置
*/
ApsCalendarShiftVo queryById(Long configId);
/**
* 查询日历班次配置列表
*/
TableDataInfo<ApsCalendarShiftVo> queryPageList(ApsCalendarShiftBo bo, PageQuery pageQuery);
/**
* 查询日历班次配置列表
*/
List<ApsCalendarShiftVo> queryList(ApsCalendarShiftBo bo);
/**
* 新增日历班次配置
*/
Boolean insertByBo(ApsCalendarShiftBo bo);
/**
* 修改日历班次配置
*/
Boolean updateByBo(ApsCalendarShiftBo bo);
/**
* 校验并批量删除日历班次配置
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 一键生成指定年份的工作日日历(状态=启用)
* @param overwriteExisting true=覆盖已有记录为工作日false=仅补齐缺失记录
*/
Integer generateWorkdayForYear(Integer year, Boolean overwriteExisting);
}

View File

@@ -0,0 +1,14 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsGanttQueryReq;
import com.klp.aps.domain.vo.ApsFactoryCalendarRespVo;
import com.klp.aps.domain.vo.ApsGanttItemVo;
import java.util.List;
public interface ApsGanttService {
List<ApsGanttItemVo> selectGanttItems(ApsGanttQueryReq req);
ApsFactoryCalendarRespVo queryFactoryCalendar(ApsGanttQueryReq req);
}

View File

@@ -0,0 +1,45 @@
package com.klp.aps.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.aps.domain.bo.ApsLineCapabilityBo;
import com.klp.aps.domain.vo.ApsLineCapabilityVo;
import java.util.Collection;
import java.util.List;
/**
* 产线能力Service接口
*/
public interface ApsLineCapabilityService {
/**
* 查询产线能力
*/
ApsLineCapabilityVo queryById(Long capabilityId);
/**
* 查询产线能力列表
*/
TableDataInfo<ApsLineCapabilityVo> queryPageList(ApsLineCapabilityBo bo, PageQuery pageQuery);
/**
* 查询产线能力列表
*/
List<ApsLineCapabilityVo> queryList(ApsLineCapabilityBo bo);
/**
* 新增产线能力
*/
Boolean insertByBo(ApsLineCapabilityBo bo);
/**
* 修改产线能力
*/
Boolean updateByBo(ApsLineCapabilityBo bo);
/**
* 校验并批量删除产线能力
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@@ -0,0 +1,11 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsLockReq;
public interface ApsLockService {
Long createLock(ApsLockReq req, String operator);
void releaseLock(Long lockId, String operator);
}

View File

@@ -0,0 +1,8 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsRescheduleReq;
public interface ApsOperationService {
void reschedule(ApsRescheduleReq req, String operator);
}

View File

@@ -0,0 +1,19 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsConvertFromProductReq;
import com.klp.aps.domain.dto.ApsPlanCreateReq;
public interface ApsPlanService {
Long createPlan(ApsPlanCreateReq req, String operator);
Long convertFromCrmOrder(String crmOrderId, String operator);
Long convertFromCrmOrderWithItems(String crmOrderId, java.util.List<String> crmItemIds, String operator);
Long convertFromProduct(Long productId, java.math.BigDecimal quantity, String productName, String remark, String operator);
Long convertFromProducts(java.util.List<ApsConvertFromProductReq> items, String remark, String operator);
void publishPlan(Long planId, String operator);
}

View File

@@ -0,0 +1,16 @@
package com.klp.aps.service;
import com.klp.aps.domain.dto.ApsScheduleSheetQueryReq;
import com.klp.aps.domain.dto.ApsScheduleSheetSupplementSaveReq;
import com.klp.aps.domain.vo.ApsScheduleSheetResp;
import javax.servlet.http.HttpServletResponse;
public interface ApsScheduleSheetService {
ApsScheduleSheetResp query(ApsScheduleSheetQueryReq req);
void exportExcel(ApsScheduleSheetQueryReq req, HttpServletResponse response);
void saveSupplement(ApsScheduleSheetSupplementSaveReq req, String operator);
}

View File

@@ -0,0 +1,45 @@
package com.klp.aps.service;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.core.page.TableDataInfo;
import com.klp.aps.domain.bo.ApsShiftTemplateBo;
import com.klp.aps.domain.vo.ApsShiftTemplateVo;
import java.util.Collection;
import java.util.List;
/**
* 班次模板Service接口
*/
public interface ApsShiftTemplateService {
/**
* 查询班次模板
*/
ApsShiftTemplateVo queryById(Long shiftId);
/**
* 查询班次模板列表
*/
TableDataInfo<ApsShiftTemplateVo> queryPageList(ApsShiftTemplateBo bo, PageQuery pageQuery);
/**
* 查询班次模板列表
*/
List<ApsShiftTemplateVo> queryList(ApsShiftTemplateBo bo);
/**
* 新增班次模板
*/
Boolean insertByBo(ApsShiftTemplateBo bo);
/**
* 修改班次模板
*/
Boolean updateByBo(ApsShiftTemplateBo bo);
/**
* 校验并批量删除班次模板
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@@ -0,0 +1,234 @@
package com.klp.aps.service.impl;
import com.alibaba.fastjson2.JSON;
import com.klp.aps.domain.row.*;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.dto.ApsAutoScheduleReq;
import com.klp.aps.domain.entity.ApsScheduleChangeLogEntity;
import com.klp.aps.domain.entity.ApsScheduleOperationEntity;
import com.klp.aps.domain.row.ApsLineCapabilityRow;
import com.klp.aps.domain.row.ApsSchedulePlanRow;
import com.klp.aps.mapper.ApsAutoScheduleMapper;
import com.klp.aps.service.ApsAutoScheduleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
@RequiredArgsConstructor
@Service
public class ApsAutoScheduleServiceImpl implements ApsAutoScheduleService {
private final ApsAutoScheduleMapper apsAutoScheduleMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void autoSchedule(ApsAutoScheduleReq req, String operator) {
boolean clearOld = req.getClearOld() == null || Boolean.TRUE.equals(req.getClearOld());
// 1) 锁定计划行,避免并发排程
ApsSchedulePlanRow plan = apsAutoScheduleMapper.selectPlanByIdForUpdate(req.getPlanId());
if (plan == null) {
throw new ServiceException("排产计划不存在");
}
if (plan.getStatus() == null || plan.getStatus() != 0) {
throw new ServiceException("仅新建状态计划可自动排程");
}
// 2) 清理旧排程(仅未锁定)
if (clearOld) {
apsAutoScheduleMapper.deleteUnlockedOperationsByPlanId(plan.getPlanId());
}
// 3) 读取订单明细(基础校验)
List<ApsOrderDetailRow> details = apsAutoScheduleMapper.selectOrderDetailsByOrderId(plan.getOrderId());
if (details == null || details.isEmpty()) {
throw new ServiceException("订单明细为空,无法排程");
}
// 4) 正排:按计划明细(仅到产线粒度,不依赖工艺路线)
List<ApsPlanDetailRow> planDetails = apsAutoScheduleMapper.selectPlanDetailsByPlanId(plan.getPlanId());
if (planDetails == null || planDetails.isEmpty()) {
throw new ServiceException("计划明细为空,无法排程");
}
int seqNo = 1;
LocalDateTime cursor = plan.getStartDate() != null ? plan.getStartDate() : LocalDateTime.now();
for (ApsPlanDetailRow pd : planDetails) {
Long productId = pd.getProductId();
Long lineId = pd.getLineId();
BigDecimal qty = safeQty(pd.getQuantity());
if (lineId == null) {
throw new ServiceException("计划明细未指定产线: detailId=" + pd.getDetailId());
}
List<ApsLineCapabilityRow> caps = apsAutoScheduleMapper.selectLineCandidatesByProductAndLine(productId, lineId);
if (caps == null || caps.isEmpty()) {
ApsLineMetaRow lineMeta = apsAutoScheduleMapper.selectLineMetaById(lineId);
ApsProductMetaRow productMeta = apsAutoScheduleMapper.selectProductMetaById(productId);
String lineName = lineMeta != null && lineMeta.getLineName() != null ? lineMeta.getLineName() : ("产线" + lineId);
String productText = productMeta == null
? ("产品" + productId)
: (safe(productMeta.getProductName()) + "-" + safe(productMeta.getSpecification()) + "-" + safe(productMeta.getMaterial()));
throw new ServiceException("产线【" + lineName + "】未配置产品产能:" + productText);
}
ApsLineCapabilityRow cap = caps.get(0);
long runMinutes = calcRunMinutes(qty, cap.getCapacityPerHour());
int setupMinutes = cap.getSetupMinutes() == null ? 0 : Math.max(0, cap.getSetupMinutes());
long totalMinutes = runMinutes + setupMinutes;
ScheduledSlot slot = findEarliestAvailableSlotWithShift(lineId, cursor, totalMinutes, plan.getPlanId());
ApsScheduleOperationEntity op = new ApsScheduleOperationEntity();
op.setPlanId(plan.getPlanId());
op.setDetailId(pd.getDetailId());
op.setOrderId(plan.getOrderId());
op.setOrderDetailId(pd.getTaskId());
op.setProductId(productId);
op.setProcessId(null);
op.setLineId(lineId);
op.setSequenceNo(seqNo++);
op.setPlanQty(qty);
op.setStartTime(slot.start);
op.setEndTime(slot.end);
op.setSetupMinutes(setupMinutes);
op.setStatus(0);
op.setLockedFlag(0);
op.setCreateBy(operator);
op.setUpdateBy(operator);
apsAutoScheduleMapper.insertOperation(op);
if (op.getOperationId() == null) {
throw new ServiceException("写入排程结果失败:未回填 operationId");
}
ApsScheduleChangeLogEntity log = new ApsScheduleChangeLogEntity();
log.setOperationId(op.getOperationId());
log.setChangeType("CREATE");
log.setBeforeValue(null);
log.setAfterValue(JSON.toJSONString(op));
log.setChangeReason("AUTO_SCHEDULE");
log.setChangeBy(operator);
apsAutoScheduleMapper.insertChangeLog(log);
cursor = slot.end;
}
// 5) 更新计划状态 -> 已排产
apsAutoScheduleMapper.updatePlanStatus(plan.getPlanId(), 1, operator);
}
/**
* 在日历班次 + 冲突/锁定约束下寻找最早可用时间窗(当前实现:仅允许落在单个班次内,跨班次暂不支持)
*/
private ScheduledSlot findEarliestAvailableSlotWithShift(Long lineId, LocalDateTime earliest, long totalMinutes, Long planId) {
if (lineId == null) {
throw new ServiceException("排程失败lineId 为空");
}
if (earliest == null) {
earliest = LocalDateTime.now();
}
if (totalMinutes <= 0) {
totalMinutes = 1;
}
// 只在 earliest 所在日期起,向后若干天内查找可用班次
LocalDate startDate = earliest.toLocalDate();
LocalDate endDate = startDate.plusDays(30);
java.util.List<ApsShiftSlotRow> shifts =
apsAutoScheduleMapper.selectAvailableShifts(lineId, startDate, endDate);
if (shifts == null || shifts.isEmpty()) {
throw new ServiceException("未配置可用班次或当前日期范围无班次lineId=" + lineId);
}
for (ApsShiftSlotRow shift : shifts) {
LocalDate d = shift.getCalendarDate();
LocalTime st = shift.getStartTime();
LocalTime et = shift.getEndTime();
if (d == null || st == null || et == null) {
continue;
}
LocalDateTime slotStart = LocalDateTime.of(d, st);
LocalDateTime slotEnd = shift.getCrossDay() != null && shift.getCrossDay() == 1
? LocalDateTime.of(d.plusDays(1), et)
: LocalDateTime.of(d, et);
// 跳过整班结束早于 earliest 的班次
if (!slotEnd.isAfter(earliest)) {
continue;
}
// 在班次内,从 max(earliest, 班次开始) 起尝试
LocalDateTime start = earliest.isAfter(slotStart) ? earliest : slotStart;
// 防止在单个班次内死循环,最多推进 100 次
for (int i = 0; i < 100; i++) {
LocalDateTime end = start.plusMinutes(totalMinutes);
if (!end.isBefore(slotEnd) && !end.equals(slotEnd)) {
// 超出当前班次结束,换下一个班次
break;
}
LocalDateTime conflictEnd = apsAutoScheduleMapper.selectFirstConflictEndTime(lineId, start, end, null);
if (conflictEnd != null && conflictEnd.isAfter(start) && conflictEnd.isBefore(slotEnd)) {
start = conflictEnd;
continue;
}
LocalDateTime lockEnd = apsAutoScheduleMapper.selectFirstHitLockEndTime(lineId, start, end, planId, null);
if (lockEnd != null && lockEnd.isAfter(start) && lockEnd.isBefore(slotEnd)) {
start = lockEnd;
continue;
}
return new ScheduledSlot(start, end, lineId, 0);
}
}
throw new ServiceException("排程失败在可用班次内无法找到可用时间窗lineId=" + lineId + "");
}
private static long calcRunMinutes(BigDecimal qty, BigDecimal capacityPerHour) {
BigDecimal q = safeQty(qty);
if (capacityPerHour == null || capacityPerHour.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("产线能力 capacityPerHour 不合法");
}
// minutes = ceil(qty / capPerHour * 60)
BigDecimal minutes = q.multiply(BigDecimal.valueOf(60))
.divide(capacityPerHour, 0, RoundingMode.CEILING);
long m = minutes.longValue();
return Math.max(1L, m);
}
private static BigDecimal safeQty(BigDecimal qty) {
BigDecimal q = qty == null ? BigDecimal.ZERO : qty;
// wms_order_detail.quantity 是 decimal(18,0),这里统一按 4 位小数存 op.plan_qty
return q.setScale(4, RoundingMode.HALF_UP);
}
private static String safe(String s) {
return s == null || s.trim().isEmpty() ? "-" : s.trim();
}
private static class ScheduledSlot {
private final LocalDateTime start;
private final LocalDateTime end;
private final Long lineId;
private final int setupMinutes;
private ScheduledSlot(LocalDateTime start, LocalDateTime end, Long lineId, int setupMinutes) {
this.start = start;
this.end = end;
this.lineId = lineId;
this.setupMinutes = setupMinutes;
}
}
}

View File

@@ -0,0 +1,124 @@
package com.klp.aps.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.exception.ServiceException;
import com.klp.common.utils.StringUtils;
import com.klp.aps.domain.bo.ApsCalendarBo;
import com.klp.aps.domain.entity.ApsCalendarEntity;
import com.klp.aps.domain.vo.ApsCalendarVo;
import com.klp.aps.mapper.ApsCalendarMapper;
import com.klp.aps.service.ApsCalendarService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 工厂日历Service业务层处理
*/
@RequiredArgsConstructor
@Service
public class ApsCalendarServiceImpl implements ApsCalendarService {
private final ApsCalendarMapper baseMapper;
private static final Map<Integer, String> CALENDAR_TYPE_MAP = new HashMap<Integer, String>() {{
put(1, "工作日");
put(2, "周末");
put(3, "法定假日");
put(4, "停机日");
}};
@Override
public ApsCalendarVo queryById(Long calendarId) {
ApsCalendarEntity entity = baseMapper.selectById(calendarId);
ApsCalendarVo vo = BeanUtil.toBean(entity, ApsCalendarVo.class);
if (vo != null && vo.getCalendarType() != null) {
vo.setCalendarTypeName(CALENDAR_TYPE_MAP.getOrDefault(vo.getCalendarType(), "未知"));
}
return vo;
}
@Override
public TableDataInfo<ApsCalendarVo> queryPageList(ApsCalendarBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ApsCalendarEntity> lqw = buildQueryWrapper(bo);
Page<ApsCalendarEntity> result = baseMapper.selectPage(pageQuery.build(), lqw);
List<ApsCalendarVo> voList = result.getRecords().stream()
.map(entity -> {
ApsCalendarVo vo = BeanUtil.toBean(entity, ApsCalendarVo.class);
if (vo.getCalendarType() != null) {
vo.setCalendarTypeName(CALENDAR_TYPE_MAP.getOrDefault(vo.getCalendarType(), "未知"));
}
return vo;
})
.collect(Collectors.toList());
TableDataInfo<ApsCalendarVo> build = TableDataInfo.build(voList);
build.setTotal(result.getTotal());
return build;
}
@Override
public List<ApsCalendarVo> queryList(ApsCalendarBo bo) {
LambdaQueryWrapper<ApsCalendarEntity> lqw = buildQueryWrapper(bo);
List<ApsCalendarEntity> list = baseMapper.selectList(lqw);
return list.stream()
.map(entity -> {
ApsCalendarVo vo = BeanUtil.toBean(entity, ApsCalendarVo.class);
if (vo.getCalendarType() != null) {
vo.setCalendarTypeName(CALENDAR_TYPE_MAP.getOrDefault(vo.getCalendarType(), "未知"));
}
return vo;
})
.collect(Collectors.toList());
}
private LambdaQueryWrapper<ApsCalendarEntity> buildQueryWrapper(ApsCalendarBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<ApsCalendarEntity> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getCalendarDate() != null, ApsCalendarEntity::getCalendarDate, bo.getCalendarDate());
lqw.eq(bo.getCalendarType() != null, ApsCalendarEntity::getCalendarType, bo.getCalendarType());
lqw.eq(StringUtils.isNotBlank(bo.getFactoryCode()), ApsCalendarEntity::getFactoryCode, bo.getFactoryCode());
return lqw;
}
@Override
public Boolean insertByBo(ApsCalendarBo bo) {
ApsCalendarEntity entity = BeanUtil.toBean(bo, ApsCalendarEntity.class);
validEntityBeforeSave(entity);
return baseMapper.insert(entity) > 0;
}
@Override
public Boolean updateByBo(ApsCalendarBo bo) {
ApsCalendarEntity entity = BeanUtil.toBean(bo, ApsCalendarEntity.class);
validEntityBeforeSave(entity);
return baseMapper.updateById(entity) > 0;
}
private void validEntityBeforeSave(ApsCalendarEntity entity) {
if (entity.getFactoryCode() == null || entity.getFactoryCode().isEmpty()) {
entity.setFactoryCode("DEFAULT");
}
ApsCalendarEntity exist = baseMapper.selectByDateAndFactory(entity.getCalendarDate(), entity.getFactoryCode());
if (exist != null && !exist.getCalendarId().equals(entity.getCalendarId())) {
throw new ServiceException("该日期和工厂编码的组合已存在");
}
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验,例如检查是否被排产计划使用
}
return baseMapper.deleteBatchIds(ids) > 0;
}
}

View File

@@ -0,0 +1,229 @@
package com.klp.aps.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.bo.ApsCalendarShiftBo;
import com.klp.aps.domain.bo.ApsShiftTemplateBo;
import com.klp.aps.domain.entity.ApsCalendarEntity;
import com.klp.aps.domain.entity.ApsCalendarShiftEntity;
import com.klp.aps.domain.vo.ApsCalendarShiftVo;
import com.klp.aps.mapper.ApsCalendarMapper;
import com.klp.aps.mapper.ApsCalendarShiftMapper;
import com.klp.aps.service.ApsCalendarShiftService;
import com.klp.service.IWmsProductionLineService;
import com.klp.domain.vo.WmsProductionLineVo;
import com.klp.aps.service.ApsShiftTemplateService;
import com.klp.aps.domain.vo.ApsShiftTemplateVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 日历班次配置Service业务层处理
*/
@RequiredArgsConstructor
@Service
public class ApsCalendarShiftServiceImpl implements ApsCalendarShiftService {
private final ApsCalendarShiftMapper baseMapper;
private final ApsCalendarMapper calendarMapper;
private final IWmsProductionLineService productionLineService;
private final ApsShiftTemplateService shiftTemplateService;
private static final Map<Integer, String> STATUS_MAP = new HashMap<Integer, String>() {{
put(0, "停用");
put(1, "启用");
}};
@Override
public ApsCalendarShiftVo queryById(Long configId) {
ApsCalendarShiftEntity entity = baseMapper.selectById(configId);
ApsCalendarShiftVo vo = BeanUtil.toBean(entity, ApsCalendarShiftVo.class);
enrichVo(vo);
return vo;
}
@Override
public TableDataInfo<ApsCalendarShiftVo> queryPageList(ApsCalendarShiftBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ApsCalendarShiftEntity> lqw = buildQueryWrapper(bo);
Page<ApsCalendarShiftEntity> result = baseMapper.selectPage(pageQuery.build(), lqw);
List<ApsCalendarShiftVo> voList = result.getRecords().stream()
.map(entity -> {
ApsCalendarShiftVo vo = BeanUtil.toBean(entity, ApsCalendarShiftVo.class);
enrichVo(vo);
return vo;
})
.collect(Collectors.toList());
TableDataInfo<ApsCalendarShiftVo> tableDataInfo = TableDataInfo.build(voList);
tableDataInfo.setTotal(result.getTotal());
return tableDataInfo;
}
@Override
public List<ApsCalendarShiftVo> queryList(ApsCalendarShiftBo bo) {
LambdaQueryWrapper<ApsCalendarShiftEntity> lqw = buildQueryWrapper(bo);
List<ApsCalendarShiftEntity> list = baseMapper.selectList(lqw);
return list.stream()
.map(entity -> {
ApsCalendarShiftVo vo = BeanUtil.toBean(entity, ApsCalendarShiftVo.class);
enrichVo(vo);
return vo;
})
.collect(Collectors.toList());
}
private void enrichVo(ApsCalendarShiftVo vo) {
if (vo == null) return;
if (vo.getStatus() != null) {
vo.setStatusName(STATUS_MAP.getOrDefault(vo.getStatus(), "未知"));
}
if (vo.getLineId() != null) {
try {
WmsProductionLineVo line = productionLineService.queryById(vo.getLineId());
if (line != null) {
vo.setLineName(line.getLineName());
}
} catch (Exception e) {
// 忽略异常,仅记录
}
}
if (vo.getShiftId() != null) {
try {
ApsShiftTemplateVo shift = shiftTemplateService.queryById(vo.getShiftId());
if (shift != null) {
vo.setShiftName(shift.getShiftName());
}
} catch (Exception e) {
// 忽略异常,仅记录
}
}
}
private LambdaQueryWrapper<ApsCalendarShiftEntity> buildQueryWrapper(ApsCalendarShiftBo bo) {
LambdaQueryWrapper<ApsCalendarShiftEntity> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getCalendarDate() != null, ApsCalendarShiftEntity::getCalendarDate, bo.getCalendarDate());
lqw.eq(bo.getLineId() != null, ApsCalendarShiftEntity::getLineId, bo.getLineId());
lqw.eq(bo.getShiftId() != null, ApsCalendarShiftEntity::getShiftId, bo.getShiftId());
lqw.eq(bo.getStatus() != null, ApsCalendarShiftEntity::getStatus, bo.getStatus());
return lqw;
}
@Override
public Boolean insertByBo(ApsCalendarShiftBo bo) {
ApsCalendarShiftEntity entity = BeanUtil.toBean(bo, ApsCalendarShiftEntity.class);
validEntityBeforeSave(entity);
return baseMapper.insert(entity) > 0;
}
@Override
public Boolean updateByBo(ApsCalendarShiftBo bo) {
ApsCalendarShiftEntity entity = BeanUtil.toBean(bo, ApsCalendarShiftEntity.class);
validEntityBeforeSave(entity);
return baseMapper.updateById(entity) > 0;
}
private void validEntityBeforeSave(ApsCalendarShiftEntity entity) {
ApsCalendarShiftEntity exist = baseMapper.selectByDateLineAndShift(
entity.getCalendarDate(), entity.getLineId(), entity.getShiftId());
if (exist != null && !exist.getConfigId().equals(entity.getConfigId())) {
throw new ServiceException("该日期、产线和班次的组合已存在");
}
if (entity.getStatus() == null) {
entity.setStatus(1);
}
if (entity.getPlannedHours() == null) {
entity.setPlannedHours(java.math.BigDecimal.ZERO);
}
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 预留业务校验扩展点
}
return baseMapper.deleteBatchIds(ids) > 0;
}
@Override
public Integer generateWorkdayForYear(Integer year, Boolean overwriteExisting) {
if (year == null || year < 2000 || year > 2100) {
throw new ServiceException("年份范围不合法,请输入 2000-2100");
}
List<Long> lineIds = baseMapper.selectDistinctLineIds();
if (lineIds == null || lineIds.isEmpty()) {
throw new ServiceException("暂无可用产线,请先维护至少一条日历班次或产线数据");
}
List<ApsShiftTemplateVo> shifts = shiftTemplateService.queryList(new ApsShiftTemplateBo());
if (shifts == null || shifts.isEmpty()) {
throw new ServiceException("暂无班次模板,请先配置班次模板");
}
LocalDate start = LocalDate.of(year, 1, 1);
LocalDate end = LocalDate.of(year, 12, 31);
int affected = 0;
for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
ApsCalendarEntity cal = calendarMapper.selectByDateAndFactory(d, "DEFAULT");
if (cal == null) {
ApsCalendarEntity c = new ApsCalendarEntity();
c.setCalendarDate(d);
c.setCalendarType(1);
c.setFactoryCode("DEFAULT");
c.setRemark("系统批量生成工作日");
affected += calendarMapper.insert(c);
} else if (Boolean.TRUE.equals(overwriteExisting) && (cal.getCalendarType() == null || cal.getCalendarType() != 1)) {
cal.setCalendarType(1);
affected += calendarMapper.updateById(cal);
}
}
for (Long lineId : lineIds) {
for (ApsShiftTemplateVo shift : shifts) {
if (shift == null || shift.getShiftId() == null) continue;
for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
ApsCalendarShiftEntity exist = baseMapper.selectByDateLineAndShift(d, lineId, shift.getShiftId());
if (exist == null) {
ApsCalendarShiftEntity entity = new ApsCalendarShiftEntity();
entity.setCalendarDate(d);
entity.setLineId(lineId);
entity.setShiftId(shift.getShiftId());
entity.setStatus(1);
entity.setPlannedHours(BigDecimal.valueOf(8));
entity.setRemark("系统批量生成");
affected += baseMapper.insert(entity);
} else {
boolean changed = false;
if (Boolean.TRUE.equals(overwriteExisting)) {
if (exist.getStatus() == null || exist.getStatus() != 1) {
exist.setStatus(1);
changed = true;
}
if (exist.getPlannedHours() == null || exist.getPlannedHours().compareTo(BigDecimal.ZERO) <= 0) {
exist.setPlannedHours(BigDecimal.valueOf(8));
changed = true;
}
}
if (changed) {
affected += baseMapper.updateById(exist);
}
}
}
}
}
return affected;
}
}

View File

@@ -0,0 +1,265 @@
package com.klp.aps.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.klp.aps.domain.bo.ApsShiftTemplateBo;
import com.klp.aps.domain.dto.ApsGanttQueryReq;
import com.klp.aps.domain.entity.ApsCalendarShiftEntity;
import com.klp.aps.domain.vo.ApsFactoryCalendarDayVo;
import com.klp.aps.domain.vo.ApsFactoryCalendarOverviewVo;
import com.klp.aps.domain.vo.ApsFactoryCalendarRespVo;
import com.klp.aps.domain.vo.ApsFactoryCalendarTaskLiteVo;
import com.klp.aps.domain.vo.ApsGanttItemVo;
import com.klp.aps.domain.vo.ApsShiftTemplateVo;
import com.klp.aps.mapper.ApsCalendarShiftMapper;
import com.klp.aps.mapper.ApsGanttMapper;
import com.klp.aps.service.ApsGanttService;
import com.klp.aps.service.ApsShiftTemplateService;
import com.klp.domain.bo.WmsProductionLineBo;
import com.klp.domain.vo.WmsProductionLineVo;
import com.klp.service.IWmsProductionLineService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RequiredArgsConstructor
@Service
public class ApsGanttServiceImpl implements ApsGanttService {
private final ApsGanttMapper apsGanttMapper;
private final ApsCalendarShiftMapper calendarShiftMapper;
private final IWmsProductionLineService productionLineService;
private final ApsShiftTemplateService shiftTemplateService;
@Override
public List<ApsGanttItemVo> selectGanttItems(ApsGanttQueryReq req) {
return apsGanttMapper.selectGanttItems(req);
}
@Override
public ApsFactoryCalendarRespVo queryFactoryCalendar(ApsGanttQueryReq req) {
if (req.getQueryStart() == null || req.getQueryEnd() == null) {
ApsFactoryCalendarRespVo empty = new ApsFactoryCalendarRespVo();
empty.setOverview(new ApsFactoryCalendarOverviewVo());
empty.setDateList(new ArrayList<>());
empty.setLineNameMap(new HashMap<>());
empty.setShiftNameMap(new HashMap<>());
empty.setLineDayMap(new HashMap<>());
empty.setLineShiftDayMap(new HashMap<>());
return empty;
}
List<ApsGanttItemVo> tasks = apsGanttMapper.selectGanttItems(req);
List<WmsProductionLineVo> lineVos = productionLineService.queryList(new WmsProductionLineBo());
List<ApsShiftTemplateVo> shiftVos = shiftTemplateService.queryList(new ApsShiftTemplateBo());
Map<Long, String> lineNameMap = new HashMap<>();
for (WmsProductionLineVo l : lineVos) {
if (l != null && l.getLineId() != null) {
lineNameMap.put(l.getLineId(), l.getLineName() != null ? l.getLineName() : l.getLineCode());
}
}
Map<Long, String> shiftNameMap = new HashMap<>();
for (ApsShiftTemplateVo s : shiftVos) {
if (s != null && s.getShiftId() != null) {
shiftNameMap.put(s.getShiftId(), s.getShiftName());
}
}
Map<String, ApsFactoryCalendarDayVo> lineDayMap = new HashMap<>();
Map<String, ApsFactoryCalendarDayVo> lineShiftDayMap = new HashMap<>();
Map<String, Set<Long>> lineDayShiftSetMap = new HashMap<>();
Map<String, Set<Long>> lineShiftLineSetMap = new HashMap<>();
List<ApsCalendarShiftEntity> calendarShiftList = calendarShiftMapper.selectList(
Wrappers.<ApsCalendarShiftEntity>lambdaQuery()
.between(ApsCalendarShiftEntity::getCalendarDate, req.getQueryStart().toLocalDate(), req.getQueryEnd().toLocalDate())
);
Map<String, Integer> lineStatusAgg = new HashMap<>();
for (ApsCalendarShiftEntity cs : calendarShiftList) {
if (cs == null || cs.getLineId() == null || cs.getShiftId() == null || cs.getCalendarDate() == null || cs.getStatus() == null) continue;
String d = cs.getCalendarDate().format(DateTimeFormatter.ISO_DATE);
String lineShiftKey = cs.getLineId() + "|" + cs.getShiftId() + "|" + d;
lineShiftDayMap.computeIfAbsent(lineShiftKey, k -> new ApsFactoryCalendarDayVo()).setDayStatus(cs.getStatus());
String lineKey = cs.getLineId() + "|" + d;
Integer old = lineStatusAgg.get(lineKey);
int cur = cs.getStatus();
if (old == null) {
lineStatusAgg.put(lineKey, cur);
} else if (old == 1 || cur == 1) {
lineStatusAgg.put(lineKey, 1);
} else {
lineStatusAgg.put(lineKey, 0);
}
}
Map<String, Integer> dayCountMap = new HashMap<>();
Map<Long, Integer> lineCountMap = new HashMap<>();
Map<String, Integer> lineShiftCountMap = new HashMap<>();
Set<Long> activeLineSet = new HashSet<>();
BigDecimal totalHours = BigDecimal.ZERO;
int validTaskCount = 0;
for (ApsGanttItemVo t : tasks) {
if (t == null || t.getStartTime() == null || t.getLineId() == null) continue;
validTaskCount++;
LocalDateTime st = t.getStartTime();
String dateText = st.toLocalDate().format(DateTimeFormatter.ISO_DATE);
Long lineId = t.getLineId();
activeLineSet.add(lineId);
dayCountMap.put(dateText, dayCountMap.getOrDefault(dateText, 0) + 1);
lineCountMap.put(lineId, lineCountMap.getOrDefault(lineId, 0) + 1);
if (t.getEndTime() != null && t.getEndTime().isAfter(st)) {
totalHours = totalHours.add(BigDecimal.valueOf(Duration.between(st, t.getEndTime()).toMinutes())
.divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP));
}
Long shiftId = matchShiftId(shiftVos, st.toLocalTime());
String lineDayKey = lineId + "|" + dateText;
ApsFactoryCalendarDayVo lineDayVo = lineDayMap.computeIfAbsent(lineDayKey, k -> new ApsFactoryCalendarDayVo());
lineDayVo.setTaskCount(lineDayVo.getTaskCount() + 1);
if (shiftId != null) {
Set<Long> shiftSet = lineDayShiftSetMap.computeIfAbsent(lineDayKey, k -> new HashSet<>());
shiftSet.add(shiftId);
lineDayVo.setShiftCount(shiftSet.size());
}
pushTaskLite(lineDayVo, t, shiftId, shiftNameMap, true);
if (shiftId != null) {
String lsDayKey = lineId + "|" + shiftId + "|" + dateText;
ApsFactoryCalendarDayVo lsDayVo = lineShiftDayMap.computeIfAbsent(lsDayKey, k -> new ApsFactoryCalendarDayVo());
lsDayVo.setTaskCount(lsDayVo.getTaskCount() + 1);
Set<Long> lineSet = lineShiftLineSetMap.computeIfAbsent(lsDayKey, k -> new HashSet<>());
lineSet.add(lineId);
lsDayVo.setLineCount(lineSet.size());
pushTaskLite(lsDayVo, t, shiftId, shiftNameMap, false);
String aggKey = lineId + "|" + shiftId;
lineShiftCountMap.put(aggKey, lineShiftCountMap.getOrDefault(aggKey, 0) + 1);
}
}
for (Map.Entry<String, Integer> e : lineStatusAgg.entrySet()) {
lineDayMap.computeIfAbsent(e.getKey(), k -> new ApsFactoryCalendarDayVo()).setDayStatus(e.getValue());
}
ApsFactoryCalendarOverviewVo overview = new ApsFactoryCalendarOverviewVo();
overview.setTaskCount(validTaskCount);
overview.setActiveLineCount(activeLineSet.size());
overview.setTotalLineCount(lineNameMap.size());
overview.setShiftCount(shiftNameMap.size());
overview.setTotalHours(totalHours.setScale(1, RoundingMode.HALF_UP).toPlainString());
String peakDay = maxKey(dayCountMap);
overview.setPeakDayText(peakDay == null ? "-" : peakDay + " (" + dayCountMap.get(peakDay) + "单)");
Long busiestLineId = maxKeyLong(lineCountMap);
overview.setBusiestLineText(busiestLineId == null ? "-" : lineNameMap.getOrDefault(busiestLineId, "产线" + busiestLineId) + " (" + lineCountMap.get(busiestLineId) + "单)");
String busiestLs = maxKey(lineShiftCountMap);
if (busiestLs == null) {
overview.setBusiestLineShiftText("-");
} else {
String[] arr = busiestLs.split("\\|");
Long lId = Long.valueOf(arr[0]);
Long sId = Long.valueOf(arr[1]);
overview.setBusiestLineShiftText(lineNameMap.getOrDefault(lId, "产线" + lId)
+ " · " + shiftNameMap.getOrDefault(sId, "班次" + sId)
+ " (" + lineShiftCountMap.get(busiestLs) + "单)");
}
ApsFactoryCalendarRespVo resp = new ApsFactoryCalendarRespVo();
resp.setOverview(overview);
resp.setDateList(buildDateList(req.getQueryStart(), req.getQueryEnd()));
resp.setLineNameMap(lineNameMap);
resp.setShiftNameMap(shiftNameMap);
resp.setLineDayMap(lineDayMap);
resp.setLineShiftDayMap(lineShiftDayMap);
return resp;
}
private List<String> buildDateList(LocalDateTime startTime, LocalDateTime endTime) {
List<String> arr = new ArrayList<>();
if (startTime == null || endTime == null) return arr;
LocalDate start = startTime.toLocalDate();
LocalDate end = endTime.toLocalDate();
for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
arr.add(d.format(DateTimeFormatter.ISO_DATE));
}
return arr;
}
private Long matchShiftId(List<ApsShiftTemplateVo> shifts, LocalTime time) {
if (shifts == null || time == null) return null;
for (ApsShiftTemplateVo s : shifts) {
if (s == null || s.getShiftId() == null || s.getStartTime() == null || s.getEndTime() == null) continue;
boolean cross = s.getCrossDay() != null && s.getCrossDay() == 1;
if (!cross) {
if (!time.isBefore(s.getStartTime()) && !time.isAfter(s.getEndTime())) return s.getShiftId();
} else {
if (!time.isBefore(s.getStartTime()) || !time.isAfter(s.getEndTime())) return s.getShiftId();
}
}
return null;
}
private void pushTaskLite(ApsFactoryCalendarDayVo vo,
ApsGanttItemVo task,
Long shiftId,
Map<Long, String> shiftNameMap,
boolean withShift) {
if (vo.getTasks().size() >= 3) return;
ApsFactoryCalendarTaskLiteVo lite = new ApsFactoryCalendarTaskLiteVo();
String st = task.getStartTime() == null ? "--:--" : task.getStartTime().toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm"));
String et = task.getEndTime() == null ? "--:--" : task.getEndTime().toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm"));
String p = task.getProductName() == null ? "产品" : task.getProductName();
String sp = task.getSpecification() == null ? "-" : task.getSpecification();
String m = task.getMaterial() == null ? "-" : task.getMaterial();
String shiftText = (withShift && shiftId != null) ? " [" + shiftNameMap.getOrDefault(shiftId, "班次" + shiftId) + "]" : "";
lite.setLabel(st + " " + p + "-" + sp + "-" + m + shiftText);
lite.setTitle(st + "-" + et + " " + p + "-" + sp + "-" + m);
vo.getTasks().add(lite);
}
private String maxKey(Map<String, Integer> map) {
String key = null;
int max = Integer.MIN_VALUE;
for (Map.Entry<String, Integer> e : map.entrySet()) {
if (e.getValue() != null && e.getValue() > max) {
key = e.getKey();
max = e.getValue();
}
}
return key;
}
private Long maxKeyLong(Map<Long, Integer> map) {
Long key = null;
int max = Integer.MIN_VALUE;
for (Map.Entry<Long, Integer> e : map.entrySet()) {
if (e.getValue() != null && e.getValue() > max) {
key = e.getKey();
max = e.getValue();
}
}
return key;
}
}

View File

@@ -0,0 +1,149 @@
package com.klp.aps.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.bo.ApsLineCapabilityBo;
import com.klp.aps.domain.entity.ApsLineCapabilityEntity;
import com.klp.aps.domain.vo.ApsLineCapabilityVo;
import com.klp.aps.mapper.ApsLineCapabilityMapper;
import com.klp.aps.service.ApsLineCapabilityService;
import com.klp.domain.vo.WmsProductionLineVo;
import com.klp.service.IWmsProductionLineService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 产线能力Service业务层处理
*/
@RequiredArgsConstructor
@Service
public class ApsLineCapabilityServiceImpl implements ApsLineCapabilityService {
private final ApsLineCapabilityMapper baseMapper;
private final IWmsProductionLineService productionLineService;
private static final Map<Integer, String> ENABLED_MAP =new HashMap<Integer, String>(){{
put(0, "停用");
put(1, "启用");
}};
@Override
public ApsLineCapabilityVo queryById(Long capabilityId) {
ApsLineCapabilityEntity entity = baseMapper.selectById(capabilityId);
ApsLineCapabilityVo vo = BeanUtil.toBean(entity, ApsLineCapabilityVo.class);
enrichVo(vo);
return vo;
}
@Override
public TableDataInfo<ApsLineCapabilityVo> queryPageList(ApsLineCapabilityBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ApsLineCapabilityEntity> lqw = buildQueryWrapper(bo);
Page<ApsLineCapabilityEntity> result = baseMapper.selectPage(pageQuery.build(), lqw);
List<ApsLineCapabilityVo> voList = result.getRecords().stream()
.map(entity -> {
ApsLineCapabilityVo vo = BeanUtil.toBean(entity, ApsLineCapabilityVo.class);
enrichVo(vo);
return vo;
})
.collect(Collectors.toList());
TableDataInfo<ApsLineCapabilityVo> build = TableDataInfo.build(voList);
build.setTotal(result.getTotal());
return build;
}
@Override
public List<ApsLineCapabilityVo> queryList(ApsLineCapabilityBo bo) {
LambdaQueryWrapper<ApsLineCapabilityEntity> lqw = buildQueryWrapper(bo);
List<ApsLineCapabilityEntity> list = baseMapper.selectList(lqw);
return list.stream()
.map(entity -> {
ApsLineCapabilityVo vo = BeanUtil.toBean(entity, ApsLineCapabilityVo.class);
enrichVo(vo);
return vo;
})
.collect(Collectors.toList());
}
private void enrichVo(ApsLineCapabilityVo vo) {
if (vo == null) return;
if (vo.getIsEnabled() != null) {
vo.setIsEnabledName(ENABLED_MAP.getOrDefault(vo.getIsEnabled(), "未知"));
}
if (vo.getLineId() != null) {
try {
WmsProductionLineVo line = productionLineService.queryById(vo.getLineId());
if (line != null) {
vo.setLineName(line.getLineName());
}
} catch (Exception e) {
// 忽略异常,仅记录
}
}
// TODO: 如果产品ID和工序ID有对应的服务可以在这里补充产品名称和工序名称
}
private LambdaQueryWrapper<ApsLineCapabilityEntity> buildQueryWrapper(ApsLineCapabilityBo bo) {
LambdaQueryWrapper<ApsLineCapabilityEntity> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getLineId() != null, ApsLineCapabilityEntity::getLineId, bo.getLineId());
lqw.eq(bo.getProductId() != null, ApsLineCapabilityEntity::getProductId, bo.getProductId());
lqw.eq(bo.getProcessId() != null, ApsLineCapabilityEntity::getProcessId, bo.getProcessId());
lqw.eq(bo.getIsEnabled() != null, ApsLineCapabilityEntity::getIsEnabled, bo.getIsEnabled());
return lqw;
}
@Override
public Boolean insertByBo(ApsLineCapabilityBo bo) {
ApsLineCapabilityEntity entity = BeanUtil.toBean(bo, ApsLineCapabilityEntity.class);
validEntityBeforeSave(entity);
return baseMapper.insert(entity) > 0;
}
@Override
public Boolean updateByBo(ApsLineCapabilityBo bo) {
ApsLineCapabilityEntity entity = BeanUtil.toBean(bo, ApsLineCapabilityEntity.class);
validEntityBeforeSave(entity);
return baseMapper.updateById(entity) > 0;
}
private void validEntityBeforeSave(ApsLineCapabilityEntity entity) {
// 如果产品ID和工序ID都为空表示通用能力允许重复
// 如果产品ID或工序ID不为空检查是否已存在
if (entity.getProductId() != null || entity.getProcessId() != null) {
ApsLineCapabilityEntity exist = baseMapper.selectByLineProductAndProcess(
entity.getLineId(), entity.getProductId(), entity.getProcessId());
if (exist != null && !exist.getCapabilityId().equals(entity.getCapabilityId())) {
throw new ServiceException("该产线、产品和工序的组合已存在");
}
}
if (entity.getIsEnabled() == null) {
entity.setIsEnabled(1);
}
if (entity.getPriority() == null) {
entity.setPriority(999);
}
if (entity.getSetupMinutes() == null) {
entity.setSetupMinutes(0);
}
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
}

View File

@@ -0,0 +1,63 @@
package com.klp.aps.service.impl;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.dto.ApsLockReq;
import com.klp.aps.domain.entity.ApsScheduleLockEntity;
import com.klp.aps.mapper.ApsLockMapper;
import com.klp.aps.service.ApsLockService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class ApsLockServiceImpl implements ApsLockService {
private final ApsLockMapper apsLockMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public Long createLock(ApsLockReq req, String operator) {
if (req.getLockType() == null) {
throw new ServiceException("lockType 不能为空");
}
if (req.getLockType() == 2 && (req.getLineId() == null || req.getLockStartTime() == null || req.getLockEndTime() == null)) {
throw new ServiceException("产线时间窗锁定必须指定 lineId / lockStartTime / lockEndTime");
}
if (req.getLockType() == 3 && req.getOperationId() == null) {
throw new ServiceException("工序锁定必须指定 operationId");
}
if (req.getLockStartTime() != null && req.getLockEndTime() != null
&& !req.getLockStartTime().isBefore(req.getLockEndTime())) {
throw new ServiceException("lockStartTime 必须早于 lockEndTime");
}
ApsScheduleLockEntity lock = new ApsScheduleLockEntity();
lock.setLockType(req.getLockType());
lock.setPlanId(req.getPlanId());
lock.setLineId(req.getLineId());
lock.setOperationId(req.getOperationId());
lock.setLockStartTime(req.getLockStartTime());
lock.setLockEndTime(req.getLockEndTime());
lock.setLockReason(req.getLockReason());
lock.setStatus(1);
lock.setCreateBy(operator);
lock.setUpdateBy(operator);
apsLockMapper.insertLock(lock);
if (lock.getLockId() == null) {
throw new ServiceException("创建锁定记录失败:未回填 lockId");
}
return lock.getLockId();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void releaseLock(Long lockId, String operator) {
int rows = apsLockMapper.updateLockStatus(lockId, 0, operator);
if (rows == 0) {
throw new ServiceException("锁定记录不存在或已失效");
}
}
}

View File

@@ -0,0 +1,75 @@
package com.klp.aps.service.impl;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.dto.ApsRescheduleReq;
import com.klp.aps.domain.entity.ApsScheduleChangeLogEntity;
import com.klp.aps.domain.entity.ApsScheduleOperationEntity;
import com.klp.aps.mapper.ApsAutoScheduleMapper;
import com.klp.aps.mapper.ApsOperationMapper;
import com.klp.aps.service.ApsOperationService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
@RequiredArgsConstructor
@Service
public class ApsOperationServiceImpl implements ApsOperationService {
private final ApsOperationMapper apsOperationMapper;
private final ApsAutoScheduleMapper apsAutoScheduleMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void reschedule(ApsRescheduleReq req, String operator) {
ApsScheduleOperationEntity origin = apsOperationMapper.selectByIdForUpdate(req.getOperationId());
if (origin == null) {
throw new ServiceException("工序排程不存在");
}
if (origin.getLockedFlag() != null && origin.getLockedFlag() == 1) {
throw new ServiceException("工序已锁定,禁止重排");
}
LocalDateTime start = req.getTargetStartTime();
LocalDateTime end = req.getTargetEndTime();
if (start.isAfter(end) || start.equals(end)) {
throw new ServiceException("目标开始时间必须早于结束时间");
}
// 冲突 & 锁定校验(排除自身)
Long lineId = req.getTargetLineId();
Long excludeId = origin.getOperationId();
LocalDateTime conflictEnd = apsAutoScheduleMapper.selectFirstConflictEndTime(lineId, start, end, excludeId);
if (conflictEnd != null) {
throw new ServiceException("目标时间窗存在产线冲突,无法重排");
}
LocalDateTime lockEnd = apsAutoScheduleMapper.selectFirstHitLockEndTime(lineId, start, end, origin.getPlanId(), excludeId);
if (lockEnd != null) {
throw new ServiceException("目标时间窗命中锁定区间,无法重排");
}
// 备份 before
String beforeJson = com.alibaba.fastjson2.JSON.toJSONString(origin);
origin.setLineId(lineId);
origin.setStartTime(start);
origin.setEndTime(end);
origin.setUpdateBy(operator);
apsOperationMapper.updateSlot(origin);
// after
String afterJson = com.alibaba.fastjson2.JSON.toJSONString(origin);
ApsScheduleChangeLogEntity log = new ApsScheduleChangeLogEntity();
log.setOperationId(origin.getOperationId());
log.setChangeType("RESCHEDULE");
log.setBeforeValue(beforeJson);
log.setAfterValue(afterJson);
log.setChangeReason(req.getReason());
log.setChangeBy(operator);
apsOperationMapper.insertChangeLog(log);
}
}

View File

@@ -0,0 +1,299 @@
package com.klp.aps.service.impl;
import com.klp.aps.domain.dto.ApsConvertFromProductReq;
import com.klp.common.exception.ServiceException;
import com.klp.common.utils.StringUtils;
import com.klp.aps.domain.dto.ApsPlanCreateReq;
import com.klp.aps.domain.dto.ApsWmsOrderCreateReq;
import com.klp.aps.domain.dto.ApsWmsOrderDetailCreateReq;
import com.klp.aps.domain.entity.ApsSchedulePlanDetailEntity;
import com.klp.aps.domain.entity.ApsSchedulePlanEntity;
import com.klp.aps.domain.row.ApsCrmOrderItemRow;
import com.klp.aps.domain.row.ApsCrmOrderRow;
import com.klp.aps.domain.row.ApsOrderDetailRow;
import com.klp.aps.domain.row.ApsSchedulePlanRow;
import com.klp.aps.domain.row.ApsWmsOrderRow;
import com.klp.aps.mapper.ApsAutoScheduleMapper;
import com.klp.aps.mapper.ApsPlanMapper;
import com.klp.aps.service.ApsPlanService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@RequiredArgsConstructor
@Service
public class ApsPlanServiceImpl implements ApsPlanService {
private final ApsPlanMapper apsPlanMapper;
private final ApsAutoScheduleMapper apsAutoScheduleMapper;
private static final DateTimeFormatter PLAN_CODE_TIME = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
@Transactional(rollbackFor = Exception.class)
@Override
public Long convertFromCrmOrder(String crmOrderId, String operator) {
return convertFromCrmOrderWithItems(crmOrderId, null, operator);
}
@Transactional(rollbackFor = Exception.class)
@Override
public Long convertFromCrmOrderWithItems(String crmOrderId, List<String> crmItemIds, String operator) {
ApsCrmOrderRow crmOrder = apsPlanMapper.selectCrmOrderById(crmOrderId);
if (crmOrder == null) {
throw new ServiceException("CRM 订单不存在");
}
List<ApsCrmOrderItemRow> items = apsPlanMapper.selectCrmOrderItemsByOrderId(crmOrderId);
if (items == null || items.isEmpty()) {
throw new ServiceException("CRM 订单明细为空,无法转换");
}
if (crmItemIds != null && !crmItemIds.isEmpty()) {
items = items.stream().filter(i -> crmItemIds.contains(i.getItemId())).collect(java.util.stream.Collectors.toList());
if (items.isEmpty()) {
throw new ServiceException("所选 CRM 明细为空,无法转换");
}
}
String wmsOrderCode = "CRM-" + crmOrder.getOrderCode();
ApsWmsOrderRow existed = apsPlanMapper.selectWmsOrderByCode(wmsOrderCode);
if (existed != null && existed.getOrderId() != null) {
return existed.getOrderId();
}
Long customerId = null;
try {
if (StringUtils.isNotBlank(crmOrder.getCustomerId())) {
customerId = Long.valueOf(crmOrder.getCustomerId());
}
} catch (Exception ignored) {
}
ApsWmsOrderCreateReq orderCreateReq = new ApsWmsOrderCreateReq();
orderCreateReq.setOrderCode(wmsOrderCode);
orderCreateReq.setCustomerId(customerId);
orderCreateReq.setCustomerName(crmOrder.getCustomerId());
orderCreateReq.setSalesManager(crmOrder.getSalesman());
orderCreateReq.setOrderStatus(0);
orderCreateReq.setRemark("由 CRM 订单 " + crmOrder.getOrderCode() + " 转换");
orderCreateReq.setTaxAmount(crmOrder.getOrderAmount());
orderCreateReq.setNoTaxAmount(crmOrder.getOrderAmount());
orderCreateReq.setCreateBy(operator);
orderCreateReq.setUpdateBy(operator);
apsPlanMapper.insertWmsOrder(orderCreateReq);
ApsWmsOrderRow created = apsPlanMapper.selectWmsOrderByCode(wmsOrderCode);
if (created == null || created.getOrderId() == null) {
throw new ServiceException("转换失败WMS 订单未生成");
}
for (ApsCrmOrderItemRow item : items) {
Long productId = parseLong(item.getProductType());
if (productId == null) {
continue;
}
BigDecimal qty = BigDecimal.valueOf(item.getProductNum() == null ? 0 : item.getProductNum());
BigDecimal taxPrice = item.getContractPrice() == null ? BigDecimal.ZERO : item.getContractPrice();
ApsWmsOrderDetailCreateReq detailCreateReq = new ApsWmsOrderDetailCreateReq();
detailCreateReq.setOrderId(created.getOrderId());
detailCreateReq.setProductId(productId);
detailCreateReq.setQuantity(qty);
detailCreateReq.setUnit("");
detailCreateReq.setRemark(item.getRemark());
detailCreateReq.setTaxPrice(taxPrice);
detailCreateReq.setNoTaxPrice(taxPrice);
detailCreateReq.setGroupId(null);
detailCreateReq.setDelFlag(0);
detailCreateReq.setCreateBy(operator);
detailCreateReq.setUpdateBy(operator);
apsPlanMapper.insertWmsOrderDetail(detailCreateReq);
}
return created.getOrderId();
}
@Transactional(rollbackFor = Exception.class)
@Override
public Long createPlan(ApsPlanCreateReq req, String operator) {
List<ApsOrderDetailRow> orderDetails = apsPlanMapper.selectOrderDetailsByOrderId(req.getOrderId());
if (orderDetails == null || orderDetails.isEmpty()) {
throw new ServiceException("订单明细为空无法创建排产计划orderId=" + req.getOrderId() + "");
}
Long selectedLineId = req.getLineId();
if (selectedLineId == null) {
selectedLineId = apsPlanMapper.selectAnyProductionLineId();
}
if (selectedLineId == null) {
throw new ServiceException("未找到可用产线wms_production_line 为空),无法创建排产明细");
}
String planCode = req.getPlanCode();
if (StringUtils.isBlank(planCode)) {
planCode = genPlanCode();
}
String version = StringUtils.isBlank(req.getVersion()) ? "V1" : req.getVersion().trim();
ApsSchedulePlanEntity plan = new ApsSchedulePlanEntity();
plan.setPlanCode(planCode);
plan.setVersion(version);
plan.setOrderId(req.getOrderId());
plan.setStatus(0);
plan.setRemark(req.getRemark());
plan.setDelFlag(0);
plan.setPriority(req.getPriority());
plan.setStartDate(req.getStartDate());
plan.setEndDate(req.getEndDate());
plan.setCreateBy(operator);
plan.setUpdateBy(operator);
apsPlanMapper.insertSchedulePlan(plan);
if (plan.getPlanId() == null) {
throw new ServiceException("创建排产计划失败:未回填 planId");
}
LocalDate detailStart = resolveDetailStart(req.getStartDate());
LocalDate detailEnd = resolveDetailEnd(req.getEndDate(), detailStart);
for (ApsOrderDetailRow od : orderDetails) {
ApsSchedulePlanDetailEntity detail = new ApsSchedulePlanDetailEntity();
detail.setPlanId(plan.getPlanId());
detail.setLineId(selectedLineId);
// wms_schedule_plan_detail.task_id 当前无外键约束MVP 先用订单明细ID占位后续接入生产任务时再替换
detail.setTaskId(od.getDetailId());
detail.setProductId(od.getProductId());
detail.setQuantity(scale4(od.getQuantity()));
detail.setStartDate(detailStart);
detail.setEndDate(detailEnd);
detail.setRemark(req.getRemark());
detail.setDelFlag(0);
detail.setCreateBy(operator);
detail.setUpdateBy(operator);
apsPlanMapper.insertSchedulePlanDetail(detail);
}
return plan.getPlanId();
}
@Transactional(rollbackFor = Exception.class)
@Override
public Long convertFromProduct(Long productId, BigDecimal quantity, String productName, String remark, String operator) {
ApsConvertFromProductReq one = new ApsConvertFromProductReq();
one.setProductId(productId);
one.setQuantity(quantity);
one.setProductName(productName);
one.setRemark(remark);
return convertFromProducts(java.util.Collections.singletonList(one), remark, operator);
}
@Transactional(rollbackFor = Exception.class)
@Override
public Long convertFromProducts(List<ApsConvertFromProductReq> items, String remark, String operator) {
if (items == null || items.isEmpty()) {
throw new ServiceException("items 不能为空");
}
String code = "PRD-" + LocalDateTime.now().format(PLAN_CODE_TIME) + ThreadLocalRandom.current().nextInt(100, 999);
ApsWmsOrderCreateReq orderCreateReq = new ApsWmsOrderCreateReq();
orderCreateReq.setOrderCode(code);
orderCreateReq.setCustomerId(null);
orderCreateReq.setCustomerName("产品直排");
orderCreateReq.setSalesManager(operator);
orderCreateReq.setOrderStatus(0);
orderCreateReq.setRemark(StringUtils.isBlank(remark) ? "APS_PRODUCT_DIRECT_BATCH" : remark);
orderCreateReq.setTaxAmount(BigDecimal.ZERO);
orderCreateReq.setNoTaxAmount(BigDecimal.ZERO);
orderCreateReq.setCreateBy(operator);
orderCreateReq.setUpdateBy(operator);
apsPlanMapper.insertWmsOrder(orderCreateReq);
ApsWmsOrderRow created = apsPlanMapper.selectWmsOrderByCode(code);
if (created == null || created.getOrderId() == null) {
throw new ServiceException("创建库存订单失败");
}
for (ApsConvertFromProductReq item : items) {
if (item == null || item.getProductId() == null) {
continue;
}
if (item.getQuantity() == null || item.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
ApsWmsOrderDetailCreateReq detailCreateReq = new ApsWmsOrderDetailCreateReq();
detailCreateReq.setOrderId(created.getOrderId());
detailCreateReq.setProductId(item.getProductId());
detailCreateReq.setQuantity(scale4(item.getQuantity()));
detailCreateReq.setUnit("");
detailCreateReq.setRemark(StringUtils.isBlank(item.getProductName()) ? "产品直排" : ("产品直排:" + item.getProductName()));
detailCreateReq.setTaxPrice(BigDecimal.ZERO);
detailCreateReq.setNoTaxPrice(BigDecimal.ZERO);
detailCreateReq.setGroupId(null);
detailCreateReq.setDelFlag(0);
detailCreateReq.setCreateBy(operator);
detailCreateReq.setUpdateBy(operator);
apsPlanMapper.insertWmsOrderDetail(detailCreateReq);
}
return created.getOrderId();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void publishPlan(Long planId, String operator) {
ApsSchedulePlanRow plan = apsAutoScheduleMapper.selectPlanByIdForUpdate(planId);
if (plan == null) {
throw new ServiceException("排产计划不存在");
}
Integer status = plan.getStatus();
if (status == null || status != 1) {
throw new ServiceException("仅已排产状态的计划可发布");
}
// 约定0=新建 1=已排产 2=已发布/生产中
apsAutoScheduleMapper.updatePlanStatus(planId, 2, operator);
}
private static String genPlanCode() {
int rnd = ThreadLocalRandom.current().nextInt(1000, 10000);
return "APS" + LocalDateTime.now().format(PLAN_CODE_TIME) + rnd;
}
private static LocalDate resolveDetailStart(LocalDateTime planStart) {
return planStart != null ? planStart.toLocalDate() : LocalDate.now();
}
private static LocalDate resolveDetailEnd(LocalDateTime planEnd, LocalDate fallbackStart) {
LocalDate d = planEnd != null ? planEnd.toLocalDate() : null;
return d != null ? d : fallbackStart;
}
private static BigDecimal scale4(BigDecimal v) {
if (v == null) {
return BigDecimal.ZERO.setScale(4);
}
try {
return v.setScale(4, RoundingMode.HALF_UP);
} catch (Exception e) {
return new BigDecimal(v.toPlainString()).setScale(4, RoundingMode.HALF_UP);
}
}
private static Long parseLong(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return Long.valueOf(text.trim());
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,232 @@
package com.klp.aps.service.impl;
import com.klp.common.exception.ServiceException;
import com.klp.aps.domain.dto.ApsScheduleSheetQueryReq;
import com.klp.aps.domain.dto.ApsScheduleSheetSupplementSaveReq;
import com.klp.aps.domain.vo.ApsScheduleSheetResp;
import com.klp.aps.domain.vo.ApsScheduleSheetRowVo;
import com.klp.aps.mapper.ApsScheduleSheetMapper;
import com.klp.aps.service.ApsScheduleSheetService;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@RequiredArgsConstructor
@Service
public class ApsScheduleSheetServiceImpl implements ApsScheduleSheetService {
private final ApsScheduleSheetMapper apsScheduleSheetMapper;
@Override
public ApsScheduleSheetResp query(ApsScheduleSheetQueryReq req) {
List<ApsScheduleSheetRowVo> rows = apsScheduleSheetMapper.selectSheetRows(req);
ApsScheduleSheetResp resp = new ApsScheduleSheetResp();
Map<String, Object> header = new HashMap<>();
header.put("planId", req.getPlanId());
header.put("orderId", req.getOrderId());
header.put("lineId", req.getLineId());
header.put("queryStart", req.getQueryStart());
header.put("queryEnd", req.getQueryEnd());
resp.setHeader(header);
resp.setRows(rows);
ApsScheduleSheetResp.Summary s = new ApsScheduleSheetResp.Summary();
s.setTotalCount(rows == null ? 0 : rows.size());
s.setTotalPlanQty(sumPlanQty(rows));
s.setTotalRawNetWeight(sumRawWeight(rows));
resp.setSummary(s);
return resp;
}
@Override
@org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
public void saveSupplement(ApsScheduleSheetSupplementSaveReq req, String operator) {
if (req == null || req.getRows() == null || req.getRows().isEmpty()) {
throw new ServiceException("保存数据不能为空");
}
for (ApsScheduleSheetSupplementSaveReq.Row row : req.getRows()) {
if (row == null || row.getOperationId() == null) {
continue;
}
Map<String, Object> remarkMap = new LinkedHashMap<>();
remarkMap.put("rawMaterialId", row.getRawMaterialId());
remarkMap.put("rawCoilNos", row.getRawCoilNos());
remarkMap.put("rawNetWeight", row.getRawNetWeight());
remarkMap.put("rawPackaging", row.getRawPackaging());
remarkMap.put("rawEdgeReq", row.getRawEdgeReq());
remarkMap.put("rawCoatingType", row.getRawCoatingType());
remarkMap.put("rawLocation", row.getRawLocation());
String remarkJson = com.alibaba.fastjson2.JSON.toJSONString(remarkMap);
apsScheduleSheetMapper.updateOperationSupplement(row.getOperationId(), remarkJson, operator);
apsScheduleSheetMapper.deleteOperationCoils(row.getOperationId(), operator);
if (row.getRawMaterialId() != null) {
apsScheduleSheetMapper.insertOperationCoil(row.getOperationId(), row.getRawMaterialId(), operator);
}
}
}
@Override
public void exportExcel(ApsScheduleSheetQueryReq req, HttpServletResponse response) {
ApsScheduleSheetResp data = query(req);
List<ApsScheduleSheetRowVo> rows = data.getRows();
try (Workbook wb = new XSSFWorkbook()) {
Sheet sheet = wb.createSheet("APS排产表");
int r = 0;
// 标题行(合并 0~14 列,高度加倍,字体加大加粗居中)
Row title = sheet.createRow(r++);
title.setHeightInPoints(40f);
Cell t0 = title.createCell(0);
t0.setCellValue("统一排产表(冷轧/酸轧等机组通用)");
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 14));
CellStyle titleStyle = wb.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
XSSFFont titleFont = ((XSSFWorkbook) wb).createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 16);
titleStyle.setFont(titleFont);
t0.setCellStyle(titleStyle);
// 查询条件行
Row info = sheet.createRow(r++);
info.createCell(0).setCellValue("时间范围:");
info.createCell(1).setCellValue(fmt(req.getQueryStart()) + " ~ " + fmt(req.getQueryEnd()));
if (req.getLineId() != null) {
info.createCell(3).setCellValue("产线ID");
info.createCell(4).setCellValue(String.valueOf(req.getLineId()));
}
if (req.getPlanId() != null) {
info.createCell(6).setCellValue("计划ID");
info.createCell(7).setCellValue(String.valueOf(req.getPlanId()));
}
// 表头
Row head = sheet.createRow(r++);
int c = 0;
head.createCell(c++).setCellValue("产线");
head.createCell(c++).setCellValue("计划号");
head.createCell(c++).setCellValue("订单号");
head.createCell(c++).setCellValue("客户");
head.createCell(c++).setCellValue("业务员");
head.createCell(c++).setCellValue("产品");
head.createCell(c++).setCellValue("计划数量");
head.createCell(c++).setCellValue("开始时间");
head.createCell(c++).setCellValue("结束时间");
head.createCell(c++).setCellValue("原料卷号");
head.createCell(c++).setCellValue("钢卷位置");
head.createCell(c++).setCellValue("原料净重");
head.createCell(c++).setCellValue("包装要求");
head.createCell(c++).setCellValue("切边要求");
head.createCell(c++).setCellValue("镀层种类");
if (rows != null) {
for (ApsScheduleSheetRowVo row : rows) {
Row rr = sheet.createRow(r++);
int cc = 0;
rr.createCell(cc++).setCellValue(nvl(row.getLineName(), row.getLineId()));
rr.createCell(cc++).setCellValue(nvl(row.getPlanCode(), row.getPlanId()));
rr.createCell(cc++).setCellValue(nvl(row.getOrderCode(), row.getOrderId()));
rr.createCell(cc++).setCellValue(nvl(row.getCustomerName(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getSalesman(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getProductName(), ""));
rr.createCell(cc++).setCellValue(row.getPlanQty() == null ? "" : row.getPlanQty().toPlainString());
rr.createCell(cc++).setCellValue(nvl(row.getStartTime(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getEndTime(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getRawCoilNos(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getRawLocation(), ""));
rr.createCell(cc++).setCellValue(row.getRawNetWeight() == null ? "" : row.getRawNetWeight().toPlainString());
rr.createCell(cc++).setCellValue(nvl(row.getRawPackaging(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getRawEdgeReq(), ""));
rr.createCell(cc++).setCellValue(nvl(row.getRawCoatingType(), ""));
}
}
// 合计行
ApsScheduleSheetResp.Summary s = data.getSummary();
Row sum = sheet.createRow(r++);
sum.createCell(0).setCellValue("合计");
sum.createCell(6).setCellValue(s.getTotalPlanQty() == null ? "" : s.getTotalPlanQty().toPlainString());
sum.createCell(10).setCellValue(s.getTotalRawNetWeight() == null ? "" : s.getTotalRawNetWeight().toPlainString());
// 自动列宽(前 14 列)
for (int i = 0; i < 14; i++) {
sheet.autoSizeColumn(i, true);
int w = sheet.getColumnWidth(i);
sheet.setColumnWidth(i, Math.min(Math.max(w, 3000), 12000));
}
String filename = "aps_schedule_sheet_" + System.currentTimeMillis() + ".xlsx";
String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8.name());
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encoded);
try (ServletOutputStream os = response.getOutputStream()) {
wb.write(os);
os.flush();
}
} catch (IOException e) {
throw new ServiceException("导出失败:" + e.getMessage());
}
}
private static BigDecimal sumPlanQty(List<ApsScheduleSheetRowVo> rows) {
BigDecimal sum = BigDecimal.ZERO;
if (rows == null) return sum;
for (ApsScheduleSheetRowVo r : rows) {
if (r.getPlanQty() != null) {
sum = sum.add(r.getPlanQty());
}
}
return sum;
}
private static BigDecimal sumRawWeight(List<ApsScheduleSheetRowVo> rows) {
BigDecimal sum = BigDecimal.ZERO;
if (rows == null) return sum;
for (ApsScheduleSheetRowVo r : rows) {
if (r.getRawNetWeight() != null) {
sum = sum.add(r.getRawNetWeight());
}
}
return sum;
}
private static final DateTimeFormatter DT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static String fmt(java.time.LocalDateTime t) {
return t == null ? "" : t.format(DT);
}
private static String nvl(String s, Object fallback) {
if (s != null && !s.isEmpty()) return s;
return fallback == null ? "" : String.valueOf(fallback);
}
}

View File

@@ -0,0 +1,116 @@
package com.klp.aps.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
import com.klp.common.exception.ServiceException;
import com.klp.common.utils.StringUtils;
import com.klp.aps.domain.bo.ApsShiftTemplateBo;
import com.klp.aps.domain.entity.ApsShiftTemplateEntity;
import com.klp.aps.domain.vo.ApsShiftTemplateVo;
import com.klp.aps.mapper.ApsShiftTemplateMapper;
import com.klp.aps.service.ApsShiftTemplateService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* 班次模板Service业务层处理
*/
@RequiredArgsConstructor
@Service
public class ApsShiftTemplateServiceImpl implements ApsShiftTemplateService {
private final ApsShiftTemplateMapper baseMapper;
@Override
public ApsShiftTemplateVo queryById(Long shiftId) {
ApsShiftTemplateEntity entity = baseMapper.selectById(shiftId);
ApsShiftTemplateVo vo = BeanUtil.toBean(entity, ApsShiftTemplateVo.class);
if (vo != null && vo.getCrossDay() != null) {
vo.setCrossDayName(vo.getCrossDay() == 1 ? "跨天" : "不跨天");
}
return vo;
}
@Override
public TableDataInfo<ApsShiftTemplateVo> queryPageList(ApsShiftTemplateBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ApsShiftTemplateEntity> lqw = buildQueryWrapper(bo);
Page<ApsShiftTemplateEntity> result = baseMapper.selectPage(pageQuery.build(), lqw);
List<ApsShiftTemplateVo> voList = result.getRecords().stream()
.map(entity -> {
ApsShiftTemplateVo vo = BeanUtil.toBean(entity, ApsShiftTemplateVo.class);
if (vo.getCrossDay() != null) {
vo.setCrossDayName(vo.getCrossDay() == 1 ? "跨天" : "不跨天");
}
return vo;
})
.collect(Collectors.toList());
TableDataInfo<ApsShiftTemplateVo> build = TableDataInfo.build(voList);
build.setTotal(result.getTotal());
return build;
}
@Override
public List<ApsShiftTemplateVo> queryList(ApsShiftTemplateBo bo) {
LambdaQueryWrapper<ApsShiftTemplateEntity> lqw = buildQueryWrapper(bo);
List<ApsShiftTemplateEntity> list = baseMapper.selectList(lqw);
return list.stream()
.map(entity -> {
ApsShiftTemplateVo vo = BeanUtil.toBean(entity, ApsShiftTemplateVo.class);
if (vo.getCrossDay() != null) {
vo.setCrossDayName(vo.getCrossDay() == 1 ? "跨天" : "不跨天");
}
return vo;
})
.collect(Collectors.toList());
}
private LambdaQueryWrapper<ApsShiftTemplateEntity> buildQueryWrapper(ApsShiftTemplateBo bo) {
LambdaQueryWrapper<ApsShiftTemplateEntity> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getShiftCode()), ApsShiftTemplateEntity::getShiftCode, bo.getShiftCode());
lqw.like(StringUtils.isNotBlank(bo.getShiftName()), ApsShiftTemplateEntity::getShiftName, bo.getShiftName());
return lqw;
}
@Override
public Boolean insertByBo(ApsShiftTemplateBo bo) {
ApsShiftTemplateEntity entity = BeanUtil.toBean(bo, ApsShiftTemplateEntity.class);
validEntityBeforeSave(entity);
return baseMapper.insert(entity) > 0;
}
@Override
public Boolean updateByBo(ApsShiftTemplateBo bo) {
ApsShiftTemplateEntity entity = BeanUtil.toBean(bo, ApsShiftTemplateEntity.class);
validEntityBeforeSave(entity);
return baseMapper.updateById(entity) > 0;
}
private void validEntityBeforeSave(ApsShiftTemplateEntity entity) {
ApsShiftTemplateEntity exist = baseMapper.selectByShiftCode(entity.getShiftCode());
if (exist != null && !exist.getShiftId().equals(entity.getShiftId())) {
throw new ServiceException("班次编码已存在");
}
if (entity.getCrossDay() == null) {
entity.setCrossDay(0);
}
if (entity.getEfficiencyRate() == null) {
entity.setEfficiencyRate(java.math.BigDecimal.ONE);
}
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO: 可以添加业务校验,例如检查是否被日历班次配置使用
}
return baseMapper.deleteBatchIds(ids) > 0;
}
}

View File

View File

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsAutoScheduleMapper">
<select id="selectPlanByIdForUpdate"
parameterType="long"
resultType="com.klp.aps.domain.row.ApsSchedulePlanRow">
SELECT
plan_id AS planId,
plan_code AS planCode,
version AS version,
order_id AS orderId,
status AS status,
start_date AS startDate,
end_date AS endDate
FROM wms_schedule_plan
WHERE plan_id = #{planId}
AND del_flag = 0
FOR UPDATE
</select>
<select id="selectOrderDetailsByOrderId"
parameterType="long"
resultType="com.klp.aps.domain.row.ApsOrderDetailRow">
SELECT
detail_id AS detailId,
product_id AS productId,
quantity AS quantity
FROM wms_order_detail
WHERE order_id = #{orderId}
AND del_flag = 0
ORDER BY detail_id ASC
</select>
<select id="selectPlanDetailsByPlanId"
parameterType="long"
resultType="com.klp.aps.domain.row.ApsPlanDetailRow">
SELECT
detail_id AS detailId,
task_id AS taskId,
line_id AS lineId,
product_id AS productId,
quantity AS quantity
FROM wms_schedule_plan_detail
WHERE plan_id = #{planId}
AND del_flag = 0
ORDER BY detail_id ASC
</select>
<select id="selectLineCandidatesByProductAndLine"
resultType="com.klp.aps.domain.row.ApsLineCapabilityRow">
SELECT
line_id AS lineId,
capacity_per_hour AS capacityPerHour,
setup_minutes AS setupMinutes,
priority AS priority
FROM wms_line_capability
WHERE is_enabled = 1
AND line_id = #{lineId}
AND (product_id = #{productId} OR product_id IS NULL)
ORDER BY CASE WHEN product_id = #{productId} THEN 0 ELSE 1 END, priority ASC, capability_id ASC
</select>
<select id="selectLineMetaById" resultType="com.klp.aps.domain.row.ApsLineMetaRow">
SELECT line_id AS lineId, line_name AS lineName
FROM wms_production_line
WHERE line_id = #{lineId}
LIMIT 1
</select>
<select id="selectProductMetaById" resultType="com.klp.aps.domain.row.ApsProductMetaRow">
SELECT product_id AS productId, product_name AS productName, specification AS specification, material AS material
FROM wms_product
WHERE product_id = #{productId}
LIMIT 1
</select>
<select id="selectAvailableShifts"
resultType="com.klp.aps.domain.row.ApsShiftSlotRow">
SELECT
cs.calendar_date AS calendarDate,
cs.line_id AS lineId,
cs.shift_id AS shiftId,
cs.planned_hours AS plannedHours,
st.start_time AS startTime,
st.end_time AS endTime,
st.cross_day AS crossDay,
st.efficiency_rate AS efficiencyRate
FROM wms_calendar_shift cs
JOIN wms_shift_template st ON st.shift_id = cs.shift_id
JOIN wms_calendar c ON c.calendar_date = cs.calendar_date
WHERE cs.line_id = #{lineId}
AND cs.status = 1
AND c.calendar_type = 1
AND cs.calendar_date BETWEEN #{startDate} AND #{endDate}
ORDER BY cs.calendar_date, st.start_time
</select>
<select id="selectPlanDetailId" resultType="long">
SELECT detail_id
FROM wms_schedule_plan_detail
WHERE plan_id = #{planId}
AND task_id = #{taskId}
AND del_flag = 0
ORDER BY detail_id ASC
LIMIT 1
</select>
<delete id="deleteUnlockedOperationsByPlanId">
DELETE FROM wms_schedule_operation
WHERE plan_id = #{planId}
AND locked_flag = 0
</delete>
<select id="selectFirstConflictEndTime" resultType="java.time.LocalDateTime">
SELECT end_time
FROM wms_schedule_operation
WHERE line_id = #{lineId}
AND status IN (0, 1, 3)
<if test="excludeOperationId != null">
AND operation_id <![CDATA[<>]]> #{excludeOperationId}
</if>
AND NOT (end_time <![CDATA[<=]]> #{startTime} OR start_time <![CDATA[>=]]> #{endTime})
ORDER BY end_time ASC
LIMIT 1
</select>
<select id="selectFirstHitLockEndTime" resultType="java.time.LocalDateTime">
SELECT l.lock_end_time
FROM wms_schedule_lock l
WHERE l.status = 1
<if test="planId != null">
AND (l.plan_id = #{planId} OR l.plan_id IS NULL)
</if>
AND (l.line_id = #{lineId} OR l.line_id IS NULL)
<if test="excludeOperationId != null">
AND (l.operation_id IS NULL OR l.operation_id <![CDATA[<>]]> #{excludeOperationId})
</if>
AND l.lock_start_time IS NOT NULL
AND l.lock_end_time IS NOT NULL
AND NOT (l.lock_end_time <![CDATA[<=]]> #{startTime} OR l.lock_start_time <![CDATA[>=]]> #{endTime})
ORDER BY l.lock_end_time ASC
LIMIT 1
</select>
<insert id="insertOperation"
parameterType="com.klp.aps.domain.entity.ApsScheduleOperationEntity"
useGeneratedKeys="true"
keyProperty="operationId"
keyColumn="operation_id">
INSERT INTO wms_schedule_operation
(
plan_id,
detail_id,
order_id,
order_detail_id,
product_id,
process_id,
line_id,
sequence_no,
plan_qty,
start_time,
end_time,
setup_minutes,
status,
locked_flag,
remark,
create_by,
update_by
)
VALUES
(
#{planId},
#{detailId},
#{orderId},
#{orderDetailId},
#{productId},
#{processId},
#{lineId},
#{sequenceNo},
#{planQty},
#{startTime},
#{endTime},
#{setupMinutes},
#{status},
#{lockedFlag},
#{remark},
#{createBy},
#{updateBy}
)
</insert>
<insert id="insertChangeLog"
parameterType="com.klp.aps.domain.entity.ApsScheduleChangeLogEntity"
useGeneratedKeys="true"
keyProperty="logId"
keyColumn="log_id">
INSERT INTO wms_schedule_change_log
(
operation_id,
change_type,
before_value,
after_value,
change_reason,
change_by
)
VALUES
(
#{operationId},
#{changeType},
#{beforeValue},
#{afterValue},
#{changeReason},
#{changeBy}
)
</insert>
<update id="updatePlanStatus">
UPDATE wms_schedule_plan
SET status = #{status},
update_by = #{updateBy},
update_time = CURRENT_TIMESTAMP
WHERE plan_id = #{planId}
AND del_flag = 0
</update>
</mapper>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsCalendarMapper">
<resultMap type="com.klp.aps.domain.entity.ApsCalendarEntity" id="ApsCalendarResult">
<result property="calendarId" column="calendar_id"/>
<result property="calendarDate" column="calendar_date"/>
<result property="calendarType" column="calendar_type"/>
<result property="factoryCode" column="factory_code"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="selectCalendarVo">
SELECT calendar_id, calendar_date, calendar_type, factory_code, remark,
create_by, create_time, update_by, update_time
FROM wms_calendar
</sql>
<select id="selectCalendarList" parameterType="com.klp.aps.domain.entity.ApsCalendarEntity" resultMap="ApsCalendarResult">
<include refid="selectCalendarVo"/>
<where>
<if test="calendarDate != null">
AND calendar_date = #{calendarDate}
</if>
<if test="calendarType != null">
AND calendar_type = #{calendarType}
</if>
<if test="factoryCode != null and factoryCode != ''">
AND factory_code = #{factoryCode}
</if>
</where>
ORDER BY calendar_date DESC
</select>
<select id="selectByDateAndFactory" resultMap="ApsCalendarResult">
<include refid="selectCalendarVo"/>
WHERE calendar_date = #{calendarDate}
AND factory_code = #{factoryCode}
LIMIT 1
</select>
</mapper>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsCalendarShiftMapper">
<select id="selectByDateLineAndShift" resultType="com.klp.aps.domain.entity.ApsCalendarShiftEntity">
SELECT *
FROM wms_calendar_shift
WHERE calendar_date = #{calendarDate}
AND line_id = #{lineId}
AND shift_id = #{shiftId}
AND del_flag = 0
LIMIT 1
</select>
<select id="selectDistinctLineIds" resultType="java.lang.Long">
SELECT DISTINCT line_id
FROM wms_production_line
WHERE del_flag = 0
</select>
</mapper>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsGanttMapper">
<select id="selectGanttItems"
parameterType="com.klp.aps.domain.dto.ApsGanttQueryReq"
resultType="com.klp.aps.domain.vo.ApsGanttItemVo">
SELECT
o.operation_id AS operationId,
o.plan_id AS planId,
p.plan_code AS planCode,
o.order_id AS orderId,
o.order_detail_id AS orderDetailId,
o.product_id AS productId,
pr.product_name AS productName,
pr.material AS material,
pr.specification AS specification,
o.process_id AS processId,
ps.process_name AS processName,
o.line_id AS lineId,
l.line_name AS lineName,
o.sequence_no AS sequenceNo,
o.plan_qty AS planQty,
o.start_time AS startTime,
o.end_time AS endTime,
o.status AS status,
o.locked_flag AS lockedFlag
FROM wms_schedule_operation o
LEFT JOIN wms_schedule_plan p ON p.plan_id = o.plan_id
LEFT JOIN wms_product pr ON pr.product_id = o.product_id
LEFT JOIN wms_processe ps ON ps.process_id = o.process_id
LEFT JOIN wms_production_line l ON l.line_id = o.line_id
WHERE o.end_time <![CDATA[>=]]> #{queryStart}
AND o.start_time <![CDATA[<=]]> #{queryEnd}
<if test="lineId != null">
AND o.line_id = #{lineId}
</if>
<if test="planId != null">
AND o.plan_id = #{planId}
</if>
<if test="orderId != null">
AND o.order_id = #{orderId}
</if>
ORDER BY o.line_id, o.start_time
</select>
</mapper>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsLineCapabilityMapper">
<select id="selectByLineProductAndProcess" resultType="com.klp.aps.domain.entity.ApsLineCapabilityEntity">
SELECT *
FROM wms_line_capability
WHERE line_id = #{lineId}
AND (product_id = #{productId} OR (product_id IS NULL AND #{productId} IS NULL))
AND (process_id = #{processId} OR (process_id IS NULL AND #{processId} IS NULL))
AND del_flag = 0
LIMIT 1
</select>
</mapper>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsLockMapper">
<insert id="insertLock"
parameterType="com.klp.aps.domain.entity.ApsScheduleLockEntity"
useGeneratedKeys="true"
keyProperty="lockId"
keyColumn="lock_id">
INSERT INTO wms_schedule_lock
(
lock_type,
plan_id,
line_id,
operation_id,
lock_start_time,
lock_end_time,
lock_reason,
status,
create_by,
update_by
)
VALUES
(
#{lockType},
#{planId},
#{lineId},
#{operationId},
#{lockStartTime},
#{lockEndTime},
#{lockReason},
#{status},
#{createBy},
#{updateBy}
)
</insert>
<update id="updateLockStatus">
UPDATE wms_schedule_lock
SET status = #{status},
update_by = #{updateBy},
update_time = CURRENT_TIMESTAMP
WHERE lock_id = #{lockId}
</update>
</mapper>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsOperationMapper">
<resultMap id="ApsScheduleOperationMap"
type="com.klp.aps.domain.entity.ApsScheduleOperationEntity">
<id property="operationId" column="operation_id"/>
<result property="planId" column="plan_id"/>
<result property="detailId" column="detail_id"/>
<result property="orderId" column="order_id"/>
<result property="orderDetailId" column="order_detail_id"/>
<result property="productId" column="product_id"/>
<result property="processId" column="process_id"/>
<result property="lineId" column="line_id"/>
<result property="sequenceNo" column="sequence_no"/>
<result property="planQty" column="plan_qty"/>
<result property="startTime" column="start_time"/>
<result property="endTime" column="end_time"/>
<result property="setupMinutes" column="setup_minutes"/>
<result property="status" column="status"/>
<result property="lockedFlag" column="locked_flag"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
</resultMap>
<select id="selectByIdForUpdate"
parameterType="long"
resultMap="ApsScheduleOperationMap">
SELECT
operation_id,
plan_id,
detail_id,
order_id,
order_detail_id,
product_id,
process_id,
line_id,
sequence_no,
plan_qty,
start_time,
end_time,
setup_minutes,
status,
locked_flag,
remark,
create_by,
update_by
FROM wms_schedule_operation
WHERE operation_id = #{operationId}
FOR UPDATE
</select>
<update id="updateSlot"
parameterType="com.klp.aps.domain.entity.ApsScheduleOperationEntity">
UPDATE wms_schedule_operation
SET line_id = #{lineId},
start_time = #{startTime},
end_time = #{endTime},
update_by = #{updateBy},
update_time = CURRENT_TIMESTAMP
WHERE operation_id = #{operationId}
</update>
<insert id="insertChangeLog"
parameterType="com.klp.aps.domain.entity.ApsScheduleChangeLogEntity"
useGeneratedKeys="true"
keyProperty="logId"
keyColumn="log_id">
INSERT INTO wms_schedule_change_log
(
operation_id,
change_type,
before_value,
after_value,
change_reason,
change_by
)
VALUES
(
#{operationId},
#{changeType},
#{beforeValue},
#{afterValue},
#{changeReason},
#{changeBy}
)
</insert>
</mapper>

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsPlanMapper">
<select id="selectOrderDetailsByOrderId"
parameterType="long"
resultType="com.klp.aps.domain.row.ApsOrderDetailRow">
SELECT
detail_id AS detailId,
product_id AS productId,
quantity AS quantity
FROM wms_order_detail
WHERE order_id = #{orderId}
AND (del_flag = 0 OR del_flag IS NULL)
ORDER BY detail_id ASC
</select>
<select id="selectCrmOrderById" resultType="com.klp.aps.domain.row.ApsCrmOrderRow">
SELECT
order_id AS orderId,
order_code AS orderCode,
customer_id AS customerId,
salesman AS salesman,
order_amount AS orderAmount,
remark AS remark
FROM crm_order
WHERE order_id = #{crmOrderId}
AND del_flag = 0
LIMIT 1
</select>
<select id="selectCrmOrderItemsByOrderId" resultType="com.klp.aps.domain.row.ApsCrmOrderItemRow">
SELECT
item_id AS itemId,
order_id AS orderId,
product_type AS productType,
product_num AS productNum,
contract_price AS contractPrice,
remark AS remark
FROM crm_order_item
WHERE order_id = #{crmOrderId}
AND del_flag = 0
ORDER BY item_id ASC
</select>
<select id="selectWmsOrderByCode" resultType="com.klp.aps.domain.row.ApsWmsOrderRow">
SELECT order_id AS orderId, order_code AS orderCode
FROM wms_order
WHERE order_code = #{orderCode}
AND del_flag = 0
LIMIT 1
</select>
<insert id="insertWmsOrder" parameterType="com.klp.aps.domain.dto.ApsWmsOrderCreateReq">
INSERT INTO wms_order
(
order_code,
customer_id,
customer_name,
sales_manager,
order_status,
remark,
tax_amount,
no_tax_amount,
del_flag,
create_by,
update_by
)
VALUES
(
#{orderCode},
#{customerId},
#{customerName},
#{salesManager},
#{orderStatus},
#{remark},
#{taxAmount},
#{noTaxAmount},
0,
#{createBy},
#{updateBy}
)
</insert>
<insert id="insertWmsOrderDetail" parameterType="com.klp.aps.domain.dto.ApsWmsOrderDetailCreateReq">
INSERT INTO wms_order_detail
(
order_id,
product_id,
quantity,
unit,
remark,
tax_price,
no_tax_price,
group_id,
del_flag,
create_by,
update_by
)
VALUES
(
#{orderId},
#{productId},
#{quantity},
#{unit},
#{remark},
#{taxPrice},
#{noTaxPrice},
#{groupId},
IFNULL(#{delFlag}, 0),
#{createBy},
#{updateBy}
)
</insert>
<select id="selectAnyProductionLineId" resultType="long">
SELECT line_id
FROM wms_production_line
WHERE del_flag = 0
ORDER BY line_id ASC
LIMIT 1
</select>
<insert id="insertSchedulePlan"
parameterType="com.klp.aps.domain.entity.ApsSchedulePlanEntity"
useGeneratedKeys="true"
keyProperty="planId"
keyColumn="plan_id">
INSERT INTO wms_schedule_plan
(
plan_code,
version,
order_id,
status,
remark,
del_flag,
create_by,
update_by,
priority,
start_date,
end_date
)
VALUES
(
#{planCode},
#{version},
#{orderId},
#{status},
#{remark},
#{delFlag},
#{createBy},
#{updateBy},
#{priority},
#{startDate},
#{endDate}
)
</insert>
<insert id="insertSchedulePlanDetail"
parameterType="com.klp.aps.domain.entity.ApsSchedulePlanDetailEntity"
useGeneratedKeys="true"
keyProperty="detailId"
keyColumn="detail_id">
INSERT INTO wms_schedule_plan_detail
(
plan_id,
line_id,
task_id,
product_id,
quantity,
start_date,
end_date,
remark,
del_flag,
create_by,
update_by
)
VALUES
(
#{planId},
#{lineId},
#{taskId},
#{productId},
#{quantity},
#{startDate},
#{endDate},
#{remark},
#{delFlag},
#{createBy},
#{updateBy}
)
</insert>
</mapper>

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.klp.aps.mapper.ApsScheduleSheetMapper">
<select id="selectSheetRows"
parameterType="com.klp.aps.domain.dto.ApsScheduleSheetQueryReq"
resultType="com.klp.aps.domain.vo.ApsScheduleSheetRowVo">
SELECT
o.operation_id AS operationId,
o.line_id AS lineId,
l.line_name AS lineName,
o.plan_id AS planId,
p.plan_code AS planCode,
o.order_id AS orderId,
ord.order_code AS orderCode,
ord.sales_manager AS salesman,
ord.customer_name AS customerName,
o.product_id AS productId,
pr.product_name AS productName,
pr.material AS material,
pr.specification AS specification,
o.process_id AS processId,
ps.process_name AS processName,
o.plan_qty AS planQty,
DATE_FORMAT(o.start_time, '%Y-%m-%d %H:%i:%s') AS startTime,
DATE_FORMAT(o.end_time, '%Y-%m-%d %H:%i:%s') AS endTime,
-- 原料钢卷信息:优先来自 wms_schedule_operation_coil + wms_material_coil
COALESCE(
CAST(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawMaterialId')) AS UNSIGNED),
MAX(soc.coil_id)
) AS rawMaterialId,
COALESCE(
NULLIF(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawCoilNos')), ''),
GROUP_CONCAT(DISTINCT COALESCE(mc.current_coil_no, mc.enter_coil_no) ORDER BY COALESCE(mc.current_coil_no, mc.enter_coil_no) SEPARATOR ',')
) AS rawCoilNos,
COALESCE(
NULLIF(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawLocation')), ''),
GROUP_CONCAT(DISTINCT aw.actual_warehouse_name ORDER BY aw.actual_warehouse_name SEPARATOR ',')
) AS rawLocation,
COALESCE(
CAST(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawNetWeight')) AS DECIMAL(18,4)),
COALESCE(SUM(mc.net_weight), 0)
) AS rawNetWeight,
COALESCE(
NULLIF(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawPackaging')), ''),
MAX(mc.packaging_requirement)
) AS rawPackaging,
COALESCE(
NULLIF(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawEdgeReq')), ''),
MAX(mc.trimming_requirement)
) AS rawEdgeReq,
COALESCE(
NULLIF(JSON_UNQUOTE(JSON_EXTRACT(MAX(CASE WHEN JSON_VALID(o.remark) THEN o.remark ELSE NULL END), '$.rawCoatingType')), ''),
MAX(mc.coating_type)
) AS rawCoatingType
FROM wms_schedule_operation o
LEFT JOIN wms_schedule_plan p ON p.plan_id = o.plan_id
LEFT JOIN wms_order ord ON ord.order_id = o.order_id
LEFT JOIN wms_product pr ON pr.product_id = o.product_id
LEFT JOIN wms_processe ps ON ps.process_id = o.process_id
LEFT JOIN wms_production_line l ON l.line_id = o.line_id
LEFT JOIN wms_schedule_operation_coil soc ON soc.operation_id = o.operation_id AND soc.del_flag = 0
LEFT JOIN wms_material_coil mc ON mc.coil_id = soc.coil_id AND mc.del_flag = 0
LEFT JOIN wms_actual_warehouse aw ON aw.actual_warehouse_id = mc.actual_warehouse_id AND aw.del_flag = 0
WHERE o.end_time <![CDATA[>=]]> #{queryStart}
AND o.start_time <![CDATA[<=]]> #{queryEnd}
<if test="lineId != null">
AND o.line_id = #{lineId}
</if>
<if test="planId != null and planId != ''">
AND (
p.plan_code = #{planId}
OR (#{planId} REGEXP '^[0-9]+$' AND o.plan_id = CAST(#{planId} AS UNSIGNED))
)
</if>
<if test="orderId != null and orderId != ''">
AND (
ord.order_code = #{orderId}
OR (#{orderId} REGEXP '^[0-9]+$' AND o.order_id = CAST(#{orderId} AS UNSIGNED))
)
</if>
GROUP BY
o.operation_id,
o.line_id,
l.line_name,
o.plan_id,
p.plan_code,
o.order_id,
ord.order_code,
ord.sales_manager,
ord.customer_name,
o.product_id,
pr.product_name,
o.process_id,
ps.process_name,
o.plan_qty,
o.start_time,
o.end_time
ORDER BY o.line_id, o.start_time
</select>
<update id="deleteOperationCoils">
UPDATE wms_schedule_operation_coil
SET del_flag = 1,
update_by = #{updateBy},
update_time = CURRENT_TIMESTAMP
WHERE operation_id = #{operationId}
AND del_flag = 0
</update>
<insert id="insertOperationCoil">
INSERT INTO wms_schedule_operation_coil
(
operation_id,
coil_id,
create_by,
update_by,
del_flag
)
VALUES
(
#{operationId},
#{coilId},
#{createBy},
#{createBy},
0
)
</insert>
<update id="updateOperationSupplement">
UPDATE wms_schedule_operation
SET remark = #{remarkJson},
update_by = #{updateBy},
update_time = CURRENT_TIMESTAMP
WHERE operation_id = #{operationId}
</update>
</mapper>

Some files were not shown because too many files have changed in this diff Show More