供应商管理数据同步, tms系统产品,服务渠道,产品渠道关系数据同步
This commit is contained in:
+11
-3
@@ -4,14 +4,22 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 老系统 Oracle TAB_SUPPLIER 只读查询(SLAVE 数据源,账号只读)
|
||||
* 老系统 Oracle 供应商相关表只读查询(SLAVE 数据源,账号只读)
|
||||
* 数据源(2026-10-10 修正,原 TAB_SUPPLIER 取数有误):
|
||||
* TAB_SERVICE_INFO 服务商(即供应商主数据)
|
||||
* TAB_SERVICE_CHANNEL 服务渠道(一个服务商下多个渠道,按 SERVICE_CODE 关联)
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
public interface OracleSupplierSyncMapper
|
||||
{
|
||||
/**
|
||||
* 全量读取 TAB_SUPPLIER 同步所需列(数据量约数百行,单次读取)
|
||||
* 全量读取服务商(供应商)主数据 TAB_SERVICE_INFO
|
||||
*/
|
||||
List<Map<String, Object>> selectAllTabSupplier();
|
||||
List<Map<String, Object>> selectAllServiceInfo();
|
||||
|
||||
/**
|
||||
* 全量读取服务渠道 TAB_SERVICE_CHANNEL(仅需服务商编码+渠道编码,用于聚合 channel_ids)
|
||||
*/
|
||||
List<Map<String, Object>> selectAllServiceChannel();
|
||||
}
|
||||
|
||||
+19
-3
@@ -10,7 +10,7 @@ import com.bms.common.enums.DataSourceType;
|
||||
import com.bms.customer.supplierinfo.mapper.OracleSupplierSyncMapper;
|
||||
|
||||
/**
|
||||
* Oracle TAB_SUPPLIER 读取器。
|
||||
* Oracle 服务商/服务渠道读取器。
|
||||
* 走 SLAVE(Oracle 只读账号)数据源;必须独立 Bean,
|
||||
* 否则 @DataSource 切面在同类自调用时不生效。
|
||||
*
|
||||
@@ -22,14 +22,30 @@ public class OracleSupplierReader
|
||||
@Autowired
|
||||
private OracleSupplierSyncMapper oracleSupplierSyncMapper;
|
||||
|
||||
/** 读取服务商(供应商)主数据 TAB_SERVICE_INFO */
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> readAll()
|
||||
public List<Map<String, Object>> readServiceInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 避免 PageHelper 把 Oracle 查询包成分页语句
|
||||
PageHelper.clearPage();
|
||||
return oracleSupplierSyncMapper.selectAllTabSupplier();
|
||||
return oracleSupplierSyncMapper.selectAllServiceInfo();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
|
||||
/** 读取服务渠道 TAB_SERVICE_CHANNEL */
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> readServiceChannels()
|
||||
{
|
||||
try
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
return oracleSupplierSyncMapper.selectAllServiceChannel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
+89
-93
@@ -1,11 +1,10 @@
|
||||
package com.bms.customer.supplierinfo.sync;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -13,16 +12,18 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.customer.supplierinfo.domain.BmsSupplier;
|
||||
import com.bms.customer.supplierinfo.mapper.SupplierSyncMapper;
|
||||
|
||||
/**
|
||||
* 老系统 Oracle TAB_SUPPLIER -> sup_supplier 每日同步。
|
||||
* 老系统 Oracle 服务商数据 -> sup_supplier 每日同步。
|
||||
*
|
||||
* 范围:只同步 sup_supplier 现有列能承接的字段;三个类型字段口径完全以 TAB_SUPPLIER 为准
|
||||
* (供应商类型 SUP_TYPE、开票类型 INVOICE_TYPE、卡车行类型 KACHE_TYPE)。
|
||||
* 按 supplier_code 判定新增/更新;源为空的非类型字段不写(新增走默认值/更新保留原值)。
|
||||
* 数据源(2026-10-10 修正,原 TAB_SUPPLIER 取数有误):
|
||||
* TAB_SERVICE_INFO 服务商(即供应商)主数据
|
||||
* TAB_SERVICE_CHANNEL 服务渠道,按 SERVICE_CODE 归属服务商,
|
||||
* 其全部渠道编码(含停用)聚合写入 channel_ids
|
||||
*
|
||||
* 按 supplier_code(SERVICE_CODE) 判定新增/更新;源为空的字段不写(新增走默认值/更新保留原值)。
|
||||
*
|
||||
* @author bms
|
||||
*/
|
||||
@@ -34,15 +35,18 @@ public class SupplierSyncService
|
||||
/** 写库人标记,便于事后甄别同步数据 */
|
||||
public static final String SYNC_MARKER = "oracle-sync";
|
||||
|
||||
/** 三个类型字段的合法字典值(以老系统 TAB_SUPPLIER 为准) */
|
||||
private static final Set<String> SUP_TYPE_CODES = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5", "6"));
|
||||
private static final Set<String> INVOICE_TYPE_CODES = new HashSet<>(Arrays.asList("1", "2", "3", "4"));
|
||||
private static final Set<String> KACHE_TYPE_CODES = new HashSet<>(Arrays.asList("0", "1"));
|
||||
/** 源中文服务商类型 -> sup_supplier.supplier_type 字典码(4清关行 5尾派服务商) */
|
||||
private static final String TYPE_QINGGUAN = "清关";
|
||||
private static final String TYPE_WEICHENG = "尾程渠道";
|
||||
private static final String CODE_QINGGUAN_HANG = "4";
|
||||
private static final String CODE_WEIPAI = "5";
|
||||
|
||||
/** sup_supplier 字符串列长度 */
|
||||
private static final int LEN_CODE = 50;
|
||||
private static final int LEN_NAME = 100;
|
||||
private static final int LEN_REMARK = 500;
|
||||
/** channel_ids varchar(1000) */
|
||||
private static final int LEN_CHANNEL_IDS = 1000;
|
||||
/** 告警明细最多打印条数 */
|
||||
private static final int WARN_LOG_LIMIT = 50;
|
||||
|
||||
@@ -63,7 +67,21 @@ public class SupplierSyncService
|
||||
public String syncFromOracle()
|
||||
{
|
||||
long t0 = System.currentTimeMillis();
|
||||
List<Map<String, Object>> rows = oracleSupplierReader.readAll();
|
||||
List<Map<String, Object>> rows = oracleSupplierReader.readServiceInfo();
|
||||
List<Map<String, Object>> channelRows = oracleSupplierReader.readServiceChannels();
|
||||
|
||||
// 按服务商编码聚合渠道编码(去重、保序)
|
||||
Map<String, LinkedHashSet<String>> channelMap = new LinkedHashMap<>();
|
||||
for (Map<String, Object> ch : channelRows)
|
||||
{
|
||||
String serviceCode = clean(getStr(ch, "SERVICE_CODE"));
|
||||
String channelCode = clean(getStr(ch, "CHANNEL_CODE"));
|
||||
if (serviceCode == null || channelCode == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
channelMap.computeIfAbsent(serviceCode, k -> new LinkedHashSet<>()).add(channelCode);
|
||||
}
|
||||
|
||||
Set<String> existingCodes = new HashSet<>(supplierSyncMapper.selectExistingCodes());
|
||||
|
||||
@@ -78,8 +96,8 @@ public class SupplierSyncService
|
||||
{
|
||||
int rowNo = i + 1;
|
||||
Map<String, Object> row = rows.get(i);
|
||||
String code = clean(getStr(row, "SUP_CODE"));
|
||||
String name = clean(getStr(row, "SUP_NAME"));
|
||||
String code = clean(getStr(row, "SERVICE_CODE"));
|
||||
String name = clean(getStr(row, "SERVICE_NAME"));
|
||||
if (code == null && name == null)
|
||||
{
|
||||
blankRows++;
|
||||
@@ -87,12 +105,12 @@ public class SupplierSyncService
|
||||
}
|
||||
if (code == null)
|
||||
{
|
||||
errors.add("行" + rowNo + ": SUP_CODE 为空,跳过");
|
||||
errors.add("行" + rowNo + ": SERVICE_CODE 为空,跳过");
|
||||
continue;
|
||||
}
|
||||
if (name == null)
|
||||
{
|
||||
errors.add("行" + rowNo + " " + code + ": SUP_NAME 为空,跳过");
|
||||
errors.add("行" + rowNo + " " + code + ": SERVICE_NAME 为空,跳过");
|
||||
continue;
|
||||
}
|
||||
if (!seenCodes.add(code))
|
||||
@@ -101,7 +119,8 @@ public class SupplierSyncService
|
||||
continue;
|
||||
}
|
||||
|
||||
BmsSupplier s = mapRow(row, code, name, warnings);
|
||||
LinkedHashSet<String> channels = channelMap.getOrDefault(code, new LinkedHashSet<>());
|
||||
BmsSupplier s = mapRow(row, code, name, channels, warnings);
|
||||
if (existingCodes.contains(code))
|
||||
{
|
||||
s.setUpdateBy(SYNC_MARKER);
|
||||
@@ -119,8 +138,9 @@ public class SupplierSyncService
|
||||
long costMs = System.currentTimeMillis() - t0;
|
||||
|
||||
String summary = String.format(
|
||||
"Oracle供应商同步完成:源行 %d,空行 %d,新增 %d,更新 %d,跳过错误 %d,告警 %d,耗时 %dms",
|
||||
rows.size(), blankRows, written[0], written[1], errors.size(), warnings.size(), costMs);
|
||||
"Oracle服务商同步完成:服务商 %d 家(渠道 %d 个),空行 %d,新增 %d,更新 %d,跳过错误 %d,告警 %d,耗时 %dms",
|
||||
rows.size(), channelRows.size(), blankRows, written[0], written[1],
|
||||
errors.size(), warnings.size(), costMs);
|
||||
log.info(summary);
|
||||
errors.forEach(e -> log.warn(" 同步跳过: {}", e));
|
||||
warnings.stream().limit(WARN_LOG_LIMIT).forEach(w -> log.warn(" 同步告警: {}", w));
|
||||
@@ -130,7 +150,7 @@ public class SupplierSyncService
|
||||
}
|
||||
if (!errors.isEmpty())
|
||||
{
|
||||
log.warn("同步存在 {} 条跳过记录,请核对 TAB_SUPPLIER 源数据", errors.size());
|
||||
log.warn("同步存在 {} 条跳过记录,请核对 TAB_SERVICE_INFO 源数据", errors.size());
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
@@ -138,30 +158,48 @@ public class SupplierSyncService
|
||||
/**
|
||||
* 单行映射
|
||||
*/
|
||||
private BmsSupplier mapRow(Map<String, Object> row, String code, String name, List<String> warnings)
|
||||
private BmsSupplier mapRow(Map<String, Object> row, String code, String name,
|
||||
LinkedHashSet<String> channels, List<String> warnings)
|
||||
{
|
||||
BmsSupplier s = new BmsSupplier();
|
||||
s.setSupplierCode(truncate(code, LEN_CODE, code, "supplier_code", warnings));
|
||||
s.setSupplierName(truncate(name, LEN_NAME, code, "supplier_name", warnings));
|
||||
s.setDelFlag("0");
|
||||
|
||||
s.setSupplierType(toCode(row, "SUP_TYPE", SUP_TYPE_CODES, code, "供应商类型", warnings));
|
||||
s.setInvoiceType(toCode(row, "INVOICE_TYPE", INVOICE_TYPE_CODES, code, "开票类型", warnings));
|
||||
s.setKacheType(toCode(row, "KACHE_TYPE", KACHE_TYPE_CODES, code, "卡车行类型", warnings));
|
||||
|
||||
s.setSupplierType(mapServiceType(getStr(row, "SERVICE_TYPE"), code, warnings));
|
||||
s.setIsEnable(mapEnable(getStr(row, "BL_OPEN")));
|
||||
|
||||
s.setEd(toDecimal(row, "QUOTA", code, warnings));
|
||||
Long period = toLong(row, "SETTLEMENT_PERIOD", code, warnings);
|
||||
s.setZq(period);
|
||||
|
||||
s.setRemark(truncate(getStr(row, "REMARK"), LEN_REMARK, code, "remark", warnings));
|
||||
|
||||
Date createTime = getDate(row, "CREATE_TIME");
|
||||
Date createTime = getDate(row, "CREATE_DATE");
|
||||
s.setCreateTime(createTime);
|
||||
|
||||
s.setChannelIds(joinChannelCodes(channels, code, warnings));
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 源 SERVICE_TYPE 为中文:尾程渠道 -> 5(尾派服务商),清关 -> 4(清关行);
|
||||
* 其他/空值置空并告警,由业务人员手工选择。
|
||||
*/
|
||||
private static String mapServiceType(String raw, String supplierCode, List<String> warnings)
|
||||
{
|
||||
String v = clean(raw);
|
||||
if (v == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (TYPE_WEICHENG.equals(v))
|
||||
{
|
||||
return CODE_WEIPAI;
|
||||
}
|
||||
if (TYPE_QINGGUAN.equals(v))
|
||||
{
|
||||
return CODE_QINGGUAN_HANG;
|
||||
}
|
||||
warnings.add(supplierCode + ": 未知服务商类型 " + v + ",类型置空");
|
||||
return null;
|
||||
}
|
||||
|
||||
/** BL_OPEN:1启用 -> is_enable=0(正常);0停用 -> 1;空按正常 */
|
||||
private static String mapEnable(String v)
|
||||
{
|
||||
@@ -174,37 +212,34 @@ public class SupplierSyncService
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle NUMBER(1) 经 JDBC 多为 BigDecimal(String.valueOf 可能带 ".0"),
|
||||
* 统一按整数取值后校验字典合法性;源为 null 返回 null(更新时以源为准置空)。
|
||||
* 渠道编码英文逗号拼接;channel_ids 长度上限 1000,
|
||||
* 超长时按完整编码边界截断并告警(保证不产生半个编码)。
|
||||
*/
|
||||
private static String toCode(Map<String, Object> row, String key, Set<String> validCodes,
|
||||
String supplierCode, String label, List<String> warnings)
|
||||
private static String joinChannelCodes(LinkedHashSet<String> channels, String supplierCode, List<String> warnings)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null || StringUtils.isEmpty(String.valueOf(v).trim()))
|
||||
if (channels == null || channels.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String code;
|
||||
if (v instanceof Number)
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int included = 0;
|
||||
for (String ch : channels)
|
||||
{
|
||||
code = String.valueOf(((Number) v).longValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
code = String.valueOf(v).trim();
|
||||
int dot = code.indexOf('.');
|
||||
if (dot >= 0)
|
||||
int add = ch.length() + (sb.length() > 0 ? 1 : 0);
|
||||
if (sb.length() + add > LEN_CHANNEL_IDS)
|
||||
{
|
||||
code = code.substring(0, dot);
|
||||
warnings.add(String.format("%s: 渠道数量过多,channel_ids 超 %d 字符,仅保留前 %d 个渠道(共 %d 个)",
|
||||
supplierCode, LEN_CHANNEL_IDS, included, channels.size()));
|
||||
break;
|
||||
}
|
||||
if (sb.length() > 0)
|
||||
{
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(ch);
|
||||
included++;
|
||||
}
|
||||
if (!validCodes.contains(code))
|
||||
{
|
||||
warnings.add(supplierCode + ": 未知" + label + " " + v + ",该类型置空");
|
||||
return null;
|
||||
}
|
||||
return code;
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String clean(String v)
|
||||
@@ -231,45 +266,6 @@ public class SupplierSyncService
|
||||
return s;
|
||||
}
|
||||
|
||||
private static BigDecimal toDecimal(Map<String, Object> row, String key, String code, List<String> warnings)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null || StringUtils.isEmpty(String.valueOf(v).trim()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
BigDecimal d = (v instanceof BigDecimal) ? (BigDecimal) v
|
||||
: (v instanceof Number ? BigDecimal.valueOf(((Number) v).doubleValue())
|
||||
: new BigDecimal(String.valueOf(v).trim()));
|
||||
return d.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warnings.add(code + ": 字段 " + key + " 非法数值 " + v + ",该字段不写入");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Long toLong(Map<String, Object> row, String key, String code, List<String> warnings)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null || StringUtils.isEmpty(String.valueOf(v).trim()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return (v instanceof Number) ? ((Number) v).longValue() : Long.parseLong(String.valueOf(v).trim());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warnings.add(code + ": 字段 " + key + " 非法整数 " + v + ",该字段不写入");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Date getDate(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ public class SupplierSyncTask
|
||||
private SupplierSyncService supplierSyncService;
|
||||
|
||||
/**
|
||||
* 每天 00:00 从 Oracle TAB_SUPPLIER 全量同步供应商主数据到 sup_supplier
|
||||
* 每天 00:00 从 Oracle TAB_SERVICE_INFO/TAB_SERVICE_CHANNEL 全量同步服务商(供应商)数据到 sup_supplier
|
||||
*/
|
||||
public void syncFromOracle()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user