diff --git a/aucma-admin/src/main/resources/application.yml b/aucma-admin/src/main/resources/application.yml index ded616b..ac52f09 100644 --- a/aucma-admin/src/main/resources/application.yml +++ b/aucma-admin/src/main/resources/application.yml @@ -5,7 +5,7 @@ ruoyi: # 版本 version: 3.8.6 # 版权年份 - copyrightYear: 2023 + copyrightYear: 2026 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) diff --git a/aucma-production/src/main/java/com/aucma/production/controller/AndonMobileController.java b/aucma-production/src/main/java/com/aucma/production/controller/AndonMobileController.java new file mode 100644 index 0000000..6f35ac9 --- /dev/null +++ b/aucma-production/src/main/java/com/aucma/production/controller/AndonMobileController.java @@ -0,0 +1,176 @@ +package com.aucma.production.controller; + +import com.aucma.base.domain.BaseProcessStation; +import com.aucma.base.service.IBaseProcessStationService; +import com.aucma.common.constant.AnDonConstants; +import com.aucma.common.core.domain.AjaxResult; +import com.aucma.common.utils.DateUtils; +import com.aucma.common.utils.SecurityUtils; +import com.aucma.common.utils.StringUtils; +import com.aucma.production.domain.AndonEvent; +import com.aucma.production.domain.AndonRule; +import com.aucma.production.mapper.AndonRuleMapper; +import com.aucma.production.service.IAndonEventService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 手持设备安灯呼叫接口(极简版) + * + * 业务流程: + * 1. 手持PDA已绑定工位(stationCode) + * 2. 用户点击安灯按钮,PDA调用 /call-types 获取该工位可用的呼叫类型 + * 3. PDA显示呼叫类型按钮,用户选择一个 + * 4. PDA调用 /call 发送 stationCode + callTypeCode + * 5. 后台匹配安灯规则,生成事件并自动派工 + * 6. 大屏看板实时显示事件 + * + * @author Yinq + * @date 2025-12-29 + */ +@RestController +@RequestMapping("/production/andon/mobile") +public class AndonMobileController { + + @Autowired + private IBaseProcessStationService baseProcessStationService; + + @Autowired + private AndonRuleMapper andonRuleMapper; + + @Autowired + private IAndonEventService andonEventService; + + /** + * 【接口1】获取工位可用的呼叫类型列表 + * + * PDA先调用此接口获取该工位配置的所有呼叫类型,然后显示按钮让用户选择 + * + * @param stationCode 工位编码 + * @return 呼叫类型列表 [{callTypeCode, callTypeName, ruleName}] + */ + @GetMapping("/call-types") + public AjaxResult getCallTypes(@RequestParam String stationCode) { + if (StringUtils.isEmpty(stationCode)) { + return AjaxResult.error("工位编码不能为空"); + } + + // 查询该工位配置的所有安灯规则 + AndonRule ruleQuery = new AndonRule(); + ruleQuery.setStationCode(stationCode); + ruleQuery.setIsFlag(AnDonConstants.FLAG_VALID); + List rules = andonRuleMapper.selectAndonRuleList(ruleQuery); + + if (rules == null || rules.isEmpty()) { + return AjaxResult.error("该工位未配置安灯规则,请联系管理员"); + } + + // 构建返回数据 + List> callTypes = new ArrayList<>(); + for (AndonRule rule : rules) { + Map item = new LinkedHashMap<>(); + item.put("callTypeCode", rule.getCallTypeCode()); + item.put("ruleName", rule.getRuleName()); + item.put("ruleId", rule.getRuleId()); + callTypes.add(item); + } + + return AjaxResult.success(callTypes); + } + + /** + * 【接口2】提交安灯呼叫 + * + * @param stationCode 工位编码(必填) + * @param callTypeCode 呼叫类型编码(必填)- 从 /call-types 接口获取 + * @return 创建的安灯事件信息 + */ + @PostMapping("/call") + public AjaxResult submitAndonCall(@RequestParam String stationCode, + @RequestParam String callTypeCode) { + System.out.println("stationCode: " + stationCode + ", callTypeCode: " + callTypeCode); + // 参数校验 + if (StringUtils.isEmpty(stationCode)) { + return AjaxResult.error("工位编码不能为空"); + } + if (StringUtils.isEmpty(callTypeCode)) { + return AjaxResult.error("呼叫类型不能为空"); + } + + // 1. 根据 stationCode 查询工位信息 + BaseProcessStation stationQuery = new BaseProcessStation(); + stationQuery.setProcessCode(stationCode); + List stations = baseProcessStationService.selectBaseProcessStationList(stationQuery); + if (stations == null || stations.isEmpty()) { + return AjaxResult.error("工位不存在:" + stationCode); + } + BaseProcessStation station = stations.get(0); + String productLineCode = station.getProductLineCode(); + + // 2. 根据 stationCode + callTypeCode 匹配安灯规则 + AndonRule ruleQuery = new AndonRule(); + ruleQuery.setStationCode(stationCode); + ruleQuery.setCallTypeCode(callTypeCode); + ruleQuery.setIsFlag(AnDonConstants.FLAG_VALID); + List rules = andonRuleMapper.selectAndonRuleList(ruleQuery); + if (rules == null || rules.isEmpty()) { + return AjaxResult.error("未找到该工位+呼叫类型的安灯规则"); + } + AndonRule rule = rules.get(0); + + // 3. 构建安灯事件 + AndonEvent event = new AndonEvent(); + + // 生成呼叫单号 + event.setCallCode(generateCallCode()); + + // 从规则获取呼叫类型 + event.setCallTypeCode(rule.getCallTypeCode()); + + // 从工位获取产线 + event.setProductLineCode(productLineCode); + event.setStationCode(stationCode); + + // 呼叫人(可选,默认为PDA) + event.setCreateBy(SecurityUtils.getLoginUser().getUserId() != null ? SecurityUtils.getLoginUser().getUserId().toString() : "PDA"); + + + // 触发源类型:工位 + event.setSourceType(AnDonConstants.SourceType.STATION); + + // 初始状态 + event.setEventStatus(AnDonConstants.EventStatus.PENDING); + event.setIsFlag(AnDonConstants.FLAG_VALID); + event.setCreateTime(DateUtils.getNowDate()); + + // 4. 调用服务保存(自动匹配规则、计算时限、创建派工) + int result = andonEventService.insertAndonEvent(event); + + if (result > 0) { + Map data = new LinkedHashMap<>(); + data.put("eventId", event.getEventId()); + data.put("callCode", event.getCallCode()); + data.put("stationName", station.getStationName() != null ? station.getStationName() : station.getProcessName()); + data.put("callType", rule.getRuleName()); + data.put("message", "安灯呼叫成功,已通知相关人员"); + return AjaxResult.success(data); + } else { + return AjaxResult.error("安灯呼叫失败"); + } + } + + /** + * 生成呼叫单号 + * 格式:AD + 年月日时分秒 + 3位随机数 + */ + private String generateCallCode() { + String dateStr = DateUtils.dateTimeNow("yyyyMMddHHmmss"); + int random = (int) (Math.random() * 900) + 100; // 100-999 + return "AD" + dateStr + random; + } +} diff --git a/aucma-production/src/main/java/com/aucma/production/domain/dto/AndonMobileCallRequest.java b/aucma-production/src/main/java/com/aucma/production/domain/dto/AndonMobileCallRequest.java new file mode 100644 index 0000000..24388cb --- /dev/null +++ b/aucma-production/src/main/java/com/aucma/production/domain/dto/AndonMobileCallRequest.java @@ -0,0 +1,65 @@ +package com.aucma.production.domain.dto; + +import java.io.Serializable; + +/** + * 手持设备安灯呼叫请求DTO(极简版) + * + * 手持PDA已绑定工位,只需发送: + * - stationCode(必填):工位编码,PDA绑定的工位 + * - callerUserName(可选):呼叫人用户名 + * - description(可选):问题描述 + * + * 后台自动根据 stationCode 匹配安灯规则,获取呼叫类型和产线信息 + * + * @author Yinq + * @date 2025-12-29 + */ +public class AndonMobileCallRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + /** 工位编码(必填)- PDA绑定的工位编码 */ + private String stationCode; + + /** 呼叫人用户名(可选)- 手持设备登录用户 */ + private String callerUserName; + + /** 问题描述(可选)- 用户输入的问题描述 */ + private String description; + + // ==================== Getter/Setter ==================== + + public String getStationCode() { + return stationCode; + } + + public void setStationCode(String stationCode) { + this.stationCode = stationCode; + } + + public String getCallerUserName() { + return callerUserName; + } + + public void setCallerUserName(String callerUserName) { + this.callerUserName = callerUserName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return "AndonMobileCallRequest{" + + "stationCode='" + stationCode + '\'' + + ", callerUserName='" + callerUserName + '\'' + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventAssignmentServiceImpl.java b/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventAssignmentServiceImpl.java index 5c33571..27d0b7c 100644 --- a/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventAssignmentServiceImpl.java +++ b/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventAssignmentServiceImpl.java @@ -170,9 +170,9 @@ public class AndonEventAssignmentServiceImpl implements IAndonEventAssignmentSer AndonRule q = new AndonRule(); q.setCallTypeCode(e.getCallTypeCode()); q.setIsFlag(AnDonConstants.FLAG_VALID); - List rules = andonRuleMapper.selectAndonRuleList(q); - if (rules == null || rules.isEmpty()) return false; - String remark = rules.get(0).getRemark(); + AndonRule bestRule = pickBestRule(andonRuleMapper.selectAndonRuleList(q), e); + if (bestRule == null) return false; + String remark = bestRule.getRemark(); if (remark == null || remark.trim().isEmpty()) return true; try { com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); @@ -185,6 +185,28 @@ public class AndonEventAssignmentServiceImpl implements IAndonEventAssignmentSer } } + /** + * 选择匹配度最高的规则:与 AndonEventServiceImpl 保持同一算法,避免派工侧逻辑偏差。 + * 计算方式:对产线/工位/班组/触发源类型进行精确匹配,每命中一项得 1 分,得分最高的规则被选中(先到先得处理并列)。 + */ + private AndonRule pickBestRule(List rules, AndonEvent e) { + if (rules == null || rules.isEmpty() || e == null) return null; + AndonRule best = null; + int bestScore = -1; + for (AndonRule r : rules) { + int score = 0; + if (r.getProductLineCode() != null && r.getProductLineCode().equals(e.getProductLineCode())) score++; + if (r.getStationCode() != null && r.getStationCode().equals(e.getStationCode())) score++; + if (r.getTeamCode() != null && r.getTeamCode().equals(e.getTeamCode())) score++; + if (r.getSourceType() != null && r.getSourceType().equals(e.getSourceType())) score++; + if (score > bestScore) { + bestScore = score; + best = r; + } + } + return best; + } + /** 取消同一事件下除当前派工外的其他派工。 */ private void cancelOtherAssignments(Long eventId, Long currentAssignmentId) { if (eventId == null) return; diff --git a/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventServiceImpl.java b/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventServiceImpl.java index 5c70756..fd2b664 100644 --- a/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventServiceImpl.java +++ b/aucma-production/src/main/java/com/aucma/production/service/impl/AndonEventServiceImpl.java @@ -15,6 +15,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.aucma.common.constant.AnDonConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; @@ -71,6 +72,7 @@ public class AndonEventServiceImpl implements IAndonEventService * @return 结果 */ @Override + @Transactional(rollbackFor = Exception.class) public int insertAndonEvent(AndonEvent andonEvent) { andonEvent.setCreateTime(DateUtils.getNowDate());