自动化立体仓库 - WMS系统
cl
2026-03-24 a1334287fc3e563ff3494553d0c1e6fa390b314d
大屏缓存
5个文件已修改
262 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/utils/RedisUtil.java 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/errorWrkMast/errorWrkMast.html 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/wrkMast/wrkMast.html 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java
@@ -26,10 +26,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.*;
/**
 * 移动端服务核心类
@@ -634,7 +631,7 @@
    @Transactional
    public void createEmptyWrk(String barcode, Long userId) {
        WrkMast wrkMast = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("barcode", barcode));
        if (!Cools.isEmpty(wrkMast)){
        if (!Cools.isEmpty(wrkMast)&& !Objects.isNull(wrkMast.getWrkSts())&&!wrkMast.getWrkSts().equals(15L)){
            throw new CoolException("托盘编号" + barcode + "已存在工作档");
        }
        List<WaitPakin> waitPakins = waitPakinService.selectList(new EntityWrapper<WaitPakin>().eq("zpallet", barcode));
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -21,6 +21,8 @@
import com.zy.common.properties.SlaveProperties;
import com.zy.common.service.CommonService;
import com.zy.common.web.WcsController;
import com.zy.asrs.task.core.ReturnT;
import com.zy.asrs.task.handler.WorkMastHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -80,6 +82,8 @@
    private WaitPakinService waitPakinService;
    @Autowired
    private MonitorLocMapCacheService monitorLocMapCacheService;
    @Autowired
    private WorkMastHandler workMastHandler;
    @Override
    @Transactional
@@ -609,14 +613,18 @@
        if (Cools.isEmpty(wrkMast)){
            throw new CoolException(workNo+"工作档不存在");
        }
        if (wrkMast.getWrkSts() == 4 || wrkMast.getWrkSts() == 14) {
        long sts = wrkMast.getWrkSts();
        if (sts == 4 || sts == 5 || sts == 14 || sts == 15 || sts == 20) {
            throw new CoolException("当前工作档已完成");
        }
        // 入库 + 库位转移
        if (wrkMast.getWrkSts() < 4 || (wrkMast.getWrkSts() > 10 && wrkMast.getIoType()==11)) {
        boolean inboundFinish = wrkMast.getWrkSts() < 4 || (wrkMast.getWrkSts() > 10 && wrkMast.getIoType() != null && wrkMast.getIoType() == 11);
        boolean outboundFinish = wrkMast.getWrkSts() > 10 && (wrkMast.getIoType() == null || wrkMast.getIoType() != 11);
        if (!inboundFinish && !outboundFinish) {
            throw new CoolException("当前工作状态无法手动完成");
        }
        if (inboundFinish) {
            wrkMast.setWrkSts(4L);
        // 出库
        } else if (wrkMast.getWrkSts() > 10) {
        } else {
            wrkMast.setWrkSts(14L);
        }
        Date now = new Date();
@@ -624,8 +632,45 @@
        wrkMast.setCrnEndTime(now);
        wrkMast.setModiTime(now);
        wrkMast.setModiUser(userId);
        // 完成操作人员记录
        wrkMast.setManuType("手动完成");
        ReturnT<String> rt = workMastHandler.start(wrkMast);
        if (!rt.isSuccess()) {
            throw new CoolException(Cools.isEmpty(rt.getMsg()) ? "完成处理失败" : rt.getMsg());
        }
        // doOut 对 103/107 不落 15 时,手动补齐 15;源库位 O 与定时任务 doOut 一致
        if (wrkMast.getWrkSts() == 14) {
            wrkMast.setWrkSts(15L);
            wrkMast.setModiTime(now);
            wrkMast.setModiUser(userId);
            if (!Cools.isEmpty(wrkMast.getSourceLocNo())) {
                LocMast src = locMastService.selectById(wrkMast.getSourceLocNo());
                if (!Cools.isEmpty(src) && !"O".equals(src.getLocSts())) {
                    Integer io = wrkMast.getIoType();
                    if (io != null && (io == 101 || io == 110)) {
                        src.setLocSts("O");
                        src.setModiTime(now);
                        src.setIoTime(now);
                        src.setModiUser(userId);
                        if (!locMastService.updateById(src)) {
                            throw new CoolException("更新源库位状态失败");
                        }
                    } else if ("R".equals(src.getLocSts())) {
                        src.setLocSts("O");
                        src.setModiTime(now);
                        src.setIoTime(now);
                        src.setModiUser(userId);
                        if (!locMastService.updateById(src)) {
                            throw new CoolException("更新源库位状态失败");
                        }
                    }
                }
            }
        }
        wrkMast.setManuType("手动完成");
        wrkMast.setModiUser(userId);
        wrkMast.setCrnStrTime(DateUtils.calculate(now, 1L, TimeUnit.SECONDS, true));
        wrkMast.setCrnEndTime(now);
        wrkMast.setModiTime(now);
        if (!wrkMastService.updateById(wrkMast)) {
            throw new CoolException("修改工作档失败");
        }
src/main/java/com/zy/common/utils/RedisUtil.java
@@ -1,15 +1,21 @@
package com.zy.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
 * redisTemplate封装
@@ -18,11 +24,21 @@
@Component
public class RedisUtil {
    private static final Logger log = LoggerFactory.getLogger(RedisUtil.class);
    private static final long LOCAL_NO_EXPIRE = Long.MAX_VALUE;
    private static final long LOCAL_DEFAULT_TTL_SEC = 86400L;
//    @Autowired
//    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    private final ConcurrentHashMap<String, LocalCacheEntry> localCache = new ConcurrentHashMap<>();
    private final Object fallbackLock = new Object();
    private volatile boolean redisFallbackMode = false;
    private final Set<String> fallbackLocalKeys = ConcurrentHashMap.newKeySet();
    public RedisUtil(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
@@ -65,10 +81,13 @@
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
            boolean ok = redisTemplate.hasKey(key);
            notifyRedisAvailable();
            return ok;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
            enterFallbackIfNeeded();
            log.info("Redis 不可用,从 JVM 内存判断 hasKey,key={},原因: {}", key, e.getMessage());
            return localHasValid(key);
        }
    }
@@ -80,10 +99,24 @@
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            try {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
                notifyRedisAvailable();
            } catch (Exception e) {
                enterFallbackIfNeeded();
                log.warn("Redis 不可用,删除时 Redis 失败,仅清理本地,原因: {}", e.getMessage());
            }
            for (String k : key) {
                if (k != null) {
                    localCache.remove(k);
                    synchronized (fallbackLock) {
                        fallbackLocalKeys.remove(k);
                    }
                }
            }
        }
    }
@@ -97,7 +130,18 @@
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
        if (key == null) {
            return null;
        }
        try {
            Object v = redisTemplate.opsForValue().get(key);
            notifyRedisAvailable();
            return v;
        } catch (Exception e) {
            enterFallbackIfNeeded();
            log.info("Redis 不可用,从 JVM 内存读取 key={},原因: {}", key, e.getMessage());
            return localGet(key);
        }
    }
    /**
@@ -125,10 +169,13 @@
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            localCache.remove(key);
            notifyRedisAvailable();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
            log.debug("Redis set 失败", e);
            localPutFallback(key, value, LOCAL_DEFAULT_TTL_SEC);
            return true;
        }
    }
@@ -146,11 +193,16 @@
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
                return true;
            }
            localCache.remove(key);
            notifyRedisAvailable();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
            log.debug("Redis set(expire) 失败", e);
            long ttl = time > 0 ? time : LOCAL_DEFAULT_TTL_SEC;
            localPutFallback(key, value, ttl);
            return true;
        }
    }
@@ -598,7 +650,15 @@
     * @return
     */
    public Set keys(String pattern) {
        return redisTemplate.keys(pattern);
        try {
            Set<?> s = redisTemplate.keys(pattern);
            notifyRedisAvailable();
            return s;
        } catch (Exception e) {
            enterFallbackIfNeeded();
            log.info("Redis 不可用,从 JVM 内存匹配 keys,pattern={},原因: {}", pattern, e.getMessage());
            return localKeysMatching(pattern);
        }
    }
    /**
@@ -611,6 +671,122 @@
        redisTemplate.convertAndSend(channel, message);
    }
    private void enterFallbackIfNeeded() {
        synchronized (fallbackLock) {
            if (!redisFallbackMode) {
                redisFallbackMode = true;
                log.warn("Redis 不可用,字符串相关缓存将暂存于 JVM 内存,待 Redis 恢复后自动切回 Redis 并清理上述键");
            }
        }
    }
    private void notifyRedisAvailable() {
        if (!redisFallbackMode && fallbackLocalKeys.isEmpty()) {
            return;
        }
        synchronized (fallbackLock) {
            if (!redisFallbackMode && fallbackLocalKeys.isEmpty()) {
                return;
            }
            int n = fallbackLocalKeys.size();
            for (String k : new ArrayList<>(fallbackLocalKeys)) {
                localCache.remove(k);
            }
            fallbackLocalKeys.clear();
            redisFallbackMode = false;
            if (n > 0) {
                log.info("Redis 已恢复,已清理 JVM 内存中 {} 个因 Redis 不可用而暂存的键", n);
            } else {
                log.info("Redis 已恢复");
            }
        }
    }
    private void localPutFallback(String key, Object value, long ttlSeconds) {
        if (key == null) {
            return;
        }
        synchronized (fallbackLock) {
            if (!redisFallbackMode) {
                redisFallbackMode = true;
                log.warn("Redis 不可用,字符串相关缓存将暂存于 JVM 内存,待 Redis 恢复后自动切回 Redis 并清理上述键");
            }
            fallbackLocalKeys.add(key);
            localPut(key, value, ttlSeconds);
        }
        log.info("Redis 不可用,键已写入 JVM 内存: {}", key);
    }
    private void localPut(String key, Object value, long ttlSeconds) {
        if (key == null) {
            return;
        }
        long expireAt = ttlSeconds > 0
                ? System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(ttlSeconds)
                : LOCAL_NO_EXPIRE;
        localCache.put(key, new LocalCacheEntry(value, expireAt));
    }
    private Object localGet(String key) {
        LocalCacheEntry e = localCache.get(key);
        if (e == null) {
            return null;
        }
        if (e.expireAtMillis != LOCAL_NO_EXPIRE && System.currentTimeMillis() > e.expireAtMillis) {
            localCache.remove(key, e);
            return null;
        }
        return e.value;
    }
    private boolean localHasValid(String key) {
        return localGet(key) != null;
    }
    private Set<String> localKeysMatching(String pattern) {
        if (pattern == null) {
            return new HashSet<>();
        }
        Pattern p = Pattern.compile(globToRegex(pattern));
        Set<String> out = new HashSet<>();
        for (String k : localCache.keySet()) {
            if (localGet(k) == null) {
                continue;
            }
            if (p.matcher(k).matches()) {
                out.add(k);
            }
        }
        return out;
    }
    private static String globToRegex(String glob) {
        StringBuilder sb = new StringBuilder("^");
        for (int i = 0; i < glob.length(); i++) {
            char c = glob.charAt(i);
            if (c == '*') {
                sb.append(".*");
            } else if (c == '?') {
                sb.append('.');
            } else if ("\\.[]{}()+-^$|".indexOf(c) >= 0) {
                sb.append('\\').append(c);
            } else {
                sb.append(c);
            }
        }
        sb.append('$');
        return sb.toString();
    }
    private static final class LocalCacheEntry {
        final Object value;
        final long expireAtMillis;
        LocalCacheEntry(Object value, long expireAtMillis) {
            this.value = value;
            this.expireAtMillis = expireAtMillis;
        }
    }
}
src/main/webapp/views/errorWrkMast/errorWrkMast.html
@@ -106,8 +106,10 @@
<script type="text/html" id="operate">
    <a class="layui-btn layui-btn-sm btn-detlShow" lay-event="detlShow">明细</a>
    {{#if (d.wrkSts != 4 && d.wrkSts != 5 && d.wrkSts != 14 && d.wrkSts != 15 && d.wrkSts != 20) { }}
    <a class="layui-btn layui-btn-danger layui-btn-sm btn-complete" lay-event="complete">完成</a>
    <a class="layui-btn layui-btn-primary layui-btn-sm btn-cancel" lay-event="cancel">取消</a>
    {{# } }}
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
src/main/webapp/views/wrkMast/wrkMast.html
@@ -120,8 +120,10 @@
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-error" lay-event="takeNone">空操作</a>
    {{# } }}
    <a class="layui-btn layui-btn-xs btn-detlShow" lay-event="detlShow">明细</a>
    {{#if (d.wrkSts != 4 && d.wrkSts != 5 && d.wrkSts != 14 && d.wrkSts != 15 && d.wrkSts != 20) { }}
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-complete" lay-event="complete">完成</a>
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-cancel" lay-event="cancel">取消</a>
    {{# } }}
    {{#if (d.ioType === 103) { }}
        <a class="layui-btn layui-btn-warm layui-btn-xs btn-pick" lay-event="pick">拣</a>
    {{# } }}