|
|
|
|
@ -10,8 +10,10 @@ import com.aucma.dms.domain.dto.DmsBaseDeviceLedgerDTO;
|
|
|
|
|
import com.aucma.dms.domain.vo.DmsBillsFaultInstanceScanVo;
|
|
|
|
|
import com.aucma.dms.domain.vo.DmsBillsInspectInstanceScanVo;
|
|
|
|
|
import com.aucma.base.domain.BaseDeviceLedger;
|
|
|
|
|
import com.aucma.base.domain.BaseOrderInfo;
|
|
|
|
|
import com.aucma.base.service.IBaseDeviceLedgerService;
|
|
|
|
|
import com.aucma.base.domain.BaseDeviceParamVal;
|
|
|
|
|
import com.aucma.base.service.IBaseOrderInfoService;
|
|
|
|
|
import com.aucma.base.service.IBaseDeviceParamValService;
|
|
|
|
|
import com.aucma.dms.service.*;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
@ -75,6 +77,17 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
@Autowired
|
|
|
|
|
private IBaseDeviceParamValService baseDeviceParamValService;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private IBaseOrderInfoService baseOrderInfoService;
|
|
|
|
|
|
|
|
|
|
private static final String PARAM_CODE_PDA_CUSTOM = "9999";
|
|
|
|
|
private static final String PARAM_NAME_ACTUAL_RUNTIME = "机台状态-实际生产时间";
|
|
|
|
|
private static final String PARAM_NAME_DAILY_OUTPUT = "机台状态-实际产出数量";
|
|
|
|
|
private static final String ORDER_EXECUTION_PENDING = "PENDING";
|
|
|
|
|
private static final String ORDER_EXECUTION_RUNNING = "RUNNING";
|
|
|
|
|
private static final String ORDER_EXECUTION_PAUSED = "PAUSED";
|
|
|
|
|
private static final String ORDER_EXECUTION_COMPLETED = "COMPLETED";
|
|
|
|
|
|
|
|
|
|
// 停机原因服务
|
|
|
|
|
@Autowired
|
|
|
|
|
private IDmsBaseShutReasonService dmsBaseShutReasonService;
|
|
|
|
|
@ -105,6 +118,7 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
DmsBaseDeviceLedger result = convertToDeviceLedger(list.get(0));
|
|
|
|
|
return success(convertDeviceLedgerToDto(result));
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方
|
|
|
|
|
log.error("PDA查询设备信息失败 | deviceCode={}, 异常信息: {}", deviceCode, e.getMessage(), e);
|
|
|
|
|
throw new ServiceException("获取设备信息失败: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
@ -133,6 +147,7 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
}
|
|
|
|
|
return success(resultList);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方
|
|
|
|
|
log.error("PDA模糊查询设备失败 | deviceName={}, 异常信息: {}", deviceName, e.getMessage(), e);
|
|
|
|
|
throw new ServiceException("查询设备失败: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
@ -237,6 +252,80 @@ 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 {
|
|
|
|
|
// 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 停机相关 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -315,6 +404,7 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
@ -331,6 +421,7 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
List<DmsRecordShutDown> list = dmsRecordShutDownService.selectPdaShutDownList();
|
|
|
|
|
return success(list);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 系统异常统一落日志并转业务异常,避免将内部堆栈直接暴露给调用方
|
|
|
|
|
log.error("PDA获取待处理停机记录失败 | 异常信息: {}", e.getMessage(), e);
|
|
|
|
|
throw new ServiceException("获取停机记录失败: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
@ -395,6 +486,7 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
@ -429,9 +521,9 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
List<BaseDeviceParamVal> records = new ArrayList<>();
|
|
|
|
|
String runVal = running ? "True" : "False";
|
|
|
|
|
String pauseVal = running ? "False" : "True";
|
|
|
|
|
records.add(buildParamVal("9999", deviceCode, resolvedDeviceId, "机台状态-三色灯机器运行", runVal, now));
|
|
|
|
|
records.add(buildParamVal("9999", deviceCode, resolvedDeviceId, "机台状态-三色灯机器暂停", pauseVal, now));
|
|
|
|
|
records.add(buildParamVal("9999", deviceCode, resolvedDeviceId, "机台状态-三色灯机器报警", "False", now));
|
|
|
|
|
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);
|
|
|
|
|
@ -451,6 +543,120 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
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-报修
|
|
|
|
|
* <p>
|
|
|
|
|
@ -849,3 +1055,5 @@ public class DmsMobileController extends BaseController {
|
|
|
|
|
return success(dmsBillsLubeInstanceService.completeLube(dmsBillsLubeDetail));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|