供应商管理数据同步, tms系统产品,服务渠道,产品渠道关系数据同步
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
package com.bms.web.controller.masterdata;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.service.IServiceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道(老系统镜像 srv_service_channel)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/masterdata/servicechannel")
|
||||
public class ServiceChannelController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IServiceChannelService serviceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道下拉选项(非分页)。
|
||||
* 供供应商绑定渠道等场景选择渠道;不做权限控制(仍需登录)。
|
||||
*/
|
||||
@GetMapping("/optionselect")
|
||||
public AjaxResult optionselect(ServiceChannel query)
|
||||
{
|
||||
List<ServiceChannel> list = serviceChannelService.selectServiceChannelList(query);
|
||||
return success(list);
|
||||
}
|
||||
}
|
||||
+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
|
||||
{
|
||||
|
||||
+88
-92
@@ -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
|
||||
int add = ch.length() + (sb.length() > 0 ? 1 : 0);
|
||||
if (sb.length() + add > LEN_CHANNEL_IDS)
|
||||
{
|
||||
code = String.valueOf(v).trim();
|
||||
int dot = code.indexOf('.');
|
||||
if (dot >= 0)
|
||||
warnings.add(String.format("%s: 渠道数量过多,channel_ids 超 %d 字符,仅保留前 %d 个渠道(共 %d 个)",
|
||||
supplierCode, LEN_CHANNEL_IDS, included, channels.size()));
|
||||
break;
|
||||
}
|
||||
if (sb.length() > 0)
|
||||
{
|
||||
code = code.substring(0, dot);
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.bms.masterdata.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 线路产品镜像对象 srv_line_product(Oracle WEISHI_USER.TAB_LINE_PRODUCT)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Data
|
||||
public class LineProduct
|
||||
{
|
||||
/** 源主键 ID */
|
||||
private String id;
|
||||
|
||||
/** 产品(线路)编码 */
|
||||
private String lineCode;
|
||||
|
||||
/** 产品(线路)名称 */
|
||||
private String lineName;
|
||||
|
||||
private String productType;
|
||||
private String lineType;
|
||||
|
||||
/** 是否可收货(1是 0否) */
|
||||
private String blCargo;
|
||||
|
||||
private String cargoName;
|
||||
|
||||
private String productStorage;
|
||||
private String productFirst;
|
||||
private String productClearance;
|
||||
private String productStorageCode;
|
||||
private String productFirstCode;
|
||||
private String productClearanceCode;
|
||||
|
||||
/** 是否启用(1启用 0停用) */
|
||||
private String blOpen;
|
||||
|
||||
/** 是否分段(1是 0否) */
|
||||
private String blSection;
|
||||
|
||||
private String dispCode;
|
||||
private String dispName;
|
||||
private String feeClass;
|
||||
private String shipperId;
|
||||
|
||||
/** 发货规则 */
|
||||
private String shipmentRule;
|
||||
|
||||
/** 限重(kg) */
|
||||
private BigDecimal weight;
|
||||
|
||||
private String cargoType;
|
||||
private String productCategory;
|
||||
|
||||
/** 体积基数(抛货系数) */
|
||||
private BigDecimal volumeBasis;
|
||||
|
||||
private String weightType;
|
||||
private String serviceType;
|
||||
private String shipmentType;
|
||||
|
||||
private BigDecimal taxAmount;
|
||||
private String taxCurrency;
|
||||
private String batteryType;
|
||||
private String remark;
|
||||
|
||||
private String createName;
|
||||
private String createManCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createDate;
|
||||
private String modifier;
|
||||
private String modifierCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date modifyDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtCreate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtModified;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.bms.masterdata.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 产品-渠道关系镜像对象 srv_product_channel(Oracle WEISHI_USER.TAB_PRODUCT_CHANNEL)
|
||||
* 注意:源端 (productId, channelCode) 不唯一(同产品同渠道按国家/重量区间多行),主键为源 GUID。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Data
|
||||
public class ProductChannel
|
||||
{
|
||||
/** 源主键 GUID */
|
||||
private String guid;
|
||||
|
||||
/** 产品ID(srv_line_product.id) */
|
||||
private String productId;
|
||||
|
||||
/** 渠道编码(srv_service_channel.channel_code) */
|
||||
private String channelCode;
|
||||
|
||||
private String channelName;
|
||||
|
||||
/** 优先级(数字越小越优先) */
|
||||
private Integer priority;
|
||||
|
||||
/** 是否启用(1启用 0停用) */
|
||||
private String isEnabled;
|
||||
|
||||
private String lineCode;
|
||||
private String lineName;
|
||||
private String countryCode;
|
||||
|
||||
private BigDecimal startWeight;
|
||||
private BigDecimal endWeight;
|
||||
|
||||
/** 下单量 */
|
||||
private Long orderCount;
|
||||
|
||||
/** 下单量占比 */
|
||||
private BigDecimal orderCountRatio;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.bms.masterdata.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像对象 srv_service_channel(Oracle WEISHI_USER.TAB_SERVICE_CHANNEL)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Data
|
||||
public class ServiceChannel
|
||||
{
|
||||
/** 源主键 ID */
|
||||
private String id;
|
||||
|
||||
/** 渠道编码 */
|
||||
private String channelCode;
|
||||
|
||||
/** 渠道名称 */
|
||||
private String channelName;
|
||||
|
||||
/** 所属服务商编码 */
|
||||
private String serviceCode;
|
||||
|
||||
/** 所属服务商名称 */
|
||||
private String serviceName;
|
||||
|
||||
/** 是否启用(1启用 0停用) */
|
||||
private String blOpen;
|
||||
|
||||
/** 是否支持多票(1是 0否) */
|
||||
private String blMultiple;
|
||||
|
||||
private String cargoType;
|
||||
private String payType;
|
||||
private String shipperName;
|
||||
private String shipperId;
|
||||
private String queryType;
|
||||
private String trajectoryCode;
|
||||
private String finalServiceName;
|
||||
private String desPort;
|
||||
|
||||
/** 重量类型 */
|
||||
private String weightType;
|
||||
|
||||
/** 体积除数(抛货系数) */
|
||||
private Long dimDivisor;
|
||||
|
||||
private String weightRule;
|
||||
private String shipmentType;
|
||||
private String dataFrom;
|
||||
private String remarks;
|
||||
|
||||
private String createName;
|
||||
private String createManCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createDate;
|
||||
private String modifier;
|
||||
private String modifierCode;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date modifyDate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtCreate;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date gmtModified;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.bms.masterdata.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
import com.bms.masterdata.domain.ProductChannel;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* 渠道/产品 镜像表写库(MASTER 数据源,按源主键 upsert)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
public interface ChannelMasterSyncMapper
|
||||
{
|
||||
/** 按源 ID 批量 upsert 服务渠道 */
|
||||
int upsertServiceChannel(@Param("list") List<ServiceChannel> list);
|
||||
|
||||
/** 按源 ID 批量 upsert 线路产品 */
|
||||
int upsertLineProduct(@Param("list") List<LineProduct> list);
|
||||
|
||||
/** 按源 GUID 批量 upsert 产品-渠道关系 */
|
||||
int upsertProductChannel(@Param("list") List<ProductChannel> list);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.bms.masterdata.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Oracle 渠道/产品 主数据只读查询(SLAVE 数据源,账号只读)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
public interface OracleChannelMasterMapper
|
||||
{
|
||||
/** 全量读取服务渠道 TAB_SERVICE_CHANNEL */
|
||||
List<Map<String, Object>> selectAllServiceChannel();
|
||||
|
||||
/** 全量读取线路产品 TAB_LINE_PRODUCT */
|
||||
List<Map<String, Object>> selectAllLineProduct();
|
||||
|
||||
/** 全量读取产品-渠道关系 TAB_PRODUCT_CHANNEL */
|
||||
List<Map<String, Object>> selectAllProductChannel();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bms.masterdata.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像表查询(MASTER 数据源)
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
public interface ServiceChannelMapper
|
||||
{
|
||||
/**
|
||||
* 查询服务渠道列表(支持渠道编码/名称/服务商编码/启用状态过滤,非分页)
|
||||
*/
|
||||
List<ServiceChannel> selectServiceChannelList(ServiceChannel query);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.bms.masterdata.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像 服务层
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
public interface IServiceChannelService
|
||||
{
|
||||
/**
|
||||
* 查询服务渠道列表(非分页,供供应商绑定渠道等下拉选择场景)
|
||||
*/
|
||||
List<ServiceChannel> selectServiceChannelList(ServiceChannel query);
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.bms.masterdata.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.mapper.ServiceChannelMapper;
|
||||
import com.bms.masterdata.service.IServiceChannelService;
|
||||
|
||||
/**
|
||||
* 服务渠道镜像 服务实现
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Service
|
||||
public class ServiceChannelServiceImpl implements IServiceChannelService
|
||||
{
|
||||
@Autowired
|
||||
private ServiceChannelMapper serviceChannelMapper;
|
||||
|
||||
@Override
|
||||
public List<ServiceChannel> selectServiceChannelList(ServiceChannel query)
|
||||
{
|
||||
return serviceChannelMapper.selectServiceChannelList(query);
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
package com.bms.masterdata.sync;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
import com.bms.masterdata.domain.ProductChannel;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
|
||||
/**
|
||||
* Oracle 渠道/产品 主数据同步编排:
|
||||
* 顺序 渠道 → 产品 → 产品渠道关系,按源主键幂等 upsert,可重复执行。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Service
|
||||
public class ChannelMasterSyncService
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(ChannelMasterSyncService.class);
|
||||
|
||||
@Autowired
|
||||
private OracleChannelMasterReader reader;
|
||||
|
||||
@Autowired
|
||||
private ChannelMasterSyncWriter writer;
|
||||
|
||||
/**
|
||||
* 全量同步三表,返回简要对账信息
|
||||
*/
|
||||
public String syncFromOracle()
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
List<Map<String, Object>> channelRows = reader.readServiceChannels();
|
||||
List<ServiceChannel> channels = new ArrayList<>(channelRows.size());
|
||||
int skipChannel = 0;
|
||||
for (Map<String, Object> row : channelRows)
|
||||
{
|
||||
String id = str(row, "ID");
|
||||
if (id == null) { skipChannel++; continue; }
|
||||
ServiceChannel c = new ServiceChannel();
|
||||
c.setId(id);
|
||||
c.setChannelCode(str(row, "CHANNEL_CODE"));
|
||||
c.setChannelName(str(row, "CHANNEL_NAME"));
|
||||
c.setServiceCode(str(row, "SERVICE_CODE"));
|
||||
c.setServiceName(str(row, "SERVICE_NAME"));
|
||||
c.setBlOpen(flag(row, "BL_OPEN"));
|
||||
c.setBlMultiple(flag(row, "BL_MULTIPLE"));
|
||||
c.setCargoType(str(row, "CARGO_TYPE"));
|
||||
c.setPayType(str(row, "PAY_TYPE"));
|
||||
c.setShipperName(str(row, "SHIPPER_NAME"));
|
||||
c.setShipperId(str(row, "SHIPPER_ID"));
|
||||
c.setQueryType(str(row, "QUERY_TYPE"));
|
||||
c.setTrajectoryCode(str(row, "TRAJECTORY_CODE"));
|
||||
c.setFinalServiceName(str(row, "FINAL_SERVICE_NAME"));
|
||||
c.setDesPort(str(row, "DES_PORT"));
|
||||
c.setWeightType(flag(row, "WEIGHT_TYPE"));
|
||||
c.setDimDivisor(lo(row, "DIM_DIVISOR"));
|
||||
c.setWeightRule(str(row, "WEIGHT_RULE"));
|
||||
c.setShipmentType(str(row, "SHIPMENT_TYPE"));
|
||||
c.setDataFrom(str(row, "DATA_FROM"));
|
||||
c.setRemarks(str(row, "REMARKS"));
|
||||
c.setCreateName(str(row, "CREATE_NAME"));
|
||||
c.setCreateManCode(str(row, "CREATE_MAN_CODE"));
|
||||
c.setCreateDate(date(row, "CREATE_DATE"));
|
||||
c.setModifier(str(row, "MODIFIER"));
|
||||
c.setModifierCode(str(row, "MODIFIER_CODE"));
|
||||
c.setModifyDate(date(row, "MODIFY_DATE"));
|
||||
c.setGmtCreate(date(row, "GMT_CREATE"));
|
||||
c.setGmtModified(date(row, "GMT_MODIFIED"));
|
||||
channels.add(c);
|
||||
}
|
||||
int channelAffected = writer.writeServiceChannels(channels);
|
||||
|
||||
List<Map<String, Object>> productRows = reader.readLineProducts();
|
||||
List<LineProduct> products = new ArrayList<>(productRows.size());
|
||||
int skipProduct = 0;
|
||||
for (Map<String, Object> row : productRows)
|
||||
{
|
||||
String id = str(row, "ID");
|
||||
if (id == null) { skipProduct++; continue; }
|
||||
LineProduct p = new LineProduct();
|
||||
p.setId(id);
|
||||
p.setLineCode(str(row, "LINE_CODE"));
|
||||
p.setLineName(str(row, "LINE_NAME"));
|
||||
p.setProductType(str(row, "PRODUCT_TYPE"));
|
||||
p.setLineType(str(row, "LINE_TYPE"));
|
||||
p.setBlCargo(flag(row, "BL_CARGO"));
|
||||
p.setCargoName(str(row, "CARGO_NAME"));
|
||||
p.setProductStorage(str(row, "PRODUCT_STORAGE"));
|
||||
p.setProductFirst(str(row, "PRODUCT_FIRST"));
|
||||
p.setProductClearance(str(row, "PRODUCT_CLEARANCE"));
|
||||
p.setProductStorageCode(str(row, "PRODUCT_STORAGE_CODE"));
|
||||
p.setProductFirstCode(str(row, "PRODUCT_FIRST_CODE"));
|
||||
p.setProductClearanceCode(str(row, "PRODUCT_CLEARANCE_CODE"));
|
||||
p.setBlOpen(flag(row, "BL_OPEN"));
|
||||
p.setBlSection(flag(row, "BL_SECTION"));
|
||||
p.setDispCode(str(row, "DISP_CODE"));
|
||||
p.setDispName(str(row, "DISP_NAME"));
|
||||
p.setFeeClass(str(row, "FEE_CLASS"));
|
||||
p.setShipperId(str(row, "SHIPPER_ID"));
|
||||
p.setShipmentRule(flag(row, "SHIPMENT_RULE"));
|
||||
p.setWeight(decimal(row, "WEIGHT"));
|
||||
p.setCargoType(str(row, "CARGO_TYPE"));
|
||||
p.setProductCategory(str(row, "PRODUCT_CATEGORY"));
|
||||
p.setVolumeBasis(decimal(row, "VOLUME_BASIS"));
|
||||
p.setWeightType(flag(row, "WEIGHT_TYPE"));
|
||||
p.setServiceType(flag(row, "SERVICE_TYPE"));
|
||||
p.setShipmentType(str(row, "SHIPMENT_TYPE"));
|
||||
p.setTaxAmount(decimal(row, "TAX_AMOUNT"));
|
||||
p.setTaxCurrency(str(row, "TAX_CURRENCY"));
|
||||
p.setBatteryType(str(row, "BATTERY_TYPE"));
|
||||
p.setRemark(str(row, "REMARK"));
|
||||
p.setCreateName(str(row, "CREATE_NAME"));
|
||||
p.setCreateManCode(str(row, "CREATE_MAN_CODE"));
|
||||
p.setCreateDate(date(row, "CREATE_DATE"));
|
||||
p.setModifier(str(row, "MODIFIER"));
|
||||
p.setModifierCode(str(row, "MODIFIER_CODE"));
|
||||
p.setModifyDate(date(row, "MODIFY_DATE"));
|
||||
p.setGmtCreate(date(row, "GMT_CREATE"));
|
||||
p.setGmtModified(date(row, "GMT_MODIFIED"));
|
||||
products.add(p);
|
||||
}
|
||||
int productAffected = writer.writeLineProducts(products);
|
||||
|
||||
List<Map<String, Object>> relRows = reader.readProductChannels();
|
||||
List<ProductChannel> relations = new ArrayList<>(relRows.size());
|
||||
int skipRel = 0;
|
||||
for (Map<String, Object> row : relRows)
|
||||
{
|
||||
String guid = str(row, "GUID");
|
||||
if (guid == null) { skipRel++; continue; }
|
||||
ProductChannel pc = new ProductChannel();
|
||||
pc.setGuid(guid);
|
||||
pc.setProductId(str(row, "PRODUCT_ID"));
|
||||
pc.setChannelCode(str(row, "CHANNEL_CODE"));
|
||||
pc.setChannelName(str(row, "CHANNEL_NAME"));
|
||||
pc.setPriority(integer(row, "PRIORITY"));
|
||||
pc.setIsEnabled(flag(row, "IS_ENABLED"));
|
||||
pc.setLineName(str(row, "LINE_NAME"));
|
||||
pc.setLineCode(str(row, "LINE_CODE"));
|
||||
pc.setCountryCode(str(row, "COUNTRY_CODE"));
|
||||
pc.setStartWeight(decimal(row, "START_WEIGHT"));
|
||||
pc.setEndWeight(decimal(row, "END_WEIGHT"));
|
||||
pc.setOrderCount(lo(row, "ORDER_COUNT"));
|
||||
pc.setOrderCountRatio(decimal(row, "ORDER_COUNT_RATIO"));
|
||||
relations.add(pc);
|
||||
}
|
||||
int relAffected = writer.writeProductChannels(relations);
|
||||
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
String summary = String.format(
|
||||
"渠道产品同步完成,耗时%dms。服务渠道:源%d行(跳过%d),写入影响%d行;"
|
||||
+ "线路产品:源%d行(跳过%d),写入影响%d行;产品渠道关系:源%d行(跳过%d),写入影响%d行",
|
||||
cost, channels.size(), skipChannel, channelAffected,
|
||||
products.size(), skipProduct, productAffected,
|
||||
relations.size(), skipRel, relAffected);
|
||||
log.info(summary);
|
||||
return summary;
|
||||
}
|
||||
|
||||
// ==================== Oracle Map 取值工具(列名忽略大小写) ====================
|
||||
|
||||
private static Object getIgnoreCase(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = row.get(key);
|
||||
if (v != null)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
for (Map.Entry<String, Object> e : row.entrySet())
|
||||
{
|
||||
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key))
|
||||
{
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String str(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String s = v.toString().trim();
|
||||
return s.isEmpty() ? null : s;
|
||||
}
|
||||
|
||||
/** NUMBER(1) 等 0/1 标志位原样存为 "1"/"0" */
|
||||
private static String flag(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (v instanceof BigDecimal)
|
||||
{
|
||||
return ((BigDecimal) v).signum() == 0 ? "0" : "1";
|
||||
}
|
||||
if (v instanceof Number)
|
||||
{
|
||||
return ((Number) v).intValue() == 0 ? "0" : "1";
|
||||
}
|
||||
String s = v.toString().trim();
|
||||
return s.isEmpty() ? null : s;
|
||||
}
|
||||
|
||||
private static BigDecimal decimal(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (v instanceof BigDecimal)
|
||||
{
|
||||
return (BigDecimal) v;
|
||||
}
|
||||
if (v instanceof Number)
|
||||
{
|
||||
return new BigDecimal(v.toString());
|
||||
}
|
||||
String s = v.toString().trim();
|
||||
return s.isEmpty() ? null : new BigDecimal(s);
|
||||
}
|
||||
|
||||
private static Integer integer(Map<String, Object> row, String key)
|
||||
{
|
||||
BigDecimal d = decimal(row, key);
|
||||
return d == null ? null : d.intValue();
|
||||
}
|
||||
|
||||
private static Long lo(Map<String, Object> row, String key)
|
||||
{
|
||||
BigDecimal d = decimal(row, key);
|
||||
return d == null ? null : d.longValue();
|
||||
}
|
||||
|
||||
private static Date date(Map<String, Object> row, String key)
|
||||
{
|
||||
Object v = getIgnoreCase(row, key);
|
||||
if (v == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (v instanceof Date)
|
||||
{
|
||||
return (Date) v;
|
||||
}
|
||||
String s = v.toString().trim();
|
||||
return s.isEmpty() ? null : new Date(s);
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.bms.masterdata.sync;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.bms.masterdata.domain.LineProduct;
|
||||
import com.bms.masterdata.domain.ProductChannel;
|
||||
import com.bms.masterdata.domain.ServiceChannel;
|
||||
import com.bms.masterdata.mapper.ChannelMasterSyncMapper;
|
||||
|
||||
/**
|
||||
* 渠道/产品 镜像表写库器(MASTER 数据源,分批 upsert,单表一个事务)。
|
||||
* 独立 Bean:保证 @Transactional 经代理生效,且与 SLAVE 读连接隔离。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Service
|
||||
public class ChannelMasterSyncWriter
|
||||
{
|
||||
/** 单批行数,控制 SQL 包大小 */
|
||||
private static final int BATCH_SIZE = 200;
|
||||
|
||||
@Autowired
|
||||
private ChannelMasterSyncMapper mapper;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int writeServiceChannels(List<ServiceChannel> list)
|
||||
{
|
||||
int rows = 0;
|
||||
for (int from = 0; from < list.size(); from += BATCH_SIZE)
|
||||
{
|
||||
rows += mapper.upsertServiceChannel(list.subList(from, Math.min(from + BATCH_SIZE, list.size())));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int writeLineProducts(List<LineProduct> list)
|
||||
{
|
||||
int rows = 0;
|
||||
for (int from = 0; from < list.size(); from += BATCH_SIZE)
|
||||
{
|
||||
rows += mapper.upsertLineProduct(list.subList(from, Math.min(from + BATCH_SIZE, list.size())));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int writeProductChannels(List<ProductChannel> list)
|
||||
{
|
||||
int rows = 0;
|
||||
for (int from = 0; from < list.size(); from += BATCH_SIZE)
|
||||
{
|
||||
rows += mapper.upsertProductChannel(list.subList(from, Math.min(from + BATCH_SIZE, list.size())));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.bms.masterdata.sync;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.bms.common.annotation.DataSource;
|
||||
import com.bms.common.enums.DataSourceType;
|
||||
import com.bms.masterdata.mapper.OracleChannelMasterMapper;
|
||||
|
||||
/**
|
||||
* Oracle 渠道/产品 主数据读取器(SLAVE 只读账号数据源)。
|
||||
* 必须独立 Bean,否则 @DataSource 切面在同类自调用时不生效。
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Service
|
||||
public class OracleChannelMasterReader
|
||||
{
|
||||
@Autowired
|
||||
private OracleChannelMasterMapper oracleChannelMasterMapper;
|
||||
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> readServiceChannels()
|
||||
{
|
||||
try
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
return oracleChannelMasterMapper.selectAllServiceChannel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> readLineProducts()
|
||||
{
|
||||
try
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
return oracleChannelMasterMapper.selectAllLineProduct();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
|
||||
@DataSource(DataSourceType.SLAVE)
|
||||
public List<Map<String, Object>> readProductChannels()
|
||||
{
|
||||
try
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
return oracleChannelMasterMapper.selectAllProductChannel();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.bms.masterdata.task;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.masterdata.sync.ChannelMasterSyncService;
|
||||
|
||||
/**
|
||||
* 老系统渠道/产品 主数据每日同步任务。
|
||||
* sys_job.invoke_target = channelMasterSyncTask.syncFromOracle()
|
||||
*
|
||||
* @author bms
|
||||
* @date 2026-10-12
|
||||
*/
|
||||
@Component("channelMasterSyncTask")
|
||||
public class ChannelMasterSyncTask
|
||||
{
|
||||
@Autowired
|
||||
private ChannelMasterSyncService channelMasterSyncService;
|
||||
|
||||
/**
|
||||
* 全量同步 Oracle TAB_SERVICE_CHANNEL / TAB_LINE_PRODUCT / TAB_PRODUCT_CHANNEL
|
||||
*/
|
||||
public void syncFromOracle()
|
||||
{
|
||||
String summary = channelMasterSyncService.syncFromOracle();
|
||||
if (StringUtils.isEmpty(summary))
|
||||
{
|
||||
throw new RuntimeException("老系统渠道产品同步未返回结果");
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-8
@@ -4,13 +4,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.customer.supplierinfo.mapper.OracleSupplierSyncMapper">
|
||||
|
||||
<!-- 老系统供应商主表全量读取;只取 sup_supplier 能承接的列。
|
||||
三个类型字段口径以 TAB_SUPPLIER 为准:
|
||||
SUP_TYPE 1卡车行 2航司代理 3报关行 4清关行 5尾派服务商 6报关代理
|
||||
INVOICE_TYPE 1增普 2增专 3电普 4其他普票;KACHE_TYPE 0国内 1国外 -->
|
||||
<select id="selectAllTabSupplier" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT SUP_CODE, SUP_NAME, SUP_TYPE, INVOICE_TYPE, KACHE_TYPE,
|
||||
BL_OPEN, QUOTA, SETTLEMENT_PERIOD, REMARK, CREATE_TIME
|
||||
FROM WEISHI_USER.TAB_SUPPLIER
|
||||
<!-- 服务商(供应商)主数据:TAB_SERVICE_INFO(2026-10-10 起替代取数错误的 TAB_SUPPLIER)
|
||||
SERVICE_TYPE 为中文口径:尾程渠道 -> 供应商类型 5(尾派服务商),清关 -> 4(清关行);
|
||||
BL_OPEN:1 启用,其余停用 -->
|
||||
<select id="selectAllServiceInfo" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT SERVICE_CODE, SERVICE_NAME, SERVICE_TYPE, BL_OPEN, REMARK, CREATE_DATE
|
||||
FROM WEISHI_USER.TAB_SERVICE_INFO
|
||||
</select>
|
||||
|
||||
<!-- 服务渠道:TAB_SERVICE_CHANNEL,按 SERVICE_CODE 归属于服务商;
|
||||
同步时将同一服务商下全部渠道编码(含停用)聚合写入 sup_supplier.channel_ids -->
|
||||
<select id="selectAllServiceChannel" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT SERVICE_CODE, CHANNEL_CODE
|
||||
FROM WEISHI_USER.TAB_SERVICE_CHANNEL
|
||||
ORDER BY SERVICE_CODE, CHANNEL_CODE
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -12,33 +12,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
INSERT INTO sup_supplier (
|
||||
supplier_code, supplier_name, is_enable, del_flag, create_by
|
||||
<if test="supplierType != null">, supplier_type</if>
|
||||
<if test="invoiceType != null">, invoice_type</if>
|
||||
<if test="kacheType != null">, kache_type</if>
|
||||
<if test="ed != null">, ed</if>
|
||||
<if test="zq != null">, zq</if>
|
||||
<if test="channelIds != null">, channel_ids</if>
|
||||
<if test="remark != null">, remark</if>
|
||||
<if test="createTime != null">, create_time</if>
|
||||
) VALUES (
|
||||
#{supplierCode}, #{supplierName}, #{isEnable}, '0', #{createBy}
|
||||
<if test="supplierType != null">, #{supplierType}</if>
|
||||
<if test="invoiceType != null">, #{invoiceType}</if>
|
||||
<if test="kacheType != null">, #{kacheType}</if>
|
||||
<if test="ed != null">, #{ed}</if>
|
||||
<if test="zq != null">, #{zq}</if>
|
||||
<if test="channelIds != null">, #{channelIds}</if>
|
||||
<if test="remark != null">, #{remark}</if>
|
||||
<if test="createTime != null">, #{createTime}</if>
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 同步源(TAB_SERVICE_INFO/CHANNEL)无开票类型、卡车行类型、额度、账期,
|
||||
更新时这些列一律不动,保留库内维护值;supplier_type/channel_ids 以源为准 -->
|
||||
<update id="updateSyncSupplierByCode" parameterType="com.bms.customer.supplierinfo.domain.BmsSupplier">
|
||||
UPDATE sup_supplier
|
||||
SET supplier_name = #{supplierName}
|
||||
, is_enable = #{isEnable}
|
||||
, supplier_type = #{supplierType}
|
||||
, invoice_type = #{invoiceType}
|
||||
, kache_type = #{kacheType}
|
||||
<if test="ed != null">, ed = #{ed}</if>
|
||||
<if test="zq != null">, zq = #{zq}</if>
|
||||
, channel_ids = #{channelIds}
|
||||
<if test="remark != null">, remark = #{remark}</if>
|
||||
, update_by = #{updateBy}
|
||||
, update_time = #{updateTime}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.masterdata.mapper.ChannelMasterSyncMapper">
|
||||
|
||||
<!-- ============ 服务渠道 ============ -->
|
||||
<insert id="upsertServiceChannel">
|
||||
INSERT INTO srv_service_channel (
|
||||
id, channel_code, channel_name, service_code, service_name,
|
||||
bl_open, bl_multiple, cargo_type, pay_type, shipper_name, shipper_id,
|
||||
query_type, trajectory_code, final_service_name, des_port,
|
||||
weight_type, dim_divisor, weight_rule, shipment_type, data_from, remarks,
|
||||
create_name, create_man_code, create_date, modifier, modifier_code, modify_date,
|
||||
gmt_create, gmt_modified, sync_time
|
||||
) VALUES
|
||||
<foreach collection="list" item="it" separator=",">
|
||||
(#{it.id}, #{it.channelCode}, #{it.channelName}, #{it.serviceCode}, #{it.serviceName},
|
||||
#{it.blOpen}, #{it.blMultiple}, #{it.cargoType}, #{it.payType}, #{it.shipperName}, #{it.shipperId},
|
||||
#{it.queryType}, #{it.trajectoryCode}, #{it.finalServiceName}, #{it.desPort},
|
||||
#{it.weightType}, #{it.dimDivisor}, #{it.weightRule}, #{it.shipmentType}, #{it.dataFrom}, #{it.remarks},
|
||||
#{it.createName}, #{it.createManCode}, #{it.createDate}, #{it.modifier}, #{it.modifierCode}, #{it.modifyDate},
|
||||
#{it.gmtCreate}, #{it.gmtModified}, NOW())
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
channel_code = VALUES(channel_code),
|
||||
channel_name = VALUES(channel_name),
|
||||
service_code = VALUES(service_code),
|
||||
service_name = VALUES(service_name),
|
||||
bl_open = VALUES(bl_open),
|
||||
bl_multiple = VALUES(bl_multiple),
|
||||
cargo_type = VALUES(cargo_type),
|
||||
pay_type = VALUES(pay_type),
|
||||
shipper_name = VALUES(shipper_name),
|
||||
shipper_id = VALUES(shipper_id),
|
||||
query_type = VALUES(query_type),
|
||||
trajectory_code = VALUES(trajectory_code),
|
||||
final_service_name = VALUES(final_service_name),
|
||||
des_port = VALUES(des_port),
|
||||
weight_type = VALUES(weight_type),
|
||||
dim_divisor = VALUES(dim_divisor),
|
||||
weight_rule = VALUES(weight_rule),
|
||||
shipment_type = VALUES(shipment_type),
|
||||
data_from = VALUES(data_from),
|
||||
remarks = VALUES(remarks),
|
||||
create_name = VALUES(create_name),
|
||||
create_man_code = VALUES(create_man_code),
|
||||
create_date = VALUES(create_date),
|
||||
modifier = VALUES(modifier),
|
||||
modifier_code = VALUES(modifier_code),
|
||||
modify_date = VALUES(modify_date),
|
||||
gmt_create = VALUES(gmt_create),
|
||||
gmt_modified = VALUES(gmt_modified),
|
||||
sync_time = NOW()
|
||||
</insert>
|
||||
|
||||
<!-- ============ 线路产品 ============ -->
|
||||
<insert id="upsertLineProduct">
|
||||
INSERT INTO srv_line_product (
|
||||
id, line_code, line_name, product_type, line_type, bl_cargo, cargo_name,
|
||||
product_storage, product_first, product_clearance,
|
||||
product_storage_code, product_first_code, product_clearance_code,
|
||||
bl_open, bl_section, disp_code, disp_name, fee_class,
|
||||
gmt_create, gmt_modified, shipper_id, shipment_rule, weight, cargo_type,
|
||||
product_category, volume_basis, weight_type, service_type, shipment_type,
|
||||
tax_amount, tax_currency, battery_type, remark,
|
||||
create_name, create_man_code, create_date, modifier, modifier_code, modify_date, sync_time
|
||||
) VALUES
|
||||
<foreach collection="list" item="it" separator=",">
|
||||
(#{it.id}, #{it.lineCode}, #{it.lineName}, #{it.productType}, #{it.lineType}, #{it.blCargo}, #{it.cargoName},
|
||||
#{it.productStorage}, #{it.productFirst}, #{it.productClearance},
|
||||
#{it.productStorageCode}, #{it.productFirstCode}, #{it.productClearanceCode},
|
||||
#{it.blOpen}, #{it.blSection}, #{it.dispCode}, #{it.dispName}, #{it.feeClass},
|
||||
#{it.gmtCreate}, #{it.gmtModified}, #{it.shipperId}, #{it.shipmentRule}, #{it.weight}, #{it.cargoType},
|
||||
#{it.productCategory}, #{it.volumeBasis}, #{it.weightType}, #{it.serviceType}, #{it.shipmentType},
|
||||
#{it.taxAmount}, #{it.taxCurrency}, #{it.batteryType}, #{it.remark},
|
||||
#{it.createName}, #{it.createManCode}, #{it.createDate}, #{it.modifier}, #{it.modifierCode}, #{it.modifyDate}, NOW())
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
line_code = VALUES(line_code),
|
||||
line_name = VALUES(line_name),
|
||||
product_type = VALUES(product_type),
|
||||
line_type = VALUES(line_type),
|
||||
bl_cargo = VALUES(bl_cargo),
|
||||
cargo_name = VALUES(cargo_name),
|
||||
product_storage = VALUES(product_storage),
|
||||
product_first = VALUES(product_first),
|
||||
product_clearance = VALUES(product_clearance),
|
||||
product_storage_code = VALUES(product_storage_code),
|
||||
product_first_code = VALUES(product_first_code),
|
||||
product_clearance_code = VALUES(product_clearance_code),
|
||||
bl_open = VALUES(bl_open),
|
||||
bl_section = VALUES(bl_section),
|
||||
disp_code = VALUES(disp_code),
|
||||
disp_name = VALUES(disp_name),
|
||||
fee_class = VALUES(fee_class),
|
||||
gmt_create = VALUES(gmt_create),
|
||||
gmt_modified = VALUES(gmt_modified),
|
||||
shipper_id = VALUES(shipper_id),
|
||||
shipment_rule = VALUES(shipment_rule),
|
||||
weight = VALUES(weight),
|
||||
cargo_type = VALUES(cargo_type),
|
||||
product_category = VALUES(product_category),
|
||||
volume_basis = VALUES(volume_basis),
|
||||
weight_type = VALUES(weight_type),
|
||||
service_type = VALUES(service_type),
|
||||
shipment_type = VALUES(shipment_type),
|
||||
tax_amount = VALUES(tax_amount),
|
||||
tax_currency = VALUES(tax_currency),
|
||||
battery_type = VALUES(battery_type),
|
||||
remark = VALUES(remark),
|
||||
create_name = VALUES(create_name),
|
||||
create_man_code = VALUES(create_man_code),
|
||||
create_date = VALUES(create_date),
|
||||
modifier = VALUES(modifier),
|
||||
modifier_code = VALUES(modifier_code),
|
||||
modify_date = VALUES(modify_date),
|
||||
sync_time = NOW()
|
||||
</insert>
|
||||
|
||||
<!-- ============ 产品-渠道关系 ============ -->
|
||||
<insert id="upsertProductChannel">
|
||||
INSERT INTO srv_product_channel (
|
||||
guid, product_id, channel_code, channel_name, priority, is_enabled,
|
||||
line_code, line_name, country_code, start_weight, end_weight,
|
||||
order_count, order_count_ratio, sync_time
|
||||
) VALUES
|
||||
<foreach collection="list" item="it" separator=",">
|
||||
(#{it.guid}, #{it.productId}, #{it.channelCode}, #{it.channelName}, #{it.priority}, #{it.isEnabled},
|
||||
#{it.lineCode}, #{it.lineName}, #{it.countryCode}, #{it.startWeight}, #{it.endWeight},
|
||||
#{it.orderCount}, #{it.orderCountRatio}, NOW())
|
||||
</foreach>
|
||||
ON DUPLICATE KEY UPDATE
|
||||
product_id = VALUES(product_id),
|
||||
channel_code = VALUES(channel_code),
|
||||
channel_name = VALUES(channel_name),
|
||||
priority = VALUES(priority),
|
||||
is_enabled = VALUES(is_enabled),
|
||||
line_code = VALUES(line_code),
|
||||
line_name = VALUES(line_name),
|
||||
country_code = VALUES(country_code),
|
||||
start_weight = VALUES(start_weight),
|
||||
end_weight = VALUES(end_weight),
|
||||
order_count = VALUES(order_count),
|
||||
order_count_ratio = VALUES(order_count_ratio),
|
||||
sync_time = NOW()
|
||||
</insert>
|
||||
</mapper>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.masterdata.mapper.OracleChannelMasterMapper">
|
||||
|
||||
<select id="selectAllServiceChannel" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT ID, CHANNEL_CODE, CHANNEL_NAME, SERVICE_CODE, SERVICE_NAME,
|
||||
CREATE_NAME, CREATE_MAN_CODE, CREATE_DATE, MODIFIER, MODIFIER_CODE, MODIFY_DATE,
|
||||
BL_OPEN, BL_MULTIPLE, GMT_CREATE, GMT_MODIFIED, DATA_FROM, REMARKS,
|
||||
CARGO_TYPE, PAY_TYPE, SHIPPER_NAME, SHIPPER_ID, QUERY_TYPE, TRAJECTORY_CODE,
|
||||
FINAL_SERVICE_NAME, DES_PORT, WEIGHT_TYPE, DIM_DIVISOR, WEIGHT_RULE, SHIPMENT_TYPE
|
||||
FROM WEISHI_USER.TAB_SERVICE_CHANNEL
|
||||
</select>
|
||||
|
||||
<select id="selectAllLineProduct" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT ID, LINE_CODE, LINE_NAME, PRODUCT_TYPE, LINE_TYPE, BL_CARGO, CARGO_NAME,
|
||||
PRODUCT_STORAGE, PRODUCT_FIRST, PRODUCT_CLEARANCE, REMARK,
|
||||
CREATE_NAME, CREATE_MAN_CODE, CREATE_DATE, MODIFIER, MODIFIER_CODE, MODIFY_DATE,
|
||||
BL_OPEN, PRODUCT_STORAGE_CODE, PRODUCT_FIRST_CODE, PRODUCT_CLEARANCE_CODE,
|
||||
BL_SECTION, DISP_CODE, DISP_NAME, FEE_CLASS, GMT_CREATE, GMT_MODIFIED,
|
||||
SHIPPER_ID, SHIPMENT_RULE, WEIGHT, CARGO_TYPE, PRODUCT_CATEGORY, VOLUME_BASIS,
|
||||
WEIGHT_TYPE, SERVICE_TYPE, SHIPMENT_TYPE, TAX_AMOUNT, TAX_CURRENCY, BATTERY_TYPE
|
||||
FROM WEISHI_USER.TAB_LINE_PRODUCT
|
||||
</select>
|
||||
|
||||
<select id="selectAllProductChannel" resultType="java.util.HashMap" fetchSize="500">
|
||||
SELECT GUID, PRODUCT_ID, CHANNEL_NAME, CHANNEL_CODE, PRIORITY, IS_ENABLED,
|
||||
LINE_NAME, LINE_CODE, COUNTRY_CODE, START_WEIGHT, END_WEIGHT,
|
||||
ORDER_COUNT, ORDER_COUNT_RATIO
|
||||
FROM WEISHI_USER.TAB_PRODUCT_CHANNEL
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.bms.masterdata.mapper.ServiceChannelMapper">
|
||||
|
||||
<resultMap id="ServiceChannelResult" type="com.bms.masterdata.domain.ServiceChannel">
|
||||
<result property="id" column="id"/>
|
||||
<result property="channelCode" column="channel_code"/>
|
||||
<result property="channelName" column="channel_name"/>
|
||||
<result property="serviceCode" column="service_code"/>
|
||||
<result property="serviceName" column="service_name"/>
|
||||
<result property="blOpen" column="bl_open"/>
|
||||
<result property="blMultiple" column="bl_multiple"/>
|
||||
<result property="cargoType" column="cargo_type"/>
|
||||
<result property="payType" column="pay_type"/>
|
||||
<result property="shipperName" column="shipper_name"/>
|
||||
<result property="shipperId" column="shipper_id"/>
|
||||
<result property="queryType" column="query_type"/>
|
||||
<result property="trajectoryCode" column="trajectory_code"/>
|
||||
<result property="finalServiceName" column="final_service_name"/>
|
||||
<result property="desPort" column="des_port"/>
|
||||
<result property="weightType" column="weight_type"/>
|
||||
<result property="dimDivisor" column="dim_divisor"/>
|
||||
<result property="weightRule" column="weight_rule"/>
|
||||
<result property="shipmentType" column="shipment_type"/>
|
||||
<result property="dataFrom" column="data_from"/>
|
||||
<result property="remarks" column="remarks"/>
|
||||
<result property="createName" column="create_name"/>
|
||||
<result property="createManCode" column="create_man_code"/>
|
||||
<result property="createDate" column="create_date"/>
|
||||
<result property="modifier" column="modifier"/>
|
||||
<result property="modifierCode" column="modifier_code"/>
|
||||
<result property="modifyDate" column="modify_date"/>
|
||||
<result property="gmtCreate" column="gmt_create"/>
|
||||
<result property="gmtModified" column="gmt_modified"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectChannelVo">
|
||||
select id, channel_code, channel_name, service_code, service_name, bl_open, bl_multiple,
|
||||
cargo_type, pay_type, shipper_name, shipper_id, query_type, trajectory_code,
|
||||
final_service_name, des_port, weight_type, dim_divisor, weight_rule, shipment_type,
|
||||
data_from, remarks, create_name, create_man_code, create_date, modifier,
|
||||
modifier_code, modify_date, gmt_create, gmt_modified, sync_time
|
||||
from srv_service_channel
|
||||
</sql>
|
||||
|
||||
<select id="selectServiceChannelList" parameterType="com.bms.masterdata.domain.ServiceChannel" resultMap="ServiceChannelResult">
|
||||
<include refid="selectChannelVo"/>
|
||||
<where>
|
||||
<if test="channelCode != null and channelCode != ''">
|
||||
AND channel_code like concat('%', #{channelCode}, '%')
|
||||
</if>
|
||||
<if test="channelName != null and channelName != ''">
|
||||
AND channel_name like concat('%', #{channelName}, '%')
|
||||
</if>
|
||||
<if test="serviceCode != null and serviceCode != ''">
|
||||
AND service_code = #{serviceCode}
|
||||
</if>
|
||||
<if test="serviceName != null and serviceName != ''">
|
||||
AND service_name like concat('%', #{serviceName}, '%')
|
||||
</if>
|
||||
<if test="blOpen != null and blOpen != ''">
|
||||
AND bl_open = #{blOpen}
|
||||
</if>
|
||||
</where>
|
||||
order by service_code, channel_code
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,125 @@
|
||||
-- ============================================================
|
||||
-- 老系统 Oracle 渠道/产品 主数据镜像表 + 每日定时同步
|
||||
-- 日期:2026-10-12
|
||||
-- 数据源(WEISHI_USER,只读账号):
|
||||
-- TAB_SERVICE_CHANNEL 服务渠道(ID/CHANNEL_CODE 均唯一,381 行)
|
||||
-- TAB_LINE_PRODUCT 线路产品(ID/LINE_CODE 均唯一,527 行)
|
||||
-- TAB_PRODUCT_CHANNEL 产品-渠道关系(GUID 唯一;同产品+渠道可多行,按国家/重量区分,1554 行)
|
||||
-- 同步策略:按源主键 upsert(ON DUPLICATE KEY UPDATE),可重复执行;不删除源端已消失数据
|
||||
-- ============================================================
|
||||
|
||||
-- 1. 服务渠道(TAB_SERVICE_CHANNEL 镜像)
|
||||
CREATE TABLE IF NOT EXISTS `srv_service_channel` (
|
||||
`id` varchar(50) NOT NULL COMMENT '老系统渠道ID(源主键 TAB_SERVICE_CHANNEL.ID)',
|
||||
`channel_code` varchar(64) NOT NULL COMMENT '渠道编码',
|
||||
`channel_name` varchar(200) NOT NULL COMMENT '渠道名称',
|
||||
`service_code` varchar(32) DEFAULT NULL COMMENT '所属服务商编码(TAB_SERVICE_INFO.SERVICE_CODE)',
|
||||
`service_name` varchar(200) DEFAULT NULL COMMENT '所属服务商名称',
|
||||
`bl_open` char(1) DEFAULT NULL COMMENT '是否启用(1启用 0停用)',
|
||||
`bl_multiple` char(1) DEFAULT NULL COMMENT '是否支持多票(1是 0否)',
|
||||
`cargo_type` varchar(255) DEFAULT NULL COMMENT '货物类型',
|
||||
`pay_type` varchar(255) DEFAULT NULL COMMENT '付款方式',
|
||||
`shipper_name` varchar(255) DEFAULT NULL COMMENT '发货人名称',
|
||||
`shipper_id` varchar(255) DEFAULT NULL COMMENT '发货人ID',
|
||||
`query_type` varchar(20) DEFAULT NULL COMMENT '轨迹查询类型',
|
||||
`trajectory_code` varchar(60) DEFAULT NULL COMMENT '轨迹编码',
|
||||
`final_service_name` varchar(255) DEFAULT NULL COMMENT '末端服务商名称',
|
||||
`des_port` varchar(500) DEFAULT NULL COMMENT '目的港/目的地区域',
|
||||
`weight_type` char(1) DEFAULT NULL COMMENT '重量类型',
|
||||
`dim_divisor` int(11) DEFAULT NULL COMMENT '体积除数(抛货系数)',
|
||||
`weight_rule` varchar(500) DEFAULT NULL COMMENT '计重规则',
|
||||
`shipment_type` varchar(32) DEFAULT NULL COMMENT '发货类型',
|
||||
`data_from` varchar(60) DEFAULT NULL COMMENT '数据来源',
|
||||
`remarks` text DEFAULT NULL COMMENT '备注',
|
||||
`create_name` varchar(60) DEFAULT NULL COMMENT '源-创建人姓名',
|
||||
`create_man_code` varchar(40) DEFAULT NULL COMMENT '源-创建人工号',
|
||||
`create_date` datetime DEFAULT NULL COMMENT '源-创建时间',
|
||||
`modifier` varchar(60) DEFAULT NULL COMMENT '源-修改人姓名',
|
||||
`modifier_code` varchar(40) DEFAULT NULL COMMENT '源-修改人工号',
|
||||
`modify_date` datetime DEFAULT NULL COMMENT '源-修改时间',
|
||||
`gmt_create` datetime DEFAULT NULL COMMENT '源-GMT创建时间',
|
||||
`gmt_modified` datetime DEFAULT NULL COMMENT '源-GMT修改时间',
|
||||
`sync_time` datetime DEFAULT NULL COMMENT '最近一次同步时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_channel_code` (`channel_code`),
|
||||
KEY `idx_service_code` (`service_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='服务渠道镜像表(Oracle TAB_SERVICE_CHANNEL)';
|
||||
|
||||
-- 2. 线路产品(TAB_LINE_PRODUCT 镜像)
|
||||
CREATE TABLE IF NOT EXISTS `srv_line_product` (
|
||||
`id` varchar(50) NOT NULL COMMENT '老系统产品ID(源主键 TAB_LINE_PRODUCT.ID)',
|
||||
`line_code` varchar(64) NOT NULL COMMENT '产品编码(线路编码)',
|
||||
`line_name` varchar(200) NOT NULL COMMENT '产品名称(线路名称)',
|
||||
`product_type` varchar(20) DEFAULT NULL COMMENT '产品类型',
|
||||
`line_type` varchar(50) DEFAULT NULL COMMENT '线路类型(如 国际小包)',
|
||||
`bl_cargo` char(1) DEFAULT NULL COMMENT '是否可收货(1是 0否)',
|
||||
`cargo_name` varchar(100) DEFAULT NULL COMMENT '货物名称',
|
||||
`product_storage` text DEFAULT NULL COMMENT '仓储服务名称',
|
||||
`product_first` text DEFAULT NULL COMMENT '头程服务名称',
|
||||
`product_clearance` text DEFAULT NULL COMMENT '清关服务名称',
|
||||
`product_storage_code` varchar(1000) DEFAULT NULL COMMENT '仓储服务编码串',
|
||||
`product_first_code` varchar(1000) DEFAULT NULL COMMENT '头程服务编码串',
|
||||
`product_clearance_code` varchar(1000) DEFAULT NULL COMMENT '清关服务编码串',
|
||||
`bl_open` char(1) DEFAULT NULL COMMENT '是否启用(1启用 0停用)',
|
||||
`bl_section` char(1) DEFAULT NULL COMMENT '是否分段(1是 0否)',
|
||||
`disp_code` varchar(60) DEFAULT NULL COMMENT '派送商编码',
|
||||
`disp_name` varchar(100) DEFAULT NULL COMMENT '派送商名称',
|
||||
`fee_class` varchar(100) DEFAULT NULL COMMENT '费用类别',
|
||||
`shipper_id` varchar(64) DEFAULT NULL COMMENT '发货人ID',
|
||||
`shipment_rule` char(1) DEFAULT NULL COMMENT '发货规则',
|
||||
`weight` decimal(12,2) DEFAULT NULL COMMENT '限重(kg)',
|
||||
`cargo_type` varchar(64) DEFAULT NULL COMMENT '货物类型编码',
|
||||
`product_category` varchar(100) DEFAULT NULL COMMENT '产品分类',
|
||||
`volume_basis` decimal(12,2) DEFAULT NULL COMMENT '体积基数(抛货系数)',
|
||||
`weight_type` char(1) DEFAULT NULL COMMENT '重量类型',
|
||||
`service_type` char(1) DEFAULT NULL COMMENT '服务类型',
|
||||
`shipment_type` varchar(32) DEFAULT NULL COMMENT '发货类型',
|
||||
`tax_amount` decimal(12,3) DEFAULT NULL COMMENT '税费金额',
|
||||
`tax_currency` varchar(16) DEFAULT NULL COMMENT '税费币种',
|
||||
`battery_type` varchar(64) DEFAULT NULL COMMENT '电池类型',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`create_name` varchar(60) DEFAULT NULL COMMENT '源-创建人姓名',
|
||||
`create_man_code` varchar(40) DEFAULT NULL COMMENT '源-创建人工号',
|
||||
`create_date` datetime DEFAULT NULL COMMENT '源-创建时间',
|
||||
`modifier` varchar(60) DEFAULT NULL COMMENT '源-修改人姓名',
|
||||
`modifier_code` varchar(40) DEFAULT NULL COMMENT '源-修改人工号',
|
||||
`modify_date` datetime DEFAULT NULL COMMENT '源-修改时间',
|
||||
`gmt_create` datetime DEFAULT NULL COMMENT '源-GMT创建时间',
|
||||
`gmt_modified` datetime DEFAULT NULL COMMENT '源-GMT修改时间',
|
||||
`sync_time` datetime DEFAULT NULL COMMENT '最近一次同步时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_line_code` (`line_code`),
|
||||
KEY `idx_line_type` (`line_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='线路产品镜像表(Oracle TAB_LINE_PRODUCT)';
|
||||
|
||||
-- 3. 产品-渠道关系(TAB_PRODUCT_CHANNEL 镜像)
|
||||
-- 注意:(product_id, channel_code) 在源端不唯一(同产品同渠道按国家/重量区间多行),主键用源 GUID
|
||||
CREATE TABLE IF NOT EXISTS `srv_product_channel` (
|
||||
`guid` varchar(64) NOT NULL COMMENT '老系统关系GUID(源主键 TAB_PRODUCT_CHANNEL.GUID)',
|
||||
`product_id` varchar(64) NOT NULL COMMENT '产品ID(srv_line_product.id)',
|
||||
`channel_code` varchar(64) NOT NULL COMMENT '渠道编码(srv_service_channel.channel_code)',
|
||||
`channel_name` varchar(200) NOT NULL COMMENT '渠道名称',
|
||||
`priority` int(11) NOT NULL COMMENT '优先级(数字越小越优先)',
|
||||
`is_enabled` char(1) NOT NULL COMMENT '是否启用(1启用 0停用)',
|
||||
`line_code` varchar(2000) DEFAULT NULL COMMENT '线路编码(源端可能为逗号串)',
|
||||
`line_name` varchar(2000) DEFAULT NULL COMMENT '线路名称(源端可能为逗号串)',
|
||||
`country_code` varchar(16) DEFAULT NULL COMMENT '国家编码',
|
||||
`start_weight` decimal(14,3) DEFAULT NULL COMMENT '起始重量(kg)',
|
||||
`end_weight` decimal(14,3) DEFAULT NULL COMMENT '截止重量(kg)',
|
||||
`order_count` int(11) DEFAULT NULL COMMENT '下单量',
|
||||
`order_count_ratio` decimal(12,2) DEFAULT NULL COMMENT '下单量占比',
|
||||
`sync_time` datetime DEFAULT NULL COMMENT '最近一次同步时间',
|
||||
PRIMARY KEY (`guid`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_channel_code` (`channel_code`),
|
||||
KEY `idx_enabled_priority` (`is_enabled`, `priority`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='产品-渠道关系镜像表(Oracle TAB_PRODUCT_CHANNEL)';
|
||||
|
||||
-- 4. 定时任务:每天 00:30 同步(供应商同步 00:00 之后),禁止并发,放弃错过执行
|
||||
INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression,
|
||||
misfire_policy, concurrent, status, create_by, create_time, remark)
|
||||
SELECT '老系统渠道产品资料每日同步', 'DEFAULT', 'channelMasterSyncTask.syncFromOracle()', '0 30 0 * * ?',
|
||||
'3', '1', '0', 'admin', NOW(),
|
||||
'每天00:30从Oracle(WEISHI_USER,只读账号)全量同步TAB_SERVICE_CHANNEL/TAB_LINE_PRODUCT/TAB_PRODUCT_CHANNEL到srv_service_channel/srv_line_product/srv_product_channel;按源主键upsert,可重复执行'
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_job WHERE invoke_target = 'channelMasterSyncTask.syncFromOracle()');
|
||||
@@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/** 服务渠道下拉选项(非分页,来源 srv_service_channel 老系统镜像) */
|
||||
export function listChannelOptions(query) {
|
||||
return request({
|
||||
url: '/masterdata/servicechannel/optionselect',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="channel-select">
|
||||
<el-button type="primary" plain icon="Connection" @click="openDialog">
|
||||
{{ selectedCodes.length ? '重新选择渠道' : '选择渠道' }}
|
||||
</el-button>
|
||||
<span class="cs-summary">
|
||||
已绑定 <b>{{ selectedCodes.length }}</b> 个渠道
|
||||
<el-button v-if="selectedCodes.length" link type="danger" @click="clearSelection">清空</el-button>
|
||||
</span>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="选择绑定渠道"
|
||||
width="960px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
@open="handleOpen"
|
||||
>
|
||||
<el-form :inline="true" class="cs-filter" @submit.prevent>
|
||||
<el-form-item label="渠道编码">
|
||||
<el-input v-model="queryParams.channelCode" placeholder="如 SK_DEPDHLL" clearable style="width: 160px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道名称">
|
||||
<el-input v-model="queryParams.channelName" placeholder="渠道名称" clearable style="width: 170px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="服务商">
|
||||
<el-input v-model="queryParams.serviceName" placeholder="服务商名称" clearable style="width: 130px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.blOpen" placeholder="全部" clearable style="width: 110px">
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="filteredList"
|
||||
row-key="channelCode"
|
||||
height="380"
|
||||
border
|
||||
@select="handleSelect"
|
||||
@select-all="handleSelectAll"
|
||||
>
|
||||
<el-table-column type="selection" width="48" align="center" />
|
||||
<el-table-column label="渠道编码" prop="channelCode" width="150" show-overflow-tooltip />
|
||||
<el-table-column label="渠道名称" prop="channelName" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column label="服务商" prop="serviceName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="付款方式" prop="payType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="货物类型" prop="cargoType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.blOpen === '1'" type="success" size="small">启用</el-tag>
|
||||
<el-tag v-else-if="row.blOpen === '0'" type="info" size="small">停用</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无渠道数据" :image-size="80" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<div class="cs-footer">
|
||||
<span>已选 <b>{{ draftCodes.size }}</b> 个渠道</span>
|
||||
<div>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ChannelSelect">
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
||||
import { useChannels } from './useChannels'
|
||||
|
||||
const props = defineProps({
|
||||
/** v-model 绑定值:逗号分隔的渠道编码串,如 "SK_DEPDHLL,015A" */
|
||||
modelValue: { type: [String, Number], default: '' }
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
const { channelList, ensureChannels } = useChannels()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const tableRef = ref(null)
|
||||
const draftCodes = reactive(new Set())
|
||||
const queryParams = reactive({
|
||||
channelCode: '',
|
||||
channelName: '',
|
||||
serviceName: '',
|
||||
blOpen: ''
|
||||
})
|
||||
|
||||
const selectedCodes = computed(() =>
|
||||
String(props.modelValue || '').split(',').map(s => s.trim()).filter(Boolean)
|
||||
)
|
||||
|
||||
const filteredList = computed(() => {
|
||||
return channelList.value.filter(c => {
|
||||
if (queryParams.channelCode && !(c.channelCode || '').toLowerCase().includes(queryParams.channelCode.trim().toLowerCase())) return false
|
||||
if (queryParams.channelName && !(c.channelName || '').includes(queryParams.channelName.trim())) return false
|
||||
if (queryParams.serviceName && !(c.serviceName || '').includes(queryParams.serviceName.trim())) return false
|
||||
if (queryParams.blOpen !== '' && queryParams.blOpen !== null && queryParams.blOpen !== undefined && String(c.blOpen) !== String(queryParams.blOpen)) return false
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
function openDialog() {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleOpen() {
|
||||
loading.value = true
|
||||
draftCodes.clear()
|
||||
selectedCodes.value.forEach(code => draftCodes.add(code))
|
||||
resetQuery(false)
|
||||
// 拉取最新渠道后,按 draftCodes 全量同步一次勾选(覆盖上次弹窗遗留状态)
|
||||
ensureChannels(true).finally(() => {
|
||||
loading.value = false
|
||||
nextTick(syncSelection)
|
||||
})
|
||||
}
|
||||
|
||||
/** 按 draftCodes 同步当前渲染行的勾选状态(draftCodes 为唯一数据源,筛选切换不丢选择) */
|
||||
function syncSelection() {
|
||||
if (!tableRef.value) return
|
||||
filteredList.value.forEach(row => {
|
||||
tableRef.value.toggleRowSelection(row, draftCodes.has(String(row.channelCode)))
|
||||
})
|
||||
}
|
||||
|
||||
/** 数据/筛选变化后同步表格勾选状态 */
|
||||
watch(filteredList, () => {
|
||||
if (!dialogVisible.value) return
|
||||
nextTick(syncSelection)
|
||||
}, { flush: 'post' })
|
||||
|
||||
function handleSelect(selection, row) {
|
||||
const code = String(row.channelCode)
|
||||
if (draftCodes.has(code)) draftCodes.delete(code)
|
||||
else draftCodes.add(code)
|
||||
}
|
||||
|
||||
function handleSelectAll(selection) {
|
||||
// selection 为当前过滤后页面被选中的行;长度>0 表示全选,否则取消全选
|
||||
if (selection.length) {
|
||||
filteredList.value.forEach(row => draftCodes.add(String(row.channelCode)))
|
||||
} else {
|
||||
filteredList.value.forEach(row => draftCodes.delete(String(row.channelCode)))
|
||||
}
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
// 纯前端过滤(全量已缓存),watch filteredList 自动同步勾选
|
||||
}
|
||||
|
||||
function resetQuery(reload = true) {
|
||||
queryParams.channelCode = ''
|
||||
queryParams.channelName = ''
|
||||
queryParams.serviceName = ''
|
||||
queryParams.blOpen = ''
|
||||
if (reload) ensureChannels(true)
|
||||
}
|
||||
|
||||
function confirmSelection() {
|
||||
const codes = Array.from(draftCodes)
|
||||
const value = codes.join(',')
|
||||
emit('update:modelValue', value)
|
||||
emit('change', codes)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
emit('update:modelValue', '')
|
||||
emit('change', [])
|
||||
}
|
||||
|
||||
/** 供父组件预加载渠道缓存(tab 内明细表格使用) */
|
||||
defineExpose({ ensureChannels })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.channel-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.cs-summary {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cs-summary b {
|
||||
color: #409eff;
|
||||
font-size: 15px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
.cs-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.cs-footer b {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
import { ref } from 'vue'
|
||||
import { listChannelOptions } from '@/api/masterdata/serviceChannel'
|
||||
|
||||
/**
|
||||
* 服务渠道列表模块级共享缓存:全应用只拉取一次
|
||||
* 用于供应商绑定渠道的选择与已绑渠道编码/名称回显
|
||||
*
|
||||
* 口径说明:供应商绑定关系统一存「渠道编码 channelCode」逗号串
|
||||
* (sup_supplier.channel_ids 历史字段名沿用,实际存的是渠道编码),
|
||||
* 与老系统 TAB_SERVICE_CHANNEL.CHANNEL_CODE 稳定对应。
|
||||
*/
|
||||
const channelList = ref([])
|
||||
let loadingPromise = null
|
||||
|
||||
/** 逗号分隔编码串 -> 字符串编码数组 */
|
||||
export function parseChannelCodes(value) {
|
||||
if (Array.isArray(value)) return value.map(v => String(v)).filter(Boolean)
|
||||
if (value === null || value === undefined) return []
|
||||
return String(value).split(',').map(s => s.trim()).filter(Boolean)
|
||||
}
|
||||
|
||||
/** 编码数组 -> 逗号分隔串(去重,保持原顺序) */
|
||||
export function joinChannelCodes(codes) {
|
||||
const seen = new Set()
|
||||
const out = []
|
||||
;(codes || []).forEach(code => {
|
||||
const key = String(code)
|
||||
if (key && !seen.has(key)) {
|
||||
seen.add(key)
|
||||
out.push(key)
|
||||
}
|
||||
})
|
||||
return out.join(',')
|
||||
}
|
||||
|
||||
export function useChannels() {
|
||||
/** 确保渠道列表已加载(force=true 强制刷新),拉取全部状态以便回显已停用渠道 */
|
||||
function ensureChannels(force = false) {
|
||||
if (!force && loadingPromise) return loadingPromise
|
||||
if (!force && channelList.value.length) return Promise.resolve(channelList.value)
|
||||
loadingPromise = listChannelOptions({})
|
||||
.then(res => {
|
||||
channelList.value = res.data || []
|
||||
return channelList.value
|
||||
})
|
||||
.finally(() => {
|
||||
loadingPromise = null
|
||||
})
|
||||
return loadingPromise
|
||||
}
|
||||
|
||||
/** 按渠道编码查渠道对象 */
|
||||
function findChannel(channelCode) {
|
||||
if (channelCode === null || channelCode === undefined || channelCode === '') return null
|
||||
const code = String(channelCode).trim()
|
||||
return channelList.value.find(c => String(c.channelCode) === code) || null
|
||||
}
|
||||
|
||||
/** 渠道展示名:编码 + 名称;查不到的渠道回显原编码(镜像未同步/已删除渠道不致空白) */
|
||||
function getChannelLabel(channelCode) {
|
||||
const c = findChannel(channelCode)
|
||||
if (!c) return String(channelCode)
|
||||
return c.channelCode + ' ' + c.channelName
|
||||
}
|
||||
|
||||
return {
|
||||
channelList,
|
||||
ensureChannels,
|
||||
findChannel,
|
||||
getChannelLabel
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,8 @@ import UserSelect from '@/components/UserSelect'
|
||||
import UserName from '@/components/UserName'
|
||||
// 产品多选绑定组件
|
||||
import ProductSelect from '@/components/ProductSelect'
|
||||
// 渠道多选绑定组件(供应商绑定老系统服务渠道)
|
||||
import ChannelSelect from '@/components/ChannelSelect'
|
||||
// 多值输入组件(一个字段搜索多个值,如订单号批量搜索)
|
||||
import MultiValueInput from '@/components/MultiValueInput'
|
||||
// 表格列设置组件(自定义列显隐)
|
||||
@@ -88,6 +90,7 @@ app.component('CustomerSelect', CustomerSelect)
|
||||
app.component('UserSelect', UserSelect)
|
||||
app.component('UserName', UserName)
|
||||
app.component('ProductSelect', ProductSelect)
|
||||
app.component('ChannelSelect', ChannelSelect)
|
||||
app.component('MultiValueInput', MultiValueInput)
|
||||
app.component('ColumnSettings', ColumnSettings)
|
||||
|
||||
|
||||
@@ -167,7 +167,14 @@
|
||||
<el-table-column v-if="isColVisible('cooperationStatus')" label="合作状态" align="center" prop="cooperationStatus">
|
||||
<template #default="scope">{{ formatOption(cooperationStatusOptions, scope.row.cooperationStatus) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColVisible('channelIds')" label="绑定渠道ID" align="center" prop="channelIds" />
|
||||
<el-table-column v-if="isColVisible('channelIds')" label="绑定渠道" align="center" prop="channelIds" min-width="200">
|
||||
<template #default="scope">
|
||||
<span v-if="channelNames(scope.row.channelIds)" :title="channelNames(scope.row.channelIds)">
|
||||
{{ channelNames(scope.row.channelIds) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="isColVisible('remark')" label="备注说明" align="center" prop="remark" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
@@ -189,10 +196,18 @@
|
||||
|
||||
<script setup name="Supplier">
|
||||
import { listSupplier, delSupplier } from "@/api/customersuppliermanagement/supplierinfo/supplier.js"
|
||||
import { useChannels, parseChannelCodes } from '@/components/ChannelSelect/useChannels'
|
||||
import { useColumnSettings } from '@/utils/columnSettings'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
/** 绑定渠道编码串 -> 渠道编码+名称展示(查不到的渠道回显原编码) */
|
||||
const { ensureChannels, getChannelLabel } = useChannels()
|
||||
function channelNames(channelIds) {
|
||||
return parseChannelCodes(channelIds).map(code => getChannelLabel(code)).join(',')
|
||||
}
|
||||
ensureChannels()
|
||||
|
||||
/** 可自定义列(默认全部可见;选择列固定、操作列固定右侧不参与设置) */
|
||||
const columnDefs = [
|
||||
{ key: 'supplierCode', label: '供应商编码' },
|
||||
@@ -217,7 +232,7 @@ const columnDefs = [
|
||||
{ key: 'throwRatio', label: '抛比' },
|
||||
{ key: 'cooperationStartDate', label: '合作开始日期' },
|
||||
{ key: 'cooperationStatus', label: '合作状态' },
|
||||
{ key: 'channelIds', label: '绑定渠道ID' },
|
||||
{ key: 'channelIds', label: '绑定渠道' },
|
||||
{ key: 'remark', label: '备注说明' }
|
||||
]
|
||||
const { colVisible, isColVisible, toggleColumn, resetColumns } = useColumnSettings('supplier-info-list', columnDefs)
|
||||
|
||||
@@ -168,11 +168,6 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="绑定渠道ID" prop="channelIds">
|
||||
<el-input v-model="form.channelIds" type="textarea" :rows="2" placeholder="多个渠道ID用英文逗号分隔" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注说明" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入内容" />
|
||||
@@ -182,6 +177,37 @@
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 绑定渠道 ============ -->
|
||||
<el-tab-pane label="绑定渠道" name="channel">
|
||||
<div class="pane-toolbar">
|
||||
<channel-select v-model="form.channelIds" />
|
||||
<span class="pane-tip">绑定结果以渠道编码逗号串存入供应商主表 channel_ids,渠道来源于老系统服务渠道镜像(每日自动同步)</span>
|
||||
</div>
|
||||
<el-table :data="boundChannels" border size="small" class="bound-table">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="渠道编码" prop="channelCode" width="160" show-overflow-tooltip />
|
||||
<el-table-column label="渠道名称" prop="channelName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="服务商" prop="serviceName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="付款方式" prop="payType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="货物类型" prop="cargoType" width="90" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.blOpen === '1'" type="success" size="small">启用</el-tag>
|
||||
<el-tag v-else-if="row.blOpen === '0'" type="info" size="small">停用</el-tag>
|
||||
<span v-else>未知</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" size="small" @click="removeChannel(row.channelCode)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂未绑定渠道,点击上方「选择渠道」进行绑定" :image-size="80" />
|
||||
</template>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 公司主体 ============ -->
|
||||
<el-tab-pane label="公司主体" name="company">
|
||||
<div class="pane-toolbar">
|
||||
@@ -589,6 +615,7 @@ import {
|
||||
import { getToken } from '@/utils/auth'
|
||||
import axios from 'axios'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { useChannels, parseChannelCodes } from '@/components/ChannelSelect/useChannels'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
@@ -596,6 +623,10 @@ const router = useRouter()
|
||||
|
||||
const { settlement_type } = useDict('settlement_type')
|
||||
|
||||
const { ensureChannels, findChannel } = useChannels()
|
||||
/** 进入页面即预加载渠道缓存,供已绑渠道明细表回显名称 */
|
||||
ensureChannels()
|
||||
|
||||
const pageLoading = ref(false)
|
||||
const saving = ref(false)
|
||||
const activeTab = ref('base')
|
||||
@@ -671,6 +702,17 @@ function buildEmptyForm() {
|
||||
}
|
||||
const form = ref(buildEmptyForm())
|
||||
|
||||
/* ---------------- 渠道绑定 ---------------- */
|
||||
/** 已绑渠道明细:镜像中能查到的回显完整信息,查不到的以占位行展示原编码(不丢失旧数据) */
|
||||
const boundChannels = computed(() =>
|
||||
parseChannelCodes(form.value.channelIds).map(code => findChannel(code) || { channelCode: code })
|
||||
)
|
||||
|
||||
function removeChannel(channelCode) {
|
||||
const codes = parseChannelCodes(form.value.channelIds).filter(code => code !== String(channelCode))
|
||||
form.value.channelIds = codes.join(',')
|
||||
}
|
||||
|
||||
/* ---------------- 用户冗余姓名 ---------------- */
|
||||
function handleUserChange(uid, user, nameField) {
|
||||
form.value[nameField] = user ? (user.nickName || user.userName || null) : null
|
||||
|
||||
Reference in New Issue
Block a user