package com.zy.asrs.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.core.common.Cools;
|
import com.core.exception.CoolException;
|
import com.zy.asrs.entity.BasMap;
|
import com.zy.asrs.entity.LocMast;
|
import com.zy.asrs.mapper.BasMapMapper;
|
import com.zy.asrs.service.BasMapService;
|
import com.zy.asrs.service.LocMastService;
|
import com.zy.asrs.utils.Utils;
|
import com.zy.common.utils.NavigateSolution;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.News;
|
import com.zy.core.enums.RedisKeyType;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.stream.Collectors;
|
|
@Service("basMapService")
|
public class BasMapServiceImpl extends ServiceImpl<BasMapMapper, BasMap> implements BasMapService {
|
|
@Autowired
|
private LocMastService locMastService;
|
@Autowired
|
private RedisUtil redisUtil;
|
|
// 地图拓扑变化后,这些可达性和站点选择结果都可能过期,必须整体失效。
|
private static final String[] MAP_DERIVED_CACHE_PREFIXES = {
|
RedisKeyType.STATION_REACHABLE_CACHE.key,
|
RedisKeyType.PRECOMPUTE_IN_TASK_ROW_CACHE.key,
|
RedisKeyType.IN_STATION_ROUTE_CACHE.key,
|
RedisKeyType.OUT_STATION_ROUTE_CACHE.key
|
};
|
|
@Override
|
public BasMap selectLatestMap(Integer lev) {
|
return this.baseMapper.selectLatestMap(lev);
|
}
|
|
@Override
|
public boolean deleteByLev(Integer lev) {
|
boolean deleted = this.baseMapper.deleteByLev(lev);
|
refreshMapRuntimeCaches(Collections.singletonList(lev));
|
return deleted;
|
}
|
|
@Override
|
public List<Integer> getLevList() {
|
return this.baseMapper.selectList(new QueryWrapper<>()).stream().map(BasMap::getLev).collect(Collectors.toList());
|
}
|
|
@Override
|
public IPage<BasMap> pageLight(Page<BasMap> page, QueryWrapper<BasMap> wrapper) {
|
QueryWrapper<BasMap> queryWrapper = wrapper == null ? new QueryWrapper<>() : wrapper;
|
queryWrapper.select("id", "create_time", "update_time", "lev", "base_row", "base_row_code", "base_bay", "base_bay_code");
|
return this.baseMapper.selectPage(page, queryWrapper);
|
}
|
|
@Override
|
public BasMap selectPayloadById(Integer id) {
|
return this.baseMapper.selectPayloadById(id);
|
}
|
|
@Override
|
@Transactional
|
public void saveMapPayloadInBatches(Integer lev, String data, String originData, Date updateTime) {
|
if (lev == null || lev <= 0) {
|
throw new CoolException("楼层不能为空");
|
}
|
Date now = updateTime == null ? new Date() : updateTime;
|
BasMap basMap = this.getOne(new QueryWrapper<BasMap>().select("id", "data").eq("lev", lev));
|
boolean existingMap = basMap != null;
|
if (basMap == null) {
|
basMap = insertLightMap(lev, now);
|
}
|
|
if (existingMap) {
|
updateLastDataOnly(basMap.getId(), basMap.getData());
|
}
|
updateDataOnly(basMap.getId(), data, now);
|
updateOriginDataOnly(basMap.getId(), originData);
|
}
|
|
private BasMap insertLightMap(Integer lev, Date now) {
|
BasMap insertMap = new BasMap();
|
insertMap.setLev(lev);
|
insertMap.setCreateTime(now);
|
insertMap.setUpdateTime(now);
|
if (!this.save(insertMap)) {
|
throw new CoolException("地图基础信息保存失败");
|
}
|
return insertMap;
|
}
|
|
private void updateLastDataOnly(Integer id, String lastData) {
|
UpdateWrapper<BasMap> updateWrapper = new UpdateWrapper<>();
|
updateWrapper.eq("id", id)
|
.set("last_data", lastData);
|
if (!this.update(updateWrapper)) {
|
throw new CoolException("地图历史数据保存失败");
|
}
|
}
|
|
private void updateDataOnly(Integer id, String data, Date updateTime) {
|
UpdateWrapper<BasMap> updateWrapper = new UpdateWrapper<>();
|
updateWrapper.eq("id", id)
|
.set("data", data)
|
.set("update_time", updateTime);
|
if (!this.update(updateWrapper)) {
|
throw new CoolException("地图运行数据保存失败");
|
}
|
}
|
|
private void updateOriginDataOnly(Integer id, String originData) {
|
UpdateWrapper<BasMap> updateWrapper = new UpdateWrapper<>();
|
updateWrapper.eq("id", id)
|
.set("origin_data", originData);
|
if (!this.update(updateWrapper)) {
|
throw new CoolException("地图编辑数据保存失败");
|
}
|
}
|
|
@Override
|
public void refreshMapRuntimeCaches(List<Integer> levList) {
|
redisUtil.del(RedisKeyType.LOC_MAP_BASE.key);
|
redisUtil.del(RedisKeyType.LOC_MAST_MAP_LIST.key);
|
clearMapDerivedRedisCaches();
|
if (levList == null || levList.isEmpty()) {
|
return;
|
}
|
|
LinkedHashSet<Integer> distinctLevSet = new LinkedHashSet<>(levList);
|
for (Integer lev : distinctLevSet) {
|
if (lev == null) {
|
continue;
|
}
|
NavigateSolution.clearMapCache(lev);
|
if (!hasMapLev(lev)) {
|
continue;
|
}
|
refreshNavigateMapCache(lev);
|
}
|
}
|
|
private void clearMapDerivedRedisCaches() {
|
for (String keyPrefix : MAP_DERIVED_CACHE_PREFIXES) {
|
redisUtil.deleteByPrefix(keyPrefix);
|
}
|
}
|
|
private boolean hasMapLev(Integer lev) {
|
if (lev == null) {
|
return false;
|
}
|
return this.count(new QueryWrapper<BasMap>().eq("lev", lev)) > 0;
|
}
|
|
private void refreshNavigateMapCache(Integer lev) {
|
try {
|
NavigateSolution.refreshMapCache(lev);
|
} catch (Exception e) {
|
News.error("地图运行缓存刷新失败,lev={}", lev, e);
|
throw e;
|
}
|
}
|
|
@Override
|
@Transactional
|
public int syncLocMastByMap(Integer lev) {
|
if (lev == null || lev <= 0) {
|
throw new CoolException("请输入有效楼层");
|
}
|
|
List<Integer> locLevList = new ArrayList<>(locMastService.getLevList());
|
if (Cools.isEmpty(locLevList) || !locLevList.contains(lev)) {
|
throw new CoolException("第" + lev + "层暂无库位数据,请先初始化库位");
|
}
|
|
List<BasMap> basMapList = this.list(new QueryWrapper<BasMap>().orderByAsc("lev"));
|
if (Cools.isEmpty(basMapList)) {
|
throw new CoolException("请先初始化地图");
|
}
|
|
Map<Integer, BasMap> basMapByLev = new LinkedHashMap<>();
|
for (BasMap basMap : basMapList) {
|
if (basMap != null && basMap.getLev() != null) {
|
basMapByLev.put(basMap.getLev(), basMap);
|
}
|
}
|
BasMap fallbackMap = basMapByLev.size() == 1 ? basMapList.get(0) : null;
|
|
BasMap basMap = basMapByLev.get(lev);
|
if (basMap == null) {
|
basMap = fallbackMap;
|
}
|
if (basMap == null) {
|
throw new CoolException("第" + lev + "层缺少地图,无法同步locType");
|
}
|
|
List<TargetLocMeta> targetList = buildTargetLocMeta(basMap, lev);
|
List<LocMast> currentList = new ArrayList<>(locMastService.selectLocByLev(lev));
|
if (targetList.size() != currentList.size()) {
|
throw new CoolException("第" + lev + "层地图货架数(" + targetList.size() + ")与现有库位数(" + currentList.size() + ")不一致,无法同步locType");
|
}
|
|
int updatedCount = syncLevelLocType(lev, currentList, targetList);
|
refreshLocMastMapListCache();
|
redisUtil.del(RedisKeyType.LOC_MAP_BASE.key);
|
return updatedCount;
|
}
|
|
private int syncLevelLocType(Integer lev, List<LocMast> currentList, List<TargetLocMeta> targetList) {
|
Map<String, TargetLocMeta> targetByLocNo = new LinkedHashMap<>();
|
for (TargetLocMeta target : targetList) {
|
if (targetByLocNo.put(target.locNo, target) != null) {
|
throw new CoolException("第" + lev + "层存在重复库位号:" + target.locNo);
|
}
|
}
|
|
Date now = new Date();
|
int updatedCount = 0;
|
for (LocMast current : currentList) {
|
TargetLocMeta target = targetByLocNo.get(current.getLocNo());
|
if (target == null) {
|
throw new CoolException("第" + lev + "层地图中未找到库位号:" + current.getLocNo());
|
}
|
if (java.util.Objects.equals(current.getLocType(), target.locType)) {
|
continue;
|
}
|
|
UpdateWrapper<LocMast> updateWrapper = new UpdateWrapper<>();
|
updateWrapper.eq("loc_no", current.getLocNo())
|
.set("loc_type", target.locType)
|
.set("modi_time", now);
|
if (!locMastService.update(null, updateWrapper)) {
|
throw new CoolException("第" + lev + "层库位locType同步失败:" + current.getLocNo());
|
}
|
updatedCount++;
|
}
|
return updatedCount;
|
}
|
|
private void refreshLocMastMapListCache() {
|
List<LocMast> locMastList = locMastService.list(new QueryWrapper<LocMast>().eq("lev1", 1));
|
redisUtil.set(RedisKeyType.LOC_MAST_MAP_LIST.key, JSON.toJSONString(locMastList), 60 * 60 * 24);
|
}
|
|
private List<TargetLocMeta> buildTargetLocMeta(BasMap basMap, Integer targetLev) {
|
if (basMap == null || Cools.isEmpty(basMap.getData())) {
|
throw new CoolException("第" + targetLev + "层地图数据为空,无法同步locType");
|
}
|
|
List<List<JSONObject>> dataList;
|
try {
|
dataList = JSON.parseObject(basMap.getData(), List.class);
|
} catch (Exception e) {
|
throw new CoolException("第" + targetLev + "层地图数据格式错误,无法同步locType");
|
}
|
if (Cools.isEmpty(dataList)) {
|
throw new CoolException("第" + targetLev + "层地图数据为空,无法同步locType");
|
}
|
|
List<TargetLocMeta> targetList = new ArrayList<>();
|
int initRow = 1;
|
for (int mapX = 0; mapX < dataList.size(); mapX++) {
|
List<JSONObject> row = dataList.get(mapX);
|
if (row == null) {
|
continue;
|
}
|
|
int initBay = -1;
|
for (int mapY = 0; mapY < row.size(); mapY++) {
|
JSONObject cell = row.get(mapY);
|
if (cell == null || !"shelf".equals(cell.getString("type"))) {
|
continue;
|
}
|
|
if (initBay == -1) {
|
initBay = 2;
|
}
|
|
String value = cell.getString("value");
|
int userConfigRow = -1;
|
int userConfigBay = -1;
|
try {
|
String[] split = value.split("-");
|
userConfigRow = Integer.parseInt(split[0]);
|
userConfigBay = Integer.parseInt(split[1]);
|
} catch (Exception ignored) {
|
}
|
if (userConfigBay != -1) {
|
initRow = userConfigRow;
|
initBay = userConfigBay;
|
}
|
|
targetList.add(new TargetLocMeta(
|
Utils.getLocNo(initRow, initBay, targetLev),
|
Utils.getLocNo(mapX, mapY, targetLev)
|
));
|
initBay++;
|
}
|
if (initBay != -1) {
|
initRow++;
|
}
|
}
|
return targetList;
|
}
|
|
private static final class TargetLocMeta {
|
private final String locNo;
|
private final String locType;
|
|
private TargetLocMeta(String locNo, String locType) {
|
this.locNo = locNo;
|
this.locType = locType;
|
}
|
}
|
}
|