package com.aucma.dms.controller;


import com.aucma.common.core.controller.BaseController;
import com.aucma.common.core.domain.AjaxResult;
import com.aucma.common.core.page.TableDataInfo;
import com.aucma.common.exception.ServiceException;
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;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.service.IBaseOrderInfoService;
import com.aucma.base.service.IBaseDeviceParamValService;
import com.aucma.base.support.DeviceParamTableRouter;
import com.aucma.dms.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Description:手持PDA接口
 * @ProjectName:HwMes
 * @Author:xins
 * @Date:2024-03-13 17:08
 * @Version:1.0
 */
@RestController
@RequestMapping("/dms/mobile")
public class DmsMobileController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(DmsMobileController.class);

    @Autowired
    private IDmsBillsFaultInstanceService dmsBillsFaultInstanceService;

    @Autowired
    private IDmsBillsInspectInstanceService dmsBillsInspectInstanceService;

    @Autowired
    private IDmsBillsMaintInstanceService dmsBillsMaintInstanceService;

    @Autowired
    private IDmsBillsLubeInstanceService dmsBillsLubeInstanceService;

    // 【重构新增】维修工单服务 - 用于PDA端自动创建维修工单
    @Autowired
    private IDmsRepairWorkOrderService dmsRepairWorkOrderService;

    // 【重构新增】派工计划服务 - 用于PDA端自动创建派工计划
    @Autowired
    private IDmsDispatchPlanService dmsDispatchPlanService;

    // 【重构新增】维修记录服务 - 用于PDA端自动创建维修记录
    @Autowired
    private IDmsRepairRecordService dmsRepairRecordService;

    // 【重构新增】基础设备台账服务 - 使用aucma-base模块的设备信息
    @Autowired
    private IBaseDeviceLedgerService baseDeviceLedgerService;

    @Autowired
    private IBaseDeviceParamValService baseDeviceParamValService;

    @Autowired
    private IBaseOrderInfoService baseOrderInfoService;

    @Autowired
    private DeviceParamTableRouter deviceParamTableRouter;

    private static final String PARAM_CODE_PDA_CUSTOM = "9999";
    private static final String PARAM_NAME_ACTUAL_RUNTIME = "机台状态-实际生产时间";
    // 为什么这样做：PDA 手工输入的是“今天已经产了多少”，
    // 其业务语义是日累计值，而不是设备累计计数器当前值。
    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;

    // 停机记录服务
    @Autowired
    private IDmsRecordShutDownService dmsRecordShutDownService;


    /**
     * 根据设备编号获取设备信息
     * 【重构】使用aucma-base模块的BaseDeviceLedger，返回类型转换为DmsBaseDeviceLedger
     *
     * @param deviceCode 设备编号
     * @return 设备信息
     */
    @GetMapping("/getDeviceByDeviceCode/{deviceCode}")
    public AjaxResult getDeviceByDeviceCode(@PathVariable("deviceCode") String deviceCode) {
        try {
            BaseDeviceLedger query = new BaseDeviceLedger();
            query.setDeviceCode(deviceCode);
            query.setIsFlag(1L); // 只查询有效设备
            List<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
            if (CollectionUtils.isEmpty(list)) {
                return success(null);
            }
            // 转换为DmsBaseDeviceLedger返回，保持PDA兼容性
            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());
        }
    }

    /**
     * 根据传参模糊查询设备名
     * 【重构】使用aucma-base模块的BaseDeviceLedger，返回类型转换为DmsBaseDeviceLedger列表
     *
     * @param deviceName 设备名称（模糊匹配）
     * @return 设备列表
     */
    @GetMapping("/likeDeviceName")
    public AjaxResult likeDeviceName(String deviceName) {
        try {
            BaseDeviceLedger query = new BaseDeviceLedger();
            query.setDeviceName(deviceName);
            query.setIsFlag(1L); // 只查询有效设备
            List<BaseDeviceLedger> list = baseDeviceLedgerService.selectBaseDeviceLedgerList(query);
            // 转换为DmsBaseDeviceLedger列表返回，保持PDA兼容性
            List<DmsBaseDeviceLedgerDTO> resultList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(list)) {
                for (BaseDeviceLedger base : list) {
                    resultList.add(convertDeviceLedgerToDto(convertToDeviceLedger(base)));
                }
            }
            return success(resultList);
        } catch (Exception e) {
            // 系统异常统一落日志并转业务异常，避免将内部堆栈直接暴露给调用方
            log.error("PDA模糊查询设备失败 | deviceName={}, 异常信息: {}", deviceName, e.getMessage(), e);
            throw new ServiceException("查询设备失败: " + e.getMessage());
        }
    }

    /**
     * 将BaseDeviceLedger转换为DmsBaseDeviceLedger
     * 【重构辅助方法】保持PDA接口返回类型不变
     *
     * @param base aucma-base模块的设备实体
     * @return DMS模块的设备实体
     */
    private DmsBaseDeviceLedger convertToDeviceLedger(BaseDeviceLedger base) {
        if (base == null) {
            return null;
        }
        DmsBaseDeviceLedger dms = new DmsBaseDeviceLedger();
        dms.setDeviceId(base.getObjId());
        dms.setDeviceCode(base.getDeviceCode());
        dms.setDeviceName(base.getDeviceName());
        dms.setDeviceTypeId(base.getDeviceType());
        dms.setDeviceStatus(base.getDeviceStatus());
        dms.setAssetNumber(base.getAssetCode());
        dms.setIsFlag(base.getIsFlag());
        dms.setSuplierName(base.getManufacturer());
        dms.setDeviceSpec(base.getDeviceModel()); // 设备型号映射到设备规格
        dms.setDeviceLocation(base.getUsedDepartment()); // 使用部门映射到设备位置
        // 时间字段映射
        dms.setCreateBy(parseLongSafely(base.getCreatedBy()));
        dms.setCreateTime(base.getCreatedTime());
        dms.setUpdateBy(parseLongSafely(base.getUpdatedBy()));
        dms.setUpdateTime(base.getUpdatedTime());
        return dms;
    }

    /**
     * 将DmsBaseDeviceLedger转换为DmsBaseDeviceLedgerDTO
     *
     * @param entity DmsBaseDeviceLedger实体
     * @return DTO对象
     */
    private DmsBaseDeviceLedgerDTO convertDeviceLedgerToDto(DmsBaseDeviceLedger entity) {
        if (entity == null) {
            return null;
        }
        DmsBaseDeviceLedgerDTO dto = new DmsBaseDeviceLedgerDTO();
        dto.setDeviceId(entity.getDeviceId());
        dto.setDeviceCode(entity.getDeviceCode());
        dto.setDeviceName(entity.getDeviceName());
        dto.setStationId(entity.getStationId());
        dto.setAssetNumber(entity.getAssetNumber());
        dto.setDeviceLocation(entity.getDeviceLocation());
        dto.setDeviceTypeId(entity.getDeviceTypeId());
        dto.setDeviceSpec(entity.getDeviceSpec());
        dto.setSupplierId(entity.getSupplierId());
        dto.setDeviceStatus(entity.getDeviceStatus());
        dto.setIsFlag(entity.getIsFlag());
        dto.setSuplierName(entity.getSuplierName());
        return dto;
    }

    /**
     * 安全地将String转换为Long
     * @param value 字符串值
     * @return Long值，转换失败返回null
     */
    private Long parseLongSafely(String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        try {
            return Long.valueOf(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }


    /**
     * 将DmsBaseDeviceLedgerDTO转换为DmsBaseDeviceLedger
     *
     * @param dto DTO对象
     * @return DmsBaseDeviceLedger实体
     */
    private DmsBaseDeviceLedger convertDtoToDeviceLedger(DmsBaseDeviceLedgerDTO dto) {
        if (dto == null) {
            return null;
        }
        DmsBaseDeviceLedger dms = new DmsBaseDeviceLedger();
        dms.setDeviceId(dto.getDeviceId());
        dms.setDeviceCode(dto.getDeviceCode());
        dms.setDeviceName(dto.getDeviceName());
        dms.setStationId(dto.getStationId());
        dms.setAssetNumber(dto.getAssetNumber());
        dms.setDeviceLocation(dto.getDeviceLocation());
        dms.setDeviceTypeId(dto.getDeviceTypeId());
        dms.setDeviceSpec(dto.getDeviceSpec());
        dms.setSupplierId(dto.getSupplierId());
        dms.setDeviceStatus(dto.getDeviceStatus());
        dms.setIsFlag(dto.getIsFlag());
        dms.setSuplierName(dto.getSuplierName());
        return dms;
    }



    /**
     * PDA-报修
     * <p>
     * 需传入
     * 设备id          deviceId
     * 故障类别         faultType
     * 故障描述         faultDescription
     * 涉及操作         designOperations
     * 维修类型         repairType
     * 外协id          outsrcId(可不传入)
     * 图片            files
     */
//    //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" )
    ////@Log(title = "故障报修工单", businessType = BusinessType.INSERT)
    @PostMapping("/insertFaultInstsanceActivity")
    public AjaxResult insertFaultInstsanceActivity(DmsBillsFaultInstance dmsBillsFaultInstance,
                                                   List<MultipartFile> files) {
        List<String> fileUrls = new ArrayList<>();
/*        if(files!=null){
            for (MultipartFile file : files) {
                R<SysFile> sysFileR = remoteFileService.upload(file);
                SysFile sysFile = sysFileR.getData();
                fileUrls.add(sysFile.getUrl());
            }
        }*/
        dmsBillsFaultInstance.setFileUrls(fileUrls);
        // PDA发起的报修默认视为已审批通过，避免进入待审批状态
        dmsBillsFaultInstance.setApproveStatus("2");
        return success(dmsBillsFaultInstanceService.insertDmsBillsFaultInstance(dmsBillsFaultInstance));
    }


    /**
     * 获取故障报修工单列表
     *
     * @return
     */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/getBillsFaultInstances")
    public TableDataInfo getBillsFaultInstances() {
//        startPage();
        DmsBillsFaultInstance queryBillsFaultInstance = new DmsBillsFaultInstance();
        List<DmsBillsFaultInstance> billsFaultInstances = dmsBillsFaultInstanceService.selectDmsBillsFaultInstanceJoinList(queryBillsFaultInstance);
        return getDataTable(billsFaultInstances);
    }

    /**
     * 获取故障报修工单详细信息,修改工单使用
     *
     * @return
     */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/getBillsFaultInstanceByRepairInstanceId/{repairInstanceId}")
    public AjaxResult getBillsFaultInstanceByRepairInstanceId(@PathVariable("repairInstanceId") Long repairInstanceId) {
        DmsBillsFaultInstance billsFaultInstance = dmsBillsFaultInstanceService.selectDmsBillsFaultInstanceByRepairInstanceId(repairInstanceId);
        return success(billsFaultInstance);
    }

    /**
     * 修改工单提交
     */
//    //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" )
    ////@Log(title = "故障报修工单", businessType = BusinessType.UPDATE)
    @PostMapping("/updateFaultInstanceActivity")
    public AjaxResult updateFaultInstanceActivity(DmsFaultInstanceActivity dmsFaultInstanceActivity,
                                                  @RequestParam(value = "files") List<MultipartFile> files) {
        List<String> fileUrls = new ArrayList<>();
        if (!CollectionUtils.isEmpty(files)){
/*            for (MultipartFile file : files) {
                R<SysFile> sysFileR = remoteFileService.upload(file);
                SysFile sysFile = sysFileR.getData();
                fileUrls.add(sysFile.getUrl());
            }*/
            dmsFaultInstanceActivity.setFileUrls(fileUrls);
        }
        return success(dmsBillsFaultInstanceService.updateDmsFaultInstanceActivity(dmsFaultInstanceActivity));
    }

    /**
     * 获取故障报修工单详细信息,维修使用
     *
     * @return
     */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/getBillsFaultInstance4Repair/{repairInstanceId}")
    public AjaxResult getBillsFaultInstance4Repair(@PathVariable("repairInstanceId") Long repairInstanceId) {
        DmsBillsFaultInstance billsFaultInstance = dmsBillsFaultInstanceService.getDmsBillsFaultInstance4Repair(repairInstanceId);
        return success(billsFaultInstance);
    }


    /**
     * 故障报修扫描设备验证
     *
     * @return
     */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/scanDevice4Repair")
    public AjaxResult scanDevice4Repair(@Validated DmsBillsFaultInstanceScanVo dmsBillsFaultInstanceScanVo) {
        dmsBillsFaultInstanceService.scanDevice4Repair(dmsBillsFaultInstanceScanVo);
        return success();
    }


    /**
     * 开始维修
     *
     * 【重构说明】
     * - 保持接口路径和参数格式不变，PDA无需修改
     * - 内部补全维修工单和派工计划的创建逻辑
     * - 触发断点1和断点2，确保数据完整性
     */
    @PostMapping("/startRepair")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult startRepair(@RequestBody DmsFaultInstanceActivity dmsFaultInstanceActivity) {
        Long repairInstanceId = dmsFaultInstanceActivity.getRepairInstanceId();
        String repairer = dmsFaultInstanceActivity.getRepairer();

        log.info("PDA开始维修，故障报修ID: {}, 维修人: {}", repairInstanceId, repairer);

        // ========== 步骤1：调用原有的开始维修逻辑 ==========
        int result = dmsBillsFaultInstanceService.startRepair(dmsFaultInstanceActivity);

        // ========== 步骤2：查询故障报修详情 ==========
        DmsBillsFaultInstance fault = dmsBillsFaultInstanceService
                .selectDmsBillsFaultInstanceByRepairInstanceId(repairInstanceId);
        if (fault == null) {
            log.warn("故障报修[{}]不存在", repairInstanceId);
            return success(result);
        }

        // ========== 步骤3：检查是否已有维修工单 ==========
        DmsRepairWorkOrder existingOrder = dmsRepairWorkOrderService.selectByFaultInstanceId(repairInstanceId);

        if (existingOrder == null) {
            // ========== 步骤4：【断点1】自动创建维修工单 ==========
            DmsRepairWorkOrder newOrder = new DmsRepairWorkOrder();
            newOrder.setFaultInstanceId(repairInstanceId);
            newOrder.setDeviceId(fault.getDeviceId());
            newOrder.setFaultType(fault.getFaultType());
            newOrder.setFaultDescription(fault.getFaultDescription());
            newOrder.setRepairType(fault.getRepairType());
            newOrder.setOrderStatus("2"); // 直接设为维修中（PDA开始维修即进入维修状态）
            newOrder.setPriority("3"); // 默认中优先级
            newOrder.setActualStartTime(new Date());
            // 设置执行人信息
            if (dmsFaultInstanceActivity.getHandleUserId() != null) {
                newOrder.setExecutorId(dmsFaultInstanceActivity.getHandleUserId());
            }
            if (repairer != null && !repairer.isEmpty()) {
                newOrder.setExecutorName(repairer);
            }

            dmsRepairWorkOrderService.insertDmsRepairWorkOrder(newOrder);
            log.info("【断点1】PDA自动创建维修工单，工单ID: {}, 工单编号: {}", 
                    newOrder.getWorkOrderId(), newOrder.getWorkOrderCode());

            // ========== 步骤5：【断点2】自动创建派工计划并接收 ==========
            DmsDispatchPlan plan = new DmsDispatchPlan();
            plan.setWorkOrderId(newOrder.getWorkOrderId());
            plan.setWorkOrderCode(newOrder.getWorkOrderCode());
            plan.setDeviceId(fault.getDeviceId());
            plan.setDispatchType("1"); // 单人派工
            plan.setReceiveStatus("1"); // 直接设为已接收（PDA开始维修即表示已接收）
            plan.setExecutionStatus("1"); // 执行中
            if (dmsFaultInstanceActivity.getHandleUserId() != null) {
                plan.setExecutorId(dmsFaultInstanceActivity.getHandleUserId());
            }
            if (repairer != null && !repairer.isEmpty()) {
                plan.setExecutorName(repairer);
            }
            plan.setDispatchNotes("由PDA端开始维修自动生成");

            dmsDispatchPlanService.insertDmsDispatchPlan(plan);
            log.info("【断点2】PDA自动创建派工计划并接收，计划ID: {}", plan.getPlanId());
        } else {
            // 已有维修工单，更新状态为维修中
            if (!"2".equals(existingOrder.getOrderStatus()) && !"3".equals(existingOrder.getOrderStatus())) {
                DmsRepairWorkOrder updateOrder = new DmsRepairWorkOrder();
                updateOrder.setWorkOrderId(existingOrder.getWorkOrderId());
                updateOrder.setOrderStatus("2"); // 维修中
                updateOrder.setActualStartTime(new Date());
                if (dmsFaultInstanceActivity.getHandleUserId() != null) {
                    updateOrder.setExecutorId(dmsFaultInstanceActivity.getHandleUserId());
                }
                if (repairer != null && !repairer.isEmpty()) {
                    updateOrder.setExecutorName(repairer);
                }
                dmsRepairWorkOrderService.updateDmsRepairWorkOrder(updateOrder);
                log.info("PDA更新已有维修工单[{}]状态为维修中", existingOrder.getWorkOrderCode());
            }
        }

        return success(result);
    }

    /**
     * 完成维修
     *
     * 【重构说明】
     * - 保持接口路径和参数格式不变，PDA无需修改
     * - 内部补全维修记录的创建逻辑
     * - 触发断点3，确保数据完整性
     */
    @PostMapping("/completeRepair")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult completeRepair(DmsFaultInstanceActivity dmsFaultInstanceActivity, @RequestParam(value = "files", required = false) List<MultipartFile> files) throws JsonProcessingException {
        Long repairInstanceId = dmsFaultInstanceActivity.getRepairInstanceId();
        String repairer = dmsFaultInstanceActivity.getRepairer();

        log.info("PDA完成维修，故障报修ID: {}, 维修人: {}", repairInstanceId, repairer);

        // ========== 步骤1：原有逻辑 - 设置确认信息和解析配件 ==========
        dmsFaultInstanceActivity.setRepairConfirm(2);
        dmsFaultInstanceActivity.setConfirmTime(new Date());

        // FIXME：PDA已注释掉，原本是为了确保维修内容/安全措施等信息持久化并可回显
        // dmsFaultInstanceActivity.setProcessActivityId(1021L);
        
        String parts1 = dmsFaultInstanceActivity.getParts1();
        if (parts1 != null && !parts1.isEmpty()) {
            ObjectMapper objectMapper = new ObjectMapper();
            List<DmsFaultCompentsParts> parts2 = objectMapper.readValue(parts1, new TypeReference<List<DmsFaultCompentsParts>>() {});
            dmsFaultInstanceActivity.setParts(parts2);
        }
        List<String> fileUrls = new ArrayList<>();
        dmsFaultInstanceActivity.setFileUrls(fileUrls);

        // ========== 步骤2：调用原有的完成维修逻辑 ==========
        int result = dmsBillsFaultInstanceService.completeRepair(dmsFaultInstanceActivity);

        // ========== 步骤3：查询关联的维修工单 ==========
        DmsRepairWorkOrder workOrder = dmsRepairWorkOrderService.selectByFaultInstanceId(repairInstanceId);

        if (workOrder != null) {
            // ========== 步骤4：更新维修工单状态为已完成 ==========
            DmsRepairWorkOrder updateOrder = new DmsRepairWorkOrder();
            updateOrder.setWorkOrderId(workOrder.getWorkOrderId());
            updateOrder.setOrderStatus("3"); // 已完成
            updateOrder.setActualEndTime(new Date());
            updateOrder.setRepairResult("1"); // 维修结果：1-成功
            updateOrder.setRepairNotes(dmsFaultInstanceActivity.getRepairContent()); // 维修内容存到维修说明
            dmsRepairWorkOrderService.updateDmsRepairWorkOrder(updateOrder);
            log.info("PDA更新维修工单[{}]状态为已完成", workOrder.getWorkOrderCode());

            // ========== 步骤5：【断点3】自动创建维修记录 ==========
            DmsRepairRecord record = new DmsRepairRecord();
            record.setWorkOrderId(workOrder.getWorkOrderId());
            record.setWorkOrderCode(workOrder.getWorkOrderCode());
            record.setDeviceId(workOrder.getDeviceId());
            record.setDeviceCode(workOrder.getDeviceCode());
            record.setDeviceName(workOrder.getDeviceName());
            record.setFaultPhenomenon(workOrder.getFaultDescription());
            // 维修人员信息
            if (dmsFaultInstanceActivity.getHandleUserId() != null) {
                record.setRepairerId(dmsFaultInstanceActivity.getHandleUserId());
            } else if (workOrder.getExecutorId() != null) {
                record.setRepairerId(workOrder.getExecutorId());
            }
            if (repairer != null && !repairer.isEmpty()) {
                record.setRepairerName(repairer);
            } else if (workOrder.getExecutorName() != null) {
                record.setRepairerName(workOrder.getExecutorName());
            }
            // 维修时间
            record.setStartTime(workOrder.getActualStartTime());
            record.setEndTime(new Date());
            // 维修结果和维修方法
            record.setRepairResult("1"); // 维修结果：1-成功
            record.setRepairMethod(dmsFaultInstanceActivity.getRepairContent()); // 维修内容存到维修方法
            // 默认未存档
            record.setIsArchived("0");

            dmsRepairRecordService.insertDmsRepairRecord(record);
            log.info("【断点3】PDA自动创建维修记录，记录ID: {}", record.getRecordId());

            // ========== 步骤6：更新派工计划状态为已完成 ==========
            DmsDispatchPlan queryPlan = new DmsDispatchPlan();
            queryPlan.setWorkOrderId(workOrder.getWorkOrderId());
            List<DmsDispatchPlan> plans = dmsDispatchPlanService.selectDmsDispatchPlanList(queryPlan);
            if (!CollectionUtils.isEmpty(plans)) {
                DmsDispatchPlan updatePlan = new DmsDispatchPlan();
                updatePlan.setPlanId(plans.get(0).getPlanId());
                updatePlan.setExecutionStatus("2"); // 已完成
                dmsDispatchPlanService.updateDmsDispatchPlan(updatePlan);
                log.info("PDA更新派工计划[{}]状态为已完成", plans.get(0).getPlanCode());
            }
        } else {
            log.warn("故障报修[{}]无关联维修工单，跳过维修记录创建", repairInstanceId);
        }

        return success(result);
    }


    /**
     * 获取最新一条待巡检或巡检中的巡检或点检工单(检查类型inspectType(1巡检,2点检))
     *
     * @return
     */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/getNewestInspectInstance")
    public AjaxResult getNewestInspectInstance(DmsBillsInspectInstance dmsBillsInspectInstance) {
        DmsBillsInspectInstance newestInspectInstance = dmsBillsInspectInstanceService.getNewestBillsInspectInstance(dmsBillsInspectInstance);
        return success(newestInspectInstance);
    }


    /**
     * 扫描设备编号或者输入设备编号进行巡检验证
     * */
    //    //@PreAuthorize("@ss.hasPermi('qms:checkrule:list')" )
    @GetMapping("/scanInspectInstanceDetail")
    public AjaxResult scanInspectInstanceDetail(@Validated DmsBillsInspectInstanceScanVo dmsBillsInspectInstanceScanVo) {
        return success(dmsBillsInspectInstanceService.scanInspectInstanceDetail(dmsBillsInspectInstanceScanVo));
    }

    /**
     * 点巡检工单
     */
//    //@PreAuthorize("@ss.hasPermi('qms:checkresultdetail:add')" )
    ////@Log(title = "点巡检工单明细", businessType = BusinessType.INSERT)
    @PostMapping("/saveInspectInstanceDetail")
    public AjaxResult saveInspectInstanceDetail(@RequestBody DmsInspectInstanceDetail dmsInspectInstanceDetail) {
         return success(dmsBillsInspectInstanceService.updateDmsInspectInstanceDetail(dmsInspectInstanceDetail));

    }




//    /**
//     * 获取最新一条待保养或保养中的保养工单
//     *
//     * @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));
//    }
//







    /**
     * 获取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-更新停机记录
     * 老设备（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 设备同步写入三色灯状态参数
            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 (deviceParamTableRouter.isOldDevice(deviceCode)) {
                        // 完成停机代表重新运行，三色灯置为"运行"
                        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 设备识别规则必须全系统统一，否则新增 OLD 编码后会出现状态丢失。
        if (!deviceParamTableRouter.isOldDevice(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("请求体不能为空");
        }
        // 为什么这样做：PDA 填的是“今天已经产了多少”，
        // 业务语义就是“生产计数-当前日期生产总数”，不能再按累计计数器参数写入。
        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);
            // 保留原始录入值，后续 RT 是否“覆盖”还是“增量”由 Service 层按参数语义决定。
            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);

            // 直接插入一条新参数记录，保留完整时间序列，避免覆盖历史值
            // 为什么这样做：PDA 手工修正也需要保留操作轨迹，后续才能追溯“何时改过今日产量”。
            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 + "必须为数字");
        }
    }


    /**
     * 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");
        }
    }
}


