diff --git a/aucma-base/src/main/java/com/aucma/base/domain/BaseOrderInfo.java b/aucma-base/src/main/java/com/aucma/base/domain/BaseOrderInfo.java index e8d46a3..b57942b 100644 --- a/aucma-base/src/main/java/com/aucma/base/domain/BaseOrderInfo.java +++ b/aucma-base/src/main/java/com/aucma/base/domain/BaseOrderInfo.java @@ -272,6 +272,8 @@ public class BaseOrderInfo extends BaseEntity { public void setActualCompleteQty(Long actualCompleteQty) { this.actualCompleteQty = actualCompleteQty; + // 同步列表展示字段 + this.completeAmount = actualCompleteQty; } public Long getActualDefectQty() { @@ -380,6 +382,8 @@ public class BaseOrderInfo extends BaseEntity { public void setCompleteAmount(Long completeAmount) { this.completeAmount = completeAmount; + // 保持展示字段与实际完工数量一致 + this.actualCompleteQty = completeAmount; } public Long getCompleteAmount() { diff --git a/aucma-base/src/main/resources/mapper/base/BaseMaterialinfoMapper.xml b/aucma-base/src/main/resources/mapper/base/BaseMaterialinfoMapper.xml index 7fa29df..eab61db 100644 --- a/aucma-base/src/main/resources/mapper/base/BaseMaterialinfoMapper.xml +++ b/aucma-base/src/main/resources/mapper/base/BaseMaterialinfoMapper.xml @@ -89,7 +89,7 @@ FROM ( - WHERE ml.is_flag = 1 + WHERE ml.is_flag = 0 AND ( UPPER(ml.material_code) LIKE '%' || UPPER(#{keyword}) || '%' diff --git a/aucma-base/src/main/resources/mapper/base/BaseOrderInfoMapper.xml b/aucma-base/src/main/resources/mapper/base/BaseOrderInfoMapper.xml index fc6aac5..e883565 100644 --- a/aucma-base/src/main/resources/mapper/base/BaseOrderInfoMapper.xml +++ b/aucma-base/src/main/resources/mapper/base/BaseOrderInfoMapper.xml @@ -141,7 +141,7 @@ - AND NVL(oi.is_flag, 1) = 1 + AND NVL(oi.is_flag, 0) = 0 AND oi.material_code IN diff --git a/aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java b/aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java index 8f21f75..f7fa2e8 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java +++ b/aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java @@ -9,6 +9,7 @@ import com.aucma.dms.domain.*; import com.aucma.dms.domain.dto.DmsBaseDeviceLedgerDTO; import com.aucma.dms.domain.vo.DmsBillsFaultInstanceScanVo; import com.aucma.dms.domain.vo.DmsBillsInspectInstanceScanVo; +import com.aucma.dms.domain.vo.DmsPdaDeviceParamUpdateVo; import com.aucma.base.domain.BaseDeviceLedger; import com.aucma.base.domain.BaseOrderInfo; import com.aucma.base.service.IBaseDeviceLedgerService; @@ -252,410 +253,7 @@ public class DmsMobileController extends BaseController { return dms; } - /** - * PDA-按物料关键字查询关联工单 - */ - @GetMapping("/order/listByMaterial") - public AjaxResult listOrdersByMaterial(String keyword) { - // 物料关键字是PDA唯一输入入口,空值时直接拒绝,避免放大后端模糊查询范围 - if (keyword == null || keyword.trim().isEmpty()) { - throw new ServiceException("物料关键字不能为空"); - } - try { - // Why:先按物料主数据检索,再按物料编码反查订单,能保证PDA搜索口径与主数据一致 - List orders = baseOrderInfoService.selectPdaOrderListByMaterialKeyword(keyword.trim()); - return success(orders); - } catch (Exception e) { - log.error("PDA按物料查询工单失败 | keyword={}, 异常信息: {}", keyword, e.getMessage(), e); - throw new ServiceException("查询工单失败: " + e.getMessage()); - } - } - /** - * PDA-维护订单状态与数量 - */ - @PostMapping("/order/updateStatusAndQty") - @Transactional(rollbackFor = Exception.class) - public AjaxResult updateOrderStatusAndQty(@RequestBody BaseOrderInfo baseOrderInfo) { - // 请求体为空时没有任何维护目标,直接拦截比进入service后再判空更清晰 - if (baseOrderInfo == null) { - throw new ServiceException("请求体不能为空"); - } - // PDA下拉选中订单后至少应能定位到一条工单,兼容前端传objId或orderCode两种方式 - if (baseOrderInfo.getObjId() == null - && (baseOrderInfo.getOrderCode() == null || baseOrderInfo.getOrderCode().trim().isEmpty())) { - throw new ServiceException("objId和orderCode不能同时为空"); - } - // 状态和数量都在控制器前置校验,避免脏数据落到service或数据库层 - validateOrderExecutionStatus(baseOrderInfo.getExecutionStatus()); - validateOrderQty(baseOrderInfo.getActualCompleteQty(), "完工数量"); - validateOrderQty(baseOrderInfo.getActualDefectQty(), "不良数量"); - - try { - // Why:PDA订单维护属于人工修正场景,统一走service封装,避免控制器直接拼装状态流转细节 - int rows = baseOrderInfoService.updatePdaOrderExecution(baseOrderInfo); - return rows > 0 ? success(rows) : error("工单不存在或更新失败"); - } catch (ServiceException e) { - throw e; - } catch (Exception e) { - log.error("PDA维护订单失败 | objId={}, orderCode={}, executionStatus={}, actualCompleteQty={}, 异常信息: {}", - baseOrderInfo.getObjId(), baseOrderInfo.getOrderCode(), baseOrderInfo.getExecutionStatus(), - baseOrderInfo.getActualCompleteQty(), e.getMessage(), e); - throw new ServiceException("维护订单失败: " + e.getMessage()); - } - } - - private void validateOrderExecutionStatus(String executionStatus) { - // PDA当前仅允许维护执行态,保持与execution.vue和现有执行页状态枚举一致 - if (executionStatus == null || executionStatus.trim().isEmpty()) { - throw new ServiceException("执行状态不能为空"); - } - String normalized = executionStatus.trim(); - if (!ORDER_EXECUTION_PENDING.equals(normalized) - && !ORDER_EXECUTION_RUNNING.equals(normalized) - && !ORDER_EXECUTION_PAUSED.equals(normalized) - && !ORDER_EXECUTION_COMPLETED.equals(normalized)) { - throw new ServiceException("执行状态不合法"); - } - } - - private void validateOrderQty(Long qty, String fieldName) { - // 数量允许为空,表示本次只改状态;但一旦传值就必须满足非负约束 - if (qty != null && qty < 0) { - throw new ServiceException(fieldName + "不能小于0"); - } - } - - // ========== PDA 停机相关 ========== - - - /** - * 获取OLD开头的设备列表(仅启用) - */ - @GetMapping("/device/listOld") - public AjaxResult listOldDevices() { - BaseDeviceLedger query = new BaseDeviceLedger(); - query.setIsFlag(1L); - List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); - List resultList = new ArrayList<>(); - if (!CollectionUtils.isEmpty(list)) { - for (BaseDeviceLedger base : list) { - if (base.getDeviceCode() != null && base.getDeviceCode().startsWith("OLD")) { - resultList.add(convertDeviceLedgerToDto(convertToDeviceLedger(base))); - } - } - } - return success(resultList); - } - - - /** - * PDA-获取停机原因列表(仅启用 isFlag=1) - */ - @GetMapping("/shutReason/list") - public AjaxResult listShutReasonsForPda(DmsBaseShutReason reason) { - reason.setIsFlag("1"); - List list = dmsBaseShutReasonService.selectDmsBaseShutReasonList(reason); - return success(list); - } - - /** - * PDA-新增停机记录:根据停机原因自动匹配停机类型 - */ - @PostMapping("/shutDown/add") - @Transactional(rollbackFor = Exception.class) - public AjaxResult addShutDownForPda(@RequestBody DmsRecordShutDown shutDown) { - try { - // 若传入了停机原因ID,则查询原因并回填类型 - if (shutDown.getShutReason() != null) { - DmsBaseShutReason query = new DmsBaseShutReason(); - query.setShutReason(shutDown.getShutReason()); - query.setIsFlag("1"); - List reasons = dmsBaseShutReasonService.selectDmsBaseShutReasonList(query); - if (!reasons.isEmpty()) { - shutDown.setShutType(reasons.get(0).getShutTypeId()); - shutDown.setShutReasonId(reasons.get(0).getShutReasonId()); - // 若前端未传 deviceId,尝试从原因带出 - if (shutDown.getDeviceId() == null) { - shutDown.setDeviceId(reasons.get(0).getDeviceId()); - } - } - } - // 默认启用标识 - if (shutDown.getIsFlag() == null) { - shutDown.setIsFlag(1L); - } - // 新增停机记录默认未结束 - if (shutDown.getDowntimeFlag() == null) { - shutDown.setDowntimeFlag("0"); - } - // 若未传入开始时间,则使用当前时间 - if (shutDown.getShutBeginTime() == null) { - shutDown.setShutBeginTime(new Date()); - } - int rows = dmsRecordShutDownService.insertDmsRecordShutDown(shutDown); - - // 对指定设备(OLD-01~OLD-05)同步写入三色灯状态参数,供设备状态统计使用 - String deviceCode = shutDown.getDeviceCode(); - if (deviceCode != null && deviceCode.startsWith("OLD-")) { - // 新增停机记录代表进入停机,三色灯置为“暂停” - insertTriColorStatusParams(deviceCode, shutDown.getDeviceId(), false); - } - - return success(rows); - } catch (Exception e) { - // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 - log.error("PDA新增停机记录失败 | deviceCode={}, shutReason={}, deviceId={}, 异常信息: {}", - shutDown.getDeviceCode(), shutDown.getShutReason(), shutDown.getDeviceId(), e.getMessage(), e); - throw new ServiceException("新增停机记录失败: " + e.getMessage()); - } - } - - /** - * PDA-获取待处理停机记录列表 - * 返回条件:停机结束时间为空(老设备未完成)或 停机原因ID=1(新设备默认原因待修改) - */ - @GetMapping("/shutDown/get") - public AjaxResult getShutDownForPda() { - try { - List list = dmsRecordShutDownService.selectPdaShutDownList(); - return success(list); - } catch (Exception e) { - // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 - log.error("PDA获取待处理停机记录失败 | 异常信息: {}", e.getMessage(), e); - throw new ServiceException("获取停机记录失败: " + e.getMessage()); - } - } - - /** - * PDA-更新停机记录 - * 老设备(shutEndTime为空):更新停机结束时间、计算停机时长、更新停机原因ID - * 新设备(shutEndTime不为空):仅更新停机原因ID - */ - @PostMapping("/shutDown/complete") - @Transactional(rollbackFor = Exception.class) - public AjaxResult completeShutDownForPda(@RequestBody DmsRecordShutDown shutDown) { - if (shutDown.getRecordShutDownId() == null) { - throw new ServiceException("recordShutDownId不能为空"); - } - if (shutDown.getShutReasonId() == null) { - throw new ServiceException("shutReasonId不能为空"); - } - try { - // 查询原记录 - DmsRecordShutDown origin = dmsRecordShutDownService.selectDmsRecordShutDownByRecordShutDownId(shutDown.getRecordShutDownId()); - if (origin == null) { - throw new ServiceException("停机记录不存在"); - } - - // 构建更新对象,仅设置需要更新的字段 - DmsRecordShutDown updateRecord = new DmsRecordShutDown(); - updateRecord.setRecordShutDownId(shutDown.getRecordShutDownId()); - updateRecord.setShutReasonId(shutDown.getShutReasonId()); - - if (origin.getShutEndTime() == null) { - // 老设备:标记停机已结束 - updateRecord.setDowntimeFlag("1"); - // 老设备:补充结束时间和停机时长 - Date end = new Date(); - Date begin = origin.getShutBeginTime(); - updateRecord.setShutEndTime(end); - if (begin != null) { - long minutes = Math.max(0, (end.getTime() - begin.getTime()) / 60000); - updateRecord.setShutTime(BigDecimal.valueOf(minutes)); - } - } - // 新设备(shutEndTime已有值):仅更新shutReasonId,无需额外处理 - - int rows = dmsRecordShutDownService.updateDmsRecordShutDown(updateRecord); - - // 对老设备(OLD-01~OLD-05)同步写入三色灯状态参数 - if (origin.getShutEndTime() == null && origin.getDeviceId() != null) { - // 原记录不含deviceCode,需通过deviceId反查设备编号 - BaseDeviceLedger deviceQuery = new BaseDeviceLedger(); - deviceQuery.setObjId(origin.getDeviceId()); - List devices = baseDeviceLedgerService.selectBaseDeviceLedgerList(deviceQuery); - if (!devices.isEmpty()) { - String deviceCode = devices.get(0).getDeviceCode(); - if (deviceCode != null && deviceCode.startsWith("OLD-")) { - // 完成停机代表重新运行,三色灯置为"运行" - insertTriColorStatusParams(deviceCode, origin.getDeviceId(), true); - } - } - } - - return rows > 0 ? success(origin.getRecordShutDownId()) : error("更新停机记录失败"); - } catch (Exception e) { - // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 - log.error("PDA完成停机记录失败 | recordShutDownId={}, shutReasonId={}, 异常信息: {}", - shutDown.getRecordShutDownId(), shutDown.getShutReasonId(), e.getMessage(), e); - throw new ServiceException("完成停机失败: " + e.getMessage()); - } - } - - /** - * 插入三色灯状态参数(运行/暂停/报警),以“暂停”为当前状态 - */ - private void insertTriColorStatusParams(String deviceCode, Long deviceId, boolean running) { - // 仅处理 OLD-01~OLD-05 - if (!"OLD-01".equals(deviceCode) && !"OLD-02".equals(deviceCode) - && !"OLD-03".equals(deviceCode) && !"OLD-04".equals(deviceCode) - && !"OLD-05".equals(deviceCode)) { - return; - } - - // 补齐 deviceId(如果未传) - Long resolvedDeviceId = deviceId; - if (resolvedDeviceId == null) { - BaseDeviceLedger query = new BaseDeviceLedger(); - query.setDeviceCode(deviceCode); - query.setIsFlag(1L); - List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); - if (!list.isEmpty()) { - resolvedDeviceId = list.get(0).getObjId(); - } - } - - Date now = new Date(); - // 运行、暂停、报警 三个参数,运行/暂停取决于状态 - List records = new ArrayList<>(); - String runVal = running ? "True" : "False"; - String pauseVal = running ? "False" : "True"; - records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器运行", runVal, now)); - records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器暂停", pauseVal, now)); - records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器报警", "False", now)); - - for (BaseDeviceParamVal rec : records) { - baseDeviceParamValService.insertBaseDeviceParamVal(rec); - } - } - - private BaseDeviceParamVal buildParamVal(String paramCode, String deviceCode, Long deviceId, - String paramName, String paramValue, Date time) { - BaseDeviceParamVal v = new BaseDeviceParamVal(); - v.setParamCode(paramCode); - v.setDeviceCode(deviceCode); - v.setDeviceId(deviceId); - v.setParamName(paramName); - v.setParamValue(paramValue); - v.setCollectTime(time); - v.setRecordTime(time); - return v; - } - - /** - * PDA-选择设备后更新运行时间(写入参数:机台状态-实际生产时间) - */ - @PostMapping("/device/updateRunTime") - @Transactional(rollbackFor = Exception.class) - public AjaxResult updateDeviceRunTime(@RequestBody BaseDeviceParamVal baseDeviceParamVal) { - return updatePdaDeviceParamValue(baseDeviceParamVal, PARAM_NAME_ACTUAL_RUNTIME, false, "运行时间"); - } - - /** - * PDA-选择设备后更新当日产量(写入参数:机台状态-实际产出数量) - */ - @PostMapping("/device/updateDailyOutput") - @Transactional(rollbackFor = Exception.class) - public AjaxResult updateDeviceDailyOutput(@RequestBody BaseDeviceParamVal baseDeviceParamVal) { - return updatePdaDeviceParamValue(baseDeviceParamVal, PARAM_NAME_DAILY_OUTPUT, true, "当日产量"); - } - - private AjaxResult updatePdaDeviceParamValue(BaseDeviceParamVal baseDeviceParamVal, String paramName, - boolean integerRequired, String bizName) { - // 请求体为空时直接拒绝,避免后续空指针并明确返回可读错误 - if (baseDeviceParamVal == null) { - throw new ServiceException("请求体不能为空"); - } - // PDA上报的业务值统一从paramValue读取,后续会按业务类型做数值校验 - String rawValue = baseDeviceParamVal.getParamValue(); - if (rawValue == null || rawValue.trim().isEmpty()) { - throw new ServiceException(bizName + "不能为空"); - } - // 运行时间允许小数;当日产量要求整数,规则由integerRequired控制 - validateNumericParamValue(rawValue.trim(), integerRequired, bizName); - - try { - // 兼容PDA只传deviceId或只传deviceCode的场景,统一补齐设备主数据标识 - fillDeviceIdentity(baseDeviceParamVal); - Date now = new Date(); - // 参数名由后端强制指定,避免前端透传导致写错业务口径 - baseDeviceParamVal.setParamName(paramName); - baseDeviceParamVal.setParamValue(rawValue.trim()); - if (baseDeviceParamVal.getParamCode() == null || baseDeviceParamVal.getParamCode().trim().isEmpty()) { - // Why:该参数值用于PDA手工更新场景,统一标识便于后续数据追溯与运维排查 - baseDeviceParamVal.setParamCode(PARAM_CODE_PDA_CUSTOM); - } - // 采集时间/记录时间均取服务端当前时间,确保同一时区口径一致 - baseDeviceParamVal.setCollectTime(now); - baseDeviceParamVal.setRecordTime(now); - - // 直接插入一条新参数记录,保留完整时间序列,避免覆盖历史值 - int rows = baseDeviceParamValService.upsertTodayParamValue(baseDeviceParamVal); - return rows > 0 ? success(rows) : error("更新" + bizName + "失败"); - } catch (ServiceException e) { - // 业务异常原样抛出,保留明确可读的提示信息 - throw e; - } catch (Exception e) { - // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 - log.error("PDA更新设备{}失败 | deviceCode={}, deviceId={}, paramName={}, paramValue={}, 异常信息: {}", - bizName, baseDeviceParamVal.getDeviceCode(), baseDeviceParamVal.getDeviceId(), - paramName, baseDeviceParamVal.getParamValue(), e.getMessage(), e); - throw new ServiceException("更新" + bizName + "失败: " + e.getMessage()); - } - } - - private void fillDeviceIdentity(BaseDeviceParamVal baseDeviceParamVal) { - String deviceCode = baseDeviceParamVal.getDeviceCode(); - Long deviceId = baseDeviceParamVal.getDeviceId(); - boolean hasCode = deviceCode != null && !deviceCode.trim().isEmpty(); - - // 未传deviceCode时,必须依赖deviceId反查设备编码;否则无法确定落库维度 - if (!hasCode) { - // 两个标识都没有时无法定位设备,直接拒绝请求 - if (deviceId == null) { - throw new ServiceException("deviceCode和deviceId不能同时为空"); - } - BaseDeviceLedger query = new BaseDeviceLedger(); - query.setObjId(deviceId); - query.setIsFlag(1L); - List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); - if (CollectionUtils.isEmpty(list)) { - throw new ServiceException("设备不存在或已停用"); - } - baseDeviceParamVal.setDeviceCode(list.get(0).getDeviceCode()); - return; - } - - // 已传deviceCode时统一去空白,并在缺少deviceId时补齐主键 - baseDeviceParamVal.setDeviceCode(deviceCode.trim()); - if (deviceId == null) { - BaseDeviceLedger query = new BaseDeviceLedger(); - query.setDeviceCode(baseDeviceParamVal.getDeviceCode()); - query.setIsFlag(1L); - List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); - if (CollectionUtils.isEmpty(list)) { - throw new ServiceException("设备不存在或已停用"); - } - baseDeviceParamVal.setDeviceId(list.get(0).getObjId()); - } - } - - private void validateNumericParamValue(String value, boolean integerRequired, String bizName) { - try { - BigDecimal decimal = new BigDecimal(value); - // 业务口径下运行时间/产量都不允许负值 - if (decimal.compareTo(BigDecimal.ZERO) < 0) { - throw new ServiceException(bizName + "不能小于0"); - } - // 当日产量必须是整数,避免出现1.2这类无意义产量值 - if (integerRequired && decimal.stripTrailingZeros().scale() > 0) { - throw new ServiceException(bizName + "必须为整数"); - } - } catch (NumberFormatException ex) { - throw new ServiceException(bizName + "必须为数字"); - } - } /** * PDA-报修 @@ -988,71 +586,513 @@ public class DmsMobileController extends BaseController { +// /** +// * 获取最新一条待保养或保养中的保养工单 +// * +// * @return +// */ +// // //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" ) +// @GetMapping("/getNewestMaintInstance") +// public AjaxResult getNewestMaintInstance(DmsBillsMaintInstance dmsBillsMaintInstance) { +// DmsBillsMaintInstance newestMaintInstance = dmsBillsMaintInstanceService.getNewestBillsMaintInstance(dmsBillsMaintInstance); +// return success(newestMaintInstance); +// } +// +// +// /** +// * 开始保养 +// */ +//// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) +// ////@Log(title = "保养工单", businessType = BusinessType.START) +// @PostMapping("/startMaint") +// public AjaxResult startMaint(@RequestBody DmsBillsMaintDetail dmsBillsMaintDetail) { +// return success(dmsBillsMaintInstanceService.startMaint(dmsBillsMaintDetail)); +// } +// +// /** +// * 完成保养 +// */ +//// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) +// ////@Log(title = "保养工单", businessType = BusinessType.COMPLETE) +// @PostMapping("/completeMaint") +// public AjaxResult completeMaint(@RequestBody DmsBillsMaintDetail dmsBillsMaintDetail) { +// return success(dmsBillsMaintInstanceService.completeMaint(dmsBillsMaintDetail)); +// } +// +// +// /** +// * 获取最新一条待润滑或润滑中的润滑工单 +// * +// * @return +// */ +// // //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" ) +// @GetMapping("/getNewestLubeInstance") +// public AjaxResult getNewestLubeInstance(DmsBillsLubeInstance dmsBillsLubeInstance) { +// DmsBillsLubeInstance newestLubeInstance = dmsBillsLubeInstanceService.getNewestBillsLubeInstance(dmsBillsLubeInstance); +// return success(newestLubeInstance); +// } +// +// /** +// * 开始润滑 +// */ +//// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) +// ////@Log(title = "润滑工单", businessType = BusinessType.START) +// @PostMapping("/startLube") +// public AjaxResult startLube(@RequestBody DmsBillsLubeDetail dmsBillsLubeDetail) { +// +// return success(dmsBillsLubeInstanceService.startLube(dmsBillsLubeDetail)); +// } +// +// /** +// * 完成润滑 +// */ +//// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) +// ////@Log(title = "润滑工单", businessType = BusinessType.COMPLETE) +// @PostMapping("/completeLube") +// public AjaxResult completeLube(@RequestBody DmsBillsLubeDetail dmsBillsLubeDetail) { +// return success(dmsBillsLubeInstanceService.completeLube(dmsBillsLubeDetail)); +// } +// + + + + + + + /** - * 获取最新一条待保养或保养中的保养工单 - * - * @return + * 获取OLD开头的设备列表(仅启用) */ - // //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" ) - @GetMapping("/getNewestMaintInstance") - public AjaxResult getNewestMaintInstance(DmsBillsMaintInstance dmsBillsMaintInstance) { - DmsBillsMaintInstance newestMaintInstance = dmsBillsMaintInstanceService.getNewestBillsMaintInstance(dmsBillsMaintInstance); - return success(newestMaintInstance); + @GetMapping("/device/listOld") + public AjaxResult listOldDevices() { + BaseDeviceLedger query = new BaseDeviceLedger(); + query.setIsFlag(1L); + List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); + List resultList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(list)) { + for (BaseDeviceLedger base : list) { + if (base.getDeviceCode() != null && base.getDeviceCode().startsWith("OLD")) { + resultList.add(convertDeviceLedgerToDto(convertToDeviceLedger(base))); + } + } + } + return success(resultList); } /** - * 开始保养 + * PDA-获取停机原因列表(仅启用 isFlag=1) */ -// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) - ////@Log(title = "保养工单", businessType = BusinessType.START) - @PostMapping("/startMaint") - public AjaxResult startMaint(@RequestBody DmsBillsMaintDetail dmsBillsMaintDetail) { - return success(dmsBillsMaintInstanceService.startMaint(dmsBillsMaintDetail)); + @GetMapping("/shutReason/list") + public AjaxResult listShutReasonsForPda(DmsBaseShutReason reason) { + reason.setIsFlag("1"); + List list = dmsBaseShutReasonService.selectDmsBaseShutReasonList(reason); + return success(list); } /** - * 完成保养 + * PDA-新增停机记录:根据停机原因自动匹配停机类型 */ -// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) - ////@Log(title = "保养工单", businessType = BusinessType.COMPLETE) - @PostMapping("/completeMaint") - public AjaxResult completeMaint(@RequestBody DmsBillsMaintDetail dmsBillsMaintDetail) { - return success(dmsBillsMaintInstanceService.completeMaint(dmsBillsMaintDetail)); + @PostMapping("/shutDown/add") + @Transactional(rollbackFor = Exception.class) + public AjaxResult addShutDownForPda(@RequestBody DmsRecordShutDown shutDown) { + try { + // 若传入了停机原因ID,则查询原因并回填类型 + if (shutDown.getShutReason() != null) { + DmsBaseShutReason query = new DmsBaseShutReason(); + query.setShutReason(shutDown.getShutReason()); + query.setIsFlag("1"); + List reasons = dmsBaseShutReasonService.selectDmsBaseShutReasonList(query); + if (!reasons.isEmpty()) { + shutDown.setShutType(reasons.get(0).getShutTypeId()); + shutDown.setShutReasonId(reasons.get(0).getShutReasonId()); + // 若前端未传 deviceId,尝试从原因带出 + if (shutDown.getDeviceId() == null) { + shutDown.setDeviceId(reasons.get(0).getDeviceId()); + } + } + } + // 默认启用标识 + if (shutDown.getIsFlag() == null) { + shutDown.setIsFlag(1L); + } + // 新增停机记录默认未结束 + if (shutDown.getDowntimeFlag() == null) { + shutDown.setDowntimeFlag("0"); + } + // 若未传入开始时间,则使用当前时间 + if (shutDown.getShutBeginTime() == null) { + shutDown.setShutBeginTime(new Date()); + } + int rows = dmsRecordShutDownService.insertDmsRecordShutDown(shutDown); + + // 对指定设备(OLD-01~OLD-05)同步写入三色灯状态参数,供设备状态统计使用 + String deviceCode = shutDown.getDeviceCode(); + if (deviceCode != null && deviceCode.startsWith("OLD-")) { + // 新增停机记录代表进入停机,三色灯置为“暂停” + insertTriColorStatusParams(deviceCode, shutDown.getDeviceId(), false); + } + + return success(rows); + } catch (Exception e) { + // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 + log.error("PDA新增停机记录失败 | deviceCode={}, shutReason={}, deviceId={}, 异常信息: {}", + shutDown.getDeviceCode(), shutDown.getShutReason(), shutDown.getDeviceId(), e.getMessage(), e); + throw new ServiceException("新增停机记录失败: " + e.getMessage()); + } + } + + /** + * PDA-获取待处理停机记录列表 + * 返回条件:停机结束时间为空(老设备未完成)或 停机原因ID=1(新设备默认原因待修改) + */ + @GetMapping("/shutDown/get") + public AjaxResult getShutDownForPda() { + try { + List list = dmsRecordShutDownService.selectPdaShutDownList(); + return success(list); + } catch (Exception e) { + // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 + log.error("PDA获取待处理停机记录失败 | 异常信息: {}", e.getMessage(), e); + throw new ServiceException("获取停机记录失败: " + e.getMessage()); + } + } + + /** + * PDA-更新停机记录 + * 老设备(shutEndTime为空):更新停机结束时间、计算停机时长、更新停机原因ID + * 新设备(shutEndTime不为空):仅更新停机原因ID + */ + @PostMapping("/shutDown/complete") + @Transactional(rollbackFor = Exception.class) + public AjaxResult completeShutDownForPda(@RequestBody DmsRecordShutDown shutDown) { + if (shutDown.getRecordShutDownId() == null) { + throw new ServiceException("recordShutDownId不能为空"); + } + if (shutDown.getShutReasonId() == null) { + throw new ServiceException("shutReasonId不能为空"); + } + try { + // 查询原记录 + DmsRecordShutDown origin = dmsRecordShutDownService.selectDmsRecordShutDownByRecordShutDownId(shutDown.getRecordShutDownId()); + if (origin == null) { + throw new ServiceException("停机记录不存在"); + } + + // 构建更新对象,仅设置需要更新的字段 + DmsRecordShutDown updateRecord = new DmsRecordShutDown(); + updateRecord.setRecordShutDownId(shutDown.getRecordShutDownId()); + updateRecord.setShutReasonId(shutDown.getShutReasonId()); + + if (origin.getShutEndTime() == null) { + // 老设备:标记停机已结束 + updateRecord.setDowntimeFlag("1"); + // 老设备:补充结束时间和停机时长 + Date end = new Date(); + Date begin = origin.getShutBeginTime(); + updateRecord.setShutEndTime(end); + if (begin != null) { + long minutes = Math.max(0, (end.getTime() - begin.getTime()) / 60000); + updateRecord.setShutTime(BigDecimal.valueOf(minutes)); + } + } + // 新设备(shutEndTime已有值):仅更新shutReasonId,无需额外处理 + + int rows = dmsRecordShutDownService.updateDmsRecordShutDown(updateRecord); + + // 对老设备(OLD-01~OLD-05)同步写入三色灯状态参数 + if (origin.getShutEndTime() == null && origin.getDeviceId() != null) { + // 原记录不含deviceCode,需通过deviceId反查设备编号 + BaseDeviceLedger deviceQuery = new BaseDeviceLedger(); + deviceQuery.setObjId(origin.getDeviceId()); + List devices = baseDeviceLedgerService.selectBaseDeviceLedgerList(deviceQuery); + if (!devices.isEmpty()) { + String deviceCode = devices.get(0).getDeviceCode(); + if (deviceCode != null && deviceCode.startsWith("OLD-")) { + // 完成停机代表重新运行,三色灯置为"运行" + insertTriColorStatusParams(deviceCode, origin.getDeviceId(), true); + } + } + } + + return rows > 0 ? success(origin.getRecordShutDownId()) : error("更新停机记录失败"); + } catch (Exception e) { + // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 + log.error("PDA完成停机记录失败 | recordShutDownId={}, shutReasonId={}, 异常信息: {}", + shutDown.getRecordShutDownId(), shutDown.getShutReasonId(), e.getMessage(), e); + throw new ServiceException("完成停机失败: " + e.getMessage()); + } + } + + /** + * 插入三色灯状态参数(运行/暂停/报警),以“暂停”为当前状态 + */ + private void insertTriColorStatusParams(String deviceCode, Long deviceId, boolean running) { + // 仅处理 OLD-01~OLD-05 + if (!"OLD-01".equals(deviceCode) && !"OLD-02".equals(deviceCode) + && !"OLD-03".equals(deviceCode) && !"OLD-04".equals(deviceCode) + && !"OLD-05".equals(deviceCode)) { + return; + } + + // 补齐 deviceId(如果未传) + Long resolvedDeviceId = deviceId; + if (resolvedDeviceId == null) { + BaseDeviceLedger query = new BaseDeviceLedger(); + query.setDeviceCode(deviceCode); + query.setIsFlag(1L); + List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); + if (!list.isEmpty()) { + resolvedDeviceId = list.get(0).getObjId(); + } + } + + Date now = new Date(); + // 运行、暂停、报警 三个参数,运行/暂停取决于状态 + List records = new ArrayList<>(); + String runVal = running ? "True" : "False"; + String pauseVal = running ? "False" : "True"; + records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器运行", runVal, now)); + records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器暂停", pauseVal, now)); + records.add(buildParamVal(PARAM_CODE_PDA_CUSTOM, deviceCode, resolvedDeviceId, "机台状态-三色灯机器报警", "False", now)); + + for (BaseDeviceParamVal rec : records) { + baseDeviceParamValService.insertBaseDeviceParamVal(rec); + } + } + + private BaseDeviceParamVal buildParamVal(String paramCode, String deviceCode, Long deviceId, + String paramName, String paramValue, Date time) { + BaseDeviceParamVal v = new BaseDeviceParamVal(); + v.setParamCode(paramCode); + v.setDeviceCode(deviceCode); + v.setDeviceId(deviceId); + v.setParamName(paramName); + v.setParamValue(paramValue); + v.setCollectTime(time); + v.setRecordTime(time); + return v; + } + + + + + // ========== PDA 参数相关 ========== + + /** + * 获取所有的设备列表(仅启用) + */ + @GetMapping("/device/getDevices") + public AjaxResult getDevices() { + BaseDeviceLedger query = new BaseDeviceLedger(); + query.setIsFlag(1L); + List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); + List resultList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(list)) { + for (BaseDeviceLedger base : list) { + resultList.add(convertDeviceLedgerToDto(convertToDeviceLedger(base))); + } + } + return success(resultList); + } + + /** + * PDA-选择设备后更新运行时间(写入参数:机台状态-实际生产时间) + */ + @PostMapping("/device/updateRunTime") + @Transactional(rollbackFor = Exception.class) + public AjaxResult updateDeviceRunTime(@RequestBody DmsPdaDeviceParamUpdateVo request) { + if (request == null) { + throw new ServiceException("请求体不能为空"); + } + return updatePdaDeviceParamValue(request.getDeviceCode(), request.getDeviceId(), request.getParamValue(), + PARAM_NAME_ACTUAL_RUNTIME, false, "运行时间"); + } + + /** + * PDA-选择设备后更新当日产量(写入参数:机台状态-实际产出数量) + */ + @PostMapping("/device/updateDailyOutput") + @Transactional(rollbackFor = Exception.class) + public AjaxResult updateDeviceDailyOutput(@RequestBody DmsPdaDeviceParamUpdateVo request) { + if (request == null) { + throw new ServiceException("请求体不能为空"); + } + return updatePdaDeviceParamValue(request.getDeviceCode(), request.getDeviceId(), request.getParamValue(), + PARAM_NAME_DAILY_OUTPUT, true, "当日产量"); + } + + private AjaxResult updatePdaDeviceParamValue(String deviceCode, Long deviceId, BigDecimal paramValue, String paramName, + boolean integerRequired, String bizName) { + // 业务参数值为空时直接拒绝,避免后续数值转换出现非预期错误 + if (paramValue == null) { + throw new ServiceException(bizName + "不能为空"); + } + // PDA上报的业务值统一从paramValue读取,后续会按业务类型做数值校验 + BigDecimal rawValue = paramValue; + if (rawValue == null || rawValue.compareTo(BigDecimal.ZERO) < 0) { + throw new ServiceException(bizName + "不能为空"); + } + // 运行时间允许小数;当日产量要求整数,规则由integerRequired控制 + validateNumericParamValue(rawValue, integerRequired, bizName); + + try { + // 兼容PDA只传deviceId或只传deviceCode的场景,统一补齐设备主数据标识 + BaseDeviceParamVal baseDeviceParamVal = fillDeviceIdentity(deviceCode, deviceId); + Date now = new Date(); + // 参数名由后端强制指定,避免前端透传导致写错业务口径 + baseDeviceParamVal.setParamName(paramName); + baseDeviceParamVal.setParamValue(rawValue.toString()); + if (baseDeviceParamVal.getParamCode() == null || baseDeviceParamVal.getParamCode().trim().isEmpty()) { + // Why:该参数值用于PDA手工更新场景,统一标识便于后续数据追溯与运维排查 + baseDeviceParamVal.setParamCode(PARAM_CODE_PDA_CUSTOM); + } + // 采集时间/记录时间均取服务端当前时间,确保同一时区口径一致 + baseDeviceParamVal.setCollectTime(now); + baseDeviceParamVal.setRecordTime(now); + + // 直接插入一条新参数记录,保留完整时间序列,避免覆盖历史值 + int rows = baseDeviceParamValService.upsertTodayParamValue(baseDeviceParamVal); + return rows > 0 ? success(rows) : error("更新" + bizName + "失败"); + } catch (ServiceException e) { + // 业务异常原样抛出,保留明确可读的提示信息 + throw e; + } catch (Exception e) { + // 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方 + log.error("PDA更新设备{}失败 | deviceCode={}, deviceId={}, paramName={}, paramValue={}, 异常信息: {}", + bizName, deviceCode, deviceId, paramName, rawValue, e.getMessage(), e); + throw new ServiceException("更新" + bizName + "失败: " + e.getMessage()); + } + } + + private BaseDeviceParamVal fillDeviceIdentity(String deviceCode, Long deviceId) { + BaseDeviceParamVal baseDeviceParamVal = new BaseDeviceParamVal(); + boolean hasCode = deviceCode != null && !deviceCode.trim().isEmpty(); + + // 未传deviceCode时,必须依赖deviceId反查设备编码;否则无法确定落库维度 + if (!hasCode) { + // 两个标识都没有时无法定位设备,直接拒绝请求 + if (deviceId == null) { + throw new ServiceException("deviceCode和deviceId不能同时为空"); + } + BaseDeviceLedger query = new BaseDeviceLedger(); + query.setObjId(deviceId); + query.setIsFlag(1L); + List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); + if (CollectionUtils.isEmpty(list)) { + throw new ServiceException("设备不存在或已停用"); + } + baseDeviceParamVal.setDeviceId(deviceId); + baseDeviceParamVal.setDeviceCode(list.get(0).getDeviceCode()); + return baseDeviceParamVal; + } + + // 已传deviceCode时统一去空白,并在缺少deviceId时补齐主键 + baseDeviceParamVal.setDeviceCode(deviceCode.trim()); + if (deviceId == null) { + BaseDeviceLedger query = new BaseDeviceLedger(); + query.setDeviceCode(baseDeviceParamVal.getDeviceCode()); + query.setIsFlag(1L); + List list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query); + if (CollectionUtils.isEmpty(list)) { + throw new ServiceException("设备不存在或已停用"); + } + baseDeviceParamVal.setDeviceId(list.get(0).getObjId()); + return baseDeviceParamVal; + } + baseDeviceParamVal.setDeviceId(deviceId); + return baseDeviceParamVal; + } + + private void validateNumericParamValue(BigDecimal value, boolean integerRequired, String bizName) { + try { + BigDecimal decimal = value; + // 业务口径下运行时间/产量都不允许负值 + if (decimal.compareTo(BigDecimal.ZERO) < 0) { + throw new ServiceException(bizName + "不能小于0"); + } + // 当日产量必须是整数,避免出现1.2这类无意义产量值 + if (integerRequired && decimal.stripTrailingZeros().scale() > 0) { + throw new ServiceException(bizName + "必须为整数"); + } + } catch (NumberFormatException ex) { + throw new ServiceException(bizName + "必须为数字"); + } } /** - * 获取最新一条待润滑或润滑中的润滑工单 - * - * @return + * PDA-按物料关键字查询关联工单 */ - // //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" ) - @GetMapping("/getNewestLubeInstance") - public AjaxResult getNewestLubeInstance(DmsBillsLubeInstance dmsBillsLubeInstance) { - DmsBillsLubeInstance newestLubeInstance = dmsBillsLubeInstanceService.getNewestBillsLubeInstance(dmsBillsLubeInstance); - return success(newestLubeInstance); + @GetMapping("/order/listByMaterial") + public AjaxResult listOrdersByMaterial(String keyword) { + // 物料关键字是PDA唯一输入入口,空值时直接拒绝,避免放大后端模糊查询范围 + if (keyword == null || keyword.trim().isEmpty()) { + throw new ServiceException("物料关键字不能为空"); + } + try { + // Why:先按物料主数据检索,再按物料编码反查订单,能保证PDA搜索口径与主数据一致 + List orders = baseOrderInfoService.selectPdaOrderListByMaterialKeyword(keyword.trim()); + return success(orders); + } catch (Exception e) { + log.error("PDA按物料查询工单失败 | keyword={}, 异常信息: {}", keyword, e.getMessage(), e); + throw new ServiceException("查询工单失败: " + e.getMessage()); + } } /** - * 开始润滑 + * PDA-维护订单状态与数量 */ -// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) - ////@Log(title = "润滑工单", businessType = BusinessType.START) - @PostMapping("/startLube") - public AjaxResult startLube(@RequestBody DmsBillsLubeDetail dmsBillsLubeDetail) { + @PostMapping("/order/updateStatusAndQty") + @Transactional(rollbackFor = Exception.class) + public AjaxResult updateOrderStatusAndQty(@RequestBody BaseOrderInfo baseOrderInfo) { + // 请求体为空时没有任何维护目标,直接拦截比进入service后再判空更清晰 + if (baseOrderInfo == null) { + throw new ServiceException("请求体不能为空"); + } + // PDA下拉选中订单后至少应能定位到一条工单,兼容前端传objId或orderCode两种方式 + if (baseOrderInfo.getObjId() == null + && (baseOrderInfo.getOrderCode() == null || baseOrderInfo.getOrderCode().trim().isEmpty())) { + throw new ServiceException("objId和orderCode不能同时为空"); + } + // 状态和数量都在控制器前置校验,避免脏数据落到service或数据库层 + validateOrderExecutionStatus(baseOrderInfo.getExecutionStatus()); + validateOrderQty(baseOrderInfo.getActualCompleteQty(), "完工数量"); + validateOrderQty(baseOrderInfo.getActualDefectQty(), "不良数量"); - return success(dmsBillsLubeInstanceService.startLube(dmsBillsLubeDetail)); + try { + // Why:PDA订单维护属于人工修正场景,统一走service封装,避免控制器直接拼装状态流转细节 + int rows = baseOrderInfoService.updatePdaOrderExecution(baseOrderInfo); + return rows > 0 ? success(rows) : error("工单不存在或更新失败"); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.error("PDA维护订单失败 | objId={}, orderCode={}, executionStatus={}, actualCompleteQty={}, 异常信息: {}", + baseOrderInfo.getObjId(), baseOrderInfo.getOrderCode(), baseOrderInfo.getExecutionStatus(), + baseOrderInfo.getActualCompleteQty(), e.getMessage(), e); + throw new ServiceException("维护订单失败: " + e.getMessage()); + } } - /** - * 完成润滑 - */ -// //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" ) - ////@Log(title = "润滑工单", businessType = BusinessType.COMPLETE) - @PostMapping("/completeLube") - public AjaxResult completeLube(@RequestBody DmsBillsLubeDetail dmsBillsLubeDetail) { - return success(dmsBillsLubeInstanceService.completeLube(dmsBillsLubeDetail)); + private void validateOrderExecutionStatus(String executionStatus) { + // PDA当前仅允许维护执行态,保持与execution.vue和现有执行页状态枚举一致 + if (executionStatus == null || executionStatus.trim().isEmpty()) { + throw new ServiceException("执行状态不能为空"); + } + String normalized = executionStatus.trim(); + if (!ORDER_EXECUTION_PENDING.equals(normalized) + && !ORDER_EXECUTION_RUNNING.equals(normalized) + && !ORDER_EXECUTION_PAUSED.equals(normalized) + && !ORDER_EXECUTION_COMPLETED.equals(normalized)) { + throw new ServiceException("执行状态不合法"); + } + } + + private void validateOrderQty(Long qty, String fieldName) { + // 数量允许为空,表示本次只改状态;但一旦传值就必须满足非负约束 + if (qty != null && qty < 0) { + throw new ServiceException(fieldName + "不能小于0"); + } } }