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