From 2a9825c49865156e9d9736434eb04511516f9e73 Mon Sep 17 00:00:00 2001 From: wuweihua <1583622118@qq.com> Date: Mon, 12 Oct 2026 10:28:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5=EF=BC=8C=20tms=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=BA=A7=E5=93=81=EF=BC=8C=E6=9C=8D=E5=8A=A1=E6=B8=A0?= =?UTF-8?q?=E9=81=93=EF=BC=8C=E4=BA=A7=E5=93=81=E6=B8=A0=E9=81=93=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../masterdata/ServiceChannelController.java | 36 +++ .../mapper/OracleSupplierSyncMapper.java | 14 +- .../sync/OracleSupplierReader.java | 22 +- .../sync/SupplierSyncService.java | 182 ++++++------ .../supplierinfo/task/SupplierSyncTask.java | 2 +- .../bms/masterdata/domain/LineProduct.java | 85 ++++++ .../bms/masterdata/domain/ProductChannel.java | 45 +++ .../bms/masterdata/domain/ServiceChannel.java | 69 +++++ .../mapper/ChannelMasterSyncMapper.java | 25 ++ .../mapper/OracleChannelMasterMapper.java | 22 ++ .../mapper/ServiceChannelMapper.java | 18 ++ .../service/IServiceChannelService.java | 18 ++ .../impl/ServiceChannelServiceImpl.java | 27 ++ .../sync/ChannelMasterSyncService.java | 264 ++++++++++++++++++ .../sync/ChannelMasterSyncWriter.java | 60 ++++ .../sync/OracleChannelMasterReader.java | 66 +++++ .../task/ChannelMasterSyncTask.java | 32 +++ .../customer/OracleSupplierSyncMapper.xml | 22 +- .../mapper/customer/SupplierSyncMapper.xml | 17 +- .../masterdata/ChannelMasterSyncMapper.xml | 148 ++++++++++ .../masterdata/OracleChannelMasterMapper.xml | 33 +++ .../masterdata/ServiceChannelMapper.xml | 69 +++++ .../sql/20261012_channel_product_sync.sql | 125 +++++++++ .../src/api/masterdata/serviceChannel.js | 10 + .../src/components/ChannelSelect/index.vue | 216 ++++++++++++++ .../components/ChannelSelect/useChannels.js | 72 +++++ bms-front/src/main.js | 3 + .../supplierinfo/index.vue | 19 +- .../supplierinfo/supplieredit/index.vue | 52 +++- 29 files changed, 1646 insertions(+), 127 deletions(-) create mode 100644 bms-back/bms-admin/src/main/java/com/bms/web/controller/masterdata/ServiceChannelController.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/domain/LineProduct.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ProductChannel.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ServiceChannel.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ChannelMasterSyncMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/OracleChannelMasterMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ServiceChannelMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/service/IServiceChannelService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/service/impl/ServiceChannelServiceImpl.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncWriter.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/sync/OracleChannelMasterReader.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/masterdata/task/ChannelMasterSyncTask.java create mode 100644 bms-back/bms-system/src/main/resources/mapper/masterdata/ChannelMasterSyncMapper.xml create mode 100644 bms-back/bms-system/src/main/resources/mapper/masterdata/OracleChannelMasterMapper.xml create mode 100644 bms-back/bms-system/src/main/resources/mapper/masterdata/ServiceChannelMapper.xml create mode 100644 bms-back/sql/20261012_channel_product_sync.sql create mode 100644 bms-front/src/api/masterdata/serviceChannel.js create mode 100644 bms-front/src/components/ChannelSelect/index.vue create mode 100644 bms-front/src/components/ChannelSelect/useChannels.js diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/masterdata/ServiceChannelController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/masterdata/ServiceChannelController.java new file mode 100644 index 00000000..cbdd55f2 --- /dev/null +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/masterdata/ServiceChannelController.java @@ -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 list = serviceChannelService.selectServiceChannelList(query); + return success(list); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/mapper/OracleSupplierSyncMapper.java b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/mapper/OracleSupplierSyncMapper.java index e39e50fc..9776a9dd 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/mapper/OracleSupplierSyncMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/mapper/OracleSupplierSyncMapper.java @@ -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> selectAllTabSupplier(); + List> selectAllServiceInfo(); + + /** + * 全量读取服务渠道 TAB_SERVICE_CHANNEL(仅需服务商编码+渠道编码,用于聚合 channel_ids) + */ + List> selectAllServiceChannel(); } diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/OracleSupplierReader.java b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/OracleSupplierReader.java index 6b7bec62..68a5e491 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/OracleSupplierReader.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/OracleSupplierReader.java @@ -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> readAll() + public List> readServiceInfo() { try { // 避免 PageHelper 把 Oracle 查询包成分页语句 PageHelper.clearPage(); - return oracleSupplierSyncMapper.selectAllTabSupplier(); + return oracleSupplierSyncMapper.selectAllServiceInfo(); + } + finally + { + PageHelper.clearPage(); + } + } + + /** 读取服务渠道 TAB_SERVICE_CHANNEL */ + @DataSource(DataSourceType.SLAVE) + public List> readServiceChannels() + { + try + { + PageHelper.clearPage(); + return oracleSupplierSyncMapper.selectAllServiceChannel(); } finally { diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/SupplierSyncService.java b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/SupplierSyncService.java index 29efd338..dafc8608 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/SupplierSyncService.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/sync/SupplierSyncService.java @@ -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 SUP_TYPE_CODES = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5", "6")); - private static final Set INVOICE_TYPE_CODES = new HashSet<>(Arrays.asList("1", "2", "3", "4")); - private static final Set 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> rows = oracleSupplierReader.readAll(); + List> rows = oracleSupplierReader.readServiceInfo(); + List> channelRows = oracleSupplierReader.readServiceChannels(); + + // 按服务商编码聚合渠道编码(去重、保序) + Map> channelMap = new LinkedHashMap<>(); + for (Map 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 existingCodes = new HashSet<>(supplierSyncMapper.selectExistingCodes()); @@ -78,8 +96,8 @@ public class SupplierSyncService { int rowNo = i + 1; Map 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 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 row, String code, String name, List warnings) + private BmsSupplier mapRow(Map row, String code, String name, + LinkedHashSet channels, List 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 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 row, String key, Set validCodes, - String supplierCode, String label, List warnings) + private static String joinChannelCodes(LinkedHashSet channels, String supplierCode, List 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 row, String key, String code, List 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 row, String key, String code, List 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 row, String key) { Object v = getIgnoreCase(row, key); diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/task/SupplierSyncTask.java b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/task/SupplierSyncTask.java index 2a0ba8e9..3fbca027 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/task/SupplierSyncTask.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/supplierinfo/task/SupplierSyncTask.java @@ -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() { diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/LineProduct.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/LineProduct.java new file mode 100644 index 00000000..99365258 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/LineProduct.java @@ -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; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ProductChannel.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ProductChannel.java new file mode 100644 index 00000000..d21d23f5 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ProductChannel.java @@ -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; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ServiceChannel.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ServiceChannel.java new file mode 100644 index 00000000..0796cef2 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/domain/ServiceChannel.java @@ -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; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ChannelMasterSyncMapper.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ChannelMasterSyncMapper.java new file mode 100644 index 00000000..84e5e5b6 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ChannelMasterSyncMapper.java @@ -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 list); + + /** 按源 ID 批量 upsert 线路产品 */ + int upsertLineProduct(@Param("list") List list); + + /** 按源 GUID 批量 upsert 产品-渠道关系 */ + int upsertProductChannel(@Param("list") List list); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/OracleChannelMasterMapper.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/OracleChannelMasterMapper.java new file mode 100644 index 00000000..b888e0c9 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/OracleChannelMasterMapper.java @@ -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> selectAllServiceChannel(); + + /** 全量读取线路产品 TAB_LINE_PRODUCT */ + List> selectAllLineProduct(); + + /** 全量读取产品-渠道关系 TAB_PRODUCT_CHANNEL */ + List> selectAllProductChannel(); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ServiceChannelMapper.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ServiceChannelMapper.java new file mode 100644 index 00000000..bf9f949a --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/mapper/ServiceChannelMapper.java @@ -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 selectServiceChannelList(ServiceChannel query); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/service/IServiceChannelService.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/service/IServiceChannelService.java new file mode 100644 index 00000000..d94253bf --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/service/IServiceChannelService.java @@ -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 selectServiceChannelList(ServiceChannel query); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/service/impl/ServiceChannelServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/service/impl/ServiceChannelServiceImpl.java new file mode 100644 index 00000000..201cff5b --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/service/impl/ServiceChannelServiceImpl.java @@ -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 selectServiceChannelList(ServiceChannel query) + { + return serviceChannelMapper.selectServiceChannelList(query); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncService.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncService.java new file mode 100644 index 00000000..0d26f6f3 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncService.java @@ -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> channelRows = reader.readServiceChannels(); + List channels = new ArrayList<>(channelRows.size()); + int skipChannel = 0; + for (Map 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> productRows = reader.readLineProducts(); + List products = new ArrayList<>(productRows.size()); + int skipProduct = 0; + for (Map 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> relRows = reader.readProductChannels(); + List relations = new ArrayList<>(relRows.size()); + int skipRel = 0; + for (Map 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 row, String key) + { + Object v = row.get(key); + if (v != null) + { + return v; + } + for (Map.Entry e : row.entrySet()) + { + if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) + { + return e.getValue(); + } + } + return null; + } + + private static String str(Map 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 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 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 row, String key) + { + BigDecimal d = decimal(row, key); + return d == null ? null : d.intValue(); + } + + private static Long lo(Map row, String key) + { + BigDecimal d = decimal(row, key); + return d == null ? null : d.longValue(); + } + + private static Date date(Map 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); + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncWriter.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncWriter.java new file mode 100644 index 00000000..42783d1a --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/ChannelMasterSyncWriter.java @@ -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 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 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 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; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/OracleChannelMasterReader.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/OracleChannelMasterReader.java new file mode 100644 index 00000000..580cb4f4 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/sync/OracleChannelMasterReader.java @@ -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> readServiceChannels() + { + try + { + PageHelper.clearPage(); + return oracleChannelMasterMapper.selectAllServiceChannel(); + } + finally + { + PageHelper.clearPage(); + } + } + + @DataSource(DataSourceType.SLAVE) + public List> readLineProducts() + { + try + { + PageHelper.clearPage(); + return oracleChannelMasterMapper.selectAllLineProduct(); + } + finally + { + PageHelper.clearPage(); + } + } + + @DataSource(DataSourceType.SLAVE) + public List> readProductChannels() + { + try + { + PageHelper.clearPage(); + return oracleChannelMasterMapper.selectAllProductChannel(); + } + finally + { + PageHelper.clearPage(); + } + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/masterdata/task/ChannelMasterSyncTask.java b/bms-back/bms-system/src/main/java/com/bms/masterdata/task/ChannelMasterSyncTask.java new file mode 100644 index 00000000..339d2d45 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/masterdata/task/ChannelMasterSyncTask.java @@ -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("老系统渠道产品同步未返回结果"); + } + } +} diff --git a/bms-back/bms-system/src/main/resources/mapper/customer/OracleSupplierSyncMapper.xml b/bms-back/bms-system/src/main/resources/mapper/customer/OracleSupplierSyncMapper.xml index 9152b33b..5db44d65 100644 --- a/bms-back/bms-system/src/main/resources/mapper/customer/OracleSupplierSyncMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/customer/OracleSupplierSyncMapper.xml @@ -4,13 +4,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - + SELECT SERVICE_CODE, SERVICE_NAME, SERVICE_TYPE, BL_OPEN, REMARK, CREATE_DATE + FROM WEISHI_USER.TAB_SERVICE_INFO + + + + diff --git a/bms-back/bms-system/src/main/resources/mapper/customer/SupplierSyncMapper.xml b/bms-back/bms-system/src/main/resources/mapper/customer/SupplierSyncMapper.xml index 2468e5a5..e0953b27 100644 --- a/bms-back/bms-system/src/main/resources/mapper/customer/SupplierSyncMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/customer/SupplierSyncMapper.xml @@ -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 , supplier_type - , invoice_type - , kache_type - , ed - , zq + , channel_ids , remark , create_time ) VALUES ( #{supplierCode}, #{supplierName}, #{isEnable}, '0', #{createBy} , #{supplierType} - , #{invoiceType} - , #{kacheType} - , #{ed} - , #{zq} + , #{channelIds} , #{remark} , #{createTime} ) + UPDATE sup_supplier SET supplier_name = #{supplierName} , is_enable = #{isEnable} , supplier_type = #{supplierType} - , invoice_type = #{invoiceType} - , kache_type = #{kacheType} - , ed = #{ed} - , zq = #{zq} + , channel_ids = #{channelIds} , remark = #{remark} , update_by = #{updateBy} , update_time = #{updateTime} diff --git a/bms-back/bms-system/src/main/resources/mapper/masterdata/ChannelMasterSyncMapper.xml b/bms-back/bms-system/src/main/resources/mapper/masterdata/ChannelMasterSyncMapper.xml new file mode 100644 index 00000000..f828b733 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/masterdata/ChannelMasterSyncMapper.xml @@ -0,0 +1,148 @@ + + + + + + + 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 + + (#{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()) + + 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 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 + + (#{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()) + + 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 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 + + (#{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()) + + 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() + + diff --git a/bms-back/bms-system/src/main/resources/mapper/masterdata/OracleChannelMasterMapper.xml b/bms-back/bms-system/src/main/resources/mapper/masterdata/OracleChannelMasterMapper.xml new file mode 100644 index 00000000..b49a0cc8 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/masterdata/OracleChannelMasterMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + diff --git a/bms-back/bms-system/src/main/resources/mapper/masterdata/ServiceChannelMapper.xml b/bms-back/bms-system/src/main/resources/mapper/masterdata/ServiceChannelMapper.xml new file mode 100644 index 00000000..f99ba55e --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/masterdata/ServiceChannelMapper.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + diff --git a/bms-back/sql/20261012_channel_product_sync.sql b/bms-back/sql/20261012_channel_product_sync.sql new file mode 100644 index 00000000..88d530c7 --- /dev/null +++ b/bms-back/sql/20261012_channel_product_sync.sql @@ -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()'); diff --git a/bms-front/src/api/masterdata/serviceChannel.js b/bms-front/src/api/masterdata/serviceChannel.js new file mode 100644 index 00000000..61b02833 --- /dev/null +++ b/bms-front/src/api/masterdata/serviceChannel.js @@ -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 + }) +} diff --git a/bms-front/src/components/ChannelSelect/index.vue b/bms-front/src/components/ChannelSelect/index.vue new file mode 100644 index 00000000..afee2f2c --- /dev/null +++ b/bms-front/src/components/ChannelSelect/index.vue @@ -0,0 +1,216 @@ + + + + + diff --git a/bms-front/src/components/ChannelSelect/useChannels.js b/bms-front/src/components/ChannelSelect/useChannels.js new file mode 100644 index 00000000..293f833b --- /dev/null +++ b/bms-front/src/components/ChannelSelect/useChannels.js @@ -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 + } +} diff --git a/bms-front/src/main.js b/bms-front/src/main.js index 9e19e653..932565fc 100644 --- a/bms-front/src/main.js +++ b/bms-front/src/main.js @@ -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) diff --git a/bms-front/src/views/customersuppliermanagement/supplierinfo/index.vue b/bms-front/src/views/customersuppliermanagement/supplierinfo/index.vue index c37e3e61..d861a0bc 100644 --- a/bms-front/src/views/customersuppliermanagement/supplierinfo/index.vue +++ b/bms-front/src/views/customersuppliermanagement/supplierinfo/index.vue @@ -167,7 +167,14 @@ - + + +