Commit ea5f0a85 张少辉

1.合并库位功能开发

1 个父辈 9849e05c
......@@ -235,6 +235,7 @@ public class MenuInit {
Menu poutSet = Menu.CreatePMenu("设置", 13, "system","system",null);
addDefaultFunctionMenu(101, poutSet, "设备管理", "bunker", "storage/storage/index", "database",DEFAULT_SHOW_MENU);
addDefaultFunctionMenu(102,poutSet, "库位管理", "storagePos", "storagePos/storagePos/index", "tree-table",DEFAULT_SHOW_MENU);
addDefaultFunctionMenu(103,poutSet, "合并库位", "selectDevice", "neolight/selectDevice/index", "selectDevice");
addDefaultFunctionMenu(103, poutSet, "菜单管理", "menu", "system/menu/index", "menu");
// Menu sysSetting = new Menu(, "barcode", "条码设置", "barcodeSetting", "system/barcodeSetting/index", "database");
// Menu outSet = new Menu(, "outSetting", "出库策略", "outSetting", "system/outSetting/index", "outSet");
......
......@@ -666,7 +666,7 @@ public class HttpHelper {
List<NameValuePair> params = toNameValuePair(paramMap);
URI uri = new URIBuilder(url).setParameters(params).build();
log.info("getParam请求,地址为:"+uri);
//log.info("getParam请求,地址为:"+uri);
HttpGet httpGet = new HttpGet(uri);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT).build();
httpGet.setConfig(requestConfig);
......
......@@ -9,6 +9,7 @@ import com.neotel.smfcore.core.api.bean.CodeValidateParam;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.inList.service.po.InList;
import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
......@@ -85,6 +86,12 @@ public class SmfApi {
@Value("${api.deviceStatusUrl:}")
protected String deviceStatusUrl = "";
@Value("${api.mergePosUrl:}")
protected String mergePosUrl = "";
@Value("${api.splitPosUrl:}")
protected String splitPosUrl = "";
@PostConstruct
public void init(){
apiName = dataCache.getConfigCache("api.name",apiName);
......@@ -100,6 +107,8 @@ public class SmfApi {
fetchOrderUrl = dataCache.getConfigCache("api.fetchOrderUrl",fetchOrderUrl);
loginCheckUrl=dataCache.getConfigCache("api.loginCheckUrl",loginCheckUrl);
deviceStatusUrl=dataCache.getConfigCache("api.deviceStatusUrl",deviceStatusUrl);
mergePosUrl = dataCache.getConfigCache("api.mergePosUrl",mergePosUrl);
splitPosUrl = dataCache.getConfigCache("api.splitPosUrl",splitPosUrl);
}
/**
......@@ -294,6 +303,37 @@ public class SmfApi {
}
return true;
}
public void splitPos(String splitPosName) {
if(isUrlExist(splitPosUrl)){
threadPool.execute(new Runnable() {
@Override
public void run() {
for (ISmfApiListener apiListener : apiListenerList) {
if(apiListener.isForThisApi(apiName)){
apiListener.splitPos(splitPosUrl,splitPosName);
}
}
}
});
}
}
public void mergePos(StoragePos storagePos) {
if(isUrlExist(mergePosUrl)){
threadPool.execute(new Runnable() {
@Override
public void run() {
for (ISmfApiListener apiListener : apiListenerList) {
if(apiListener.isForThisApi(apiName)){
apiListener.mergePos(mergePosUrl,storagePos);
}
}
}
});
}
}
public String getApiName(){
return apiName;
}
......
......@@ -20,6 +20,7 @@ import com.neotel.smfcore.core.inList.util.InListCache;
import com.neotel.smfcore.core.order.LiteOrderCache;
import com.neotel.smfcore.core.order.service.manager.ILiteOrderManager;
import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
......@@ -152,4 +153,14 @@ public abstract class BaseSmfApiListener implements ISmfApiListener {
public boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status) {
return true;
}
@Override
public void splitPos(String splitPosUrl, String splitPosName) {
}
@Override
public void mergePos(String mergePosUrl, StoragePos storagePos) {
}
}
......@@ -6,6 +6,7 @@ import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.api.bean.CodeValidateParam;
import com.neotel.smfcore.core.inList.service.po.InList;
import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
public interface ISmfApiListener {
......@@ -80,4 +81,9 @@ public interface ISmfApiListener {
* @return
*/
boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status );
void splitPos(String splitPosUrl, String splitPosName);
void mergePos(String mergePosUrl, StoragePos storagePos);
}
......@@ -107,4 +107,5 @@ public interface IStoragePosManager extends IBaseManager<StoragePos> {
StoragePos getSpEmptyPosByStorage(Storage storage, Barcode barcode, Collection<String> excludePosIds,String endStr) throws ValidateException;
void saveMergePos(StoragePos storagePos);
}
......@@ -1176,4 +1176,9 @@ public class StoragePosManagerImpl implements IStoragePosManager {
StoragePos pos = storagePosDao.findOne(query);
return pos;
}
@Override
public void saveMergePos(StoragePos storagePos) {
storagePosDao.save(storagePos);
}
}
......@@ -38,91 +38,15 @@ public class HaoBoNlpPosHandler {
@PostConstruct
private void init() {
String apiName = smfApi.getApiName();
if (haoboApi.isForThisApi(apiName)) {
scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
String apiName = smfApi.getApiName();
if (haoboApi.isForThisApi(apiName)) {
handlePos();
handleTriColorLight();
}
}, 30, 2, TimeUnit.SECONDS);
}
}
// 新增:定义静态缓存,记录每个货架(cid)上一次点亮的灯通道
private static final Map<String, Integer> LAST_LIGHT_CHANNEL_CACHE = new HashMap<>();
private void handleTriColorLight() {
int left = 81;
int right = 82;
int buzzer = 9;
int color_green = 10;
int color_yellow = 11;
int color_red = 12;
for (Storage storage : dataCache.getAllStorage().values()) {
if (storage.isNLPHBShelf()) {
boolean hasTask = false;
boolean hasException = false;
Collection<DataLog> queueTasks = taskService.getQueueTasks(storage.getCid());
if (queueTasks != null && !queueTasks.isEmpty()) {
hasTask = true;
}
List<List<String>> lastDeviceData = DevicesStatusUtil.getDeviceData(storage.getCid());
List<String> lastHasReelPosErrorList = new ArrayList<>();
List<String> lastNoReelPosErrorList = new ArrayList<>();
if (lastDeviceData != null && lastDeviceData.size() == 2) {
lastHasReelPosErrorList = lastDeviceData.get(0);
lastNoReelPosErrorList = lastDeviceData.get(1);
}
if (lastHasReelPosErrorList != null && !lastHasReelPosErrorList.isEmpty()) {
hasException = true;
}
if (lastNoReelPosErrorList != null && !lastNoReelPosErrorList.isEmpty()) {
hasException = true;
}
int ioBoardAddress = left;
if (storage.getCid().endsWith("A")) {
ioBoardAddress = left;
} else if (storage.getCid().endsWith("B")) {
ioBoardAddress = right;
}
int channel = color_green;
if (hasException) {
channel = color_red;
} else if (!hasException && hasTask) {
channel = color_yellow;
} else {
channel = color_green;
}
// ===== 核心修改:首次无缓存则全部关闭,非首次关闭上一个灯 =====
String cid = storage.getCid();
Integer lastChannel = LAST_LIGHT_CHANNEL_CACHE.get(cid);
if (lastChannel == null) {
// 第一次操作:全部关闭绿、黄、红三个通道
haoboApi.controlIoBoard(ioBoardAddress, color_green, false);
haoboApi.controlIoBoard(ioBoardAddress, color_yellow, false);
haoboApi.controlIoBoard(ioBoardAddress, color_red, false);
} else if (lastChannel != channel) {
// 非首次且通道不同:仅关闭上一次点亮的灯
haoboApi.controlIoBoard(ioBoardAddress, lastChannel, false);
}
// 点亮本次目标灯(原有逻辑保留)
if(lastChannel == null || lastChannel != channel ){
haoboApi.controlIoBoard(ioBoardAddress, channel, true);
}
// 更新缓存,记录本次点亮的通道
LAST_LIGHT_CHANNEL_CACHE.put(cid, channel);
// ==============================================
}
}
}, 30, 5, TimeUnit.SECONDS);
}
private void handlePos() {
......@@ -147,7 +71,6 @@ public class HaoBoNlpPosHandler {
List<String> totalAddList = posTotalDiffResult.getTotalAddList();
List<String> totalReduceList = posTotalDiffResult.getTotalReduceList();
haoboApi.lightUpSomeLampBeads(totalAddList, totalReduceList);
DevicesStatusUtil.updateDeviceData(cid, newData);
}
}
......
......@@ -9,6 +9,7 @@ import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.api.listener.BaseSmfApiListener;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.order.enums.ORDER_COLOR;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.custom.haobo.bean.response.GetInventoryResponse;
import lombok.extern.slf4j.Slf4j;
......@@ -91,6 +92,39 @@ public class HaoboApi extends BaseSmfApiListener {
}
}
@Override
public void mergePos(String mergePosUrl, StoragePos storagePos) {
List<String> mergePosList = storagePos.getMergePosList();
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("locationList",mergePosList);
paramMap.put("oneMergedQuantity",mergePosList.size());
log.info("合并料架,请求浩博料架,地址为:"+mergePosUrl+",参数为:"+ JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(mergePosUrl, paramMap);
log.info("合并料架,浩博料架返回结果为:"+result);
} catch (ApiException e) {
log.info("合并料架,浩博料架返回异常,异常信息为:"+e.getMessage());
}
}
@Override
public void splitPos(String splitPosUrl, String splitPosName) {
/**
* {"locationList":["T001A1001"]}
*/
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("locationList",Collections.singletonList(splitPosName));
log.info("拆分料架,请求浩博料架,地址为:"+splitPosUrl+",参数为:"+ JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(splitPosUrl, paramMap);
log.info("拆分料架,浩博料架返回结果为:"+result);
} catch (ApiException e) {
log.info("拆分料架,浩博料架返回异常,异常信息为:"+e.getMessage());
}
}
protected List<String> getInventoryPosName(String cid) {
List<String> hasReelPosNameList = new ArrayList<>();
Map<String, Object> paramMap = new HashMap<>();
......
......@@ -158,7 +158,7 @@ public class HaoboNlpController {
storagePos.setBarcode(barcode);
storagePos.setUsed(true);
storagePos.setCanCheckOutTime(System.currentTimeMillis());
storagePosManager.save(storagePos);
storagePosManager.saveMergePos(storagePos);
if (barcode != null) {
dataCache.updateInventory(storagePos, barcode);
//记录日志,完成 task
......@@ -210,7 +210,7 @@ public class HaoboNlpController {
storagePos.setBarcode(null);
storagePos.setUsed(false);
storagePosManager.save(storagePos);
storagePosManager.saveMergePos(storagePos);
log.info("出库完成,清空仓位: " + storagePos.getId() + "[" + storagePos.getPosName() + "]");
......
......@@ -463,4 +463,11 @@ smfcore.reelPosMove.posIsNull=\u5E93\u4F4D[{1}]\u4E3A\u7A7A
smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F
smfcore.pos.merge.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.merge.numError=\u8BF7\u9009\u62E9[{0}]\u4E2A\u8981\u5408\u5E76\u7684\u5E93\u4F4D
smfcore.pos.merge.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u5E76
smfcore.pos.merge.hasMergePos=\u5E93\u4F4D[{0}]\u5DF2\u5408\u5E76,\u4E0D\u80FD\u7EE7\u7EED\u5408\u5E76
smfcore.pos.merge.notAdjacent=\u8BF7\u9009\u62E9\u76F8\u90BB\u7684\u5E93\u4F4D\u5408\u5E76
smfcore.pos.split.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.split.notMergePos=\u5E93\u4F4D[{0}]\u672A\u5408\u5E76,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
......@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Bedarfsauftrag {0} existiert bereits
smfcore.reelPosMove.barcodeError=Der Barcode des Lagerorts {0} ist {1}, nicht identisch mit dem Umzugscode {1}
smfcore.barcode.notFindFeerder=Stationsinformation [{0}] nicht gefunden
smfcore.storagePos.provider=Lieferant
smfcore.pos.merge.notNull=Lagerposition darf nicht leer sein
smfcore.pos.merge.numError=W\u00E4hlen Sie [{0}] Lagerpositionen zum Zusammenf\u00FChren aus
smfcore.pos.merge.hasMaterial=Lagerposition [{0}] enth\u00E4lt Materialien und kann nicht zusammengef\u00FChrt werden
smfcore.pos.merge.hasMergePos=Lagerposition [{0}] wurde bereits zusammengef\u00FChrt und kann nicht weiter zusammengef\u00FChrt werden
smfcore.pos.merge.notAdjacent=W\u00E4hlen Sie benachbarte Lagerpositionen zum Zusammenf\u00FChren aus
smfcore.pos.split.notNull=Lagerposition darf nicht leer sein
smfcore.pos.split.notMergePos=Lagerposition [{0}] wurde nicht zusammengef\u00FChrt und kann nicht aufgeteilt werden
smfcore.pos.split.hasMaterial=Lagerposition [{0}] enth\u00E4lt Materialien und kann nicht aufgeteilt werden
......@@ -454,3 +454,11 @@ smfcore.liteOrder.exist=Requirement order {0} already exists
smfcore.reelPosMove.barcodeError=Location {0} barcode is {1}, inconsistent with the transfer barcode {1}
smfcore.barcode.notFindFeerder=Station information [{0}] not found
smfcore.storagePos.provider=Provider
smfcore.pos.merge.notNull=Storage position cannot be empty
smfcore.pos.merge.numError=Please select [{0}] storage positions to merge
smfcore.pos.merge.hasMaterial=Storage position [{0}] contains materials and cannot be merged
smfcore.pos.merge.hasMergePos=Storage position [{0}] has been merged and cannot be merged further
smfcore.pos.merge.notAdjacent=Please select adjacent storage positions to merge
smfcore.pos.split.notNull=Storage position cannot be empty
smfcore.pos.split.notMergePos=Storage position [{0}] has not been merged and cannot be split
smfcore.pos.split.hasMaterial=Storage position [{0}] contains materials and cannot be split
......@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Le bon de commande {0} existe d\u00E9j\u00E0
smfcore.reelPosMove.barcodeError=Le code-barres de l'emplacement {0} est {1}, incompatible avec le code-barres de transfert {1}
smfcore.barcode.notFindFeerder=Informations de station [{0}] non trouv\u00E9es
smfcore.storagePos.provider=Fournisseur
smfcore.pos.merge.notNull=L'emplacement de stockage ne peut pas \u00EAtre vide
smfcore.pos.merge.numError=Veuillez s\u00E9lectionner [{0}] emplacements de stockage \u00E0 fusionner
smfcore.pos.merge.hasMaterial=L'emplacement de stockage [{0}] contient des mati\u00E8res et ne peut pas \u00EAtre fusionn\u00E9
smfcore.pos.merge.hasMergePos=L'emplacement de stockage [{0}] a d\u00E9j\u00E0 \u00E9t\u00E9 fusionn\u00E9 et ne peut pas \u00EAtre fusionn\u00E9 davantage
smfcore.pos.merge.notAdjacent=Veuillez s\u00E9lectionner des emplacements de stockage adjacents \u00E0 fusionner
smfcore.pos.split.notNull=L'emplacement de stockage ne peut pas \u00EAtre vide
smfcore.pos.split.notMergePos=L'emplacement de stockage [{0}] n'a pas \u00E9t\u00E9 fusionn\u00E9 et ne peut pas \u00EAtre divis\u00E9
smfcore.pos.split.hasMaterial=L'emplacement de stockage [{0}] contient des mati\u00E8res et ne peut pas \u00EAtre divis\u00E9
......@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u8981\u6C42\u5358 {0} \u306F\u65E2\u306B\u5B58\u5728\u3
smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u306E\u30D0\u30FC\u30B3\u30FC\u30C9\u306F {1} \u3067\u3001\u79FB\u5EAB\u30D0\u30FC\u30B3\u30FC\u30C9 {1} \u3068\u4E0D\u4E00\u81F4\u3067\u3059
smfcore.barcode.notFindFeerder=[{0}]\u30B9\u30C6\u30FC\u30B7\u30E7\u30F3\u60C5\u5831\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
smfcore.storagePos.provider=\u4F9B\u7D66\u8005
smfcore.pos.merge.notNull=\u68DA\u4F4D\u7F6E\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.numError=\u7D71\u5408\u3059\u308B\u68DA\u4F4D\u7F6E\u3092[{0}]\u3064\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044
smfcore.pos.merge.hasMaterial=\u68DA\u4F4D\u7F6E[{0}]\u306B\u8CC7\u6750\u304C\u3042\u308B\u305F\u3081\u3001\u7D71\u5408\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.hasMergePos=\u68DA\u4F4D\u7F6E[{0}]\u306F\u65E2\u306B\u7D71\u5408\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u3001\u3055\u3089\u306B\u7D71\u5408\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.notAdjacent=\u96A3\u63A5\u3059\u308B\u68DA\u4F4D\u7F6E\u3092\u9078\u629E\u3057\u3066\u7D71\u5408\u3057\u3066\u304F\u3060\u3055\u3044
smfcore.pos.split.notNull=\u68DA\u4F4D\u7F6E\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093
smfcore.pos.split.notMergePos=\u68DA\u4F4D\u7F6E[{0}]\u306F\u7D71\u5408\u3055\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u5206\u5272\u3067\u304D\u307E\u305B\u3093
smfcore.pos.split.hasMaterial=\u68DA\u4F4D\u7F6E[{0}]\u306B\u8CC7\u6750\u304C\u3042\u308B\u305F\u3081\u3001\u5206\u5272\u3067\u304D\u307E\u305B\u3093
......@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F
smfcore.storagePos.provider=\u4F9B\u5E94\u5546
smfcore.pos.merge.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.merge.numError=\u8BF7\u9009\u62E9[{0}]\u4E2A\u8981\u5408\u5E76\u7684\u5E93\u4F4D
smfcore.pos.merge.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u5E76
smfcore.pos.merge.hasMergePos=\u5E93\u4F4D[{0}]\u5DF2\u5408\u5E76,\u4E0D\u80FD\u7EE7\u7EED\u5408\u5E76
smfcore.pos.merge.notAdjacent=\u8BF7\u9009\u62E9\u76F8\u90BB\u7684\u5E93\u4F4D\u5408\u5E76
smfcore.pos.split.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.split.notMergePos=\u5E93\u4F4D[{0}]\u672A\u5408\u5E76,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
......@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u55AE {0} \u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u689D\u78BC\u70BA {1}\uFF0C\u8207\u79FB\u5EAB\u689D\u78BC {1} \u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u8CC7\u8A0A
smfcore.storagePos.provider=\u4F9B\u7D66\u8005
smfcore.pos.merge.notNull=\u5EAB\u4F4D\u4E0D\u80FD\u70BA\u7A7A
smfcore.pos.merge.numError=\u8ACB\u9078\u64C7[{0}]\u500B\u8981\u5408\u4F75\u7684\u5EAB\u4F4D
smfcore.pos.merge.hasMaterial=\u5EAB\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u4F75
smfcore.pos.merge.hasMergePos=\u5EAB\u4F4D[{0}]\u5DF2\u5408\u4F75,\u4E0D\u80FD\u7E7C\u7E8C\u5408\u4F75
smfcore.pos.merge.notAdjacent=\u8ACB\u9078\u64C7\u76F8\u9130\u7684\u5EAB\u4F4D\u5408\u4F75
smfcore.pos.split.notNull=\u5EAB\u4F4D\u4E0D\u80FD\u70BA\u7A7A
smfcore.pos.split.notMergePos=\u5EAB\u4F4D[{0}]\u672A\u5408\u4F75,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5EAB\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!