From 9dc90b5606347fbf8730474282c237a42c2fbf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=B8=E7=86=9F=E5=90=B4=E5=BD=A6=E7=A5=96?= Date: Mon, 19 Oct 2026 17:54:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89agv=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../autoWareHourceHaian/HAIAN_AI_CONTEXT.md | 1 + .../impl/HaiAnWmsMessageServiceImpl.java | 51 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gaotao/modules/autoWareHourceHaian/HAIAN_AI_CONTEXT.md b/src/main/java/com/gaotao/modules/autoWareHourceHaian/HAIAN_AI_CONTEXT.md index 842d293d..2b3421ac 100644 --- a/src/main/java/com/gaotao/modules/autoWareHourceHaian/HAIAN_AI_CONTEXT.md +++ b/src/main/java/com/gaotao/modules/autoWareHourceHaian/HAIAN_AI_CONTEXT.md @@ -100,6 +100,7 @@ - 海安 AGV 反馈接口 `POST /api/wms54/count/agvCallback` 必填 `site/wmsId/taskId/托盘号/targetPoint/targetArea/type`;托盘号兼容 `palletId` 与 `pallet`,两者都有时优先 `palletId`。`targetPoint` 命中三个端口时按 `site+A01` 解析区域,允许在途旧区域码但不依赖旧端口区域记录;其它点位仍按 `haian_area.wcs_area_code -> area_id` 转换后写明细 `to_area`,禁止改变车间等最终目的地。解析后的托盘号在“下达任务”场景写入明细 `pallet_id`;`comment` 在“下达任务”场景写 `source_bill_no`、在RFID复核场景保存复核备注 - rqrq - 海安 AGV 回调采用“两段式”:第一段接口仅写 `haian_wms_transport_task_detail(status=已录入)`;第二段由 `HaianWcsSchedule.scheduleAgvFeedback(3.1秒)` 乐观锁消费明细并驱动主任务/托盘状态(下达=calling_flag:Y并创建主任务含from_area、取货=托运中、放货完成=完成+按需移库+回写area/location_code且不改wcs_location、取消=已取消且calling_flag:N)。放货完成若按来源任务号或搬运任务号命中同site、同栈板、类型为“运输栈板”的立库任务,须在本次回调事务内完成该立库主单和明细;外部来源不联动。协议正式 type 只有 `放货完成`,旧名称 `送货完成` 入库/消费前归一成 `放货完成`;接收阶段不申请运输明细写入应用锁,仅按请求 `site+wmsId(taskNo)+type(action_type)` 查询在线表,任意状态已有记录即幂等跳过,不查询 `_history`。- rqrq - 海安同步 RFID 复核 `POST /api/wms54/inbound/verifyPalletRfid` 必填 `site=54/taskNo/palletId`;`taskNo` 原样对应 `haian_wms_transport_task_detail.task_no`,但不要求运输主表存在,也不校验任务与托盘的归属。仅按在线表 `site+taskNo+action_type=RFID复核` 判断幂等,不申请写入应用锁,不查询 `_history`;在线已有记录时返回首次响应。在线无记录时按site+palletId一致性读取标签并比较,写 `item_no=1/status=已录入/comment=通过或不通过/pallet_id`,失败再写 `error_code=复核失败/error_msg=原立库resMsg`,完整响应存 `rfid_verify_result_json`。字段及唯一索引升级脚本为 `sql/20261018_haian_rfid_verify_first_result.sql`,归档过程同步复制响应快照仅供追溯。- rqrq +- 海安同步RFID复核每次调用(首次通过、不通过、在线幂等返回及异常)都通过 `haiAnLogCompleteCall` 独立写 `haian_interface_call_log`,`interface_name=HaiAnWmsMessageService/method_name=verifyInboundPalletRfid/business_key=taskNo`。业务由事务模板完成后记录完整请求、响应(含null字段)、状态及毫秒耗时;正常复核无论通过与否均为 `SUCCESS`,业务或提交异常为 `ERROR` 并保存原错误信息,日志故障不覆盖业务结果。- rqrq - 海安 RFID复核消费(`action_type=RFID复核`)只用明细 `site/task_no/pallet_id/comment`,不要求运输主表。`comment` 去空格后不等于 `通过`:先把明细改 `已完成` 并保留首次复核 `error_code/error_msg`,再调 `notifyOutboundPortRfidPalletAction(type=2)` 去异常位;等于 `通过`:先查 `haian_pallet_detail`;无标签则明细改 `已完成` 后再调 `type=1`;有标签且 `scheduler.haianAutoTask.autoIssue.enabled=true` 时,收集全部 `reserve_flag=Y` 且 `reserve_order_ref1` 能关联 `haian_SOIssueNotifyHeader(site+notify_no)` 且 `order_type=shoporder` 的预定标签:只要有一张命中,就写自动发料任务并调用现有海安移库入队,RFID 明细改 `待自动发料`(不进 AGV 定时扫描、不调立库);开关关闭或未命中则明细改 `已完成` 后再调 `type=1`。立库接口返回仅供记录,托盘移动由定时消费后下发 `POST /api/wms/outbound/rfid-action`;WCS调用失败由明细事务回滚后重试。- rqrq - 海安自动发料移库目标区域写死 `HaiAnAreaStaticData.AUTO_ISSUE_TRANSFER_AREA = "A99"`,实际仓库/库位按显式site+A99查询haian_area配置,缺少映射中断建单。`HaiAnPalletTransferService.doWareHouseForPalletForAuto` 回参改为实体 `HaiAnPalletTransferEnqueueResult`(`queueIds`+`message`),无需搬库时 `queueIds` 为空列表并视为 IFS 已完成;三个原调用方仍忽略回参,禁止改 `haian_ifs_pallet_move_queue` 表结构,禁止改老模块 `WmsMessageService.doWareHouseForPalletForAuto` - rqrq - 海安自动发料定时任务只读 IFS 队列表:关联 `queueId` 存在 N/P 则继续等;全部关联队列均为Y后,任一条 `result_flag=F` 则不发料,任务与 RFID 明细都改 `已完成`,`error_code=移库失败`,覆盖写 `haian_pallet.remark` 与 RFID `error_msg`,再调立库 `type=2`。全部 `Y` 且全 `S`(或无队列)后:实时查 `handling_unit`,用 `haian_SOIssueNotifyOrderMaterialList_detail` 关联 `haian_SOIssueNotifyOrderList` 按 `site+soorder_no+release+sequence` 分组,经 `HaiAnProductionAutoIssueAdapter` 调用同事 `AutoIssueService.autoIssue`(不改同事方法,`labelCode` 空、`workOrderNo` 空、release/sequence 空填 `*`、`lineItemNo` 由 `BOM_item_no` 转 Integer 失败为 0、`issueQty=qty` 全发)。回参只有 failures,成功标签用送出清单减去失败 handlingUnitId 反推;全成功则解绑、`type=1`、不写失败码;只要有一张失败则成功的解绑、失败的不解绑、`type=2`、`error_code=发料失败`。不论成败先改本地再调立库。已 `issue_flag=S` 的标签禁止再送同事 - rqrq diff --git a/src/main/java/com/gaotao/modules/autoWareHourceHaian/api/service/impl/HaiAnWmsMessageServiceImpl.java b/src/main/java/com/gaotao/modules/autoWareHourceHaian/api/service/impl/HaiAnWmsMessageServiceImpl.java index 8d9639d2..9dd2f19a 100644 --- a/src/main/java/com/gaotao/modules/autoWareHourceHaian/api/service/impl/HaiAnWmsMessageServiceImpl.java +++ b/src/main/java/com/gaotao/modules/autoWareHourceHaian/api/service/impl/HaiAnWmsMessageServiceImpl.java @@ -1,6 +1,7 @@ package com.gaotao.modules.autoWareHourceHaian.api.service.impl; import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.gaotao.modules.autoWareHourceHaian.staticData.HaiAnAgvCallbackTypeStaticData; import com.gaotao.modules.autoWareHourceHaian.staticData.HaiAnAreaStaticData; @@ -47,7 +48,9 @@ import com.gaotao.modules.check.service.PhysicalInventoryService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.StringUtils; import java.math.BigDecimal; @@ -92,6 +95,10 @@ public class HaiAnWmsMessageServiceImpl implements HaiAnWmsMessageService { @Autowired private HaiAnInterfaceCallLogService haiAnInterfaceCallLogService; + /** RFID复核业务事务管理器;在事务提交或回滚后记录接口结果,避免把提交失败误记为调用成功。- rqrq */ + @Autowired + private PlatformTransactionManager transactionManager; + @Autowired private HaiAnSysErrorLogService haiAnSysErrorLogService; @@ -185,11 +192,51 @@ public class HaiAnWmsMessageServiceImpl implements HaiAnWmsMessageService { /** * @Description 立库同步RFID复核:在线表同site+taskNo+RFID复核动作已有记录时复用首次结果,否则比对并写入已录入明细后返回立库报文。- rqrq * @param data site、搬运taskNo、palletId必填;rfidBarcodes空值按空盘比对,不要求运输主表已存在。- rqrq - * @return 首次响应的完整字段;重复调用返回已保存的首次响应,不因再次扫描改变运输动作。- rqrq + * @return 首次响应的完整字段;每次调用独立记录请求、完整响应或异常和耗时,重复调用仍返回已保存的首次响应。- rqrq */ @Override - @Transactional(rollbackFor = Exception.class) public HaiAnInboundRfidVerifyResult verifyInboundPalletRfid(HaiAnInboundRfidVerifyRequest data) { + long startTime = System.currentTimeMillis(); + // 日志以请求site及taskNo定位;缺site时仅日志归属回落54,业务事务仍严格校验原请求,禁止用日志兜底值执行业务。- rqrq + String logSite = resolveSite(data == null ? null : data.getSite()); + String businessKey = data == null ? null : normalizeText(data.getTaskNo()); + String requestJson = JSONObject.toJSONString(data); + HaiAnInboundRfidVerifyResult result = null; + String callStatus = "ERROR"; + String errorMessage = null; + try { + // 每次调用新建事务模板,由模板统一提交或回滚复核明细;私有方法不依赖同类事务代理,提交失败进入异常日志分支。- rqrq + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + result = transactionTemplate.execute(transactionStatus -> verifyInboundPalletRfidInTransaction(data)); + // verifyPassed=false是已完成的业务复核结论,调用状态仍为SUCCESS;标签差异保留在完整响应中供日志页面查看。- rqrq + callStatus = "SUCCESS"; + return result; + } catch (RuntimeException ex) { + // 校验、数据库及事务提交异常保持原样抛给统一异常处理器;独立日志保存异常原因,不改变原接口错误协议。- rqrq + errorMessage = ex.getMessage(); + throw ex; + } finally { + try { + // 每次首次复核或幂等返回均落一条完整海安日志;响应包含null字段,异常保存errorMessage,耗时包含业务事务提交。- rqrq + String responseJson = "SUCCESS".equals(callStatus) + ? JSONObject.toJSONString(result, JSONWriter.Feature.WriteNulls) : null; + // 通过独立日志服务事务写haian_interface_call_log;业务回滚不丢失日志,日志事务异常仅告警,不覆盖原响应或异常。- rqrq + haiAnInterfaceCallLogService.haiAnLogCompleteCall( + "HaiAnWmsMessageService", "verifyInboundPalletRfid", requestJson, + responseJson, callStatus, errorMessage, System.currentTimeMillis() - startTime, + logSite, businessKey, "海安入库RFID同步复核 /api/wms54/inbound/verifyPalletRfid"); + } catch (RuntimeException logException) { + log.warn("海安RFID复核接口日志写入失败 - rqrq,site=" + logSite + ",taskNo=" + businessKey, logException); + } + } + } + + /** + * @Description 在调用方事务模板内执行RFID复核与在线幂等判断;本方法不申请应用锁,不读取历史表,异常交由外层回滚并记接口日志。- rqrq + * @param data 原始立库请求,site、taskNo和palletId在本方法中校验,不使用日志兜底参数。- rqrq + * @return 已在线保存的首次完整复核响应;在线无记录时完成本次比对并写入明细。- rqrq + */ + private HaiAnInboundRfidVerifyResult verifyInboundPalletRfidInTransaction(HaiAnInboundRfidVerifyRequest data) { if (data == null || !StringUtils.hasText(data.getSite()) || !StringUtils.hasText(data.getTaskNo()) || !StringUtils.hasText(data.getPalletId())) {