From 42c9d125047b0e0236769d8eaa61b8f3cfdde3ee Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Sun, 4 Jan 2026 10:30:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(business):=20=E5=90=8C=E6=AD=A5G30?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=91=E9=80=81=E4=BB=BB=E5=8A=A1=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=8A=9F=E8=83=BD=E5=B9=B6=E6=89=A9=E5=B1=95=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E5=AE=9E=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 BizSendJob、BizSendJobGroup、BizSendJobItem 实体类用于发送任务管理 - 新增 BizSendTemplate、BizSendTemplateItem 实体类用于发送模板配置 - 实现发送模板的增删改查和批量保存功能 - 添加 DashboardController 提供首页仪表板统计接口 - 实现发送任务查询和执行服务 - 扩展 PdiPlan 相关实体类增加锌层厚度字段 - 优化 OPC 消息发送功能,支持多种数据类型转换 - 更新日志配置,调整错误日志处理策略 --- .idea/dbnavigator.xml | 12 + .../business/comm/OPC/OpcMessageSend.java | 55 ++- .../controller/BizSendTemplateController.java | 93 +++++ .../controller/DashboardController.java | 39 ++ .../controller/SendJobController.java | 90 +++++ .../controller/SteelGradeInfoController.java | 30 +- .../com/fizz/business/domain/BizSendJob.java | 60 +++ .../fizz/business/domain/BizSendJobGroup.java | 46 +++ .../fizz/business/domain/BizSendJobItem.java | 64 ++++ .../fizz/business/domain/BizSendTemplate.java | 41 +++ .../business/domain/BizSendTemplateItem.java | 49 +++ .../com/fizz/business/domain/CrmPdiPlan.java | 4 + .../fizz/business/domain/CrmPdoExcoil.java | 4 + .../com/fizz/business/domain/StdAlloy.java | 2 + .../business/domain/dto/SendJobCreateDTO.java | 75 ++++ .../business/domain/dto/SendJobQueryDTO.java | 21 ++ .../dto/SendTemplateItemsBatchSaveDTO.java | 23 ++ .../domain/vo/BizSendTemplateItemVO.java | 22 ++ .../business/domain/vo/BizSendTemplateVO.java | 21 ++ .../business/domain/vo/SendJobDetailVO.java | 26 ++ .../business/domain/vo/SendJobGroupVO.java | 27 ++ .../business/domain/vo/SendJobItemVO.java | 39 ++ .../domain/vo/SendJobLastSuccessVO.java | 26 ++ .../fizz/business/domain/vo/SendJobVO.java | 27 ++ .../com/fizz/business/dto/PdiPlanSubDTO.java | 4 + .../fizz/business/dto/PdoExCoilSubDTO.java | 4 + .../fizz/business/form/CrmPdiPlanForm.java | 2 +- .../fizz/business/form/CrmPdoExcoilForm.java | 4 + .../mapper/BizSendJobGroupMapper.java | 10 + .../business/mapper/BizSendJobItemMapper.java | 10 + .../business/mapper/BizSendJobMapper.java | 10 + .../mapper/BizSendTemplateItemMapper.java | 10 + .../mapper/BizSendTemplateMapper.java | 10 + .../business/mapper/PlanDashboardMapper.java | 20 + .../business/mapper/SegmentTotalMapper.java | 18 +- .../business/mapper/SteelGradeInfoMapper.java | 1 - .../business/service/DashboardService.java | 27 ++ .../service/IBizSendTemplateItemService.java | 23 ++ .../service/IBizSendTemplateService.java | 17 + .../service/ISendJobQueryService.java | 15 + .../business/service/ISendJobService.java | 41 +++ .../service/SteelGradeInfoService.java | 1 - .../impl/BizSendTemplateItemServiceImpl.java | 98 +++++ .../impl/BizSendTemplateServiceImpl.java | 55 +++ .../service/impl/DashboardServiceImpl.java | 113 ++++++ .../service/impl/PdoExCoilServiceImpl.java | 2 +- .../service/impl/SendJobQueryServiceImpl.java | 196 ++++++++++ .../service/impl/SendJobServiceImpl.java | 344 ++++++++++++++++++ .../service/manager/OpcMessageIdsManager.java | 10 + .../com/fizz/business/vo/CrmPdiPlanVO.java | 3 + .../fizz/business/vo/PdiPlanSetupInfoVO.java | 4 + .../java/com/fizz/business/vo/PdiPlanVO.java | 4 + .../java/com/fizz/business/vo/Plan2PdoVO.java | 4 + .../resources/mapper/PlanDashboardMapper.xml | 34 ++ .../resources/mapper/SegmentTotalMapper.xml | 22 +- ruoyi-admin/src/main/resources/logback.xml | 1 + ruoyi-admin/target/classes/logback.xml | 8 +- .../ruoyi/common/core/domain/BaseEntity.java | 5 +- 58 files changed, 1981 insertions(+), 45 deletions(-) create mode 100644 business/src/main/java/com/fizz/business/controller/BizSendTemplateController.java create mode 100644 business/src/main/java/com/fizz/business/controller/DashboardController.java create mode 100644 business/src/main/java/com/fizz/business/controller/SendJobController.java create mode 100644 business/src/main/java/com/fizz/business/domain/BizSendJob.java create mode 100644 business/src/main/java/com/fizz/business/domain/BizSendJobGroup.java create mode 100644 business/src/main/java/com/fizz/business/domain/BizSendJobItem.java create mode 100644 business/src/main/java/com/fizz/business/domain/BizSendTemplate.java create mode 100644 business/src/main/java/com/fizz/business/domain/BizSendTemplateItem.java create mode 100644 business/src/main/java/com/fizz/business/domain/dto/SendJobCreateDTO.java create mode 100644 business/src/main/java/com/fizz/business/domain/dto/SendJobQueryDTO.java create mode 100644 business/src/main/java/com/fizz/business/domain/dto/SendTemplateItemsBatchSaveDTO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateItemVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/SendJobDetailVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/SendJobGroupVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/SendJobItemVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/SendJobLastSuccessVO.java create mode 100644 business/src/main/java/com/fizz/business/domain/vo/SendJobVO.java create mode 100644 business/src/main/java/com/fizz/business/mapper/BizSendJobGroupMapper.java create mode 100644 business/src/main/java/com/fizz/business/mapper/BizSendJobItemMapper.java create mode 100644 business/src/main/java/com/fizz/business/mapper/BizSendJobMapper.java create mode 100644 business/src/main/java/com/fizz/business/mapper/BizSendTemplateItemMapper.java create mode 100644 business/src/main/java/com/fizz/business/mapper/BizSendTemplateMapper.java create mode 100644 business/src/main/java/com/fizz/business/mapper/PlanDashboardMapper.java create mode 100644 business/src/main/java/com/fizz/business/service/DashboardService.java create mode 100644 business/src/main/java/com/fizz/business/service/IBizSendTemplateItemService.java create mode 100644 business/src/main/java/com/fizz/business/service/IBizSendTemplateService.java create mode 100644 business/src/main/java/com/fizz/business/service/ISendJobQueryService.java create mode 100644 business/src/main/java/com/fizz/business/service/ISendJobService.java create mode 100644 business/src/main/java/com/fizz/business/service/impl/BizSendTemplateItemServiceImpl.java create mode 100644 business/src/main/java/com/fizz/business/service/impl/BizSendTemplateServiceImpl.java create mode 100644 business/src/main/java/com/fizz/business/service/impl/DashboardServiceImpl.java create mode 100644 business/src/main/java/com/fizz/business/service/impl/SendJobQueryServiceImpl.java create mode 100644 business/src/main/java/com/fizz/business/service/impl/SendJobServiceImpl.java create mode 100644 business/src/main/resources/mapper/PlanDashboardMapper.xml diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml index ba84fb8..5afd1a0 100644 --- a/.idea/dbnavigator.xml +++ b/.idea/dbnavigator.xml @@ -7,9 +7,21 @@ + + + + + + + + + + + + diff --git a/business/src/main/java/com/fizz/business/comm/OPC/OpcMessageSend.java b/business/src/main/java/com/fizz/business/comm/OPC/OpcMessageSend.java index c1effdd..92e25b9 100644 --- a/business/src/main/java/com/fizz/business/comm/OPC/OpcMessageSend.java +++ b/business/src/main/java/com/fizz/business/comm/OPC/OpcMessageSend.java @@ -61,6 +61,59 @@ public class OpcMessageSend { } } + /** + * 通用写入方法,用于向指定 OPC 节点写入一个值 + * @param address OPC 节点地址 (e.g., "ns=2;s=ProcessCGL.PLCLine.ExitCut.cutLength") + * @param value 要写入的值 + */ + public void writeNode(String address, Object value) { + try { + List entities = new ArrayList<>(); + entities.add(ReadWriteEntity.builder() + .identifier(address) + .value(value) + .build()); + miloService.writeToOpcUa(entities); + log.info("写入 OPC 成功, node={}, value={}", address, value); + } catch (Exception e) { + log.error("写入 OPC 失败, node={}, value={}, 原因: {}", address, value, e.getMessage(), e); + // 抛出运行时异常,以便上层调用者(如 SendJobServiceImpl)可以捕获并处理失败状态 + throw new RuntimeException("写入 OPC 失败: " + e.getMessage(), e); + } + } + + /** + * 写入 OPC 节点(增强版):根据字符串值尝试转换为数值/布尔,再写入 + * 规则: + * 1) 先尝试转 Integer + * 2) 再尝试转 Double + * 3) 再尝试转 Boolean(true/false/1/0) + * 4) 都不行则按原字符串写入 + */ + public void writeNode(String address, String valueRaw) { + Object v = valueRaw; + if (valueRaw != null) { + String s = valueRaw.trim(); + // boolean + if ("1".equals(s) || "0".equals(s) || "true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) { + v = ("1".equals(s) || "true".equalsIgnoreCase(s)); + } else { + // int + try { + v = Integer.parseInt(s); + } catch (Exception ignore) { + // double + try { + v = Double.parseDouble(s); + } catch (Exception ignore2) { + v = valueRaw; + } + } + } + } + writeNode(address, v); + } + private List getWriteEntities(OpcMessage msg, Map msgIds) { List entities = new ArrayList<>(); for (String key : msgIds.keySet()) { @@ -76,7 +129,7 @@ public class OpcMessageSend { } catch (NoSuchFieldException | IllegalAccessException e) { // 处理字段不存在或访问异常,可记录日志或设置默认值 e.printStackTrace(); - return new ArrayList<>(); + return new ArrayList<>(); } ReadWriteEntity entity = ReadWriteEntity.builder() diff --git a/business/src/main/java/com/fizz/business/controller/BizSendTemplateController.java b/business/src/main/java/com/fizz/business/controller/BizSendTemplateController.java new file mode 100644 index 0000000..43035da --- /dev/null +++ b/business/src/main/java/com/fizz/business/controller/BizSendTemplateController.java @@ -0,0 +1,93 @@ +package com.fizz.business.controller; + +import com.fizz.business.domain.BizSendTemplate; +import com.fizz.business.domain.BizSendTemplateItem; +import com.fizz.business.domain.dto.SendTemplateItemsBatchSaveDTO; +import com.fizz.business.domain.vo.BizSendTemplateVO; +import com.fizz.business.service.IBizSendTemplateItemService; +import com.fizz.business.service.IBizSendTemplateService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; + +/** + * 发送模板配置 Controller + */ +@RestController +@RequestMapping("/business/sendTemplate") +public class BizSendTemplateController extends BaseController { + + @Autowired + private IBizSendTemplateService templateService; + + @Autowired + private IBizSendTemplateItemService templateItemService; + + /** + * 按模板编码获取模板(含明细) + */ + @GetMapping("/{templateCode}") + public AjaxResult getByCode(@PathVariable String templateCode) { + BizSendTemplateVO vo = templateService.getTemplateWithItems(templateCode); + if (vo == null) { + return AjaxResult.error("Template not found"); + } + return AjaxResult.success(vo); + } + + /** + * 更新模板主表(如 deviceName) + */ + @PutMapping + public AjaxResult updateTemplate(@RequestBody BizSendTemplate template) { + if (template == null || template.getTemplateId() == null) { + return AjaxResult.error("templateId is required"); + } + template.setUpdateBy(SecurityUtils.getUsername()); + template.setUpdateTime(new Date()); + return toAjax(templateService.updateById(template)); + } + + /** + * 批量更新模板明细(address/defaultValueRaw/enabled等) + */ + @PutMapping("/items") + public AjaxResult updateTemplateItems(@RequestBody List items) { + if (items == null || items.isEmpty()) { + return AjaxResult.success(); + } + Date now = new Date(); + String username = SecurityUtils.getUsername(); + for (BizSendTemplateItem it : items) { + it.setUpdateBy(username); + it.setUpdateTime(now); + } + return toAjax(templateItemService.updateItemsBatch(items)); + } + + /** + * 模板明细批量保存(新增/更新/删除) + */ + @PutMapping("/items/batchSave") + public AjaxResult batchSaveItems(@RequestBody SendTemplateItemsBatchSaveDTO dto) { + if (dto == null || dto.getTemplateId() == null) { + return AjaxResult.error("templateId is required"); + } + try { + Boolean ok = templateItemService.batchSave( + dto.getTemplateId(), + dto.getItems(), + dto.getDeleteIds(), + SecurityUtils.getUsername() + ); + return toAjax(ok); + } catch (IllegalArgumentException e) { + return AjaxResult.error(e.getMessage()); + } + } +} diff --git a/business/src/main/java/com/fizz/business/controller/DashboardController.java b/business/src/main/java/com/fizz/business/controller/DashboardController.java new file mode 100644 index 0000000..e4472f7 --- /dev/null +++ b/business/src/main/java/com/fizz/business/controller/DashboardController.java @@ -0,0 +1,39 @@ +package com.fizz.business.controller; + + +import com.fizz.business.service.DashboardService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * 首页仪表板相关接口 + */ +@RestController +public class DashboardController extends BaseController { + + @Resource + private DashboardService dashboardService; + + /** + * 当前生产中的计划信息(crm_pdi_plan.status = 'PRODUCING') + */ + @GetMapping("/api/business/dashboard/currentPlan") + public AjaxResult getCurrentProducingPlan() { + return AjaxResult.success(dashboardService.getCurrentProducingPlan()); + } + + /** + * 当前生产卷的关键工艺参数 + * - 从 cpl_segment_total.total_values_json 解析 + * - 键名来自 DeviceEnum.paramFields + */ + @GetMapping("/api/business/dashboard/currentProcess") + public AjaxResult getCurrentProcessParams() { + return AjaxResult.success(dashboardService.getCurrentProcessParams()); + } +} + diff --git a/business/src/main/java/com/fizz/business/controller/SendJobController.java b/business/src/main/java/com/fizz/business/controller/SendJobController.java new file mode 100644 index 0000000..65b9260 --- /dev/null +++ b/business/src/main/java/com/fizz/business/controller/SendJobController.java @@ -0,0 +1,90 @@ +package com.fizz.business.controller; + +import com.fizz.business.domain.BizSendJob; +import com.fizz.business.domain.dto.SendJobCreateDTO; +import com.fizz.business.domain.dto.SendJobQueryDTO; +import com.fizz.business.domain.vo.SendJobDetailVO; +import com.fizz.business.service.ISendJobService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.fizz.business.domain.vo.SendJobLastSuccessVO; +import com.fizz.business.service.ISendJobQueryService; +import com.ruoyi.common.annotation.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 发送任务 Controller + */ +@RestController +@RequestMapping("/business/sendJob") +public class SendJobController extends BaseController { + + @Autowired + private ISendJobService sendJobService; + + @Autowired + private ISendJobQueryService sendJobQueryService; + + /** + * 创建发送任务 + */ + @Log(title = "发送任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult create(@Validated @RequestBody SendJobCreateDTO dto) { + Integer jobId = sendJobService.createSendJob(dto); + return success(jobId); + } + + /** + * 查询发送任务列表 + */ + @GetMapping("/list") + public TableDataInfo list(SendJobQueryDTO query) { + startPage(); // 使用BaseController的分页方法 + List list = sendJobService.selectSendJobList(query); + return getDataTable(list); + } + + /** + * 获取发送任务详情 + */ + @GetMapping("/{jobId}") + public AjaxResult getDetail(@PathVariable Integer jobId) { + SendJobDetailVO detail = sendJobService.selectSendJobDetail(jobId); + return success(detail); + } + + /** + * 删除发送任务(逻辑删除) + */ + @Log(title = "发送任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Integer[] jobIds) { + return toAjax(sendJobService.deleteSendJobByJobIds(jobIds)); + } + + /** + * 执行发送任务 + */ + @Log(title = "发送任务", businessType = BusinessType.UPDATE) + @PostMapping("/{jobId}/execute") + public AjaxResult execute(@PathVariable Integer jobId) { + return toAjax(sendJobService.executeSendJob(jobId)); + } + + /** + * 查询最近一次成功发送(用于界面显示上次发送时间 + 推荐上次值) + * @param groupType DRIVE / FURNACE + */ + @GetMapping("/lastSuccess") + public AjaxResult lastSuccess(@RequestParam String groupType) { + SendJobLastSuccessVO vo = sendJobQueryService.getLastSuccess(groupType); + return AjaxResult.success(vo); + } +} diff --git a/business/src/main/java/com/fizz/business/controller/SteelGradeInfoController.java b/business/src/main/java/com/fizz/business/controller/SteelGradeInfoController.java index 6c70f4b..0b5b1fd 100644 --- a/business/src/main/java/com/fizz/business/controller/SteelGradeInfoController.java +++ b/business/src/main/java/com/fizz/business/controller/SteelGradeInfoController.java @@ -30,57 +30,45 @@ public class SteelGradeInfoController { @Operation(summary = "查询钢种列表") public R> list(@RequestParam(value = "keyword", required = false) String keyword) { - // 使用 LambdaQueryWrapper 查询 StdAlloy 表中的数据,支持按名称/编号模糊查询 + // 严格按 cgldb.sql:查询 std_alloy(GRADEID, NAME) LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.select(StdAlloy::getGradeid, StdAlloy::getName); // 只查询 gradeId 和 name 字段 + queryWrapper.select(StdAlloy::getGradeid, StdAlloy::getName); if (StringUtils.isNotBlank(keyword)) { queryWrapper.like(StdAlloy::getName, keyword) .or() .like(StdAlloy::getGradeid, keyword); } - queryWrapper.orderByAsc(StdAlloy::getName); // 按 name 排序 + queryWrapper.orderByAsc(StdAlloy::getName); - // 查询 StdAlloy 数据 List stdAlloyList = steelGradeInfoService.list(queryWrapper); - // 使用 BeanUtils 将 StdAlloy 对象的字段映射到 StdAlloyVO List stdAlloyVOList = new ArrayList<>(); for (StdAlloy stdAlloy : stdAlloyList) { StdAlloyVO stdAlloyVO = new StdAlloyVO(); - BeanUtils.copyProperties(stdAlloy, stdAlloyVO); // 将 StdAlloy 属性复制到 StdAlloyVO + BeanUtils.copyProperties(stdAlloy, stdAlloyVO); stdAlloyVOList.add(stdAlloyVO); } - // 返回结果 return R.ok(stdAlloyVOList); } @GetMapping("/info") @Operation(summary ="询单个钢种详情") public R getSteelGradeInfo(@RequestParam Integer gradeid) { - - // 使用 LambdaQueryWrapper 查询 StdAlloy 表中的数据 - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(StdAlloy::getGradeid, gradeid); // 只查询 gradeId 和 name 字段 - - // 查询 StdAlloy 数据 - StdAlloy stdAlloyList = steelGradeInfoService.getById(gradeid); - - // 返回结果 - return R.ok(stdAlloyList); + return R.ok(steelGradeInfoService.getById(gradeid)); } @PostMapping("/add") @Operation(summary ="新增") - public R add(@RequestBody StdAlloy steelGradeInfo) { - return R.ok(steelGradeInfoService.save(steelGradeInfo)); + public R add(@RequestBody StdAlloy stdAlloy) { + return R.ok(steelGradeInfoService.save(stdAlloy)); } @PutMapping("/update") @Operation(summary ="更新") - public R update(@RequestBody StdAlloy steelGradeInfo) { - return R.ok(steelGradeInfoService.updateById(steelGradeInfo)); + public R update(@RequestBody StdAlloy stdAlloy) { + return R.ok(steelGradeInfoService.updateById(stdAlloy)); } @Operation(summary ="删除") diff --git a/business/src/main/java/com/fizz/business/domain/BizSendJob.java b/business/src/main/java/com/fizz/business/domain/BizSendJob.java new file mode 100644 index 0000000..4002865 --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/BizSendJob.java @@ -0,0 +1,60 @@ +package com.fizz.business.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 业务发送批次表 biz_send_job + * + * @author Cascade + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("biz_send_job") +public class BizSendJob extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 批次ID */ + @TableId(type = IdType.AUTO) + private Integer jobId; + + /** 业务唯一键, 用于幂等控制 */ + private String bizKey; + + /** 目标设备/产线名称 (如 CGL_LINE_1, FURNACE_A) */ + private String deviceName; + + /** 计划发送时间 */ + private Date planSendTime; + + /** 实际开始发送时间 */ + private Date actualSendTime; + + /** 发送完成时间 */ + private Date finishTime; + + /** 批次状态: PENDING, IN_PROGRESS, COMPLETED, PARTIAL_SUCCESS, FAILED */ + private String status; + + /** 操作员ID */ + private Long operatorId; + + /** 操作员姓名 */ + private String operatorName; + + // 解决 BaseEntity 字段导致的未知列问题 + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private String searchValue; + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private java.util.Map params; + +// GroupType + private String groupType; +} + diff --git a/business/src/main/java/com/fizz/business/domain/BizSendJobGroup.java b/business/src/main/java/com/fizz/business/domain/BizSendJobGroup.java new file mode 100644 index 0000000..5286f8b --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/BizSendJobGroup.java @@ -0,0 +1,46 @@ +package com.fizz.business.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 业务发送分组表 biz_send_job_group + * + * @author Cascade + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("biz_send_job_group") +public class BizSendJobGroup extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 分组ID */ + @TableId(type = IdType.AUTO) + private Integer groupId; + + /** 所属批次ID */ + private Integer jobId; + + /** 在本批次内的组序号 */ + private Integer groupNo; + + /** 组类型: DRIVE(传动), FURNACE(炉火) */ + private String groupType; + + /** 组名称 (可选) */ + private String groupName; + + /** 分组状态: PENDING, IN_PROGRESS, COMPLETED, FAILED */ + private String status; + + // 解决 BaseEntity 字段导致的未知列问题 + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private String searchValue; + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private java.util.Map params; +} + diff --git a/business/src/main/java/com/fizz/business/domain/BizSendJobItem.java b/business/src/main/java/com/fizz/business/domain/BizSendJobItem.java new file mode 100644 index 0000000..301022d --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/BizSendJobItem.java @@ -0,0 +1,64 @@ +package com.fizz.business.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 业务发送项历史表 biz_send_job_item + * + * @author Cascade + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("biz_send_job_item") +public class BizSendJobItem extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 发送项ID */ + @TableId(type = IdType.AUTO) + private Integer itemId; + + /** 所属批次ID (冗余) */ + private Integer jobId; + + /** 所属分组ID */ + private Integer groupId; + + /** 参数业务编码 */ + private String paramCode; + + /** 设定地址 (OPC地址) */ + private String address; + + /** 设定的原始值 */ + private String valueRaw; + + /** 设定值的数值形式 */ + private BigDecimal valueNum; + + /** 参数的设定时间 */ + private Date setTime; + + /** 发送结果: PENDING, SUCCESS, FAILED */ + private String resultStatus; + + /** 结果消息 */ + private String resultMsg; + + /** 重试次数 */ + private Integer retryCount; + + // 解决 BaseEntity 字段导致的未知列问题 + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private String searchValue; + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private java.util.Map params; +} + diff --git a/business/src/main/java/com/fizz/business/domain/BizSendTemplate.java b/business/src/main/java/com/fizz/business/domain/BizSendTemplate.java new file mode 100644 index 0000000..1e9480a --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/BizSendTemplate.java @@ -0,0 +1,41 @@ +package com.fizz.business.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 发送默认模板主表 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("biz_send_template") +public class BizSendTemplate extends BaseEntity { + @TableId(type = IdType.AUTO) + private Integer templateId; + + // 解决 MyBatis-Plus 父类字段映射导致的 search_value 报错 + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private String searchValue; + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private java.util.Map params; + + /** 模板编码 */ + private String templateCode; + + /** 模板名称 */ + private String templateName; + + /** 默认设备名称 */ + private String deviceName; + + /** 组类型 DRIVE / FURNACE */ + private String groupType; + + /** 是否启用 */ + private Integer enabled; +} + diff --git a/business/src/main/java/com/fizz/business/domain/BizSendTemplateItem.java b/business/src/main/java/com/fizz/business/domain/BizSendTemplateItem.java new file mode 100644 index 0000000..b7c087c --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/BizSendTemplateItem.java @@ -0,0 +1,49 @@ +package com.fizz.business.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 发送默认模板明细表 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("biz_send_template_item") +public class BizSendTemplateItem extends BaseEntity { + @TableId(type = IdType.AUTO) + private Integer templateItemId; + + private Integer templateId; + + /** 明细序号 */ + private Integer itemNo; + + /** 参数编码 */ + private String paramCode; + + /** 英文显示名 */ + private String labelEn; + + /** 英文分组名 */ + private String groupNameEn; + + /** OPC地址 */ + private String address; + + /** 默认值(字符串) */ + private String defaultValueRaw; + + /** 是否启用 */ + private Integer enabled; + + // 解决 BaseEntity 字段导致的未知列问题 + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private String searchValue; + @com.baomidou.mybatisplus.annotation.TableField(exist = false) + private java.util.Map params; +} + diff --git a/business/src/main/java/com/fizz/business/domain/CrmPdiPlan.java b/business/src/main/java/com/fizz/business/domain/CrmPdiPlan.java index ded6278..9c6327a 100644 --- a/business/src/main/java/com/fizz/business/domain/CrmPdiPlan.java +++ b/business/src/main/java/com/fizz/business/domain/CrmPdiPlan.java @@ -313,4 +313,8 @@ public class CrmPdiPlan implements Serializable { @Schema(description = "原卷号") private String originCoilid; + + //锌层厚度 zinc_coating_thickness + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } diff --git a/business/src/main/java/com/fizz/business/domain/CrmPdoExcoil.java b/business/src/main/java/com/fizz/business/domain/CrmPdoExcoil.java index 1ea33b3..ea35ea4 100644 --- a/business/src/main/java/com/fizz/business/domain/CrmPdoExcoil.java +++ b/business/src/main/java/com/fizz/business/domain/CrmPdoExcoil.java @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.io.Serializable; +import java.math.BigDecimal; import java.sql.Timestamp; import java.time.LocalDateTime; @@ -130,4 +131,7 @@ public class CrmPdoExcoil implements Serializable { @Schema(description = "计划来源:L3-L3计划,MANUAL-人工") private String planOrigin; + + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } \ No newline at end of file diff --git a/business/src/main/java/com/fizz/business/domain/StdAlloy.java b/business/src/main/java/com/fizz/business/domain/StdAlloy.java index a8c259b..d79c4f7 100644 --- a/business/src/main/java/com/fizz/business/domain/StdAlloy.java +++ b/business/src/main/java/com/fizz/business/domain/StdAlloy.java @@ -2,10 +2,12 @@ package com.fizz.business.domain; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; @Data +@TableName("std_alloy") public class StdAlloy { @TableId("GRADEID") diff --git a/business/src/main/java/com/fizz/business/domain/dto/SendJobCreateDTO.java b/business/src/main/java/com/fizz/business/domain/dto/SendJobCreateDTO.java new file mode 100644 index 0000000..ef9b9aa --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/dto/SendJobCreateDTO.java @@ -0,0 +1,75 @@ +package com.fizz.business.domain.dto; + +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.List; + +/** + * 创建发送任务 DTO + */ +@Data +public class SendJobCreateDTO { + + /** 目标设备/产线名称 (如 CGL_LINE_1, FURNACE_A) */ + @NotBlank(message = "deviceName不能为空") + private String deviceName; + + /** 计划发送时间 */ + private Date planSendTime; + + /** 操作员ID */ + private Long operatorId; + + /** 操作员姓名 */ + private String operatorName; + + /** 分组列表 */ + @Valid + @NotNull(message = "groups不能为空") + @Size(min = 1, message = "至少需要一个分组") + private List groups; + + @Data + public static class GroupDTO { + + /** 在本批次内的组序号 */ + @NotNull(message = "groupNo不能为空") + private Integer groupNo; + + /** 组类型: DRIVE(传动), FURNACE(炉火) */ + @NotBlank(message = "groupType不能为空") + private String groupType; + + /** 组名称 (可选) */ + private String groupName; + + /** 参数项列表 */ + @Valid + @NotNull(message = "items不能为空") + @Size(min = 1, message = "至少需要一个参数项") + private List items; + } + + @Data + public static class ItemDTO { + + /** 参数业务编码 */ + private String paramCode; + + /** 设定地址 (OPC地址) */ + @NotBlank(message = "address不能为空") + private String address; + + /** 设定的原始值 */ + private String valueRaw; + + /** 参数的设定时间 */ + private Date setTime; + } +} + diff --git a/business/src/main/java/com/fizz/business/domain/dto/SendJobQueryDTO.java b/business/src/main/java/com/fizz/business/domain/dto/SendJobQueryDTO.java new file mode 100644 index 0000000..19f4e4c --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/dto/SendJobQueryDTO.java @@ -0,0 +1,21 @@ +package com.fizz.business.domain.dto; + +import lombok.Data; + +/** + * 发送任务查询 DTO + * 说明:分页参数沿用 RuoYi BaseController 的 startPage() 机制,从 request 里取 pageNum/pageSize。 + */ +@Data +public class SendJobQueryDTO { + + /** 设备/产线名称 */ + private String deviceName; + + /** 状态: PENDING, IN_PROGRESS, COMPLETED, PARTIAL_SUCCESS, FAILED, DELETED */ + private String status; + + /** 分组类型: DRIVE / FURNACE */ + private String groupType; +} + diff --git a/business/src/main/java/com/fizz/business/domain/dto/SendTemplateItemsBatchSaveDTO.java b/business/src/main/java/com/fizz/business/domain/dto/SendTemplateItemsBatchSaveDTO.java new file mode 100644 index 0000000..e19e965 --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/dto/SendTemplateItemsBatchSaveDTO.java @@ -0,0 +1,23 @@ +package com.fizz.business.domain.dto; + +import com.fizz.business.domain.BizSendTemplateItem; +import lombok.Data; + +import java.util.List; + +/** + * 模板明细批量保存(新增/更新/删除) + */ +@Data +public class SendTemplateItemsBatchSaveDTO { + + /** 模板ID */ + private Integer templateId; + + /** 需要新增/更新的明细 */ + private List items; + + /** 需要删除的明细ID */ + private List deleteIds; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateItemVO.java b/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateItemVO.java new file mode 100644 index 0000000..beb9107 --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateItemVO.java @@ -0,0 +1,22 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +/** + * 模板明细 VO + */ +@Data +public class BizSendTemplateItemVO { + private Integer templateItemId; + private Integer templateId; + private Integer itemNo; + private String paramCode; + private String labelEn; + private String groupNameEn; + private String address; + private String defaultValueRaw; + + private Boolean enabled; + +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateVO.java b/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateVO.java new file mode 100644 index 0000000..316dccd --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/BizSendTemplateVO.java @@ -0,0 +1,21 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 模板主VO(含明细) + */ +@Data +public class BizSendTemplateVO { + private Integer templateId; + private String templateCode; + private String templateName; + private String deviceName; + private String groupType; + private Integer enabled; + + private List items; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/SendJobDetailVO.java b/business/src/main/java/com/fizz/business/domain/vo/SendJobDetailVO.java new file mode 100644 index 0000000..ecf8e74 --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/SendJobDetailVO.java @@ -0,0 +1,26 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; +import java.util.List; + +/** + * 发送任务详情 VO + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SendJobDetailVO extends SendJobVO { + + private Date planSendTime; + + private Date actualSendTime; + + private Date finishTime; + + private String remark; + + private List groups; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/SendJobGroupVO.java b/business/src/main/java/com/fizz/business/domain/vo/SendJobGroupVO.java new file mode 100644 index 0000000..744a327 --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/SendJobGroupVO.java @@ -0,0 +1,27 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 发送任务分组 VO + */ +@Data +public class SendJobGroupVO { + + private Integer groupId; + + private Integer jobId; + + private Integer groupNo; + + private String groupType; + + private String groupName; + + private String status; + + private List items; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/SendJobItemVO.java b/business/src/main/java/com/fizz/business/domain/vo/SendJobItemVO.java new file mode 100644 index 0000000..b949d8b --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/SendJobItemVO.java @@ -0,0 +1,39 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * 发送任务明细项 VO + */ +@Data +public class SendJobItemVO { + + private Integer itemId; + + private Integer jobId; + + private Integer groupId; + + private String paramCode; + + private String address; + + private String valueRaw; + + private BigDecimal valueNum; + + private Date setTime; + + private String resultStatus; + + private String resultMsg; + + private Integer retryCount; + + private LocalDateTime createTime; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/SendJobLastSuccessVO.java b/business/src/main/java/com/fizz/business/domain/vo/SendJobLastSuccessVO.java new file mode 100644 index 0000000..4b52f6a --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/SendJobLastSuccessVO.java @@ -0,0 +1,26 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +import java.util.Date; +import java.util.Map; + +/** + * 最近一次成功发送结果(用于前端推荐默认值 + 显示上次发送时间) + */ +@Data +public class SendJobLastSuccessVO { + + /** 最近一次成功发送时间(job.finish_time) */ + private Date lastSendTime; + + /** paramCode -> valueRaw */ + private Map values; + + /** 最近一次成功的jobId(可选) */ + private Integer jobId; + + //IsFromHistory + private Boolean isFromHistory; +} + diff --git a/business/src/main/java/com/fizz/business/domain/vo/SendJobVO.java b/business/src/main/java/com/fizz/business/domain/vo/SendJobVO.java new file mode 100644 index 0000000..2804b4a --- /dev/null +++ b/business/src/main/java/com/fizz/business/domain/vo/SendJobVO.java @@ -0,0 +1,27 @@ +package com.fizz.business.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 发送任务列表 VO + */ +@Data +public class SendJobVO { + + private Integer jobId; + + private String bizKey; + + private String deviceName; + + private String status; + + private Long operatorId; + + private String operatorName; + + private Date createTime; +} + diff --git a/business/src/main/java/com/fizz/business/dto/PdiPlanSubDTO.java b/business/src/main/java/com/fizz/business/dto/PdiPlanSubDTO.java index ed72cc9..a81405f 100644 --- a/business/src/main/java/com/fizz/business/dto/PdiPlanSubDTO.java +++ b/business/src/main/java/com/fizz/business/dto/PdiPlanSubDTO.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.Setter; import java.io.Serializable; +import java.math.BigDecimal; /** * 子计划数据 @@ -57,4 +58,7 @@ public class PdiPlanSubDTO implements Serializable { @Schema(description = "实际重量") private Double actualWeight; + + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } diff --git a/business/src/main/java/com/fizz/business/dto/PdoExCoilSubDTO.java b/business/src/main/java/com/fizz/business/dto/PdoExCoilSubDTO.java index 41f661b..650e93f 100644 --- a/business/src/main/java/com/fizz/business/dto/PdoExCoilSubDTO.java +++ b/business/src/main/java/com/fizz/business/dto/PdoExCoilSubDTO.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.Setter; import java.io.Serializable; +import java.math.BigDecimal; import java.time.LocalDateTime; /** @@ -71,4 +72,7 @@ public class PdoExCoilSubDTO implements Serializable { @Schema(description = "结束时间") private LocalDateTime endTime; + + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } diff --git a/business/src/main/java/com/fizz/business/form/CrmPdiPlanForm.java b/business/src/main/java/com/fizz/business/form/CrmPdiPlanForm.java index e696c1e..ed7f669 100644 --- a/business/src/main/java/com/fizz/business/form/CrmPdiPlanForm.java +++ b/business/src/main/java/com/fizz/business/form/CrmPdiPlanForm.java @@ -115,5 +115,5 @@ public class CrmPdiPlanForm { private BigDecimal tailendGaugeLength; // 尾端测厚长度(mm) private String origin; // 产地 private String originCoilid; // 原卷号 - + private BigDecimal zincCoatingThickness;//锌层厚度 } diff --git a/business/src/main/java/com/fizz/business/form/CrmPdoExcoilForm.java b/business/src/main/java/com/fizz/business/form/CrmPdoExcoilForm.java index 2326d15..bad0f62 100644 --- a/business/src/main/java/com/fizz/business/form/CrmPdoExcoilForm.java +++ b/business/src/main/java/com/fizz/business/form/CrmPdoExcoilForm.java @@ -3,6 +3,7 @@ package com.fizz.business.form; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDateTime; @Data @@ -18,4 +19,7 @@ public class CrmPdoExcoilForm { @Schema(description = "结束日期") private String endDate; + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; + } diff --git a/business/src/main/java/com/fizz/business/mapper/BizSendJobGroupMapper.java b/business/src/main/java/com/fizz/business/mapper/BizSendJobGroupMapper.java new file mode 100644 index 0000000..6cf14d5 --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/BizSendJobGroupMapper.java @@ -0,0 +1,10 @@ +package com.fizz.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fizz.business.domain.BizSendJobGroup; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BizSendJobGroupMapper extends BaseMapper { +} + diff --git a/business/src/main/java/com/fizz/business/mapper/BizSendJobItemMapper.java b/business/src/main/java/com/fizz/business/mapper/BizSendJobItemMapper.java new file mode 100644 index 0000000..3639d7a --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/BizSendJobItemMapper.java @@ -0,0 +1,10 @@ +package com.fizz.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fizz.business.domain.BizSendJobItem; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BizSendJobItemMapper extends BaseMapper { +} + diff --git a/business/src/main/java/com/fizz/business/mapper/BizSendJobMapper.java b/business/src/main/java/com/fizz/business/mapper/BizSendJobMapper.java new file mode 100644 index 0000000..e44c172 --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/BizSendJobMapper.java @@ -0,0 +1,10 @@ +package com.fizz.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fizz.business.domain.BizSendJob; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BizSendJobMapper extends BaseMapper { +} + diff --git a/business/src/main/java/com/fizz/business/mapper/BizSendTemplateItemMapper.java b/business/src/main/java/com/fizz/business/mapper/BizSendTemplateItemMapper.java new file mode 100644 index 0000000..4487a17 --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/BizSendTemplateItemMapper.java @@ -0,0 +1,10 @@ +package com.fizz.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fizz.business.domain.BizSendTemplateItem; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BizSendTemplateItemMapper extends BaseMapper { +} + diff --git a/business/src/main/java/com/fizz/business/mapper/BizSendTemplateMapper.java b/business/src/main/java/com/fizz/business/mapper/BizSendTemplateMapper.java new file mode 100644 index 0000000..6eab0a6 --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/BizSendTemplateMapper.java @@ -0,0 +1,10 @@ +package com.fizz.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fizz.business.domain.BizSendTemplate; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BizSendTemplateMapper extends BaseMapper { +} + diff --git a/business/src/main/java/com/fizz/business/mapper/PlanDashboardMapper.java b/business/src/main/java/com/fizz/business/mapper/PlanDashboardMapper.java new file mode 100644 index 0000000..df1a41d --- /dev/null +++ b/business/src/main/java/com/fizz/business/mapper/PlanDashboardMapper.java @@ -0,0 +1,20 @@ +package com.fizz.business.mapper; + +import org.apache.ibatis.annotations.Mapper; + +import java.util.Map; + +/** + * 计划相关仪表板 Mapper(crm_pdi_plan) + */ +@Mapper +public interface PlanDashboardMapper { + + /** + * 查询当前生产中的计划(status = 'PRODUCING') + * 返回字段至少包括: + * - coilid, planid, steel_grade, entry_weight, entry_thick, entry_width, start_date 等 + */ + Map selectCurrentProducingPlan(); +} + diff --git a/business/src/main/java/com/fizz/business/mapper/SegmentTotalMapper.java b/business/src/main/java/com/fizz/business/mapper/SegmentTotalMapper.java index cc74d2c..9124dcb 100644 --- a/business/src/main/java/com/fizz/business/mapper/SegmentTotalMapper.java +++ b/business/src/main/java/com/fizz/business/mapper/SegmentTotalMapper.java @@ -1,18 +1,16 @@ package com.fizz.business.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.fizz.business.domain.SegmentTotal; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; /** - *

- * 各机张力,电流等架跟踪表 Mapper 接口 - *

- * - * @author baomidou - * @since 2023-10-26 + * 带钢段工艺参数 Mapper(cpl_segment_total) */ @Mapper -public interface SegmentTotalMapper extends BaseMapper { - SegmentTotal getLatestRecord(); +public interface SegmentTotalMapper { + + /** + * 根据入库钢卷号查询最新一段的 total_values_json + */ + String selectLatestTotalValuesJsonByCoilId(@Param("coilId") String coilId); } diff --git a/business/src/main/java/com/fizz/business/mapper/SteelGradeInfoMapper.java b/business/src/main/java/com/fizz/business/mapper/SteelGradeInfoMapper.java index 1bf6504..7690059 100644 --- a/business/src/main/java/com/fizz/business/mapper/SteelGradeInfoMapper.java +++ b/business/src/main/java/com/fizz/business/mapper/SteelGradeInfoMapper.java @@ -3,7 +3,6 @@ package com.fizz.business.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.fizz.business.domain.StdAlloy; -import com.fizz.business.domain.SteelGradeInfo; import org.apache.ibatis.annotations.Mapper; @Mapper diff --git a/business/src/main/java/com/fizz/business/service/DashboardService.java b/business/src/main/java/com/fizz/business/service/DashboardService.java new file mode 100644 index 0000000..e503e3c --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/DashboardService.java @@ -0,0 +1,27 @@ +package com.fizz.business.service; + +import java.util.Map; + +/** + * 首页仪表板统计服务 + */ +public interface DashboardService { + + /** + * 当前生产中的计划信息(包含卷号、规格、时间等) + */ + Map getCurrentProducingPlan(); + + /** + * 当前生产卷的关键工艺参数 + * 结构说明(示例): + * { + * "coilId": "...", + * "entrySection": { "POR1": { ... }, "POR2": { ... }, ... }, + * "processSection":{ "CLEAN": { ... }, "FUR1": { ... }, ... }, + * "exitSection": { "TR": { ... }, "CXL1": { ... }, ... } + * } + */ + Map getCurrentProcessParams(); +} + diff --git a/business/src/main/java/com/fizz/business/service/IBizSendTemplateItemService.java b/business/src/main/java/com/fizz/business/service/IBizSendTemplateItemService.java new file mode 100644 index 0000000..edf2f2e --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/IBizSendTemplateItemService.java @@ -0,0 +1,23 @@ +package com.fizz.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fizz.business.domain.BizSendTemplateItem; + +import java.util.List; + +/** + * 发送模板明细 Service + */ +public interface IBizSendTemplateItemService extends IService { + + /** + * 批量更新模板明细(仅更新已有ID) + */ + Boolean updateItemsBatch(List items); + + /** + * 批量保存模板明细(新增/更新/删除) + */ + Boolean batchSave(Integer templateId, List items, List deleteIds, String username); +} + diff --git a/business/src/main/java/com/fizz/business/service/IBizSendTemplateService.java b/business/src/main/java/com/fizz/business/service/IBizSendTemplateService.java new file mode 100644 index 0000000..230e4d2 --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/IBizSendTemplateService.java @@ -0,0 +1,17 @@ +package com.fizz.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fizz.business.domain.BizSendTemplate; +import com.fizz.business.domain.vo.BizSendTemplateVO; + +/** + * 发送默认模板 Service + */ +public interface IBizSendTemplateService extends IService { + + /** + * 按模板编码获取模板(含明细) + */ + BizSendTemplateVO getTemplateWithItems(String templateCode); +} + diff --git a/business/src/main/java/com/fizz/business/service/ISendJobQueryService.java b/business/src/main/java/com/fizz/business/service/ISendJobQueryService.java new file mode 100644 index 0000000..2dcf5c8 --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/ISendJobQueryService.java @@ -0,0 +1,15 @@ +package com.fizz.business.service; + +import com.fizz.business.domain.vo.SendJobLastSuccessVO; + +/** + * 发送任务查询扩展(用于推荐值、上次发送时间) + */ +public interface ISendJobQueryService { + + /** + * 查询最近一次成功发送(按 groupType 过滤:DRIVE / FURNACE) + */ + SendJobLastSuccessVO getLastSuccess(String groupType); +} + diff --git a/business/src/main/java/com/fizz/business/service/ISendJobService.java b/business/src/main/java/com/fizz/business/service/ISendJobService.java new file mode 100644 index 0000000..6403ced --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/ISendJobService.java @@ -0,0 +1,41 @@ +package com.fizz.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fizz.business.domain.BizSendJob; +import com.fizz.business.domain.dto.SendJobCreateDTO; +import com.fizz.business.domain.dto.SendJobQueryDTO; +import com.fizz.business.domain.vo.SendJobDetailVO; + +import java.util.List; + +/** + * 发送任务 Service + */ +public interface ISendJobService extends IService { + + /** + * 创建发送任务(包含分组与明细) + */ + Integer createSendJob(SendJobCreateDTO dto); + + /** + * 查询发送任务列表(分页由 Controller 的 startPage() 控制) + */ + List selectSendJobList(SendJobQueryDTO query); + + /** + * 查询发送任务详情(包含分组与明细) + */ + SendJobDetailVO selectSendJobDetail(Integer jobId); + + /** + * 删除任务(逻辑删除:status=DELETED) + */ + Boolean deleteSendJobByJobIds(Integer[] jobIds); + + /** + * 执行发送:写入 OPC,并将发送结果保存为历史(更新 job/group/item 状态) + */ + Boolean executeSendJob(Integer jobId); +} + diff --git a/business/src/main/java/com/fizz/business/service/SteelGradeInfoService.java b/business/src/main/java/com/fizz/business/service/SteelGradeInfoService.java index 0e8b403..1821ac6 100644 --- a/business/src/main/java/com/fizz/business/service/SteelGradeInfoService.java +++ b/business/src/main/java/com/fizz/business/service/SteelGradeInfoService.java @@ -2,7 +2,6 @@ package com.fizz.business.service; import com.baomidou.mybatisplus.extension.service.IService; import com.fizz.business.domain.StdAlloy; -import com.fizz.business.domain.SteelGradeInfo; public interface SteelGradeInfoService extends IService { } \ No newline at end of file diff --git a/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateItemServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateItemServiceImpl.java new file mode 100644 index 0000000..864bb2f --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateItemServiceImpl.java @@ -0,0 +1,98 @@ +package com.fizz.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fizz.business.domain.BizSendTemplateItem; +import com.fizz.business.mapper.BizSendTemplateItemMapper; +import com.fizz.business.service.IBizSendTemplateItemService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +public class BizSendTemplateItemServiceImpl extends ServiceImpl implements IBizSendTemplateItemService { + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateItemsBatch(List items) { + if (items == null || items.isEmpty()) { + return true; + } + // MyBatis-Plus 批量更新:这里简单循环 updateById(数量约40~100可接受) + for (BizSendTemplateItem it : items) { + this.updateById(it); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchSave(Integer templateId, List items, List deleteIds, String username) { + Date now = new Date(); + + // 1) 删除(物理删除)。如果你们希望逻辑删除,可在此改为 enabled=0 / deleted 标记。 + if (deleteIds != null && !deleteIds.isEmpty()) { + this.removeByIds(deleteIds); + } + + // 2) 新增/更新 + if (items == null) { + items = Collections.emptyList(); + } + + // 基础校验:templateId 必须一致 + for (BizSendTemplateItem it : items) { + if (it.getTemplateId() == null) { + it.setTemplateId(templateId); + } + } + + // 校验:同一 templateId 下 paramCode 唯一 + // - 先校验本次提交内是否重复 + Set seen = new HashSet<>(); + for (BizSendTemplateItem it : items) { + if (it.getParamCode() == null || it.getParamCode().trim().isEmpty()) { + throw new IllegalArgumentException("paramCode is required"); + } + String code = it.getParamCode().trim(); + if (!seen.add(code)) { + throw new IllegalArgumentException("Duplicate paramCode in request: " + code); + } + it.setParamCode(code); + } + + // - 再校验数据库里是否已存在同 paramCode(排除自身ID) + for (BizSendTemplateItem it : items) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(BizSendTemplateItem::getTemplateId, templateId) + .eq(BizSendTemplateItem::getParamCode, it.getParamCode()); + if (it.getTemplateItemId() != null) { + qw.ne(BizSendTemplateItem::getTemplateItemId, it.getTemplateItemId()); + } + long cnt = this.count(qw); + if (cnt > 0) { + throw new IllegalArgumentException("paramCode already exists: " + it.getParamCode()); + } + } + + for (BizSendTemplateItem it : items) { + it.setTemplateId(templateId); + if (it.getTemplateItemId() == null) { + it.setCreateBy(username); + it.setCreateTime(now); + it.setUpdateBy(username); + it.setUpdateTime(now); + this.save(it); + } else { + it.setUpdateBy(username); + it.setUpdateTime(now); + this.updateById(it); + } + } + + return true; + } +} + + diff --git a/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateServiceImpl.java new file mode 100644 index 0000000..7a15983 --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/impl/BizSendTemplateServiceImpl.java @@ -0,0 +1,55 @@ +package com.fizz.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fizz.business.domain.BizSendTemplate; +import com.fizz.business.domain.BizSendTemplateItem; +import com.fizz.business.domain.vo.BizSendTemplateItemVO; +import com.fizz.business.domain.vo.BizSendTemplateVO; +import com.fizz.business.mapper.BizSendTemplateItemMapper; +import com.fizz.business.mapper.BizSendTemplateMapper; +import com.fizz.business.service.IBizSendTemplateService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class BizSendTemplateServiceImpl extends ServiceImpl implements IBizSendTemplateService { + + @Autowired + private BizSendTemplateItemMapper templateItemMapper; + + @Override + public BizSendTemplateVO getTemplateWithItems(String templateCode) { + BizSendTemplate template = this.lambdaQuery() + .eq(BizSendTemplate::getTemplateCode, templateCode) + .eq(BizSendTemplate::getEnabled, 1) + .one(); + + if (template == null) { + return null; + } + + List items = templateItemMapper.selectList( + new LambdaQueryWrapper() + .eq(BizSendTemplateItem::getTemplateId, template.getTemplateId()) + .eq(BizSendTemplateItem::getEnabled, 1) + .orderByAsc(BizSendTemplateItem::getItemNo) + ); + + BizSendTemplateVO vo = new BizSendTemplateVO(); + BeanUtils.copyProperties(template, vo); + + List itemVOs = items.stream().map(item -> { + BizSendTemplateItemVO it = new BizSendTemplateItemVO(); + BeanUtils.copyProperties(item, it); + return it; + }).collect(Collectors.toList()); + + vo.setItems(itemVOs); + return vo; + } +} diff --git a/business/src/main/java/com/fizz/business/service/impl/DashboardServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/DashboardServiceImpl.java new file mode 100644 index 0000000..5fbba29 --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/impl/DashboardServiceImpl.java @@ -0,0 +1,113 @@ +package com.fizz.business.service.impl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fizz.business.constants.enums.DeviceEnum; +import com.fizz.business.mapper.PlanDashboardMapper; +import com.fizz.business.mapper.SegmentTotalMapper; +import com.fizz.business.service.DashboardService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.*; + +/** + * 首页仪表板统计服务实现 + */ +@Service +public class DashboardServiceImpl implements DashboardService { + + @Resource + private PlanDashboardMapper planDashboardMapper; + + @Resource + private SegmentTotalMapper segmentTotalMapper; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public Map getCurrentProducingPlan() { + // 查询当前 PRODUCING 的计划 + Map plan = planDashboardMapper.selectCurrentProducingPlan(); + if (plan == null) { + return Collections.emptyMap(); + } + return plan; + } + + @Override + public Map getCurrentProcessParams() { + Map result = new HashMap<>(); + + // 1. 当前生产计划 + Map plan = planDashboardMapper.selectCurrentProducingPlan(); + if (plan == null || plan.get("coilid") == null) { + return result; + } + String coilId = String.valueOf(plan.get("coilid")); + result.put("coilId", coilId); + + // 2. 查询该卷最新一段的 total_values_json + String totalValuesJson = segmentTotalMapper.selectLatestTotalValuesJsonByCoilId(coilId); + if (totalValuesJson == null || totalValuesJson.isEmpty()) { + return result; + } + + try { + // 3. 解析 JSON -> Map + Map valuesMap = objectMapper.readValue( + totalValuesJson, + new TypeReference>() {} + ); + + // 4. 按设备 + 段类型整理数据 + Map entrySection = new LinkedHashMap<>(); + Map processSection = new LinkedHashMap<>(); + Map exitSection = new LinkedHashMap<>(); + + for (DeviceEnum device : DeviceEnum.values()) { + List fields = device.getParamFields(); + if (fields == null || fields.isEmpty()) { + continue; + } + + Map devData = new LinkedHashMap<>(); + for (String field : fields) { + if (valuesMap.containsKey(field)) { + devData.put(field, valuesMap.get(field)); + } + } + + if (devData.isEmpty()) { + continue; + } + + // key 用设备英文枚举名,如 POR1/FUR1/TM/TL/COAT 等 + String key = device.name(); + switch (device.getSectionType()) { + case ENTRY: + entrySection.put(key, devData); + break; + case PROCESS: + processSection.put(key, devData); + break; + case EXIT: + exitSection.put(key, devData); + break; + default: + break; + } + } + + result.put("entrySection", entrySection); + result.put("processSection", processSection); + result.put("exitSection", exitSection); + } catch (Exception e) { + // 解析异常时,可按你项目的日志方案记录 + e.printStackTrace(); + } + + return result; + } +} + diff --git a/business/src/main/java/com/fizz/business/service/impl/PdoExCoilServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/PdoExCoilServiceImpl.java index 2f35f9e..3faae6a 100644 --- a/business/src/main/java/com/fizz/business/service/impl/PdoExCoilServiceImpl.java +++ b/business/src/main/java/com/fizz/business/service/impl/PdoExCoilServiceImpl.java @@ -50,7 +50,7 @@ public class PdoExCoilServiceImpl implements PdoExCoilService { double aimWeightTop = 1; double aimWeightBottom = 1; - + pdoExCoilDTO.setZincCoatingThickness(plan.getZincCoatingThickness()); pdoExCoilDTO.setPlanOrigin(plan.getUnitCode()); pdoExCoilDTO.setUnitCode(plan.getUnitCode()); pdoExCoilDTO.setProcessCode(SYSTEM_MODULE); diff --git a/business/src/main/java/com/fizz/business/service/impl/SendJobQueryServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/SendJobQueryServiceImpl.java new file mode 100644 index 0000000..976c6ed --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/impl/SendJobQueryServiceImpl.java @@ -0,0 +1,196 @@ +package com.fizz.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fizz.business.domain.BizSendJob; +import com.fizz.business.domain.BizSendJobGroup; +import com.fizz.business.domain.BizSendJobItem; +import com.fizz.business.domain.CrmPdiPlan; +import com.fizz.business.domain.vo.BizSendTemplateItemVO; +import com.fizz.business.domain.vo.BizSendTemplateVO; +import com.fizz.business.domain.vo.SendJobLastSuccessVO; +import com.fizz.business.mapper.BizSendJobGroupMapper; +import com.fizz.business.mapper.BizSendJobItemMapper; +import com.fizz.business.mapper.BizSendJobMapper; +import com.fizz.business.mapper.CrmPdiPlanMapper; +import com.fizz.business.service.IBizSendTemplateService; +import com.fizz.business.service.ISendJobQueryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class SendJobQueryServiceImpl implements ISendJobQueryService { + + @Autowired + private BizSendJobMapper jobMapper; + + @Autowired + private BizSendJobGroupMapper groupMapper; + + @Autowired + private BizSendJobItemMapper itemMapper; + + @Autowired + private IBizSendTemplateService templateService; + + @Autowired + private CrmPdiPlanMapper planMapper; + + @Override + public SendJobLastSuccessVO getLastSuccess(String groupType) { + // 1. 根据 groupType 从 group 表中找到所有相关的 job_id + List groupsWithType = groupMapper.selectList( + new LambdaQueryWrapper().eq(BizSendJobGroup::getGroupType, groupType) + ); + + if (CollectionUtils.isEmpty(groupsWithType)) { + // 如果一个相关的 group 都没有,直接走 fallback 逻辑 + return getFallbackValues(groupType); + } + + List jobIds = groupsWithType.stream() + .map(BizSendJobGroup::getJobId) + .distinct() + .collect(Collectors.toList()); + + // 2. 在这些 job_id 中,找到状态为 COMPLETED 且时间最新的一个 job + BizSendJob lastJob = jobMapper.selectOne( + new LambdaQueryWrapper() + .in(BizSendJob::getJobId, jobIds) + .eq(BizSendJob::getStatus, "COMPLETED") + .orderByDesc(BizSendJob::getFinishTime) + .last("LIMIT 1") + ); + + // 如果找到了,直接返回上次成功的值 + if (lastJob != null) { + List groups = groupMapper.selectList( + new LambdaQueryWrapper() + .eq(BizSendJobGroup::getJobId, lastJob.getJobId()) + ); + if (groups != null && !groups.isEmpty()) { + List groupIdsForJob = groups.stream().map(BizSendJobGroup::getGroupId).collect(Collectors.toList()); + List items = itemMapper.selectList( + new LambdaQueryWrapper() + .in(BizSendJobItem::getGroupId, groupIdsForJob) + .eq(BizSendJobItem::getResultStatus, "SUCCESS") + .orderByAsc(BizSendJobItem::getItemId) + ); + + Map values = new HashMap<>(); + for (BizSendJobItem it : items) { + if (it.getParamCode() == null) continue; + values.put(it.getParamCode(), it.getValueRaw()); + } + + SendJobLastSuccessVO vo = new SendJobLastSuccessVO(); + vo.setJobId(lastJob.getJobId()); + vo.setLastSendTime(lastJob.getFinishTime()); + vo.setValues(values); + // 添加一个标志,告诉前端这是真实发送过的值 + vo.setIsFromHistory(true); + return vo; + } + } + + // 如果没找到符合条件的 job,走 fallback 逻辑 + return getFallbackValues(groupType); + } + + private SendJobLastSuccessVO getFallbackValues(String groupType) { + if ("FURNACE".equalsIgnoreCase(groupType)) { + return getFurnaceTemplateValues(); + } else if ("DRIVE".equalsIgnoreCase(groupType)) { + return getDrivePlanValues(); + } + return null; // 其他情况返回 null + } + + /** + * 获取炉火工艺模板的默认值 + */ + private SendJobLastSuccessVO getFurnaceTemplateValues() { + // "FURNACE_DEFAULT" 是前端写死的模板编码 + BizSendTemplateVO template = templateService.getTemplateWithItems("FURNACE_DEFAULT"); + if (template == null || template.getItems() == null || template.getItems().isEmpty()) { + return createEmptyResponse("Furnace template 'FURNACE_DEFAULT' not found or is empty."); + } + + Map values = new HashMap<>(); + for (BizSendTemplateItemVO item : template.getItems()) { + if (item.getEnabled() && item.getParamCode() != null) { + values.put(item.getParamCode(), item.getDefaultValueRaw()); + } + } + + SendJobLastSuccessVO vo = new SendJobLastSuccessVO(); + vo.setValues(values); + // 标志为非历史值 + vo.setIsFromHistory(false); + return vo; + } + + /** + * 获取当前计划的传动参数 + */ + private SendJobLastSuccessVO getDrivePlanValues() { + // 优先找 PRODUCING 或 ONLINE 状态的计划,按更新时间倒序取最新的一个 + CrmPdiPlan currentPlan = planMapper.selectOne( + new LambdaQueryWrapper() + .in(CrmPdiPlan::getStatus, "PRODUCING", "ONLINE") + .orderByDesc(CrmPdiPlan::getUpdateTime) + .last("LIMIT 1") + ); + + // 如果没有正在生产的,就找第一个 READY 的计划 + if (currentPlan == null) { + currentPlan = planMapper.selectOne( + new LambdaQueryWrapper() + .eq(CrmPdiPlan::getStatus, "READY") + .orderByAsc(CrmPdiPlan::getSeqid) + .last("LIMIT 1") + ); + } + + if (currentPlan == null) { + return createEmptyResponse("No active or ready plan found."); + } + + // 将 CrmPdiPlan 对象的字段反射为 Map + Map values = new HashMap<>(); + Field[] fields = CrmPdiPlan.class.getDeclaredFields(); + for (Field field : fields) { + try { + field.setAccessible(true); + Object value = field.get(currentPlan); + if (value != null) { + values.put(field.getName(), String.valueOf(value)); + } + } catch (IllegalAccessException e) { + // 忽略无法访问的字段 + } + } + + SendJobLastSuccessVO vo = new SendJobLastSuccessVO(); + vo.setValues(values); + vo.setIsFromHistory(false); // 标志为非历史值 + return vo; + } + + /** + * 创建一个空的带消息的返回对象 + */ + private SendJobLastSuccessVO createEmptyResponse(String message) { + SendJobLastSuccessVO vo = new SendJobLastSuccessVO(); + vo.setValues(Collections.singletonMap("_message", message)); + vo.setIsFromHistory(false); + return vo; + } +} diff --git a/business/src/main/java/com/fizz/business/service/impl/SendJobServiceImpl.java b/business/src/main/java/com/fizz/business/service/impl/SendJobServiceImpl.java new file mode 100644 index 0000000..ca267e8 --- /dev/null +++ b/business/src/main/java/com/fizz/business/service/impl/SendJobServiceImpl.java @@ -0,0 +1,344 @@ +package com.fizz.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fizz.business.domain.BizSendJob; +import com.fizz.business.domain.BizSendJobGroup; +import com.fizz.business.domain.BizSendJobItem; +import com.fizz.business.domain.dto.SendJobCreateDTO; +import com.fizz.business.domain.dto.SendJobQueryDTO; +import com.fizz.business.domain.vo.SendJobDetailVO; +import com.fizz.business.domain.vo.SendJobGroupVO; +import com.fizz.business.domain.vo.SendJobItemVO; +import com.fizz.business.mapper.BizSendJobGroupMapper; +import com.fizz.business.mapper.BizSendJobItemMapper; +import com.fizz.business.mapper.BizSendJobMapper; +import com.fizz.business.service.ISendJobService; +import com.fizz.business.service.manager.OpcMessageIdsManager; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class SendJobServiceImpl extends ServiceImpl implements ISendJobService { + + @Autowired + private com.fizz.business.comm.OPC.OpcMessageSend opcMessageSend; + + + @Autowired + private BizSendJobGroupMapper jobGroupMapper; + + @Autowired + private BizSendJobItemMapper jobItemMapper; + + @Autowired + private OpcMessageIdsManager opcMessageIdsManager; + + @Override + @Transactional(rollbackFor = Exception.class) + public Integer createSendJob(SendJobCreateDTO dto) { + // 1. 创建批次 + BizSendJob job = new BizSendJob(); + BeanUtils.copyProperties(dto, job); + + // 生成业务唯一键 + String bizKey = opcMessageIdsManager.generateMessageId("SEND_JOB"); + job.setBizKey(bizKey); + job.setStatus("PENDING"); + + // 设置操作人 + String username = SecurityUtils.getUsername(); + job.setCreateBy(username); + job.setCreateTime(new Date()); + + // 保存批次 + baseMapper.insert(job); + + // 2. 保存分组与明细 + if (dto.getGroups() != null) { + for (SendJobCreateDTO.GroupDTO groupDTO : dto.getGroups()) { + // 2.1 保存分组 + BizSendJobGroup group = new BizSendJobGroup(); + BeanUtils.copyProperties(groupDTO, group); + group.setJobId(job.getJobId()); + group.setStatus("PENDING"); + group.setCreateBy(username); + group.setCreateTime(new Date()); + jobGroupMapper.insert(group); + + // 2.2 保存明细项 + if (groupDTO.getItems() != null) { + for (SendJobCreateDTO.ItemDTO itemDTO : groupDTO.getItems()) { + BizSendJobItem item = new BizSendJobItem(); + BeanUtils.copyProperties(itemDTO, item); + item.setJobId(job.getJobId()); + item.setGroupId(group.getGroupId()); + item.setResultStatus("PENDING"); + item.setCreateBy(username); + item.setCreateTime(new Date()); + + // 尝试将valueRaw转为数值 + try { + item.setValueNum(new BigDecimal(itemDTO.getValueRaw())); + } catch (Exception e) { + log.warn("转换数值失败: {}", itemDTO.getValueRaw(), e); + } + + jobItemMapper.insert(item); + } + } + } + } + + return job.getJobId(); + } + + @Override + public List selectSendJobList(SendJobQueryDTO query) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(StringUtils.isNotBlank(query.getDeviceName()), + BizSendJob::getDeviceName, query.getDeviceName()) + .eq(StringUtils.isNotBlank(query.getStatus()), + BizSendJob::getStatus, query.getStatus()) + .orderByDesc(BizSendJob::getCreateTime); + + List jobs = baseMapper.selectList(qw); + if (jobs == null || jobs.isEmpty()) { + return jobs; + } + + // 如果传了 groupType(如 FURNACE),则仅保留包含该 groupType 的 job + if (StringUtils.isNotBlank(query.getGroupType())) { + String gt = query.getGroupType().trim(); + + List jobIds = jobs.stream().map(BizSendJob::getJobId).collect(Collectors.toList()); + List groups = jobGroupMapper.selectList( + new LambdaQueryWrapper() + .in(BizSendJobGroup::getJobId, jobIds) + .eq(BizSendJobGroup::getGroupType, gt) + ); + if (groups == null || groups.isEmpty()) { + return Collections.emptyList(); + } + + Set allowedJobIds = groups.stream().map(BizSendJobGroup::getJobId).collect(Collectors.toSet()); + return jobs.stream().filter(j -> allowedJobIds.contains(j.getJobId())).collect(Collectors.toList()); + } + + return jobs; + } + + @Override + public SendJobDetailVO selectSendJobDetail(Integer jobId) { + // 1. 查询任务 + BizSendJob job = baseMapper.selectById(jobId); + if (job == null) { + return null; + } + + // 2. 转换为VO + SendJobDetailVO detailVO = new SendJobDetailVO(); + BeanUtils.copyProperties(job, detailVO); + + // 3. 查询分组 + List groups = jobGroupMapper.selectList( + new LambdaQueryWrapper() + .eq(BizSendJobGroup::getJobId, jobId) + .orderByAsc(BizSendJobGroup::getGroupNo) + ); + + if (groups.isEmpty()) { + return detailVO; + } + + // 4. 查询所有明细项 + List groupIds = groups.stream() + .map(BizSendJobGroup::getGroupId) + .collect(Collectors.toList()); + + List items = jobItemMapper.selectList( + new LambdaQueryWrapper() + .in(BizSendJobItem::getGroupId, groupIds) + ); + + // 5. 按groupId分组 + Map> groupItemsMap = items.stream() + .collect(Collectors.groupingBy(BizSendJobItem::getGroupId)); + + // 6. 构建分组VO + List groupVOs = groups.stream().map(group -> { + SendJobGroupVO groupVO = new SendJobGroupVO(); + BeanUtils.copyProperties(group, groupVO); + + // 设置分组下的明细项 + List groupItems = groupItemsMap + .getOrDefault(group.getGroupId(), Collections.emptyList()); + + List itemVOs = groupItems.stream().map(item -> { + SendJobItemVO itemVO = new SendJobItemVO(); + BeanUtils.copyProperties(item, itemVO); + return itemVO; + }).collect(Collectors.toList()); + + groupVO.setItems(itemVOs); + return groupVO; + }).collect(Collectors.toList()); + + detailVO.setGroups(groupVOs); + return detailVO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteSendJobByJobIds(Integer[] jobIds) { + if (jobIds == null || jobIds.length == 0) { + return true; + } + + // 1. 更新任务状态为已删除 + BizSendJob updateJob = new BizSendJob(); + updateJob.setStatus("DELETED"); + updateJob.setUpdateBy(SecurityUtils.getUsername()); + updateJob.setUpdateTime(new Date()); + + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.in(BizSendJob::getJobId, Arrays.asList(jobIds)); + + return update(updateJob, qw); + } + + /** + * 执行发送:写入 OPC,并将发送结果保存为历史(更新 job/group/item 状态) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean executeSendJob(Integer jobId) { + BizSendJob job = baseMapper.selectById(jobId); + if (job == null) { + return false; + } + if ("DELETED".equalsIgnoreCase(job.getStatus())) { + return false; + } + + // 更新 job 状态为发送中 + BizSendJob jobUpd = new BizSendJob(); + jobUpd.setJobId(jobId); + jobUpd.setStatus("IN_PROGRESS"); + jobUpd.setActualSendTime(new Date()); + jobUpd.setUpdateBy(SecurityUtils.getUsername()); + jobUpd.setUpdateTime(new Date()); + baseMapper.updateById(jobUpd); + + // 查询该job下所有 PENDING 的 item + List items = jobItemMapper.selectList( + new LambdaQueryWrapper() + .eq(BizSendJobItem::getJobId, jobId) + .eq(BizSendJobItem::getResultStatus, "PENDING") + ); + + // 如果没有 PENDING 的项,检查是否有其他状态的项 + if (items == null || items.isEmpty()) { + boolean hasItems = jobItemMapper.selectCount( + new LambdaQueryWrapper().eq(BizSendJobItem::getJobId, jobId) + ) > 0; + + BizSendJob finish = new BizSendJob(); + finish.setJobId(jobId); + finish.setStatus(hasItems ? "COMPLETED" : "FAILED"); + finish.setFinishTime(new Date()); + finish.setUpdateBy(SecurityUtils.getUsername()); + finish.setUpdateTime(new Date()); + finish.setRemark(hasItems ? "没有待发送的项" : "没有找到任何发送项"); + baseMapper.updateById(finish); + return hasItems; + } + + // 查询该job下group + List groups = jobGroupMapper.selectList( + new LambdaQueryWrapper().eq(BizSendJobGroup::getJobId, jobId) + ); + for (BizSendJobGroup g : groups) { + BizSendJobGroup gu = new BizSendJobGroup(); + gu.setGroupId(g.getGroupId()); + gu.setStatus("IN_PROGRESS"); + gu.setUpdateBy(SecurityUtils.getUsername()); + gu.setUpdateTime(new Date()); + jobGroupMapper.updateById(gu); + } + + boolean allSuccess = true; + + // 按 address 分组,只发送最后一条记录 + Map> itemsByAddr = items.stream() + .collect(Collectors.groupingBy(BizSendJobItem::getAddress)); + + for (Map.Entry> entry : itemsByAddr.entrySet()) { + List addrItems = entry.getValue(); + // 取最后一条(按 itemId 最大 || createTime 最新) + addrItems.sort(Comparator.comparing(BizSendJobItem::getItemId)); + BizSendJobItem last = addrItems.get(addrItems.size() - 1); + boolean success = true; + String failMsg = null; + try { + opcMessageSend.writeNode(last.getAddress(), last.getValueRaw()); + } catch (Exception ex) { + success = false; + allSuccess = false; + failMsg = ex.getMessage(); + } + + // 更新该 address 下所有项的状态(统一) + for (BizSendJobItem it : addrItems) { + BizSendJobItem upd = new BizSendJobItem(); + upd.setItemId(it.getItemId()); + upd.setResultStatus(success ? "SUCCESS" : "FAILED"); + upd.setResultMsg(success ? "OK" : failMsg); + upd.setUpdateBy(SecurityUtils.getUsername()); + upd.setUpdateTime(new Date()); + jobItemMapper.updateById(upd); + } + } + + // 更新 group 状态(按 group 下 item 是否全部成功) + Map> itemByGroup = jobItemMapper.selectList( + new LambdaQueryWrapper().eq(BizSendJobItem::getJobId, jobId) + ).stream().collect(Collectors.groupingBy(BizSendJobItem::getGroupId)); + + for (BizSendJobGroup g : groups) { + List gi = itemByGroup.getOrDefault(g.getGroupId(), Collections.emptyList()); + boolean groupOk = gi.stream().allMatch(x -> "SUCCESS".equalsIgnoreCase(x.getResultStatus())); + + BizSendJobGroup gu = new BizSendJobGroup(); + gu.setGroupId(g.getGroupId()); + gu.setStatus(groupOk ? "COMPLETED" : "FAILED"); + gu.setUpdateBy(SecurityUtils.getUsername()); + gu.setUpdateTime(new Date()); + jobGroupMapper.updateById(gu); + } + + // 更新 job 最终状态 + BizSendJob finish = new BizSendJob(); + finish.setJobId(jobId); + finish.setFinishTime(new Date()); + finish.setUpdateBy(SecurityUtils.getUsername()); + finish.setUpdateTime(new Date()); + finish.setStatus(allSuccess ? "COMPLETED" : "PARTIAL_SUCCESS"); + finish.setRemark(allSuccess ? "全部发送成功" : "部分发送失败"); + baseMapper.updateById(finish); + + return true; + } +} + diff --git a/business/src/main/java/com/fizz/business/service/manager/OpcMessageIdsManager.java b/business/src/main/java/com/fizz/business/service/manager/OpcMessageIdsManager.java index a455779..d2a858a 100644 --- a/business/src/main/java/com/fizz/business/service/manager/OpcMessageIdsManager.java +++ b/business/src/main/java/com/fizz/business/service/manager/OpcMessageIdsManager.java @@ -14,6 +14,16 @@ import java.util.Map; @Component public class OpcMessageIdsManager { + /** + * 生成业务/消息唯一ID(用于发送批次 bizKey 等) + * 规则:PREFIX_时间戳_8位随机 + */ + public String generateMessageId(String prefix) { + String p = (prefix == null || prefix.trim().isEmpty()) ? "MSG" : prefix.trim(); + String random = java.util.UUID.randomUUID().toString().replace("-", "").substring(0, 8); + return (p + "_" + System.currentTimeMillis() + "_" + random).toUpperCase(); + } + public static List msgTriggers = Lists.newArrayList(); public static Map lineMeasureIds = Maps.newHashMap(); diff --git a/business/src/main/java/com/fizz/business/vo/CrmPdiPlanVO.java b/business/src/main/java/com/fizz/business/vo/CrmPdiPlanVO.java index a3ae746..3e964e9 100644 --- a/business/src/main/java/com/fizz/business/vo/CrmPdiPlanVO.java +++ b/business/src/main/java/com/fizz/business/vo/CrmPdiPlanVO.java @@ -311,4 +311,7 @@ public class CrmPdiPlanVO { @Schema(description = "原卷号") private String originCoilid; + + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } diff --git a/business/src/main/java/com/fizz/business/vo/PdiPlanSetupInfoVO.java b/business/src/main/java/com/fizz/business/vo/PdiPlanSetupInfoVO.java index ba4b075..ca6b5c3 100644 --- a/business/src/main/java/com/fizz/business/vo/PdiPlanSetupInfoVO.java +++ b/business/src/main/java/com/fizz/business/vo/PdiPlanSetupInfoVO.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -145,4 +146,7 @@ public class PdiPlanSetupInfoVO { @Schema(description = "设定值列表") List lists; + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; + } diff --git a/business/src/main/java/com/fizz/business/vo/PdiPlanVO.java b/business/src/main/java/com/fizz/business/vo/PdiPlanVO.java index a46de98..7351998 100644 --- a/business/src/main/java/com/fizz/business/vo/PdiPlanVO.java +++ b/business/src/main/java/com/fizz/business/vo/PdiPlanVO.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.io.Serializable; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -157,4 +158,7 @@ public class PdiPlanVO implements Serializable { @Schema(description = "实验类别") private String experimentType; + + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; } diff --git a/business/src/main/java/com/fizz/business/vo/Plan2PdoVO.java b/business/src/main/java/com/fizz/business/vo/Plan2PdoVO.java index 8290d3a..1c15195 100644 --- a/business/src/main/java/com/fizz/business/vo/Plan2PdoVO.java +++ b/business/src/main/java/com/fizz/business/vo/Plan2PdoVO.java @@ -3,6 +3,7 @@ package com.fizz.business.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDateTime; @@ -80,5 +81,8 @@ public class Plan2PdoVO { @Schema(description = "热卷温度") private Double hotCoilTemp; + @Schema(description = "锌层厚度") + private BigDecimal zincCoatingThickness; + } diff --git a/business/src/main/resources/mapper/PlanDashboardMapper.xml b/business/src/main/resources/mapper/PlanDashboardMapper.xml new file mode 100644 index 0000000..e339384 --- /dev/null +++ b/business/src/main/resources/mapper/PlanDashboardMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/business/src/main/resources/mapper/SegmentTotalMapper.xml b/business/src/main/resources/mapper/SegmentTotalMapper.xml index 3c92be4..64bfd70 100644 --- a/business/src/main/resources/mapper/SegmentTotalMapper.xml +++ b/business/src/main/resources/mapper/SegmentTotalMapper.xml @@ -1,7 +1,21 @@ - - + + + - + SELECT + total_values_json + FROM cpl_segment_total + WHERE en_coil_id = #{coilId} + ORDER BY seg_no DESC + LIMIT 1 + + diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index c7df6a3..05e7552 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -41,6 +41,7 @@ ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 100MB 0 0MB diff --git a/ruoyi-admin/target/classes/logback.xml b/ruoyi-admin/target/classes/logback.xml index 9998c41..c7df6a3 100644 --- a/ruoyi-admin/target/classes/logback.xml +++ b/ruoyi-admin/target/classes/logback.xml @@ -42,7 +42,8 @@ ${log.path}/sys-error.%d{yyyy-MM-dd}.log - 7 + 0 + 0MB ${log.pattern} @@ -51,7 +52,8 @@ ERROR - ACCEPT + + DENY DENY @@ -83,7 +85,7 @@ - + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java index 15bf66b..7449305 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -7,10 +7,10 @@ import java.util.Map; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; - +import com.baomidou.mybatisplus.annotation.TableField; /** * Entity基类 - * + * * @author ruoyi */ public class BaseEntity implements Serializable @@ -19,6 +19,7 @@ public class BaseEntity implements Serializable /** 搜索值 */ @JsonIgnore + @TableField(exist = false) private String searchValue; /** 创建者 */