package com.zy.asrs.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.serializer.SerializerFeature; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.core.common.Cools; import com.core.exception.CoolException; import com.zy.asrs.domain.BasMapEditorDoc; import com.zy.asrs.domain.BasMapEditorElement; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.BasMap; import com.zy.asrs.entity.BasStation; import com.zy.asrs.entity.DeviceConfig; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.BasMapEditorService; import com.zy.asrs.service.BasMapService; import com.zy.asrs.service.BasStationService; import com.zy.asrs.service.DeviceConfigService; import com.zy.asrs.utils.MapExcelUtils; import com.zy.common.utils.RedisUtil; import com.zy.core.enums.RedisKeyType; import com.zy.core.enums.SlaveType; import com.zy.core.model.StationObjModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; @Service("basMapEditorService") public class BasMapEditorServiceImpl implements BasMapEditorService { private static final String FREE_EDITOR_MODE = "free-v1"; private static final Set RUNTIME_TYPES = new HashSet<>(Arrays.asList( "shelf", "crn", "dualCrn", "devp", "rgv" )); private static final int DEFAULT_ROW_HEIGHT = 200; private static final int DEFAULT_COL_WIDTH = 1000; private static final double DEFAULT_CANVAS_WIDTH = 4200D; private static final double DEFAULT_CANVAS_HEIGHT = 5200D; private static final int X_SCALE = 40; private static final int Y_SCALE = 8; @Autowired private BasMapService basMapService; @Autowired private BasDevpService basDevpService; @Autowired private DeviceConfigService deviceConfigService; @Autowired private BasStationService basStationService; @Autowired private MapExcelUtils mapExcelUtils; @Autowired private RedisUtil redisUtil; @Override public BasMapEditorDoc getEditorDoc(Integer lev) { BasMap basMap = basMapService.getOne(new QueryWrapper().eq("lev", lev)); if (basMap == null) { return null; } BasMapEditorDoc editorDoc = parseEditorDocJson(lev, basMap.getOriginData()); if (editorDoc != null) { return editorDoc; } if (Cools.isEmpty(basMap.getData())) { return null; } return toFreeEditorDoc(lev, parseStoredMapData(basMap.getData())); } @Override public List importExcelToEditorDocs(String filePath) throws IOException { HashMap>>> rawDataMap = mapExcelUtils.readExcel(filePath); List levList = new ArrayList<>(rawDataMap.keySet()); Collections.sort(levList); List result = new ArrayList<>(); for (Integer lev : levList) { List>> storedData = convertRawExcelData(rawDataMap.get(lev)); result.add(toFreeEditorDoc(lev, storedData)); } return result; } @Override @Transactional(rollbackFor = Exception.class) public void importExcelAndPersist(String filePath) throws IOException { HashMap>>> rawDataMap = mapExcelUtils.readExcel(filePath); List levList = new ArrayList<>(rawDataMap.keySet()); Collections.sort(levList); for (Integer lev : levList) { List>> storedData = convertRawExcelData(rawDataMap.get(lev)); persistFloorMap(lev, storedData, toFreeEditorDoc(lev, storedData)); } rebuildDeviceAndStationSync(); clearMapCaches(); } @Override @Transactional(rollbackFor = Exception.class) public void saveEditorDoc(BasMapEditorDoc doc) { BasMapEditorDoc normalizedDoc = normalizeFreeDoc(doc); List>> storedData = compileToStoredMapData(normalizedDoc); persistFloorMap(normalizedDoc.getLev(), storedData, normalizedDoc); rebuildDeviceAndStationSync(); clearMapCaches(); } private void persistFloorMap(Integer lev, List>> storedData, BasMapEditorDoc editorDoc) { if (lev == null || lev <= 0) { throw new CoolException("楼层不能为空"); } String dataJson = JSON.toJSONString(storedData); String editorJson = JSON.toJSONString(editorDoc); BasMap basMap = basMapService.getOne(new QueryWrapper().eq("lev", lev)); Date now = new Date(); if (basMap == null) { basMap = new BasMap(); basMap.setCreateTime(now); } else { basMap.setLastData(basMap.getData()); } basMap.setLev(lev); basMap.setData(dataJson); basMap.setOriginData(editorJson); basMap.setUpdateTime(now); basMapService.saveOrUpdate(basMap); } private BasMapEditorDoc parseEditorDocJson(Integer lev, String json) { if (Cools.isEmpty(json)) { return null; } try { JSONObject object = JSON.parseObject(json); if (object == null || !FREE_EDITOR_MODE.equals(object.getString("editorMode"))) { return null; } BasMapEditorDoc doc = JSON.toJavaObject(object, BasMapEditorDoc.class); if (doc == null) { return null; } if (doc.getLev() == null || doc.getLev() <= 0) { doc.setLev(lev); } return normalizeFreeDoc(doc); } catch (Exception ignore) { return null; } } private BasMapEditorDoc toFreeEditorDoc(Integer lev, List>> storedData) { BasMapEditorDoc doc = new BasMapEditorDoc(); doc.setLev(lev); doc.setEditorMode(FREE_EDITOR_MODE); doc.setElements(new ArrayList<>()); if (storedData == null) { doc.setCanvasWidth(DEFAULT_CANVAS_WIDTH); doc.setCanvasHeight(DEFAULT_CANVAS_HEIGHT); return doc; } int rowCount = storedData.size(); int colCount = 0; for (List> row : storedData) { if (row != null && row.size() > colCount) { colCount = row.size(); } } List rowHeights = new ArrayList<>(); for (int r = 0; r < rowCount; r++) { rowHeights.add(extractRowHeight(storedData, r)); } List colWidths = new ArrayList<>(); for (int c = 0; c < colCount; c++) { colWidths.add(extractColWidth(storedData, c)); } int[] yOffsets = new int[rowCount]; int[] xOffsets = new int[colCount]; int totalHeight = 0; for (int r = 0; r < rowCount; r++) { yOffsets[r] = totalHeight; totalHeight += rowHeights.get(r); } int totalWidth = 0; for (int c = 0; c < colCount; c++) { xOffsets[c] = totalWidth; totalWidth += colWidths.get(c); } List elements = new ArrayList<>(); for (int r = 0; r < rowCount; r++) { List> row = storedData.get(r); if (row == null) { continue; } for (int c = 0; c < row.size(); c++) { HashMap cell = row.get(c); String type = normalizeType(getString(cell, "type", "none")); if (!RUNTIME_TYPES.contains(type)) { continue; } int rowSpan = Math.max(1, toInt(cell == null ? null : cell.get("rowSpan"), 1)); int colSpan = Math.max(1, toInt(cell == null ? null : cell.get("colSpan"), 1)); int widthRaw = 0; for (int cc = c; cc < Math.min(c + colSpan, colWidths.size()); cc++) { widthRaw += colWidths.get(cc); } int heightRaw = 0; for (int rr = r; rr < Math.min(r + rowSpan, rowHeights.size()); rr++) { heightRaw += rowHeights.get(rr); } BasMapEditorElement element = new BasMapEditorElement(); element.setId("el_" + r + "_" + c); element.setType(type); element.setX(xOffsets[c] / (double) X_SCALE); element.setY(yOffsets[r] / (double) Y_SCALE); element.setWidth(widthRaw / (double) X_SCALE); element.setHeight(heightRaw / (double) Y_SCALE); element.setValue(stringifyValue(cell == null ? null : cell.get("value"))); elements.add(element); } } doc.setElements(elements); doc.setCanvasWidth(Math.max(DEFAULT_CANVAS_WIDTH, totalWidth / (double) X_SCALE + 120D)); doc.setCanvasHeight(Math.max(DEFAULT_CANVAS_HEIGHT, totalHeight / (double) Y_SCALE + 120D)); return normalizeFreeDoc(doc); } private BasMapEditorDoc normalizeFreeDoc(BasMapEditorDoc source) { if (source == null || source.getLev() == null || source.getLev() <= 0) { throw new CoolException("楼层不能为空"); } BasMapEditorDoc doc = new BasMapEditorDoc(); doc.setLev(source.getLev()); doc.setEditorMode(FREE_EDITOR_MODE); doc.setCanvasWidth(toPositiveCanvasValue(source.getCanvasWidth(), DEFAULT_CANVAS_WIDTH, "画布宽度")); doc.setCanvasHeight(toPositiveCanvasValue(source.getCanvasHeight(), DEFAULT_CANVAS_HEIGHT, "画布高度")); List elements = new ArrayList<>(); List sourceElements = source.getElements(); if (sourceElements != null) { for (int i = 0; i < sourceElements.size(); i++) { elements.add(normalizeElement(sourceElements.get(i), i + 1)); } } ensureNoRectOverlap(elements); doc.setElements(elements); return doc; } private BasMapEditorElement normalizeElement(BasMapEditorElement source, int index) { BasMapEditorElement element = new BasMapEditorElement(); element.setId(Cools.isEmpty(source == null ? null : source.getId()) ? "el_" + index : source.getId().trim()); element.setType(normalizeType(source == null ? null : source.getType())); if (!RUNTIME_TYPES.contains(element.getType())) { throw new CoolException("存在不支持的节点类型: " + element.getType()); } double x = toNonNegativeDouble(source == null ? null : source.getX(), "元素 X 坐标"); double y = toNonNegativeDouble(source == null ? null : source.getY(), "元素 Y 坐标"); double width = toPositiveCanvasValue(source == null ? null : source.getWidth(), 0D, "元素宽度"); double height = toPositiveCanvasValue(source == null ? null : source.getHeight(), 0D, "元素高度"); element.setX(x); element.setY(y); element.setWidth(width); element.setHeight(height); element.setValue(stringifyValue(source == null ? null : source.getValue())); if ("devp".equals(element.getType())) { validateDevpValue(element.getValue(), index); } return element; } private void ensureNoRectOverlap(List elements) { for (int i = 0; i < elements.size(); i++) { BasMapEditorElement a = elements.get(i); for (int j = i + 1; j < elements.size(); j++) { BasMapEditorElement b = elements.get(j); if (rectanglesOverlap(a, b)) { throw new CoolException("元素存在重叠: " + a.getId() + " 与 " + b.getId()); } } } } private boolean rectanglesOverlap(BasMapEditorElement a, BasMapEditorElement b) { int aLeft = toRawWidth(a.getX()); int aTop = toRawHeight(a.getY()); int aRight = toRawWidth(safeDouble(a.getX()) + safeDouble(a.getWidth())); int aBottom = toRawHeight(safeDouble(a.getY()) + safeDouble(a.getHeight())); int bLeft = toRawWidth(b.getX()); int bTop = toRawHeight(b.getY()); int bRight = toRawWidth(safeDouble(b.getX()) + safeDouble(b.getWidth())); int bBottom = toRawHeight(safeDouble(b.getY()) + safeDouble(b.getHeight())); return aLeft < bRight && aRight > bLeft && aTop < bBottom && aBottom > bTop; } private List>> compileToStoredMapData(BasMapEditorDoc doc) { List elements = doc.getElements() == null ? new ArrayList() : doc.getElements(); if (elements.isEmpty()) { return buildEmptyStoredMapData(doc); } TreeSet xBounds = new TreeSet<>(); TreeSet yBounds = new TreeSet<>(); xBounds.add(0); yBounds.add(0); List rects = new ArrayList<>(); for (BasMapEditorElement element : elements) { CompiledRect rect = toCompiledRect(element); rects.add(rect); xBounds.add(rect.left); xBounds.add(rect.right); yBounds.add(rect.top); yBounds.add(rect.bottom); } List xList = new ArrayList<>(xBounds); List yList = new ArrayList<>(yBounds); if (xList.size() < 2 || yList.size() < 2) { return buildEmptyStoredMapData(doc); } int rowCount = yList.size() - 1; int colCount = xList.size() - 1; String[][] occupancy = new String[rowCount][colCount]; Map xIndexMap = buildBoundaryIndexMap(xList); Map yIndexMap = buildBoundaryIndexMap(yList); List>> stored = new ArrayList<>(); for (int r = 0; r < rowCount; r++) { List> row = new ArrayList<>(); int cellHeight = yList.get(r + 1) - yList.get(r); for (int c = 0; c < colCount; c++) { int cellWidth = xList.get(c + 1) - xList.get(c); row.add(createStoredCell("none", "", cellWidth, cellHeight, 1, 1, "")); } stored.add(row); } for (CompiledRect rect : rects) { Integer rowStart = yIndexMap.get(String.valueOf(rect.top)); Integer rowEndIndex = yIndexMap.get(String.valueOf(rect.bottom)); Integer colStart = xIndexMap.get(String.valueOf(rect.left)); Integer colEndIndex = xIndexMap.get(String.valueOf(rect.right)); if (rowStart == null || rowEndIndex == null || colStart == null || colEndIndex == null) { throw new CoolException("地图编译失败: 元素边界无法映射到网格"); } int rowSpan = rowEndIndex - rowStart; int colSpan = colEndIndex - colStart; if (rowSpan <= 0 || colSpan <= 0) { throw new CoolException("地图编译失败: 元素尺寸无效 " + rect.id); } for (int r = rowStart; r < rowStart + rowSpan; r++) { for (int c = colStart; c < colStart + colSpan; c++) { if (occupancy[r][c] != null) { throw new CoolException("地图编译失败: 元素重叠 " + rect.id + " 与 " + occupancy[r][c]); } occupancy[r][c] = rect.id; } } stored.get(rowStart).set(colStart, createStoredCell( rect.type, rect.value, xList.get(colStart + 1) - xList.get(colStart), yList.get(rowStart + 1) - yList.get(rowStart), rowSpan, colSpan, "" )); for (int r = rowStart; r < rowStart + rowSpan; r++) { for (int c = colStart; c < colStart + colSpan; c++) { if (r == rowStart && c == colStart) { continue; } stored.get(r).set(c, createStoredCell( "merge", rect.value, xList.get(c + 1) - xList.get(c), yList.get(r + 1) - yList.get(r), 1, 1, rect.type )); } } } return stored; } private List>> buildEmptyStoredMapData(BasMapEditorDoc doc) { int widthRaw = Math.max(DEFAULT_COL_WIDTH, toRawWidth(doc == null ? null : doc.getCanvasWidth())); int heightRaw = Math.max(DEFAULT_ROW_HEIGHT, toRawHeight(doc == null ? null : doc.getCanvasHeight())); List>> stored = new ArrayList<>(); List> row = new ArrayList<>(); row.add(createStoredCell("none", "", widthRaw, heightRaw, 1, 1, "")); stored.add(row); return stored; } private CompiledRect toCompiledRect(BasMapEditorElement element) { CompiledRect rect = new CompiledRect(); rect.id = element.getId(); rect.type = element.getType(); rect.value = normalizeCellValue(element.getType(), element.getValue()); rect.left = toRawWidth(element.getX()); rect.top = toRawHeight(element.getY()); rect.right = toRawWidth(safeDouble(element.getX()) + safeDouble(element.getWidth())); rect.bottom = toRawHeight(safeDouble(element.getY()) + safeDouble(element.getHeight())); if (rect.right <= rect.left || rect.bottom <= rect.top) { throw new CoolException("元素尺寸无效: " + element.getId()); } return rect; } private Map buildBoundaryIndexMap(List bounds) { Map result = new LinkedHashMap<>(); for (int i = 0; i < bounds.size(); i++) { result.put(String.valueOf(bounds.get(i)), i); } return result; } private HashMap createStoredCell(String type, String value, int cellWidth, int cellHeight, int rowSpan, int colSpan, String mergeType) { HashMap cell = new HashMap<>(); cell.put("type", normalizeType(type)); cell.put("value", stringifyValue(value)); cell.put("cellWidth", Math.max(1, cellWidth)); cell.put("cellHeight", Math.max(1, cellHeight)); cell.put("rowSpan", Math.max(1, rowSpan)); cell.put("colSpan", Math.max(1, colSpan)); if ("merge".equals(type)) { cell.put("mergeType", normalizeType(mergeType)); } return cell; } private List>> parseStoredMapData(String json) { if (Cools.isEmpty(json)) { return new ArrayList<>(); } List>> data = JSON.parseObject(json, new TypeReference>>>() {}); return data == null ? new ArrayList>>() : data; } private List>> convertRawExcelData(List>> rawData) { List>> result = new ArrayList<>(); if (rawData == null) { return result; } for (List> row : rawData) { List> rowResult = new ArrayList<>(); if (row != null) { for (HashMap cell : row) { rowResult.add(convertRawExcelCell(cell)); } } result.add(rowResult); } return result; } private HashMap convertRawExcelCell(HashMap rawCell) { HashMap target = new HashMap<>(); String nodeType = getString(rawCell, "nodeType", "none"); String normalizedType; if ("shelf".equals(nodeType)) { normalizedType = "shelf"; } else if ("crn".equals(nodeType)) { normalizedType = "crn"; } else if ("dualCrn".equals(nodeType) || "dualcrn".equals(nodeType)) { normalizedType = "dualCrn"; } else if ("devp".equals(nodeType)) { normalizedType = "devp"; } else if ("rgv".equals(nodeType)) { normalizedType = "rgv"; } else if ("merge".equals(nodeType)) { normalizedType = "merge"; } else { normalizedType = "none"; } target.put("type", normalizedType); target.put("value", stringifyValue(rawCell == null ? null : rawCell.get("value"))); target.put("cellWidth", Math.max(1, toInt(rawCell == null ? null : rawCell.get("cellWidth"), DEFAULT_COL_WIDTH))); target.put("cellHeight", Math.max(1, toInt(rawCell == null ? null : rawCell.get("cellHeight"), DEFAULT_ROW_HEIGHT))); target.put("rowSpan", Math.max(1, toInt(rawCell == null ? null : rawCell.get("rowSpan"), 1))); target.put("colSpan", Math.max(1, toInt(rawCell == null ? null : rawCell.get("colSpan"), 1))); if ("merge".equals(normalizedType)) { target.put("mergeType", normalizeType(getString(rawCell, "mergeType", "none"))); } return target; } private int extractRowHeight(List>> storedData, int rowIndex) { List> row = storedData.get(rowIndex); if (row == null) { return DEFAULT_ROW_HEIGHT; } for (HashMap cell : row) { int height = toInt(cell == null ? null : cell.get("cellHeight"), 0); if (height > 0) { return height; } } return DEFAULT_ROW_HEIGHT; } private int extractColWidth(List>> storedData, int colIndex) { for (List> row : storedData) { if (row == null || colIndex >= row.size()) { continue; } HashMap cell = row.get(colIndex); int width = toInt(cell == null ? null : cell.get("cellWidth"), 0); if (width > 0) { return width; } } return DEFAULT_COL_WIDTH; } private void validateDevpValue(String value, int elementIndex) { if (Cools.isEmpty(value)) { throw new CoolException("输送线节点缺少配置: 元素#" + elementIndex); } JSONObject jsonObject; try { jsonObject = JSON.parseObject(value); } catch (Exception ex) { throw new CoolException("输送线节点配置不是合法JSON: 元素#" + elementIndex); } if (jsonObject == null || jsonObject.getInteger("stationId") == null || jsonObject.getInteger("deviceNo") == null) { throw new CoolException("输送线节点必须包含stationId和deviceNo: 元素#" + elementIndex); } Integer isInStation = jsonObject.getInteger("isInStation"); Integer isBarcodeStation = jsonObject.getInteger("isBarcodeStation"); Integer barcodeIdx = jsonObject.getInteger("barcodeIdx"); Integer barcodeStation = jsonObject.getInteger("barcodeStation"); Integer barcodeStationDeviceNo = jsonObject.getInteger("barcodeStationDeviceNo"); Integer backStation = jsonObject.getInteger("backStation"); Integer backStationDeviceNo = jsonObject.getInteger("backStationDeviceNo"); if (isInStation != null && isInStation == 1) { if (!isPositiveInteger(barcodeStation) || !isPositiveInteger(barcodeStationDeviceNo)) { throw new CoolException("入站点必须包含barcodeStation和barcodeStationDeviceNo: 元素#" + elementIndex); } } if (isBarcodeStation != null && isBarcodeStation == 1) { if (!isPositiveInteger(barcodeIdx) || !isPositiveInteger(backStation) || !isPositiveInteger(backStationDeviceNo)) { throw new CoolException("条码站必须包含barcodeIdx、backStation和backStationDeviceNo: 元素#" + elementIndex); } } } private boolean isPositiveInteger(Integer value) { return value != null && value > 0; } private double toPositiveCanvasValue(Double rawValue, double defaultValue, String fieldLabel) { double value = safeDouble(rawValue); if (value <= 0) { if (defaultValue > 0) { return defaultValue; } throw new CoolException(fieldLabel + "必须大于0"); } return value; } private double toNonNegativeDouble(Double rawValue, String fieldLabel) { double value = safeDouble(rawValue); if (value < 0) { throw new CoolException(fieldLabel + "不能小于0"); } return value; } private int toRawWidth(Double displayValue) { return Math.max(0, (int) Math.round(safeDouble(displayValue) * X_SCALE)); } private int toRawHeight(Double displayValue) { return Math.max(0, (int) Math.round(safeDouble(displayValue) * Y_SCALE)); } private double safeDouble(Double value) { return value == null ? 0D : value; } private void rebuildDeviceAndStationSync() { SyncContext context = new SyncContext(); List basMapList = basMapService.list(new QueryWrapper().orderByAsc("lev")); for (BasMap basMap : basMapList) { collectStationsFromMap(context, basMap.getLev(), parseStoredMapData(basMap.getData())); } basStationService.remove(new QueryWrapper()); syncBasDevp(context); syncBasStation(context); } private void collectStationsFromMap(SyncContext context, Integer lev, List>> storedData) { if (storedData == null) { return; } for (List> row : storedData) { if (row == null) { continue; } for (HashMap cell : row) { String type = normalizeType(getString(cell, "type", "none")); if (!"devp".equals(type)) { continue; } JSONObject value = parseJsonObject(stringifyValue(cell.get("value"))); if (value == null) { continue; } Integer deviceNo = value.getInteger("deviceNo"); Integer stationId = value.getInteger("stationId"); if (deviceNo == null || stationId == null) { continue; } StationObjModel stationObjModel = new StationObjModel(); stationObjModel.setDeviceNo(deviceNo); stationObjModel.setStationId(stationId); stationObjModel.setStationLev(lev); addStationModel(context.deviceStationMap, context.deviceStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), stationObjModel); context.stationRegistry.put(stationId, stationObjModel); Integer isBarcodeStation = value.getInteger("isBarcodeStation"); if (isBarcodeStation != null && isBarcodeStation == 1) { StationObjModel barcodeStationModel = new StationObjModel(); barcodeStationModel.setDeviceNo(deviceNo); barcodeStationModel.setStationId(stationId); barcodeStationModel.setBarcodeIdx(value.getInteger("barcodeIdx")); if (value.getInteger("backStation") != null) { StationObjModel backStation = new StationObjModel(); backStation.setDeviceNo(value.getInteger("backStationDeviceNo")); backStation.setStationId(value.getInteger("backStation")); barcodeStationModel.setBackStation(backStation); } addStationModel(context.barcodeStationMap, context.barcodeStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), barcodeStationModel); } Integer isInStation = value.getInteger("isInStation"); if (isInStation != null && isInStation == 1) { StationObjModel inStationModel = new StationObjModel(); inStationModel.setDeviceNo(deviceNo); inStationModel.setStationId(stationId); StationObjModel barcodeStation = new StationObjModel(); barcodeStation.setDeviceNo(value.getInteger("barcodeStationDeviceNo")); barcodeStation.setStationId(value.getInteger("barcodeStation")); inStationModel.setBarcodeStation(barcodeStation); addStationModel(context.inStationMap, context.inStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), inStationModel); } Integer isOutStation = value.getInteger("isOutStation"); if (isOutStation != null && isOutStation == 1) { addStationModel(context.outStationMap, context.outStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel)); } Integer runBlockReassign = value.getInteger("runBlockReassign"); if (runBlockReassign != null && runBlockReassign == 1) { addStationModel(context.runBlockReassignStationMap, context.runBlockReassignDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel)); } Integer isOutOrder = value.getInteger("isOutOrder"); if (isOutOrder != null && isOutOrder == 1) { addStationModel(context.outOrderStationMap, context.outOrderDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel)); } Integer isLiftTransfer = value.getInteger("isLiftTransfer"); if (isLiftTransfer != null && isLiftTransfer == 1) { addStationModel(context.liftTransferStationMap, context.liftTransferDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel)); } } } } private void syncBasDevp(SyncContext context) { Map existingMap = new LinkedHashMap<>(); List existingList = basDevpService.list(); for (BasDevp basDevp : existingList) { existingMap.put(basDevp.getDevpNo(), basDevp); } Set allDeviceNos = new LinkedHashSet<>(); allDeviceNos.addAll(existingMap.keySet()); allDeviceNos.addAll(context.deviceStationMap.keySet()); Date now = new Date(); for (Integer deviceNo : allDeviceNos) { BasDevp basDevp = existingMap.get(deviceNo); if (basDevp == null) { basDevp = new BasDevp(); basDevp.setDevpNo(deviceNo); basDevp.setStatus(1); basDevp.setCreateTime(now); } List stationList = context.deviceStationMap.get(deviceNo); List barcodeStationList = context.barcodeStationMap.get(deviceNo); List inStationList = context.inStationMap.get(deviceNo); List outStationList = context.outStationMap.get(deviceNo); List runBlockReassignStationList = context.runBlockReassignStationMap.get(deviceNo); List outOrderStationList = context.outOrderStationMap.get(deviceNo); List liftTransferStationList = context.liftTransferStationMap.get(deviceNo); basDevp.setStationList(toJsonOrNull(stationList)); basDevp.setBarcodeStationList(toJsonOrNull(barcodeStationList)); basDevp.setInStationList(toJsonOrNull(inStationList)); basDevp.setOutStationList(toJsonOrNull(outStationList)); basDevp.setRunBlockReassignLocStationList(toJsonOrNull(runBlockReassignStationList)); basDevp.setIsOutOrderList(toJsonOrNull(outOrderStationList)); basDevp.setIsLiftTransferList(toJsonOrNull(liftTransferStationList)); basDevp.setUpdateTime(now); basDevpService.saveOrUpdate(basDevp); DeviceConfig deviceConfig = deviceConfigService.getOne(new QueryWrapper() .eq("device_no", deviceNo) .eq("device_type", String.valueOf(SlaveType.Devp))); if (deviceConfig != null) { deviceConfig.setFakeInitStatus(toJsonOrNull(stationList)); deviceConfigService.updateById(deviceConfig); } } } private void syncBasStation(SyncContext context) { Date now = new Date(); for (StationObjModel stationObjModel : context.stationRegistry.values()) { BasStation basStation = new BasStation(); basStation.setStationId(stationObjModel.getStationId()); basStation.setDeviceNo(stationObjModel.getDeviceNo()); basStation.setStationLev(stationObjModel.getStationLev()); basStation.setCreateTime(now); basStation.setStatus(1); basStationService.save(basStation); } } private void addStationModel(Map> targetMap, Map> dedupMap, Integer deviceNo, String dedupKey, StationObjModel stationObjModel) { Set set = dedupMap.get(deviceNo); if (set == null) { set = new LinkedHashSet<>(); dedupMap.put(deviceNo, set); } if (!set.add(dedupKey)) { return; } List list = targetMap.get(deviceNo); if (list == null) { list = new ArrayList<>(); targetMap.put(deviceNo, list); } list.add(stationObjModel); } private StationObjModel cloneStationModel(StationObjModel source) { StationObjModel clone = new StationObjModel(); clone.setDeviceNo(source.getDeviceNo()); clone.setStationId(source.getStationId()); clone.setStationLev(source.getStationLev()); clone.setBarcodeIdx(source.getBarcodeIdx()); clone.setDeviceBay(source.getDeviceBay()); clone.setDeviceLev(source.getDeviceLev()); clone.setDeviceRow(source.getDeviceRow()); clone.setDualCrnExecuteStation(source.getDualCrnExecuteStation()); clone.setBarcodeStation(source.getBarcodeStation()); clone.setBackStation(source.getBackStation()); return clone; } private String toJsonOrNull(List list) { if (list == null || list.isEmpty()) { return null; } return JSON.toJSONString(list, SerializerFeature.DisableCircularReferenceDetect); } private void clearMapCaches() { redisUtil.del(RedisKeyType.LOC_MAP_BASE.key); redisUtil.del(RedisKeyType.LOC_MAST_MAP_LIST.key); } private String normalizeType(String type) { if (type == null) { return "none"; } String value = String.valueOf(type).trim(); if ("dualcrn".equalsIgnoreCase(value)) { return "dualCrn"; } if (Cools.isEmpty(value)) { return "none"; } return value; } private String normalizeCellValue(String type, String value) { if ("none".equals(type)) { return ""; } return stringifyValue(value); } private String stringifyValue(Object value) { if (value == null) { return ""; } if (value instanceof String) { return (String) value; } if (value instanceof JSONObject || value instanceof Map || value instanceof List) { return JSON.toJSONString(value); } return String.valueOf(value); } private String getString(Map map, String key, String defaultValue) { if (map == null || !map.containsKey(key) || map.get(key) == null) { return defaultValue; } String value = stringifyValue(map.get(key)); return Cools.isEmpty(value) ? defaultValue : value; } private int toInt(Object value, int defaultValue) { if (value == null) { return defaultValue; } if (value instanceof Number) { return ((Number) value).intValue(); } try { return (int) Math.round(Double.parseDouble(String.valueOf(value))); } catch (Exception ignore) { return defaultValue; } } private JSONObject parseJsonObject(String json) { if (Cools.isEmpty(json)) { return null; } try { return JSON.parseObject(json); } catch (Exception ignore) { return null; } } private String buildStationKey(Integer deviceNo, Integer stationId, Integer lev) { return deviceNo + "_" + stationId + "_" + lev; } private static class CompiledRect { private String id; private String type; private String value; private int left; private int top; private int right; private int bottom; } private static class SyncContext { private final Map> deviceStationMap = new LinkedHashMap<>(); private final Map> barcodeStationMap = new LinkedHashMap<>(); private final Map> inStationMap = new LinkedHashMap<>(); private final Map> outStationMap = new LinkedHashMap<>(); private final Map> runBlockReassignStationMap = new LinkedHashMap<>(); private final Map> outOrderStationMap = new LinkedHashMap<>(); private final Map> liftTransferStationMap = new LinkedHashMap<>(); private final Map stationRegistry = new LinkedHashMap<>(); private final Map> deviceStationDedup = new LinkedHashMap<>(); private final Map> barcodeStationDedup = new LinkedHashMap<>(); private final Map> inStationDedup = new LinkedHashMap<>(); private final Map> outStationDedup = new LinkedHashMap<>(); private final Map> runBlockReassignDedup = new LinkedHashMap<>(); private final Map> outOrderDedup = new LinkedHashMap<>(); private final Map> liftTransferDedup = new LinkedHashMap<>(); } }