订单比对加上状态
This commit is contained in:
@@ -3,10 +3,12 @@ package com.bms.price.domain.vo;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 订单比对异步任务进度(Redis)。
|
||||
* ok=本地已有;fail/缺失=上游有而本地无(仅转单号)。
|
||||
* missing = 上游有转单号、本地无;
|
||||
* statusMismatch = 本地有转单号但 order_status 与上游不一致。
|
||||
*/
|
||||
public class TabOrderCompareTask implements Serializable
|
||||
{
|
||||
@@ -27,9 +29,16 @@ public class TabOrderCompareTask implements Serializable
|
||||
private int matched;
|
||||
/** 本地缺失 */
|
||||
private int missing;
|
||||
/** 本地有但订单状态不一致 */
|
||||
private int statusMismatch;
|
||||
private String error;
|
||||
/** 缺失转单号样本(有上限) */
|
||||
private List<String> missingTransferNos = new ArrayList<String>();
|
||||
/**
|
||||
* 状态不一致样本(有上限)。
|
||||
* 每项:transferNo / upstreamStatus / localStatus
|
||||
*/
|
||||
private List<Map<String, Object>> statusMismatchSamples = new ArrayList<Map<String, Object>>();
|
||||
|
||||
public String getTaskId() { return taskId; }
|
||||
public void setTaskId(String taskId) { this.taskId = taskId; }
|
||||
@@ -45,6 +54,8 @@ public class TabOrderCompareTask implements Serializable
|
||||
public void setMatched(int matched) { this.matched = matched; }
|
||||
public int getMissing() { return missing; }
|
||||
public void setMissing(int missing) { this.missing = missing; }
|
||||
public int getStatusMismatch() { return statusMismatch; }
|
||||
public void setStatusMismatch(int statusMismatch) { this.statusMismatch = statusMismatch; }
|
||||
public String getError() { return error; }
|
||||
public void setError(String error) { this.error = error; }
|
||||
public List<String> getMissingTransferNos() { return missingTransferNos; }
|
||||
@@ -53,4 +64,10 @@ public class TabOrderCompareTask implements Serializable
|
||||
this.missingTransferNos = missingTransferNos == null
|
||||
? new ArrayList<String>() : missingTransferNos;
|
||||
}
|
||||
public List<Map<String, Object>> getStatusMismatchSamples() { return statusMismatchSamples; }
|
||||
public void setStatusMismatchSamples(List<Map<String, Object>> statusMismatchSamples)
|
||||
{
|
||||
this.statusMismatchSamples = statusMismatchSamples == null
|
||||
? new ArrayList<Map<String, Object>>() : statusMismatchSamples;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public interface OracleUpstreamOrderMapper
|
||||
@Param("endRow") long endRow);
|
||||
|
||||
/**
|
||||
* 订单比对专用(异步分页):仅返回 TRANSFER_ORDER_ID。
|
||||
* 订单比对专用(异步分页):返回 TRANSFER_ORDER_ID + ORDER_STATUS。
|
||||
*/
|
||||
List<Map<String, Object>> selectTabOrderCompareKeyPage(
|
||||
@Param("orderTable") String orderTable,
|
||||
|
||||
@@ -43,6 +43,11 @@ public interface PrcOrderBillMapper
|
||||
List<String> selectExistingTransferNos(@Param("transferNos") List<String> transferNos,
|
||||
@Param("customerCodes") List<String> customerCodes);
|
||||
|
||||
/** 订单比对:按转单号批量查本地 transfer_no + order_status。 */
|
||||
List<java.util.Map<String, Object>> selectTransferOrderStatuses(
|
||||
@Param("transferNos") List<String> transferNos,
|
||||
@Param("customerCodes") List<String> customerCodes);
|
||||
|
||||
/** 订单号下拉搜索(带数据权限) */
|
||||
public List<String> selectOrderNoOptions(@Param("query") PrcOrderBill query, @Param("limit") int limit);
|
||||
|
||||
|
||||
@@ -295,10 +295,38 @@ public class TabOrderMonitorService
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单比对异步分页:专用 SQL 仅返回当前页转单号。
|
||||
* 订单比对异步分页:返回当前页转单号(兼容旧调用)。
|
||||
*/
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<String> selectCompareTransferPage(TabOrderMonitorQuery query, int pageNum, int pageSize)
|
||||
{
|
||||
List<Map<String, Object>> rows = selectCompareKeyPage(query, pageNum, pageSize);
|
||||
if (rows == null || rows.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> out = new ArrayList<String>(rows.size());
|
||||
for (Map<String, Object> row : rows)
|
||||
{
|
||||
Object v = row == null ? null : row.get("transferNo");
|
||||
if (v == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String tn = String.valueOf(v).trim();
|
||||
if (!tn.isEmpty())
|
||||
{
|
||||
out.add(tn);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单比对异步分页:返回 transferNo + orderStatus(上游 ORDER_STATUS)。
|
||||
*/
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> selectCompareKeyPage(TabOrderMonitorQuery query, int pageNum, int pageSize)
|
||||
{
|
||||
// 比对与回填列表完全隔离:先清单号再 validate,仅走客户+提货时间
|
||||
if (query != null)
|
||||
@@ -334,7 +362,7 @@ public class TabOrderMonitorService
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> out = new ArrayList<String>(rows.size());
|
||||
List<Map<String, Object>> out = new ArrayList<Map<String, Object>>(rows.size());
|
||||
for (Map<String, Object> row : rows)
|
||||
{
|
||||
Object v = value(row, "TRANSFER_ORDER_ID");
|
||||
@@ -343,10 +371,14 @@ public class TabOrderMonitorService
|
||||
continue;
|
||||
}
|
||||
String tn = String.valueOf(v).trim();
|
||||
if (!tn.isEmpty())
|
||||
if (tn.isEmpty())
|
||||
{
|
||||
out.add(tn);
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> one = new LinkedHashMap<String, Object>();
|
||||
one.put("transferNo", tn);
|
||||
one.put("orderStatus", integer(row, "ORDER_STATUS"));
|
||||
out.add(one);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
+15
-2
@@ -497,6 +497,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
productCode, productName, cc, cn, zone.zoneCode, weight, bizDate);
|
||||
if (hit != null)
|
||||
{
|
||||
// Billing requires product catalog price (产品价); package hit alone is not enough.
|
||||
if (request instanceof TmsPriceBillRequest && !hasProductCatalogPrice(catalog))
|
||||
{
|
||||
missCode = Math.max(missCode,
|
||||
diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate));
|
||||
continue;
|
||||
}
|
||||
stampRequestMeta(request, hit);
|
||||
return hit;
|
||||
}
|
||||
@@ -504,7 +511,7 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
}
|
||||
else if (TYPE_DISCOUNT.equals(setType))
|
||||
{
|
||||
if (catalog == null)
|
||||
if (!hasProductCatalogPrice(catalog))
|
||||
{
|
||||
missCode = Math.max(missCode, diagnoseCatalogMiss(productCode, cc, cn, zone.zoneCode, bizDate));
|
||||
continue;
|
||||
@@ -747,7 +754,13 @@ public class TmsPriceQuoteServiceImpl implements ITmsPriceQuoteService
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Catalog match for product_amount / DISCOUNT base; null is allowed for FIXED sales. */
|
||||
/** True when product catalog row has a usable freight price (产品价). */
|
||||
private static boolean hasProductCatalogPrice(BmsProductCountry catalog)
|
||||
{
|
||||
return catalog != null && catalog.getFreightPrice() != null;
|
||||
}
|
||||
|
||||
/** Catalog match for product_amount / DISCOUNT base; quote FIXED may still hit without catalog. */
|
||||
private BmsProductCountry softMatchCatalog(String productCode, String countryCode, String countryName,
|
||||
String zoneCode, BigDecimal weight, Date bizDate, PrcZone resolvedZone)
|
||||
{
|
||||
|
||||
+145
-33
@@ -1,12 +1,11 @@
|
||||
package com.bms.price.task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -23,7 +22,7 @@ import com.bms.price.search.IBillSearchStore;
|
||||
import com.bms.price.search.TabOrderSyncEsLog;
|
||||
|
||||
/**
|
||||
* 订单比对异步任务:分页拉上游转单号,与本地 transfer_no 比对差异。
|
||||
* 订单比对异步任务:分页拉上游转单号+状态,与本地 transfer_no / order_status 比对。
|
||||
*/
|
||||
@Component
|
||||
public class TabOrderCompareAsyncRunner
|
||||
@@ -37,6 +36,8 @@ public class TabOrderCompareAsyncRunner
|
||||
public static final int SCAN_HARD_LIMIT = 50000;
|
||||
/** 进度/抽屉中保留的缺失转单号上限 */
|
||||
public static final int MISSING_SAMPLE_LIMIT = 5000;
|
||||
/** 状态不一致样本上限 */
|
||||
public static final int STATUS_MISMATCH_SAMPLE_LIMIT = 5000;
|
||||
|
||||
@Autowired
|
||||
private TabOrderMonitorService tabOrderMonitorService;
|
||||
@@ -62,11 +63,14 @@ public class TabOrderCompareAsyncRunner
|
||||
query.setOrgOrderId(null);
|
||||
query.setTransferOrderId(null);
|
||||
}
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, 0, "starting", 0, 0, 0, null, null);
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, 0, "starting",
|
||||
0, 0, 0, 0, null, null, null);
|
||||
int scanned = 0;
|
||||
int matched = 0;
|
||||
int missing = 0;
|
||||
int statusMismatch = 0;
|
||||
List<String> missingSample = new ArrayList<String>();
|
||||
List<Map<String, Object>> statusMismatchSample = new ArrayList<Map<String, Object>>();
|
||||
int pageNum = 1;
|
||||
while (true)
|
||||
{
|
||||
@@ -77,43 +81,72 @@ public class TabOrderCompareAsyncRunner
|
||||
}
|
||||
int remain = SCAN_HARD_LIMIT - scanned;
|
||||
int pageSize = Math.min(PAGE_SIZE, remain);
|
||||
List<String> transferNos = tabOrderMonitorService.selectCompareTransferPage(query, pageNum, pageSize);
|
||||
if (transferNos == null || transferNos.isEmpty())
|
||||
List<Map<String, Object>> pageRows =
|
||||
tabOrderMonitorService.selectCompareKeyPage(query, pageNum, pageSize);
|
||||
if (pageRows == null || pageRows.isEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
Set<String> existing = findExisting(transferNos, query == null ? null : query.getCustomerCodes());
|
||||
for (String tn : transferNos)
|
||||
List<String> transferNos = new ArrayList<String>(pageRows.size());
|
||||
for (Map<String, Object> row : pageRows)
|
||||
{
|
||||
if (StringUtils.isEmpty(tn))
|
||||
Object tn = row == null ? null : row.get("transferNo");
|
||||
if (tn != null && StringUtils.isNotEmpty(String.valueOf(tn).trim()))
|
||||
{
|
||||
transferNos.add(String.valueOf(tn).trim());
|
||||
}
|
||||
}
|
||||
Map<String, Integer> localStatusByTn =
|
||||
findLocalStatuses(transferNos, query == null ? null : query.getCustomerCodes());
|
||||
for (Map<String, Object> row : pageRows)
|
||||
{
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
scanned++;
|
||||
String key = tn.trim().toUpperCase(Locale.ROOT);
|
||||
if (existing.contains(key))
|
||||
Object tnObj = row.get("transferNo");
|
||||
if (tnObj == null || StringUtils.isEmpty(String.valueOf(tnObj).trim()))
|
||||
{
|
||||
matched++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
String tn = String.valueOf(tnObj).trim();
|
||||
scanned++;
|
||||
String key = tn.toUpperCase(Locale.ROOT);
|
||||
Integer upstreamStatus = toInteger(row.get("orderStatus"));
|
||||
if (!localStatusByTn.containsKey(key))
|
||||
{
|
||||
missing++;
|
||||
if (missingSample.size() < MISSING_SAMPLE_LIMIT)
|
||||
{
|
||||
missingSample.add(tn.trim());
|
||||
missingSample.add(tn);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
matched++;
|
||||
Integer localStatus = localStatusByTn.get(key);
|
||||
if (!statusEquals(upstreamStatus, localStatus))
|
||||
{
|
||||
statusMismatch++;
|
||||
if (statusMismatchSample.size() < STATUS_MISMATCH_SAMPLE_LIMIT)
|
||||
{
|
||||
Map<String, Object> one = new LinkedHashMap<String, Object>();
|
||||
one.put("transferNo", tn);
|
||||
one.put("upstreamStatus", upstreamStatus);
|
||||
one.put("localStatus", localStatus);
|
||||
statusMismatchSample.add(one);
|
||||
}
|
||||
}
|
||||
}
|
||||
int percent = Math.min(99, (int) ((scanned * 100L) / Math.max(scanned + pageSize, 1)));
|
||||
// 最后一页不满页时近乎完成;否则按已扫描量给一个缓慢增长的百分比
|
||||
if (transferNos.size() < pageSize)
|
||||
if (pageRows.size() < pageSize)
|
||||
{
|
||||
percent = 99;
|
||||
}
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_RUNNING, percent,
|
||||
"scanning page=" + pageNum + " scanned=" + scanned + " missing=" + missing,
|
||||
scanned, matched, missing, null, missingSample);
|
||||
if (transferNos.size() < pageSize)
|
||||
"scanning page=" + pageNum + " scanned=" + scanned
|
||||
+ " missing=" + missing + " statusMismatch=" + statusMismatch,
|
||||
scanned, matched, missing, statusMismatch, null, missingSample, statusMismatchSample);
|
||||
if (pageRows.size() < pageSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -124,14 +157,19 @@ public class TabOrderCompareAsyncRunner
|
||||
resp.put("scanned", Integer.valueOf(scanned));
|
||||
resp.put("matched", Integer.valueOf(matched));
|
||||
resp.put("missing", Integer.valueOf(missing));
|
||||
resp.put("statusMismatch", Integer.valueOf(statusMismatch));
|
||||
resp.put("missingTransferNos", missingSample);
|
||||
resp.put("statusMismatchSamples", statusMismatchSample);
|
||||
resp.put("missingTruncated", Boolean.valueOf(missing > missingSample.size()));
|
||||
resp.put("statusMismatchTruncated",
|
||||
Boolean.valueOf(statusMismatch > statusMismatchSample.size()));
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_SUCCESS, 100,
|
||||
"done scanned=" + scanned + " missing=" + missing,
|
||||
scanned, matched, missing, null, missingSample);
|
||||
"done scanned=" + scanned + " missing=" + missing + " statusMismatch=" + statusMismatch,
|
||||
scanned, matched, missing, statusMismatch, null, missingSample, statusMismatchSample);
|
||||
TabOrderSyncEsLog.save(billSearchStore, asyncTaskId, "COMPARE_DONE", query, resp, "SUCCESS", null);
|
||||
log.info("orderSource compare done asyncTaskId={} scanned={} matched={} missing={}",
|
||||
asyncTaskId, Integer.valueOf(scanned), Integer.valueOf(matched), Integer.valueOf(missing));
|
||||
log.info("orderSource compare done asyncTaskId={} scanned={} matched={} missing={} statusMismatch={}",
|
||||
asyncTaskId, Integer.valueOf(scanned), Integer.valueOf(matched),
|
||||
Integer.valueOf(missing), Integer.valueOf(statusMismatch));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -141,33 +179,101 @@ public class TabOrderCompareAsyncRunner
|
||||
{
|
||||
msg = e.getClass().getSimpleName();
|
||||
}
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_FAIL, 100, "failed", 0, 0, 0, msg, null);
|
||||
put(asyncTaskId, TabOrderCompareTask.STATUS_FAIL, 100, "failed",
|
||||
0, 0, 0, 0, msg, null, null);
|
||||
TabOrderSyncEsLog.save(billSearchStore, asyncTaskId, "COMPARE_DONE", query, null, "FAIL", msg);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> findExisting(List<String> transferNos, List<String> customerCodes)
|
||||
/** transferNo(upper) -> local order_status (null value means local row exists with null status). */
|
||||
private Map<String, Integer> findLocalStatuses(List<String> transferNos, List<String> customerCodes)
|
||||
{
|
||||
Set<String> found = new HashSet<String>();
|
||||
Map<String, Integer> found = new HashMap<String, Integer>();
|
||||
if (transferNos == null || transferNos.isEmpty())
|
||||
{
|
||||
return found;
|
||||
}
|
||||
List<String> hit = prcOrderBillMapper.selectExistingTransferNos(transferNos, cleanCodes(customerCodes));
|
||||
if (hit == null)
|
||||
List<Map<String, Object>> rows =
|
||||
prcOrderBillMapper.selectTransferOrderStatuses(transferNos, cleanCodes(customerCodes));
|
||||
if (rows == null)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
for (String s : hit)
|
||||
for (Map<String, Object> row : rows)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(s))
|
||||
if (row == null)
|
||||
{
|
||||
found.add(s.trim().toUpperCase(Locale.ROOT));
|
||||
continue;
|
||||
}
|
||||
Object tn = first(row, "transferNo", "TRANSFERNO", "transfer_no");
|
||||
if (tn == null || StringUtils.isEmpty(String.valueOf(tn).trim()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String key = String.valueOf(tn).trim().toUpperCase(Locale.ROOT);
|
||||
Integer st = toInteger(first(row, "orderStatus", "ORDERSTATUS", "order_status"));
|
||||
// Keep first hit; duplicate transfer_no rare.
|
||||
if (!found.containsKey(key))
|
||||
{
|
||||
found.put(key, st);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private static Object first(Map<String, Object> row, String... keys)
|
||||
{
|
||||
if (row == null || keys == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (String k : keys)
|
||||
{
|
||||
if (row.containsKey(k) && row.get(k) != null)
|
||||
{
|
||||
return row.get(k);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Integer toInteger(Object v)
|
||||
{
|
||||
if (v == null || "".equals(String.valueOf(v).trim()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (v instanceof Integer)
|
||||
{
|
||||
return (Integer) v;
|
||||
}
|
||||
if (v instanceof Number)
|
||||
{
|
||||
return Integer.valueOf(((Number) v).intValue());
|
||||
}
|
||||
try
|
||||
{
|
||||
return Integer.valueOf(String.valueOf(v).trim());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean statusEquals(Integer a, Integer b)
|
||||
{
|
||||
if (a == null && b == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (a == null || b == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return a.intValue() == b.intValue();
|
||||
}
|
||||
|
||||
private static List<String> cleanCodes(List<String> codes)
|
||||
{
|
||||
if (codes == null || codes.isEmpty())
|
||||
@@ -186,7 +292,8 @@ public class TabOrderCompareAsyncRunner
|
||||
}
|
||||
|
||||
public void put(String taskId, String status, int percent, String message,
|
||||
int scanned, int matched, int missing, String error, List<String> missingSample)
|
||||
int scanned, int matched, int missing, int statusMismatch, String error,
|
||||
List<String> missingSample, List<Map<String, Object>> statusMismatchSample)
|
||||
{
|
||||
TabOrderCompareTask task = new TabOrderCompareTask();
|
||||
task.setTaskId(taskId);
|
||||
@@ -196,11 +303,16 @@ public class TabOrderCompareAsyncRunner
|
||||
task.setScanned(scanned);
|
||||
task.setMatched(matched);
|
||||
task.setMissing(missing);
|
||||
task.setStatusMismatch(statusMismatch);
|
||||
task.setError(error);
|
||||
if (missingSample != null)
|
||||
{
|
||||
task.setMissingTransferNos(new ArrayList<String>(missingSample));
|
||||
}
|
||||
if (statusMismatchSample != null)
|
||||
{
|
||||
task.setStatusMismatchSamples(new ArrayList<Map<String, Object>>(statusMismatchSample));
|
||||
}
|
||||
redisCache.setCacheObject(CACHE_KEY_PREFIX + taskId, task, TTL_MINUTES, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user