自动化立体仓库 - WMS系统
zwl
2 天以前 b0877a3275ed5bc96fb80f84949904e149946cf2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package com.zy.asrs.service.impl;
 
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.common.R;
import com.zy.asrs.entity.param.MesToCombParam;
import com.zy.asrs.entity.param.OpenOrderPakoutExecuteParam;
import com.zy.asrs.entity.param.OutTaskParam;
import com.zy.asrs.service.ExternalTaskFacadeService;
import com.zy.asrs.service.LocDetlService;
import com.zy.asrs.service.OpenService;
import com.zy.asrs.service.WaitPakinService;
import com.zy.asrs.service.WrkDetlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.Collections;
import java.util.Objects;
 
/**
 * 对外任务门面。
 * 把 Controller 里的校验和放行逻辑沉到 service,便于 HTTP 和 MQTT 两条入口共用。
 */
@Service
public class ExternalTaskFacadeServiceImpl implements ExternalTaskFacadeService {
 
    @Autowired
    private OpenService openService;
    @Autowired
    private LocDetlService locDetlService;
    @Autowired
    private WrkDetlService wrkDetlService;
    @Autowired
    private WaitPakinService waitPakinService;
 
    /**
     * 复用现有入库通知建档逻辑,并补充托盘重复校验。
     */
    @Override
    public R acceptInboundNotice(MesToCombParam param) {
        if (param == null) {
            return R.error("请求参数不能为空");
        }
        if (Cools.isEmpty(param.getPalletId())) {
            return R.error("palletId不能为空");
        }
        if (Cools.isEmpty(param.getBizNo())) {
            return R.error("bizNo不能为空");
        }
 
        int countLoc = locDetlService.selectCount(new EntityWrapper<com.zy.asrs.entity.LocDetl>().eq("zpallet", param.getPalletId()));
        int countWrk = wrkDetlService.selectCount(new EntityWrapper<com.zy.asrs.entity.WrkDetl>().eq("zpallet", param.getPalletId()));
        if (countLoc > 0 || countWrk > 0) {
            return R.error("托盘已在库存中/已开始入库");
        }
 
        if (waitPakinService.selectCount(new EntityWrapper<com.zy.asrs.entity.WaitPakin>()
                .eq("zpallet", param.getPalletId())
                .eq("io_status", "N")) > 0) {
            // 同托盘存在旧的待入库通知时,删除旧记录,保留最新一次预登记。
            waitPakinService.delete(new EntityWrapper<com.zy.asrs.entity.WaitPakin>().eq("zpallet", param.getPalletId()));
        }
 
        R result = openService.mesToComb(param);
        return result == null ? R.ok() : result;
    }
 
    /**
     * 复用统一出库订单化逻辑。
     *
     * MQTT/IoT 入口不再绕过订单直接生成 WrkMast:
     * - autoConfirm=false:只创建 status=0 的订单,等待外部调用执行接口;
     * - autoConfirm=true:创建订单后立即走执行逻辑,生成当前批次任务并确认可下发。
     *
     * 这样做是为了让 HTTP /outOrder 和 MQTT/IoT 直调共享同一套:
     * - 订单明细保存字段;
     * - 批次键计算;
     * - 中止取消和 work_qty 回滚;
     * - pdcType 放行。
     */
    @Override
    public R createOutboundTask(OutTaskParam param, boolean autoConfirm) {
        if (param == null) {
            return R.error("请求参数不能为空");
        }
        if (Cools.isEmpty(param.getOrderId())) {
            return R.error("出库单号不能为空");
        }
        if (Cools.isEmpty(param.getPalletId())) {
            return R.error("palletId不能为空");
        }
        if (Cools.isEmpty(param.getStationId())) {
            return R.error("stationId不能为空");
        }
        int countLoc = locDetlService.selectCount(new EntityWrapper<com.zy.asrs.entity.LocDetl>().eq("zpallet", param.getPalletId()));
        if (countLoc == 0) {
            return R.error("库存中不存在该托盘:" + param.getPalletId());
        }
 
        if (param.getSeq() == null) {
            // 设备直调通常是单托盘出库,没有 ERP 顺序号;0 表示无序,和 /outOrder 的校验语义一致。
            param.setSeq(0);
        }
        if (Cools.isEmpty(param.getBatchSeq())) {
            // batchSeq 是接口原始字段,明细里会保存;实际生成任务时低站点仍按 orderId 作为批次键。
            param.setBatchSeq(param.getOrderId());
        }
        if (isHighStation(param.getStationId()) && Cools.isEmpty(param.getEntryWmsCode())) {
            // IoT 直调常见为单托盘任务,没有 ERP 进仓编号;用 orderId 作为批次键,
            // 这样既满足高站点订单明细校验,也能让执行后 WrkMast.batchSeq 保持可追溯。
            param.setEntryWmsCode(param.getOrderId());
        }
 
        // IoT/MQTT 默认只预创建订单,status=0 不会被定时器扫描。
        // 只有 autoConfirm=true 或外部后续调用执行接口时,才会把 status 恢复为 1 并生成任务。
        R orderResult = openService.outOrderCreatePakoutOrder(Collections.singletonList(param), false);
        if (!Objects.equals(orderResult.get("code"), 200) || !autoConfirm) {
            return orderResult;
        }
 
        // IoT pick 约定为“收到即执行”,因此建单后直接复用公开执行接口的服务逻辑。
        OpenOrderPakoutExecuteParam executeParam = new OpenOrderPakoutExecuteParam();
        executeParam.setOrderId(param.getOrderId());
        executeParam.setExecute(1);
        return openService.pakoutOrderExecute(executeParam);
    }
 
    private boolean isHighStation(String stationId) {
        if (Cools.isEmpty(stationId)) {
            return false;
        }
        try {
            return Integer.valueOf(stationId) > 600;
        } catch (NumberFormatException ignored) {
            return false;
        }
    }
}