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 0f6a4635..1803cea0 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 @@ -24,6 +24,7 @@ import com.gaotao.modules.autoWareHourceHaian.api.entity.HaiAnRobotErrorRequest; import com.gaotao.modules.autoWareHourceHaian.api.entity.HaiAnWcsInboundCallbackRequest; import com.gaotao.modules.autoWareHourceHaian.api.service.HaiAnWmsMessageService; import com.gaotao.modules.autoWareHourceHaian.entity.HaiAnCountResultInsertData; +import com.gaotao.modules.autoWareHourceHaian.entity.HaiAnTransportTaskDetailLockParam; import com.gaotao.modules.autoWareHourceHaian.entity.pda.HaiAnAreaOption; import com.gaotao.modules.autoWareHourceHaian.entity.pda.HaiAnHandlingUnitLabelRow; import com.gaotao.modules.autoWareHourceHaian.entity.pda.HaiAnPalletDetailRow; @@ -205,11 +206,8 @@ public class HaiAnWmsMessageServiceImpl implements HaiAnWmsMessageService { throw new RuntimeException("RFID复核taskNo或palletId超过运输明细字段长度"); } - // 与原AGV回调使用同一事务级任务锁;只串行化同site+taskNo的写入,不查询或要求运输主表存在。- rqrq - Integer lockResult = haiAnAgvTaskMapper.lockTransportTaskDetailInsert(site, taskNo); - if (lockResult == null || lockResult < 0) { - throw new RuntimeException("RFID复核取得运输明细写入锁失败,taskNo=" + taskNo + ",lockResult=" + lockResult); - } + // 与AGV回调共用事务级任务锁;锁失败中断首次复核写入,不查询或要求运输主表存在。- rqrq + acquireTransportTaskDetailInsertLock(site, taskNo); // 锁内先读取历史及在线首条;重复请求直接返回首轮响应,禁止用本次扫描覆盖复核事实。- rqrq HaiAnWmsTransportTaskDetail firstDetail = haiAnAgvTaskMapper.getFirstRfidVerifyDetail( site, taskNo, HaiAnAgvCallbackTypeStaticData.RFID_CHECK); @@ -333,6 +331,25 @@ public class HaiAnWmsMessageServiceImpl implements HaiAnWmsMessageService { return result; } + /** + * @Description 通过单次Callable取得同site+taskNo的事务级应用锁;两入口共用锁键并在锁内分配明细序号。- rqrq + * @param site 请求大site,必须已按入口规则校验,不能取登录态。- rqrq + * @param taskNo 搬运任务号,直接对应明细task_no,不要求主任务存在。- rqrq + */ + private void acquireTransportTaskDetailInsertLock(String site, String taskNo) { + if (!StringUtils.hasText(site) || !StringUtils.hasText(taskNo)) { + throw new RuntimeException("运输明细写入锁缺少site或taskNo"); + } + HaiAnTransportTaskDetailLockParam lockParam = new HaiAnTransportTaskDetailLockParam(site, taskNo); + // 单次调用SQL Server sp_getapplock并读取返回码;数据库异常或负码须回滚,禁止无锁写入首次复核。- rqrq + haiAnAgvTaskMapper.lockTransportTaskDetailInsert(lockParam); + Integer lockResult = lockParam.getLockResult(); + if (lockResult == null || lockResult < 0) { + throw new RuntimeException("取得运输明细写入锁失败,site=" + site + ",taskNo=" + taskNo + + ",lockResult=" + lockResult); + } + } + /** * @author rqrq * @Description 处理海安立库出入库回调:按事务类型分流落库,到达出库口(原取货通知)执行幂等校验,目标区域固定A01并映射仓库/库位;仅认新中文 - rqrq @@ -1512,10 +1529,7 @@ public class HaiAnWmsMessageServiceImpl implements HaiAnWmsMessageService { // 2. 只写haian_wms_transport_task_detail:task_no用wmsId,状态统一“已录入”,targetArea落to_area,不校验主记录存在 - rqrq // 与同步RFID复核共用事务级任务锁;失败中断,避免两入口并发分到同一seqNo或产生两条首次复核。- rqrq - Integer lockResult = haiAnAgvTaskMapper.lockTransportTaskDetailInsert(site, wmsId); - if (lockResult == null || lockResult < 0) { - throw new RuntimeException("AGV反馈取得运输明细写入锁失败,taskNo=" + wmsId + ",lockResult=" + lockResult); - } + acquireTransportTaskDetailInsertLock(site, wmsId); // 旧AGV接口的RFID动作也须遵守首次结果;历史或在线已有复核时直接幂等返回,不在归档后补写第二条。- rqrq if (HaiAnAgvCallbackTypeStaticData.RFID_CHECK.equals(normalizedType)) { HaiAnWmsTransportTaskDetail firstRfidDetail = haiAnAgvTaskMapper.getFirstRfidVerifyDetail( diff --git a/src/main/java/com/gaotao/modules/autoWareHourceHaian/entity/HaiAnTransportTaskDetailLockParam.java b/src/main/java/com/gaotao/modules/autoWareHourceHaian/entity/HaiAnTransportTaskDetailLockParam.java new file mode 100644 index 00000000..0254ef90 --- /dev/null +++ b/src/main/java/com/gaotao/modules/autoWareHourceHaian/entity/HaiAnTransportTaskDetailLockParam.java @@ -0,0 +1,28 @@ +package com.gaotao.modules.autoWareHourceHaian.entity; + +import lombok.Data; + +import java.util.Locale; + +/** + * @Description 海安运输明细事务级应用锁参数;将site与搬运任务号归一为同一锁键,并承接SQL Server过程返回码。- rqrq + */ +@Data +public class HaiAnTransportTaskDetailLockParam { + + /** 应用锁资源名,由请求大site和搬运taskNo组成;仅传给sp_getapplock,不落库。- rqrq */ + private String resource; + + /** 过程返回码,由sp_getapplock写回;非负为取得锁,负数须使调用方事务回滚。- rqrq */ + private Integer lockResult; + + /** + * @Description 按数据库业务键的大小写不敏感口径构造锁键,供同步复核与AGV回调共用。- rqrq + * @param site 请求大site;调用方已校验非空。- rqrq + * @param taskNo 搬运任务号;调用方已校验非空。- rqrq + */ + public HaiAnTransportTaskDetailLockParam(String site, String taskNo) { + this.resource = "HAIAN_TRANSPORT_DETAIL:" + site.toUpperCase(Locale.ROOT) + + ":" + taskNo.toUpperCase(Locale.ROOT); + } +} diff --git a/src/main/java/com/gaotao/modules/autoWareHourceHaian/mapper/HaiAnAgvTaskMapper.java b/src/main/java/com/gaotao/modules/autoWareHourceHaian/mapper/HaiAnAgvTaskMapper.java index 6c44d039..4ed1b883 100644 --- a/src/main/java/com/gaotao/modules/autoWareHourceHaian/mapper/HaiAnAgvTaskMapper.java +++ b/src/main/java/com/gaotao/modules/autoWareHourceHaian/mapper/HaiAnAgvTaskMapper.java @@ -3,6 +3,7 @@ package com.gaotao.modules.autoWareHourceHaian.mapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gaotao.modules.autoWareHourceHaian.entity.HaiAnWmsTransportTaskData; +import com.gaotao.modules.autoWareHourceHaian.entity.HaiAnTransportTaskDetailLockParam; import com.gaotao.modules.autoWareHourceHaian.entity.vo.HaiAnWmsTransportTask; import com.gaotao.modules.autoWareHourceHaian.entity.vo.HaiAnWmsTransportTaskDetail; import org.apache.ibatis.annotations.Mapper; @@ -48,12 +49,10 @@ public interface HaiAnAgvTaskMapper { Integer getNextSeqNoForTaskDetail(@Param("site") String site, @Param("taskNo") String taskNo); /** - * @Description 对同site、同任务号的回调插入取得事务级应用锁;负数表示超时或失败,调用方必须中断,避免首次RFID复核和序号并发错写。- rqrq - * @param site 请求大site,参与锁键,不能使用登录态。- rqrq - * @param taskNo 搬运任务号,参与锁键;不要求运输主表存在。- rqrq - * @return SQL Server sp_getapplock结果,非负表示取得锁。- rqrq + * @Description 通过单次Callable调用对同site、同任务号的插入取得事务级应用锁;不放宽Druid多语句限制。- rqrq + * @param param 由请求site和搬运taskNo组成的锁键;过程返回码写入lockResult,负数须回滚。- rqrq */ - Integer lockTransportTaskDetailInsert(@Param("site") String site, @Param("taskNo") String taskNo); + void lockTransportTaskDetailInsert(HaiAnTransportTaskDetailLockParam param); /** * @Description 按site、taskNo和RFID动作读取最早落库的复核明细;历史优先于在线表,供重复请求返回首次响应。- rqrq diff --git a/src/main/resources/mapper/autoWareHourceHaian/HaiAnAgvTaskMapper.xml b/src/main/resources/mapper/autoWareHourceHaian/HaiAnAgvTaskMapper.xml index c72e9b7f..f5ccce7f 100644 --- a/src/main/resources/mapper/autoWareHourceHaian/HaiAnAgvTaskMapper.xml +++ b/src/main/resources/mapper/autoWareHourceHaian/HaiAnAgvTaskMapper.xml @@ -135,17 +135,16 @@ AND item_no = 1 - - + {#{lockResult, mode=OUT, jdbcType=INTEGER} = call sys.sp_getapplock( + #{resource, mode=IN, jdbcType=NVARCHAR}, + 'Exclusive', + 'Transaction', + 10000 + )}