fix(pda): 修复PDA数据查询逻辑及设备参数更新功能

- 修正物料查询条件,将is_flag判断从1改为0以正确显示启用物料
- 修正工单查询条件,将NVL默认值从1改为0以正确过滤有效工单
- 重构设备参数更新逻辑,优化数值校验和设备标识补全流程
- 添加DmsPdaDeviceParamUpdateVo数据传输对象支持参数更新
- 修复订单完工数量与完成金额字段同步问题,保持数据一致性
- 重写设备运行时间和产量更新接口,提升参数验证准确性
- 恢复并优化停机管理相关功能,包括新增、查询和完成停机记录
master
zangch@mesnac.com 5 days ago
parent 3ec5e2f7bc
commit 7a6d2a68c4

@ -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() {

@ -89,7 +89,7 @@
FROM (
<include refid="selectBaseMaterialInfoVo"/>
<!-- 只返回启用物料避免PDA选择到已停用主数据 -->
WHERE ml.is_flag = 1
WHERE ml.is_flag = 0
AND (
<!-- 编码走不区分大小写匹配兼容PDA扫码枪/人工输入大小写不统一 -->
UPPER(ml.material_code) LIKE '%' || UPPER(#{keyword}) || '%'

@ -141,7 +141,7 @@
<include refid="selectBaseOrderInfoVo"/>
<where>
<!-- PDA只维护有效工单避免停用/脏数据进入下拉框 -->
AND NVL(oi.is_flag, 1) = 1
AND NVL(oi.is_flag, 0) = 0
<if test="materialCodes != null and materialCodes.size() > 0">
<!-- 这里按物料编码精确匹配,模糊搜索已在物料主数据层完成 -->
AND oi.material_code IN

@ -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<BaseOrderInfo> 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 {
// WhyPDA订单维护属于人工修正场景统一走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<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
List<DmsBaseDeviceLedgerDTO> 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<DmsBaseShutReason> 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<DmsBaseShutReason> 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<DmsRecordShutDown> list = dmsRecordShutDownService.selectPdaShutDownList();
return success(list);
} catch (Exception e) {
// 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方
log.error("PDA获取待处理停机记录失败 | 异常信息: {}", e.getMessage(), e);
throw new ServiceException("获取停机记录失败: " + e.getMessage());
}
}
/**
* PDA-
* shutEndTimeID
* shutEndTimeID
*/
@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<BaseDeviceLedger> 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<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
if (!list.isEmpty()) {
resolvedDeviceId = list.get(0).getObjId();
}
}
Date now = new Date();
// 运行、暂停、报警 三个参数,运行/暂停取决于状态
List<BaseDeviceParamVal> 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<BaseDeviceLedger> 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<BaseDeviceLedger> 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<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
List<DmsBaseDeviceLedgerDTO> 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<DmsBaseShutReason> 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<DmsBaseShutReason> 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<DmsRecordShutDown> list = dmsRecordShutDownService.selectPdaShutDownList();
return success(list);
} catch (Exception e) {
// 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方
log.error("PDA获取待处理停机记录失败 | 异常信息: {}", e.getMessage(), e);
throw new ServiceException("获取停机记录失败: " + e.getMessage());
}
}
/**
* PDA-
* shutEndTimeID
* shutEndTimeID
*/
@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<BaseDeviceLedger> 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<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
if (!list.isEmpty()) {
resolvedDeviceId = list.get(0).getObjId();
}
}
Date now = new Date();
// 运行、暂停、报警 三个参数,运行/暂停取决于状态
List<BaseDeviceParamVal> 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<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
List<DmsBaseDeviceLedgerDTO> 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<BaseDeviceLedger> 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<BaseDeviceLedger> 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<BaseOrderInfo> 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 {
// WhyPDA订单维护属于人工修正场景统一走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");
}
}
}

Loading…
Cancel
Save