From 80d639d8870f6dae54597765c02571838a0ccd5c Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Sat, 14 Nov 2026 18:53:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=94=AF=E4=BB=98=E8=B4=A6?= =?UTF-8?q?=E5=8D=95=E6=9C=8D=E5=8A=A1=E9=80=BB=E8=BE=91=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E6=BA=90ID=E8=B5=8B=E5=80=BC=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E6=94=B9SQL=E6=98=A0=E5=B0=84=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E6=BA=90ID=E5=AD=97=E6=AE=B5=E4=B8=BA=E8=B4=B9?= =?UTF-8?q?=E7=94=A8ID=EF=BC=9B=E6=96=B0=E5=A2=9EUUID=E5=88=B0BIGINT?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitor/UuidToNewMonitorController.java | 106 +++ .../src/main/resources/application.yml | 8 + .../bms/price/config/UuidToNewProperties.java | 83 +++ .../price/migration/domain/MigBiJobLog.java | 49 ++ .../migration/domain/UuidToNewFeeRow.java | 15 + .../migration/domain/UuidToNewIdHolder.java | 54 ++ .../migration/domain/UuidToNewProgressVo.java | 38 + .../migration/mapper/UuidToNewSyncMapper.java | 71 ++ .../service/IUuidToNewMonitorService.java | 23 + .../service/IUuidToNewSyncService.java | 16 + .../impl/UuidToNewMonitorServiceImpl.java | 120 ++++ .../impl/UuidToNewSyncServiceImpl.java | 331 +++++++++ .../service/impl/PayBillServiceImpl.java | 2 +- .../bms/price/task/UuidToNewSyncRunner.java | 110 +++ .../mapper/price/PrcPayBillFeeMapper.xml | 2 +- .../mapper/price/UuidToNewSyncMapper.xml | 415 +++++++++++ .../20261114_uuid_to_bi_shadow_dev.md | 381 ++++++++++ .../20261114_uuid_to_bi_shadow_tables.sql | 651 ++++++++++++++++++ bms-front/src/api/monitor/uuidToNew.js | 44 ++ .../src/views/monitor/uuidToNew/index.vue | 329 +++++++++ 20 files changed, 2846 insertions(+), 2 deletions(-) create mode 100644 bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/UuidToNewMonitorController.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/config/UuidToNewProperties.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/domain/MigBiJobLog.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewFeeRow.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewIdHolder.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewProgressVo.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/mapper/UuidToNewSyncMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewMonitorService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewSyncService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewMonitorServiceImpl.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewSyncServiceImpl.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/task/UuidToNewSyncRunner.java create mode 100644 bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml create mode 100644 bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_dev.md create mode 100644 bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_tables.sql create mode 100644 bms-front/src/api/monitor/uuidToNew.js create mode 100644 bms-front/src/views/monitor/uuidToNew/index.vue diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/UuidToNewMonitorController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/UuidToNewMonitorController.java new file mode 100644 index 00000000..7161128f --- /dev/null +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/monitor/UuidToNewMonitorController.java @@ -0,0 +1,106 @@ +package com.bms.web.controller.monitor; + +import java.util.List; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.bms.common.annotation.Log; +import com.bms.common.core.controller.BaseController; +import com.bms.common.core.domain.AjaxResult; +import com.bms.common.core.page.TableDataInfo; +import com.bms.common.enums.BusinessType; +import com.bms.price.migration.domain.MigBiJobLog; +import com.bms.price.migration.service.IUuidToNewMonitorService; + +/** + * 监控:UUID -> bigint 阴影同步进度与开关 + */ +@RestController +@RequestMapping("/monitor/uuidToNew") +public class UuidToNewMonitorController extends BaseController +{ + @Autowired + private IUuidToNewMonitorService monitorService; + + @PreAuthorize("@ss.hasPermi('monitor:uuidToNew:list')") + @GetMapping("/progress") + public AjaxResult progress() + { + return success(monitorService.getProgress()); + } + + @PreAuthorize("@ss.hasPermi('monitor:uuidToNew:list')") + @GetMapping("/jobLog/list") + public TableDataInfo jobLogList(MigBiJobLog query) + { + startPage(); + List list = monitorService.selectJobLogList(query); + return getDataTable(list); + } + + /** + * 打开/关闭连续同步。 + * body: { "enabled": true, "threadCount": 2, "batchSize": 200 } + */ + @PreAuthorize("@ss.hasPermi('monitor:uuidToNew:switch')") + @Log(title = "UUID阴影同步开关", businessType = BusinessType.UPDATE) + @PostMapping("/enabled") + public AjaxResult setEnabled(@RequestBody Map body) + { + boolean enabled = false; + Integer threadCount = null; + Integer batchSize = null; + if (body != null) + { + if (body.get("enabled") != null) + { + enabled = Boolean.parseBoolean(String.valueOf(body.get("enabled"))); + } + threadCount = toInt(body.get("threadCount")); + batchSize = toInt(body.get("batchSize")); + } + monitorService.setEnabled(enabled, threadCount, batchSize); + return success(monitorService.getProgress()); + } + + /** + * 仅开关关闭时修改线程/批次。 + * body: { "threadCount": 2, "batchSize": 200 } + */ + @PreAuthorize("@ss.hasPermi('monitor:uuidToNew:switch')") + @Log(title = "UUID阴影同步配置", businessType = BusinessType.UPDATE) + @PostMapping("/config") + public AjaxResult updateConfig(@RequestBody Map body) + { + Integer threadCount = null; + Integer batchSize = null; + if (body != null) + { + threadCount = toInt(body.get("threadCount")); + batchSize = toInt(body.get("batchSize")); + } + monitorService.updateRuntimeConfig(threadCount, batchSize); + return success(monitorService.getProgress()); + } + + private Integer toInt(Object v) + { + if (v == null || "".equals(String.valueOf(v).trim())) + { + return null; + } + try + { + return Integer.valueOf(String.valueOf(v).trim()); + } + catch (Exception e) + { + return null; + } + } +} diff --git a/bms-back/bms-admin/src/main/resources/application.yml b/bms-back/bms-admin/src/main/resources/application.yml index fd1387ea..afde0d08 100644 --- a/bms-back/bms-admin/src/main/resources/application.yml +++ b/bms-back/bms-admin/src/main/resources/application.yml @@ -26,6 +26,14 @@ bms: tms: # Agreed API identification code for TMS callers api-code: BMS.TMS.ZYaOvlQzpcfau8TUaCFfO3Hp8ieHIqGw + # UUID -> bigint shadow sync. Toggle on monitor page (no cron). + mig: + uuid-to-new: + batch-size: 200 + # 1=single thread; sprint on 4c8g use 2~3 (max 4) + thread-count: 2 + # sleep when no pending orders while switch is ON + idle-sleep-ms: 3000 billing: # MQ mode: redis (default list queue) | local (Spring event) mq-mode: redis diff --git a/bms-back/bms-system/src/main/java/com/bms/price/config/UuidToNewProperties.java b/bms-back/bms-system/src/main/java/com/bms/price/config/UuidToNewProperties.java new file mode 100644 index 00000000..21989435 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/config/UuidToNewProperties.java @@ -0,0 +1,83 @@ +package com.bms.price.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * UUID -> bigint 阴影同步配置(bms.mig.uuid-to-new) + * 运行开关/线程/批次由监控页控制,不走 cron。 + */ +@Component +@ConfigurationProperties(prefix = "bms.mig.uuid-to-new") +public class UuidToNewProperties +{ + /** 每轮抓取订单数(1~1000) */ + private int batchSize = 200; + /** + * 批内工作线程数(1~4)。 + * 默认1;冲刺追数可设2~3(生产4核谨慎)。 + */ + private int threadCount = 1; + /** 无待处理订单时空转睡眠毫秒(开关仍为开时) */ + private long idleSleepMs = 3000L; + + public int getBatchSize() + { + return batchSize; + } + + public void setBatchSize(int batchSize) + { + this.batchSize = batchSize; + } + + /** 限定在 1~1000 */ + public int resolvedBatchSize() + { + int n = batchSize; + if (n < 1) + { + return 1; + } + if (n > 1000) + { + return 1000; + } + return n; + } + + public int getThreadCount() + { + return threadCount; + } + + public void setThreadCount(int threadCount) + { + this.threadCount = threadCount; + } + + /** 限定在1~4 */ + public int resolvedThreadCount() + { + int n = threadCount; + if (n < 1) + { + return 1; + } + if (n > 4) + { + return 4; + } + return n; + } + + public long getIdleSleepMs() + { + return idleSleepMs; + } + + public void setIdleSleepMs(long idleSleepMs) + { + this.idleSleepMs = idleSleepMs; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/MigBiJobLog.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/MigBiJobLog.java new file mode 100644 index 00000000..75ce47bc --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/MigBiJobLog.java @@ -0,0 +1,49 @@ +package com.bms.price.migration.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * mig_bi_job_log 批次日志 + */ +public class MigBiJobLog +{ + private Long id; + private String batchNo; + private Integer fetched; + private Integer success; + private Integer fail; + private Integer skip; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startedAt; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishedAt; + private String remark; + /** 查询:开始时间起 */ + private String beginStartedAt; + /** 查询:开始时间止 */ + private String endStartedAt; + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getBatchNo() { return batchNo; } + public void setBatchNo(String batchNo) { this.batchNo = batchNo; } + public Integer getFetched() { return fetched; } + public void setFetched(Integer fetched) { this.fetched = fetched; } + public Integer getSuccess() { return success; } + public void setSuccess(Integer success) { this.success = success; } + public Integer getFail() { return fail; } + public void setFail(Integer fail) { this.fail = fail; } + public Integer getSkip() { return skip; } + public void setSkip(Integer skip) { this.skip = skip; } + public Date getStartedAt() { return startedAt; } + public void setStartedAt(Date startedAt) { this.startedAt = startedAt; } + public Date getFinishedAt() { return finishedAt; } + public void setFinishedAt(Date finishedAt) { this.finishedAt = finishedAt; } + public String getRemark() { return remark; } + public void setRemark(String remark) { this.remark = remark; } + public String getBeginStartedAt() { return beginStartedAt; } + public void setBeginStartedAt(String beginStartedAt) { this.beginStartedAt = beginStartedAt; } + public String getEndStartedAt() { return endStartedAt; } + public void setEndStartedAt(String endStartedAt) { this.endStartedAt = endStartedAt; } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewFeeRow.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewFeeRow.java new file mode 100644 index 00000000..b35e0986 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewFeeRow.java @@ -0,0 +1,15 @@ +package com.bms.price.migration.domain; + +/** + * 旧费用行:用于逐行插入并建内存 Map + */ +public class UuidToNewFeeRow +{ + private String feeId; + private String parentId; + + public String getFeeId() { return feeId; } + public void setFeeId(String feeId) { this.feeId = feeId; } + public String getParentId() { return parentId; } + public void setParentId(String parentId) { this.parentId = parentId; } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewIdHolder.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewIdHolder.java new file mode 100644 index 00000000..7245f220 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewIdHolder.java @@ -0,0 +1,54 @@ +package com.bms.price.migration.domain; + +/** + * 阴影插入时传参 / 接收自增主键 + */ +public class UuidToNewIdHolder +{ + private String oldBillId; + private Long newBillId; + private String oldId; + private Long newId; + private Long newIncomeId; + private Long newPayId; + private String orderNo; + private String status; + private String failReason; + private String batchNo; + private Integer fetched; + private Integer success; + private Integer fail; + private Integer skip; + private Long jobLogId; + + public String getOldBillId() { return oldBillId; } + public void setOldBillId(String oldBillId) { this.oldBillId = oldBillId; } + public Long getNewBillId() { return newBillId; } + public void setNewBillId(Long newBillId) { this.newBillId = newBillId; } + public String getOldId() { return oldId; } + public void setOldId(String oldId) { this.oldId = oldId; } + public Long getNewId() { return newId; } + public void setNewId(Long newId) { this.newId = newId; } + public Long getNewIncomeId() { return newIncomeId; } + public void setNewIncomeId(Long newIncomeId) { this.newIncomeId = newIncomeId; } + public Long getNewPayId() { return newPayId; } + public void setNewPayId(Long newPayId) { this.newPayId = newPayId; } + public String getOrderNo() { return orderNo; } + public void setOrderNo(String orderNo) { this.orderNo = orderNo; } + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + public String getFailReason() { return failReason; } + public void setFailReason(String failReason) { this.failReason = failReason; } + public String getBatchNo() { return batchNo; } + public void setBatchNo(String batchNo) { this.batchNo = batchNo; } + public Integer getFetched() { return fetched; } + public void setFetched(Integer fetched) { this.fetched = fetched; } + public Integer getSuccess() { return success; } + public void setSuccess(Integer success) { this.success = success; } + public Integer getFail() { return fail; } + public void setFail(Integer fail) { this.fail = fail; } + public Integer getSkip() { return skip; } + public void setSkip(Integer skip) { this.skip = skip; } + public Long getJobLogId() { return jobLogId; } + public void setJobLogId(Long jobLogId) { this.jobLogId = jobLogId; } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewProgressVo.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewProgressVo.java new file mode 100644 index 00000000..6aab46d2 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/domain/UuidToNewProgressVo.java @@ -0,0 +1,38 @@ +package com.bms.price.migration.domain; + +/** + * UUID 阴影同步进度概览 + */ +public class UuidToNewProgressVo +{ + private long oldCnt; + private long doneCnt; + private long newCnt; + private long pendingCnt; + private double donePercent; + /** 监控页开关:是否要跑 */ + private boolean enabled; + /** 循环是否正在执行中 */ + private boolean running; + private int batchSize; + private int threadCount; + + public long getOldCnt() { return oldCnt; } + public void setOldCnt(long oldCnt) { this.oldCnt = oldCnt; } + public long getDoneCnt() { return doneCnt; } + public void setDoneCnt(long doneCnt) { this.doneCnt = doneCnt; } + public long getNewCnt() { return newCnt; } + public void setNewCnt(long newCnt) { this.newCnt = newCnt; } + public long getPendingCnt() { return pendingCnt; } + public void setPendingCnt(long pendingCnt) { this.pendingCnt = pendingCnt; } + public double getDonePercent() { return donePercent; } + public void setDonePercent(double donePercent) { this.donePercent = donePercent; } + public boolean isEnabled() { return enabled; } + public void setEnabled(boolean enabled) { this.enabled = enabled; } + public boolean isRunning() { return running; } + public void setRunning(boolean running) { this.running = running; } + public int getBatchSize() { return batchSize; } + public void setBatchSize(int batchSize) { this.batchSize = batchSize; } + public int getThreadCount() { return threadCount; } + public void setThreadCount(int threadCount) { this.threadCount = threadCount; } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/mapper/UuidToNewSyncMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/mapper/UuidToNewSyncMapper.java new file mode 100644 index 00000000..7fa8f54f --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/mapper/UuidToNewSyncMapper.java @@ -0,0 +1,71 @@ +package com.bms.price.migration.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.bms.price.migration.domain.MigBiJobLog; +import com.bms.price.migration.domain.UuidToNewFeeRow; +import com.bms.price.migration.domain.UuidToNewIdHolder; +import com.bms.price.migration.domain.UuidToNewProgressVo; + +/** + * UUID -> bigint 阴影同步 Mapper + */ +public interface UuidToNewSyncMapper +{ + List selectPendingBillIds(@Param("limit") int limit); + + String selectOrderNo(@Param("oldBillId") String oldBillId); + + int countOrderBill(@Param("oldBillId") String oldBillId); + + int insertOrderBillNew(UuidToNewIdHolder holder); + + int insertOrderDone(UuidToNewIdHolder holder); + + /** INSERT IGNORE 占坑;返回 0 表示已被占用 */ + int claimOrderDone(UuidToNewIdHolder holder); + + int updateOrderDoneSuccess(UuidToNewIdHolder holder); + + int insertOrderExtNew(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + List selectIncomeIds(@Param("oldBillId") String oldBillId); + + int insertIncomeBillNew(UuidToNewIdHolder holder); + + List selectIncomeFees(@Param("oldBillId") String oldBillId); + + int insertIncomeFeeNew(UuidToNewIdHolder holder); + + List selectPayIds(@Param("oldBillId") String oldBillId); + + int insertPayBillNew(UuidToNewIdHolder holder); + + List selectPayFees(@Param("oldBillId") String oldBillId); + + int insertPayFeeNew(UuidToNewIdHolder holder); + + int updatePayFeeIdEqualsId(@Param("id") Long id); + + int insertArDetailNew(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + List selectArDetailFees(@Param("oldBillId") String oldBillId); + + int insertArDetailFeeNew(@Param("detailFeeId") Long detailFeeId, @Param("newFeeId") Long newFeeId); + + List selectPayBillLines(@Param("oldBillId") String oldBillId); + + int insertPayBillLineNew(@Param("lineId") Long lineId, @Param("newBillId") Long newBillId, + @Param("newFeeId") Long newFeeId); + + int insertBillingTaskNew(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + int insertJobLogStart(UuidToNewIdHolder holder); + + int updateJobLogFinish(UuidToNewIdHolder holder); + + UuidToNewProgressVo selectProgress(); + + List selectJobLogList(MigBiJobLog query); +} + diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewMonitorService.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewMonitorService.java new file mode 100644 index 00000000..8d399677 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewMonitorService.java @@ -0,0 +1,23 @@ +package com.bms.price.migration.service; + +import java.util.List; +import com.bms.price.migration.domain.MigBiJobLog; +import com.bms.price.migration.domain.UuidToNewProgressVo; + +/** + * UUID 阴影同步监控 + */ +public interface IUuidToNewMonitorService +{ + UuidToNewProgressVo getProgress(); + + List selectJobLogList(MigBiJobLog query); + + /** 打开/关闭连续同步;开启前可带线程/批次 */ + void setEnabled(boolean enabled, Integer threadCount, Integer batchSize); + + /** + * 仅开关关闭时可改线程与批次。 + */ + void updateRuntimeConfig(Integer threadCount, Integer batchSize); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewSyncService.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewSyncService.java new file mode 100644 index 00000000..2c4bf27e --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/IUuidToNewSyncService.java @@ -0,0 +1,16 @@ +package com.bms.price.migration.service; + +/** + * UUID -> bigint 阴影同步服务 + */ +public interface IUuidToNewSyncService +{ + /** + * 执行一批。 + * @return 本批抓取条数(fetched) + */ + int syncBatch(int batchSize); + + /** 一单一事务;返回 SUCCESS / FAIL / SKIP */ + String syncOneOrder(String oldBillId); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewMonitorServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewMonitorServiceImpl.java new file mode 100644 index 00000000..3404744a --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewMonitorServiceImpl.java @@ -0,0 +1,120 @@ +package com.bms.price.migration.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.bms.common.exception.ServiceException; +import com.bms.price.config.UuidToNewProperties; +import com.bms.price.migration.domain.MigBiJobLog; +import com.bms.price.migration.domain.UuidToNewProgressVo; +import com.bms.price.migration.mapper.UuidToNewSyncMapper; +import com.bms.price.migration.service.IUuidToNewMonitorService; +import com.bms.price.task.UuidToNewSyncRunner; + +/** + * UUID 阴影同步监控 + */ +@Service +public class UuidToNewMonitorServiceImpl implements IUuidToNewMonitorService +{ + @Autowired + private UuidToNewSyncMapper syncMapper; + + @Autowired + private UuidToNewProperties properties; + + @Autowired + private UuidToNewSyncRunner syncRunner; + + @Override + public UuidToNewProgressVo getProgress() + { + UuidToNewProgressVo vo = syncMapper.selectProgress(); + if (vo == null) + { + vo = new UuidToNewProgressVo(); + } + long pending = vo.getOldCnt() - vo.getDoneCnt(); + if (pending < 0L) + { + pending = 0L; + } + vo.setPendingCnt(pending); + if (vo.getOldCnt() > 0L) + { + vo.setDonePercent(Math.round(vo.getDoneCnt() * 10000.0 / vo.getOldCnt()) / 100.0); + } + else + { + vo.setDonePercent(0.0); + } + vo.setEnabled(syncRunner.isEnabled()); + vo.setRunning(syncRunner.isRunning()); + if (properties != null) + { + vo.setBatchSize(properties.resolvedBatchSize()); + vo.setThreadCount(properties.resolvedThreadCount()); + } + return vo; + } + + @Override + public List selectJobLogList(MigBiJobLog query) + { + return syncMapper.selectJobLogList(query); + } + + @Override + public void setEnabled(boolean enabled, Integer threadCount, Integer batchSize) + { + if (enabled) + { + applyConfig(threadCount, batchSize); + } + syncRunner.setEnabled(enabled); + } + + @Override + public void updateRuntimeConfig(Integer threadCount, Integer batchSize) + { + if (syncRunner.isEnabled() || syncRunner.isRunning()) + { + throw new ServiceException("请先关闭连续同步再修改线程/批次"); + } + applyConfig(threadCount, batchSize); + } + + private void applyConfig(Integer threadCount, Integer batchSize) + { + if (properties == null) + { + return; + } + if (threadCount != null) + { + int n = threadCount.intValue(); + if (n < 1) + { + n = 1; + } + if (n > 4) + { + n = 4; + } + properties.setThreadCount(n); + } + if (batchSize != null) + { + int n = batchSize.intValue(); + if (n < 1) + { + n = 1; + } + if (n > 1000) + { + n = 1000; + } + properties.setBatchSize(n); + } + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewSyncServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewSyncServiceImpl.java new file mode 100644 index 00000000..30facab5 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/migration/service/impl/UuidToNewSyncServiceImpl.java @@ -0,0 +1,331 @@ +package com.bms.price.migration.service.impl; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.bms.price.config.UuidToNewProperties; +import com.bms.price.migration.domain.UuidToNewFeeRow; +import com.bms.price.migration.domain.UuidToNewIdHolder; +import com.bms.price.migration.mapper.UuidToNewSyncMapper; +import com.bms.price.migration.service.IUuidToNewSyncService; + +/** + * UUID -> bigint 阴影同步:一单一事务;批内可多线程 + */ +@Service +public class UuidToNewSyncServiceImpl implements IUuidToNewSyncService +{ + private static final Logger log = LoggerFactory.getLogger(UuidToNewSyncServiceImpl.class); + + public static final String SUCCESS = "SUCCESS"; + public static final String FAIL = "FAIL"; + public static final String SKIP = "SKIP"; + public static final String RUNNING = "RUNNING"; + + @Autowired + private UuidToNewSyncMapper syncMapper; + + @Autowired + private UuidToNewProperties properties; + + @Autowired + @Lazy + private IUuidToNewSyncService self; + + @Override + public int syncBatch(int batchSize) + { + int limit = Math.max(1, Math.min(batchSize, 1000)); + int threads = properties == null ? 1 : properties.resolvedThreadCount(); + String batchNo = UUID.randomUUID().toString().replace("-", ""); + UuidToNewIdHolder job = new UuidToNewIdHolder(); + job.setBatchNo(batchNo); + syncMapper.insertJobLogStart(job); + + List billIds = syncMapper.selectPendingBillIds(limit); + AtomicInteger success = new AtomicInteger(0); + AtomicInteger fail = new AtomicInteger(0); + AtomicInteger skip = new AtomicInteger(0); + + if (billIds.isEmpty()) + { + // no-op + } + else if (threads <= 1) + { + for (String oldBillId : billIds) + { + applyResult(self, oldBillId, success, fail, skip); + } + } + else + { + ExecutorService pool = Executors.newFixedThreadPool(threads); + try + { + List> futures = new ArrayList>(); + for (String oldBillId : billIds) + { + final String id = oldBillId; + futures.add(pool.submit(new Runnable() + { + @Override + public void run() + { + applyResult(self, id, success, fail, skip); + } + })); + } + for (Future f : futures) + { + try + { + f.get(); + } + catch (Exception e) + { + fail.incrementAndGet(); + log.warn("uuid-to-new worker future error: {}", e.toString()); + } + } + } + finally + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(2, TimeUnit.HOURS)) + { + pool.shutdownNow(); + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + job.setFetched(billIds.size()); + job.setSuccess(success.get()); + job.setFail(fail.get()); + job.setSkip(skip.get()); + job.setFailReason("threads=" + threads); + syncMapper.updateJobLogFinish(job); + String summary = String.format( + "batch=%s threads=%d fetched=%d success=%d fail=%d skip=%d", + batchNo, threads, billIds.size(), success.get(), fail.get(), skip.get()); + log.info("uuid-to-new {}", summary); + return billIds.size(); + } + + private void applyResult(IUuidToNewSyncService svc, String oldBillId, + AtomicInteger success, AtomicInteger fail, AtomicInteger skip) + { + try + { + String r = svc.syncOneOrder(oldBillId); + if (SUCCESS.equals(r)) + { + success.incrementAndGet(); + } + else if (SKIP.equals(r)) + { + skip.incrementAndGet(); + } + else + { + fail.incrementAndGet(); + } + } + catch (Exception e) + { + fail.incrementAndGet(); + log.warn("uuid-to-new sync fail billId={}: {}", oldBillId, e.toString()); + } + } + + /** + * 占坑临时 new_bill_id:负数,避免与自增正数冲突。 + */ + static long claimTempNewBillId(String oldBillId) + { + long h = UUID.nameUUIDFromBytes(oldBillId.getBytes(StandardCharsets.UTF_8)).getMostSignificantBits(); + if (h == 0L) + { + h = -1L; + } + else if (h > 0L) + { + h = -h; + } + return h; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String syncOneOrder(String oldBillId) + { + if (oldBillId == null || oldBillId.isEmpty()) + { + return SKIP; + } + if (syncMapper.countOrderBill(oldBillId) <= 0) + { + return SKIP; + } + + String orderNo = syncMapper.selectOrderNo(oldBillId); + UuidToNewIdHolder claim = new UuidToNewIdHolder(); + claim.setOldBillId(oldBillId); + claim.setNewBillId(claimTempNewBillId(oldBillId)); + claim.setOrderNo(orderNo); + claim.setStatus(RUNNING); + if (syncMapper.claimOrderDone(claim) == 0) + { + return SKIP; + } + + Map incomeMap = new HashMap(); + Map feeMap = new HashMap(); + Map payMap = new HashMap(); + Map payFeeMap = new HashMap(); + + UuidToNewIdHolder order = new UuidToNewIdHolder(); + order.setOldBillId(oldBillId); + syncMapper.insertOrderBillNew(order); + Long newBillId = order.getNewBillId(); + if (newBillId == null) + { + throw new IllegalStateException("insert prc_order_bill_new no generated bill_id"); + } + + UuidToNewIdHolder done = new UuidToNewIdHolder(); + done.setOldBillId(oldBillId); + done.setNewBillId(newBillId); + done.setOrderNo(orderNo); + syncMapper.updateOrderDoneSuccess(done); + + syncMapper.insertOrderExtNew(oldBillId, newBillId); + + for (String oldIncomeId : syncMapper.selectIncomeIds(oldBillId)) + { + UuidToNewIdHolder h = new UuidToNewIdHolder(); + h.setOldId(oldIncomeId); + h.setNewBillId(newBillId); + syncMapper.insertIncomeBillNew(h); + if (h.getNewId() == null) + { + throw new IllegalStateException("insert income_new no id for " + oldIncomeId); + } + incomeMap.put(oldIncomeId, h.getNewId()); + } + + for (UuidToNewFeeRow row : syncMapper.selectIncomeFees(oldBillId)) + { + Long newIncomeId = null; + if (row.getParentId() != null && !row.getParentId().isEmpty()) + { + newIncomeId = incomeMap.get(row.getParentId()); + if (newIncomeId == null) + { + throw new IllegalStateException("incomeMap miss " + row.getParentId()); + } + } + UuidToNewIdHolder h = new UuidToNewIdHolder(); + h.setOldId(row.getFeeId()); + h.setNewBillId(newBillId); + h.setNewIncomeId(newIncomeId); + syncMapper.insertIncomeFeeNew(h); + if (h.getNewId() == null) + { + throw new IllegalStateException("insert income_fee_new no id for " + row.getFeeId()); + } + feeMap.put(row.getFeeId(), h.getNewId()); + } + + for (String oldPayId : syncMapper.selectPayIds(oldBillId)) + { + UuidToNewIdHolder h = new UuidToNewIdHolder(); + h.setOldId(oldPayId); + h.setNewBillId(newBillId); + syncMapper.insertPayBillNew(h); + if (h.getNewId() == null) + { + throw new IllegalStateException("insert pay_new no id for " + oldPayId); + } + payMap.put(oldPayId, h.getNewId()); + } + + for (UuidToNewFeeRow row : syncMapper.selectPayFees(oldBillId)) + { + Long newPayId = null; + if (row.getParentId() != null && !row.getParentId().isEmpty()) + { + newPayId = payMap.get(row.getParentId()); + if (newPayId == null) + { + throw new IllegalStateException("payMap miss " + row.getParentId()); + } + } + UuidToNewIdHolder h = new UuidToNewIdHolder(); + h.setOldId(row.getFeeId()); + h.setNewBillId(newBillId); + h.setNewPayId(newPayId); + syncMapper.insertPayFeeNew(h); + Long rowId = h.getNewId(); + if (rowId == null) + { + throw new IllegalStateException("insert pay_fee_new no id for " + row.getFeeId()); + } + syncMapper.updatePayFeeIdEqualsId(rowId); + payFeeMap.put(row.getFeeId(), rowId); + } + + syncMapper.insertArDetailNew(oldBillId, newBillId); + + for (UuidToNewFeeRow row : syncMapper.selectArDetailFees(oldBillId)) + { + Long newFeeId = feeMap.get(row.getFeeId()); + if (newFeeId == null) + { + throw new IllegalStateException("feeMap miss for ar detail fee " + row.getFeeId()); + } + Long detailFeeId = Long.valueOf(row.getParentId()); + syncMapper.insertArDetailFeeNew(detailFeeId, newFeeId); + } + + for (UuidToNewFeeRow row : syncMapper.selectPayBillLines(oldBillId)) + { + Long lineId = Long.valueOf(row.getParentId()); + Long newFeeId = null; + if (row.getFeeId() != null && !row.getFeeId().isEmpty()) + { + newFeeId = payFeeMap.get(row.getFeeId()); + if (newFeeId == null) + { + throw new IllegalStateException("payFeeMap miss for line fee " + row.getFeeId()); + } + } + syncMapper.insertPayBillLineNew(lineId, newBillId, newFeeId); + } + + syncMapper.insertBillingTaskNew(oldBillId, newBillId); + return SUCCESS; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java index 33527f3e..fa7a4e38 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PayBillServiceImpl.java @@ -584,7 +584,7 @@ public class PayBillServiceImpl implements IPayBillService adjust.setAmount(delta); adjust.setCurrency(line.getCurrency()); adjust.setFeeSource("MANUAL"); - adjust.setSourceId(source != null && source.getId() != null ? String.valueOf(source.getId()) : null); + adjust.setSourceId(source == null ? null : source.getFeeId()); adjust.setRemark(open.getReason()); adjust.setSettleSupplierId(bill.getSupplierId()); adjust.setSettleSupplierName(bill.getSupplierName()); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/task/UuidToNewSyncRunner.java b/bms-back/bms-system/src/main/java/com/bms/price/task/UuidToNewSyncRunner.java new file mode 100644 index 00000000..a54e37c5 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/task/UuidToNewSyncRunner.java @@ -0,0 +1,110 @@ +package com.bms.price.task; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.bms.price.config.UuidToNewProperties; +import com.bms.price.migration.service.IUuidToNewSyncService; + +/** + * UUID 阴影同步运行器:监控页开关控制; + * 开启后立即跑批,一批结束立刻下一批;关闭后当前批结束停。 + */ +@Component +public class UuidToNewSyncRunner +{ + private static final Logger log = LoggerFactory.getLogger(UuidToNewSyncRunner.class); + + private final AtomicBoolean wantRun = new AtomicBoolean(false); + private final AtomicBoolean loopActive = new AtomicBoolean(false); + + @Autowired + private UuidToNewProperties properties; + + @Autowired + private IUuidToNewSyncService syncService; + + public boolean isEnabled() + { + return wantRun.get(); + } + + public boolean isRunning() + { + return loopActive.get(); + } + + /** + * 打开:立即启动连续批循环;关闭:标记停止(当前批跑完)。 + */ + public synchronized void setEnabled(boolean enabled) + { + wantRun.set(enabled); + if (enabled) + { + startLoopIfNeeded(); + } + else + { + log.info("uuid-to-new switch OFF, will stop after current batch"); + } + } + + private void startLoopIfNeeded() + { + if (!loopActive.compareAndSet(false, true)) + { + log.info("uuid-to-new loop already running"); + return; + } + Thread t = new Thread(this::runLoop, "uuid-to-new-sync"); + t.setDaemon(true); + t.start(); + log.info("uuid-to-new loop started"); + } + + private void runLoop() + { + try + { + while (wantRun.get()) + { + int batchSize = properties == null ? 200 : properties.resolvedBatchSize(); + int fetched = 0; + try + { + fetched = syncService.syncBatch(batchSize); + } + catch (Exception e) + { + log.error("uuid-to-new batch error", e); + } + if (!wantRun.get()) + { + break; + } + if (fetched <= 0) + { + long sleepMs = properties == null ? 3000L : Math.max(500L, properties.getIdleSleepMs()); + try + { + Thread.sleep(sleepMs); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + break; + } + } + // fetched > 0:立刻下一批 + } + } + finally + { + loopActive.set(false); + log.info("uuid-to-new loop stopped"); + } + } +} diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml index a050fa34..9e3b64ee 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPayBillFeeMapper.xml @@ -94,7 +94,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update prc_pay_bill_fee - set source_id = id + set source_id = fee_id where (source_id is null or source_id = '') and fee_id in #{feeId} diff --git a/bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml new file mode 100644 index 00000000..1d35ee29 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml @@ -0,0 +1,415 @@ + + + + + + + + + + + + insert into prc_order_bill_new ( + bill_key, order_no, tracking_no, transfer_no, + belong_saler, belong_saler_code, settlement_man, settlement_man_code, + service_man, service_man_code, customer_id, customer_code, + customer_name, pieces, actual_weight, receive_weight, + volume, volume_weight, pkg_length, pkg_width, + pkg_height, shipper_name, consignee_name, dest_postcode, + warehouse_code, order_time, order_status, bl_pickup, + pickup_date, order_remark, product_code, product_name, + country_code, country_name, weight, biz_date, + tms_id, product_floor_code, product_floor, price_set_id, + price_set_code, price_set_name, set_type, bind_priority, + customer_priceset_id, psv_id, psd_id, weight_min, + weight_max, weight_label, effective_date, charge_weight, + freight_rate, base_freight_rate, discount_percent, registration_fee, + freight_amount, surcharge_amount, income_total, pay_total, + currency, ar_bill_status, customer_active_status, credit_arrears_status, + refund_status, billing_fail_status, error_message, round_mode, + created_by, create_time, updated_by, update_time + ) + select + o.bill_key, o.order_no, o.tracking_no, o.transfer_no, + o.belong_saler, o.belong_saler_code, o.settlement_man, o.settlement_man_code, + o.service_man, o.service_man_code, o.customer_id, o.customer_code, + o.customer_name, o.pieces, o.actual_weight, o.receive_weight, + o.volume, o.volume_weight, o.pkg_length, o.pkg_width, + o.pkg_height, o.shipper_name, o.consignee_name, o.dest_postcode, + o.warehouse_code, o.order_time, o.order_status, o.bl_pickup, + o.pickup_date, o.order_remark, o.product_code, o.product_name, + o.country_code, o.country_name, o.weight, o.biz_date, + o.tms_id, o.product_floor_code, o.product_floor, o.price_set_id, + o.price_set_code, o.price_set_name, o.set_type, o.bind_priority, + o.customer_priceset_id, o.psv_id, o.psd_id, o.weight_min, + o.weight_max, o.weight_label, o.effective_date, o.charge_weight, + o.freight_rate, o.base_freight_rate, o.discount_percent, o.registration_fee, + o.freight_amount, o.surcharge_amount, o.income_total, o.pay_total, + o.currency, o.ar_bill_status, o.customer_active_status, o.credit_arrears_status, + o.refund_status, o.billing_fail_status, o.error_message, o.round_mode, + o.created_by, o.create_time, o.updated_by, o.update_time + from prc_order_bill o + where o.bill_id = #{oldBillId} + + + + insert into mig_bi_order_done (old_bill_id, new_bill_id, order_no, status, fail_reason, synced_at) + values (#{oldBillId}, #{newBillId}, #{orderNo}, #{status}, #{failReason}, now()) + + + + insert ignore into mig_bi_order_done (old_bill_id, new_bill_id, order_no, status, fail_reason, synced_at) + values (#{oldBillId}, #{newBillId}, #{orderNo}, #{status}, #{failReason}, now()) + + + + update mig_bi_order_done + set new_bill_id = #{newBillId}, + order_no = #{orderNo}, + status = 'SUCCESS', + fail_reason = null, + updated_at = now() + where old_bill_id = #{oldBillId} + + + + insert into prc_order_bill_ext_new ( + bill_id, order_no, create_time, update_time, + upstream_id, line_no, package_num, actual_volume, + cp_type, consignee_mobile, order_returnsign, trade_type, + duty_type, battery_type, consignee_companyname, consignee_address, + consignee_telephone, consignee_state, consignee_city, consignee_suburb, + consignee_passportno, consignee_email, consignee_taxno, consignee_taxnotype, + consignee_taxnocountry, consignee_streetno, consignee_doorno, shipper_taxnotype, + shipper_taxno, shipper_taxnocountry, customer_id, product_imagepath, + order_transactionurl, order_cargoamount, cargo_type, shipper_companyname, + shipper_address1, shipper_address2, shipper_address3, shipper_city, + shipper_state, shipper_postcode, shipper_country, shipper_telephone, + shipper_suburb, shipper_email, shipper_passportno, import_code, + import_name, import_companyname, import_address, import_address2, + import_address3, import_telephone, import_email, import_postcode, + import_city, import_state, import_country, buyerid, + create_at, pay_type, line_code, line_name, + product_code, product_name, product_type, line_type, + cargo_name, order_insurance, product_sum_price, product_sum, + shipper_doorno, tariff_payment_account, topayment, product_floor, + latest_trajectory, way_bill, bl_baging, bag_date, + org_order_status, status_description, zz_date, shipping_date, + order_type, receiving_photo, freight, is_receivable, + is_meet, bill_no_receivable, bill_no_meet, country_eingth_name, + is_receivable_fee, settlement_fee, product_id, service_freight, + is_pay_fee, has_added_surcharge, yf_fee, yf_totail_fee, + trajectory_status, cargo_type_name, service_code, three_code, + volume_height_old, volume_length_old, volume_width_old, register_man_old, + bag_no_old, air_no_old, lading_id, box_sizi_old, + baging_man_old, retrieval_man_old, invoice_amount_old, invoice_title_old, + invoice_weight_old, sku_old, hs_code_old, register_date_old, + invoice_currency_old, trajectory_sub_state, sign_in_time, warehouse_time, + declaration_time, release_time, station_time, takeoff_time, + landing_time, clearance_and_delivery_time, supplier_name, credit_code, + other_id, platform_code, sync_shipment_status, update_weight, + other_bag_no, bag_flag, online_time, outbound_time, + air_confirm_date, shipment_type, track_query_time, air_confirm_time, + package_type, confirm_weight, update_by, customer_pickup_site, + barcode_number, tracking_reflect_status, remote_postcode, clearance_time, + arrive_stock_time, departure_time, first_deliver_time, hs_code_tax_count, + hs_code_tax_amount, hs_code_tax_currency, invoice_url, clearance_no, + push_weight, internal_check_time, air_delay_time, first_landing_time, + second_takeoff_time, des_port_check_time, stay_remark, miss_box_remark, + product_channel_id, customer_track_id, register_site, package_type_financial, + mrn, product_floor_code, order_status, pickup_date, + bl_pickup, dimensional_weight_factor + ) + select + #{newBillId}, e.order_no, e.create_time, e.update_time, + e.upstream_id, e.line_no, e.package_num, e.actual_volume, + e.cp_type, e.consignee_mobile, e.order_returnsign, e.trade_type, + e.duty_type, e.battery_type, e.consignee_companyname, e.consignee_address, + e.consignee_telephone, e.consignee_state, e.consignee_city, e.consignee_suburb, + e.consignee_passportno, e.consignee_email, e.consignee_taxno, e.consignee_taxnotype, + e.consignee_taxnocountry, e.consignee_streetno, e.consignee_doorno, e.shipper_taxnotype, + e.shipper_taxno, e.shipper_taxnocountry, e.customer_id, e.product_imagepath, + e.order_transactionurl, e.order_cargoamount, e.cargo_type, e.shipper_companyname, + e.shipper_address1, e.shipper_address2, e.shipper_address3, e.shipper_city, + e.shipper_state, e.shipper_postcode, e.shipper_country, e.shipper_telephone, + e.shipper_suburb, e.shipper_email, e.shipper_passportno, e.import_code, + e.import_name, e.import_companyname, e.import_address, e.import_address2, + e.import_address3, e.import_telephone, e.import_email, e.import_postcode, + e.import_city, e.import_state, e.import_country, e.buyerid, + e.create_at, e.pay_type, e.line_code, e.line_name, + e.product_code, e.product_name, e.product_type, e.line_type, + e.cargo_name, e.order_insurance, e.product_sum_price, e.product_sum, + e.shipper_doorno, e.tariff_payment_account, e.topayment, e.product_floor, + e.latest_trajectory, e.way_bill, e.bl_baging, e.bag_date, + e.org_order_status, e.status_description, e.zz_date, e.shipping_date, + e.order_type, e.receiving_photo, e.freight, e.is_receivable, + e.is_meet, e.bill_no_receivable, e.bill_no_meet, e.country_eingth_name, + e.is_receivable_fee, e.settlement_fee, e.product_id, e.service_freight, + e.is_pay_fee, e.has_added_surcharge, e.yf_fee, e.yf_totail_fee, + e.trajectory_status, e.cargo_type_name, e.service_code, e.three_code, + e.volume_height_old, e.volume_length_old, e.volume_width_old, e.register_man_old, + e.bag_no_old, e.air_no_old, e.lading_id, e.box_sizi_old, + e.baging_man_old, e.retrieval_man_old, e.invoice_amount_old, e.invoice_title_old, + e.invoice_weight_old, e.sku_old, e.hs_code_old, e.register_date_old, + e.invoice_currency_old, e.trajectory_sub_state, e.sign_in_time, e.warehouse_time, + e.declaration_time, e.release_time, e.station_time, e.takeoff_time, + e.landing_time, e.clearance_and_delivery_time, e.supplier_name, e.credit_code, + e.other_id, e.platform_code, e.sync_shipment_status, e.update_weight, + e.other_bag_no, e.bag_flag, e.online_time, e.outbound_time, + e.air_confirm_date, e.shipment_type, e.track_query_time, e.air_confirm_time, + e.package_type, e.confirm_weight, e.update_by, e.customer_pickup_site, + e.barcode_number, e.tracking_reflect_status, e.remote_postcode, e.clearance_time, + e.arrive_stock_time, e.departure_time, e.first_deliver_time, e.hs_code_tax_count, + e.hs_code_tax_amount, e.hs_code_tax_currency, e.invoice_url, e.clearance_no, + e.push_weight, e.internal_check_time, e.air_delay_time, e.first_landing_time, + e.second_takeoff_time, e.des_port_check_time, e.stay_remark, e.miss_box_remark, + e.product_channel_id, e.customer_track_id, e.register_site, e.package_type_financial, + e.mrn, e.product_floor_code, e.order_status, e.pickup_date, + e.bl_pickup, e.dimensional_weight_factor + from prc_order_bill_ext e + where e.bill_id = #{oldBillId} + + + + + + insert into prc_income_bill_new ( + bill_id, currency, product_amount, redline_amount, + sales_amount, income_total, refund_status, belong_saler, + belong_saler_code, settlement_man, settlement_man_code, create_by, + create_time, update_by, update_time + ) + select + #{newBillId}, i.currency, i.product_amount, i.redline_amount, + i.sales_amount, i.income_total, i.refund_status, i.belong_saler, + i.belong_saler_code, i.settlement_man, i.settlement_man_code, i.create_by, + i.create_time, i.update_by, i.update_time + from prc_income_bill i + where i.income_bill_id = #{oldId} + + + + + + insert into prc_income_bill_fee_new ( + bill_id, income_bill_id, fee_type, fee_name, + sc_id, amount, product_amount, redline_amount, + currency, round_mode, remark, ar_bill_status, + manual_flag, create_time + ) + select + #{newBillId}, #{newIncomeId}, f.fee_type, f.fee_name, + f.sc_id, f.amount, f.product_amount, f.redline_amount, + f.currency, f.round_mode, f.remark, f.ar_bill_status, + f.manual_flag, f.create_time + from prc_income_bill_fee f + where f.fee_id = #{oldId} + + + + + + insert into prc_pay_bill_new ( + bill_id, currency, product_amount, redline_amount, + cost_amount, pay_total, amount_a, amount_b, + amount_c, amount_d, belong_saler, belong_saler_code, + settlement_man, settlement_man_code, create_by, create_time, + update_by, update_time + ) + select + #{newBillId}, p.currency, p.product_amount, p.redline_amount, + p.cost_amount, p.pay_total, p.amount_a, p.amount_b, + p.amount_c, p.amount_d, p.belong_saler, p.belong_saler_code, + p.settlement_man, p.settlement_man_code, p.create_by, p.create_time, + p.update_by, p.update_time + from prc_pay_bill p + where p.pay_bill_id = #{oldId} + + + + + + insert into prc_pay_bill_fee_new ( + fee_id, bill_id, pay_bill_id, fee_type, + sc_id, fee_level, amount, currency, + round_mode, remark, fee_source, source_id, + is_deleted, statement_line_id, settle_supplier_id, settle_supplier_name, + create_time + ) + select + 0, #{newBillId}, #{newPayId}, f.fee_type, + f.sc_id, f.fee_level, f.amount, f.currency, + f.round_mode, f.remark, f.fee_source, f.source_id, + f.is_deleted, f.statement_line_id, f.settle_supplier_id, f.settle_supplier_name, + f.create_time + from prc_pay_bill_fee f + where f.fee_id = #{oldId} + + + + update prc_pay_bill_fee_new set fee_id = id where id = #{id} + + + + insert into ar_bill_detail_new ( + detail_id, ar_bill_id, prc_order_bill_id, detail_no, + order_no, tracking_no, transfer_no, receive_time, + product_code, product_name, goods_description, country_code, + country_name, dest_postcode, actual_weight, receive_weight, + volume_weight, charge_weight, freight_rate, registration_fee, + pkg_length, pkg_width, pkg_height, currency, + total_amount, product_amount, redline_amount, billing_fail_status, + remark, create_by, create_time + ) + select + d.detail_id, d.ar_bill_id, #{newBillId}, d.detail_no, + d.order_no, d.tracking_no, d.transfer_no, d.receive_time, + d.product_code, d.product_name, d.goods_description, d.country_code, + d.country_name, d.dest_postcode, d.actual_weight, d.receive_weight, + d.volume_weight, d.charge_weight, d.freight_rate, d.registration_fee, + d.pkg_length, d.pkg_width, d.pkg_height, d.currency, + d.total_amount, d.product_amount, d.redline_amount, d.billing_fail_status, + d.remark, d.create_by, d.create_time + from ar_bill_detail d + where d.prc_order_bill_id = #{oldBillId} + + + + + + insert into ar_bill_detail_fee_new ( + detail_fee_id, detail_id, prc_income_bill_fee_id, fee_no, + fee_type, fee_name, sc_id, quantity, + unit, unit_price, amount, product_amount, + redline_amount, currency, calculation_formula, round_mode, + remark, create_by, create_time + ) + select + f.detail_fee_id, f.detail_id, #{newFeeId}, f.fee_no, + f.fee_type, f.fee_name, f.sc_id, f.quantity, + f.unit, f.unit_price, f.amount, f.product_amount, + f.redline_amount, f.currency, f.calculation_formula, f.round_mode, + f.remark, f.create_by, f.create_time + from ar_bill_detail_fee f + where f.detail_fee_id = #{detailFeeId} + + + + + + insert into pay_bill_line_new ( + line_id, bill_id, fee_id, order_bill_id, + order_no, lading_no, lading_fee_id, fee_name, + fee_source, fee_level, amount, supplier_amount, + diff_amount, diff_status, tax_rate, amount_net, + tax_amount, base_amount, currency, price_date, + version_no, remote_flag, density_level, line_source + ) + select + l.line_id, l.bill_id, #{newFeeId}, #{newBillId}, + l.order_no, l.lading_no, l.lading_fee_id, l.fee_name, + l.fee_source, l.fee_level, l.amount, l.supplier_amount, + l.diff_amount, l.diff_status, l.tax_rate, l.amount_net, + l.tax_amount, l.base_amount, l.currency, l.price_date, + l.version_no, l.remote_flag, l.density_level, l.line_source + from pay_bill_line l + where l.line_id = #{lineId} + + + + insert into prc_billing_task_new ( + task_id, transfer_no, tracking_no, order_no, + customer_code, bill_type, success_status, bill_time, + bill_id, fail_code, error_message, create_by, + create_time, update_by, update_time + ) + select + t.task_id, t.transfer_no, t.tracking_no, t.order_no, + t.customer_code, t.bill_type, t.success_status, t.bill_time, + #{newBillId}, t.fail_code, t.error_message, t.create_by, + t.create_time, t.update_by, t.update_time + from prc_billing_task t + where t.bill_id = #{oldBillId} + + + + insert into mig_bi_job_log (batch_no, fetched, success, fail, skip, started_at, remark) + values (#{batchNo}, 0, 0, 0, 0, now(), null) + + + + update mig_bi_job_log + set fetched = #{fetched}, + success = #{success}, + fail = #{fail}, + skip = #{skip}, + finished_at = now(), + remark = #{failReason} + where id = #{jobLogId} + + + + + + + + diff --git a/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_dev.md b/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_dev.md new file mode 100644 index 00000000..0c60dc5b --- /dev/null +++ b/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_dev.md @@ -0,0 +1,381 @@ +# UUID -> bigint 阴影同步方案 + +> DDL:`20261114_uuid_to_bi_shadow_tables.sql` +> 字段对齐**测试库** `47.106.220.100:3307/bms`(不是生产库)。 +> 测试库 `prc_pay_bill_fee` 已有 `id` 自增主键,故 `fee_id` 只改 bigint、不再设 AUTO_INCREMENT。 + +## 1. 目标 + +旧表 + 旧代码继续服务。新建 `*_new` 阴影表与定时任务先把数据拷过去;追上后再切 Long 代码并 `RENAME`。 + +要点: + +1. `*_new` = 旧表**全字段**复制,仅 UUID 主键/外键改为 bigint +2. **不加** `old_*` 等额外业务列 +3. 已处理 UUID **只记一张表** `mig_bi_order_done`(不建 `mig_bi_map_*`) +4. 子表 UUID→bigint 映射仅在「一单同步事务」内存中使用,不落库 +5. 一期不改业务代码、不做换表 + +## 2. 命名 + +| 类型 | 命名 | 说明 | +|---|---|---| +| 阴影业务表 | `{原表}_new` | 例:`prc_order_bill_new` | +| UUID 进度(唯一) | `mig_bi_order_done` | 已处理订单 UUID + 新 bill_id | +| 任务日志 | `mig_bi_job_log` | 批次统计(非 UUID 映射) | + +## 3. 表范围与改动列 + +### 3.1 核心阴影表 + +| 旧表 | 新表 | 仅改这些列 | +|---|---|---| +| `prc_order_bill` | `prc_order_bill_new` | `bill_id` -> bigint AI | +| `prc_order_bill_ext` | `prc_order_bill_ext_new` | `bill_id` -> bigint | +| `prc_income_bill` | `prc_income_bill_new` | `income_bill_id` AI;`bill_id` | +| `prc_income_bill_fee` | `prc_income_bill_fee_new` | `fee_id` AI;`bill_id`;`income_bill_id` | +| `prc_pay_bill` | `prc_pay_bill_new` | `pay_bill_id` AI;`bill_id` | +| `prc_pay_bill_fee` | `prc_pay_bill_fee_new` | `fee_id`/`bill_id`/`pay_bill_id`(保留原 `id` AI) | + +其余业务字段与旧表一致。完整列定义见 SQL。 + +### 3.2 引用类阴影表 + +| 旧表 | 新表 | 仅改这些列 | +|---|---|---| +| `ar_bill_detail` | `ar_bill_detail_new` | `prc_order_bill_id` | +| `ar_bill_detail_fee` | `ar_bill_detail_fee_new` | `prc_income_bill_fee_id` | +| `pay_bill_line` | `pay_bill_line_new` | `fee_id`、`order_bill_id` | +| `prc_billing_task` | `prc_billing_task_new` | `bill_id` | +| `ar_bill_generate_task` | `ar_bill_generate_task_new` | `last_source_bill_id` | + +### 3.3 本期不动 + +`prc_quote*`;`ar_bill` / `pay_bill` 等已是 bigint 的账单主表。 + +### 3.4 控制表 + +**mig_bi_order_done**(唯一 UUID 追踪表) + +| 字段 | 类型 | 说明 | +|---|---|---| +| old_bill_id | varchar(36) PK | 原订单 UUID | +| new_bill_id | bigint UK | 新订单 ID | +| order_no | varchar(64) | 订单号 | +| status | varchar(20) | SUCCESS / FAIL / SKIP | +| fail_reason | varchar(500) | 失败原因 | +| synced_at / updated_at | datetime | 时间 | + +**mig_bi_job_log**:batch_no、fetched/success/fail/skip、started_at/finished_at、remark。 + +合计:控制 2 张 + 业务阴影 11 张。不建 `mig_bi_map_*`。 + +## 4. 建表原则 + +1. 全字段复制旧表(对齐测试库结构) +2. 只改 UUID(varchar36)主键/外键为 bigint +3. 业务 `*_new` **不增加**任何额外列 +4. 已处理订单只写 `mig_bi_order_done`;子表映射不落库 +5. 不建指向旧表的外键 + +## 5. 定时同步逻辑 + +### 5.1 运行约定 + +| 项 | 约定 | +|---|---| +| 入口类(拟) | `UuidToNewSyncRunner` / `UuidToNewSyncServiceImpl` | +| 调度 | 监控页开关;开启后连续跑批(无 cron) | +| 开关 | 监控页「连续同步」开关 / API `POST /monitor/uuidToNew/enabled` | +| 批次 | `bms.mig.uuid-to-new.batch-size`(建议 200) | +| 并发 | **单线程**:上一轮未结束不启下一轮 | +| 事务 | **一单一事务**;失败整单回滚 | +| 映射 | 内存 `Map`,事务结束即丢弃 | + +### 5.2 总流程图 + +```mermaid +flowchart TD + A[监控页打开开关] --> B{开关 enabled?} + B -->|否| Z[直接结束] + B -->|是| C{获取单机锁/防并发} + C -->|未拿到| Z + C -->|拿到| D[写 job_log: started
生成 batch_no] + D --> E[查询未处理订单
LEFT JOIN mig_bi_order_done
LIMIT batchSize] + E --> F{fetched == 0?} + F -->|是| G[job_log: success=0 结束] + F -->|否| H[逐单循环] + H --> I[syncOneOrder 一单一事务] + I --> J{结果} + J -->|SUCCESS| K[success++] + J -->|FAIL| L[fail++
记日志/可选写 FAIL 行] + J -->|SKIP| M[skip++] + K --> N{还有下一单?} + L --> N + M --> N + N -->|是| H + N -->|否| O[更新 mig_bi_job_log
finished + 计数] + O --> P[释放锁] + P --> Z2[本轮结束] + G --> P +``` + +### 5.3 单订单同步流程图(syncOneOrder) + +```mermaid +flowchart TD + S[开始: oldBillId] --> T[开启事务] + T --> U[内存 Map 清空
income/pay/fee 映射] + U --> V[读旧 prc_order_bill] + V --> W{订单存在?} + W -->|否| X[SKIP / 回滚] + W -->|是| Y[INSERT prc_order_bill_new
拿到 newBillId] + Y --> Z1[INSERT mig_bi_order_done
SUCCESS + newBillId] + Z1 --> A1[拷贝 prc_order_bill_ext_new
bill_id=newBillId] + A1 --> B1[拷贝应收 prc_income_bill_new
内存记 income 映射] + B1 --> C1[拷贝 prc_income_bill_fee_new
内存记 fee 映射] + C1 --> D1[拷贝应付 prc_pay_bill_new
内存记 pay 映射] + D1 --> E1[拷贝 prc_pay_bill_fee_new
内存记 payFee 映射] + E1 --> F1[拷贝 ar_bill_detail_new
prc_order_bill_id=newBillId] + F1 --> G1[拷贝 ar_bill_detail_fee_new
fee 外键用内存 map] + G1 --> H1[拷贝 pay_bill_line_new
fee_id/order_bill_id 用 map] + H1 --> I1[可选: billing_task /
ar_bill_generate_task] + I1 --> J1[提交事务] + J1 --> K1[返回 SUCCESS] + X --> L1[返回 SKIP] + V -.->|任意步骤异常| R1[整单回滚] + R1 --> R2[返回 FAIL
可另事务写 FAIL 到 done] +``` + +### 5.4 抓单 SQL + +```sql +SELECT o.bill_id + FROM prc_order_bill o + LEFT JOIN mig_bi_order_done d ON d.old_bill_id = o.bill_id + WHERE d.old_bill_id IS NULL + ORDER BY o.create_time, o.bill_id + LIMIT :batchSize; +``` + +### 5.5 单订单写入顺序 + +```text +1. prc_order_bill_new -> newBillId(AI) +2. mig_bi_order_done -> oldBillId / newBillId / SUCCESS +3. prc_order_bill_ext_new -> bill_id = newBillId +4. prc_income_bill_new -> 内存 map: oldIncomeId -> newIncomeId +5. prc_income_bill_fee_new -> bill_id/income_bill_id 用 map;内存记 fee +6. prc_pay_bill_new -> 内存 map: oldPayId -> newPayId +7. prc_pay_bill_fee_new -> bill_id/pay_bill_id 用 map;内存记 payFee +8. ar_bill_detail_new -> prc_order_bill_id = newBillId +9. ar_bill_detail_fee_new -> prc_income_bill_fee_id 用 fee map +10. pay_bill_line_new -> fee_id / order_bill_id 用 map +11. (可选)prc_billing_task_new / ar_bill_generate_task_new +``` + +### 5.6 实例讲解(一单走完) + +假设本轮 `batchSize=200`,抓到其中一单如下(简化 ID,仅演示映射关系)。 + +#### 旧库里已有(UUID) + +| 表 | 主键 UUID | 关联 | +|---|---|---| +| `prc_order_bill` | `bill_id = aaa-001` | order_no=`O20261114001` | +| `prc_order_bill_ext` | 同行 `bill_id=aaa-001` | | +| `prc_income_bill` | `income_bill_id = iii-001` | `bill_id=aaa-001` | +| `prc_income_bill_fee` | `fee_id = iff-101` | `bill_id=aaa-001`,`income_bill_id=iii-001` | +| `prc_income_bill_fee` | `fee_id = iff-102` | 同上,另一费用行 | +| `prc_pay_bill` | `pay_bill_id = ppp-001` | `bill_id=aaa-001` | +| `prc_pay_bill_fee` | `fee_id = pff-201` | `bill_id=aaa-001`,`pay_bill_id=ppp-001` | +| `ar_bill_detail` | (自有 bigint PK) | `prc_order_bill_id=aaa-001` | +| `ar_bill_detail_fee` | (自有 bigint PK) | `prc_income_bill_fee_id=iff-101` | +| `pay_bill_line` | (自有 bigint PK) | `order_bill_id=aaa-001`,`fee_id=pff-201` | + +`mig_bi_order_done` 里还没有 `aaa-001` → 本轮会被抓到。 + +#### 同步过程(一单一事务) + +```text +步骤0 内存 Map 清空 + incomeMap={}, feeMap={}, payMap={}, payFeeMap={} + +步骤1 INSERT prc_order_bill_new + (其它业务字段从旧行原样拷) + AI 得到 newBillId = 10001 + 内存:本单 newBillId=10001 + +步骤2 INSERT mig_bi_order_done + old_bill_id=aaa-001, new_bill_id=10001, order_no=O20261114001, status=SUCCESS + +步骤3 INSERT prc_order_bill_ext_new + bill_id = 10001 ← 已不是 aaa-001 + +步骤4 INSERT prc_income_bill_new + AI 得到 newIncomeId = 20001 + bill_id = 10001 + 内存:incomeMap[iii-001] = 20001 + +步骤5 INSERT prc_income_bill_fee_new ×2 + iff-101 → AI newFeeId=30001;bill_id=10001;income_bill_id=20001 + iff-102 → AI newFeeId=30002;bill_id=10001;income_bill_id=20001 + 内存:feeMap[iff-101]=30001, feeMap[iff-102]=30002 + +步骤6 INSERT prc_pay_bill_new + AI 得到 newPayId = 40001 + bill_id = 10001 + 内存:payMap[ppp-001] = 40001 + +步骤7 INSERT prc_pay_bill_fee_new + pff-201 → AI newPayFeeId=50001;bill_id=10001;pay_bill_id=40001 + 内存:payFeeMap[pff-201] = 50001 + (测试库该表另有 id AI,照旧拷,与 fee_id 无关) + +步骤8 INSERT ar_bill_detail_new + prc_order_bill_id = 10001 ← 用 newBillId,不是 aaa-001 + +步骤9 INSERT ar_bill_detail_fee_new + prc_income_bill_fee_id = 30001 ← feeMap[iff-101],不是 iff-101 + +步骤10 INSERT pay_bill_line_new + order_bill_id = 10001 + fee_id = 50001 ← payFeeMap[pff-201] + +步骤11 (可选)billing_task / generate_task 同类替换 bill_id + +步骤12 COMMIT + 内存 Map 丢弃;下次再同步别的单重新建 Map +``` + +#### 同步后对照 + +| 含义 | 旧 UUID | 新 bigint | +|---|---|---| +| 订单 | aaa-001 | **10001**(落在 `mig_bi_order_done`) | +| 应收主 | iii-001 | 20001(仅内存用过,不落 map 表) | +| 应收费用 | iff-101 / iff-102 | 30001 / 30002 | +| 应付主 | ppp-001 | 40001 | +| 应付费用 | pff-201 | 50001 | + +旧表 `prc_order_bill` 等**一行未改**;现网代码仍读旧 UUID 表。 + +#### 下一轮会怎样 + +- 抓单 SQL 再 LEFT JOIN `mig_bi_order_done`:`aaa-001` 已存在 → **不会再抓** +- 若步骤 5 抛错:整单 ROLLBACK → `*_new` 与 `done` 都没有这单 → 下轮还会抓到 `aaa-001` 重试 + +```mermaid +flowchart LR + subgraph OLD[旧表 UUID] + O1[aaa-001 订单] + O2[iii-001 应收] + O3[iff-101 应收费] + O4[ppp-001 应付] + O5[pff-201 应付费] + end + subgraph MEM[本单内存 Map] + M1[aaa-001→10001] + M2[iii-001→20001] + M3[iff-101→30001] + M4[ppp-001→40001] + M5[pff-201→50001] + end + subgraph NEW[阴影表 bigint] + N1[order_new 10001] + N2[income_new 20001] + N3[income_fee_new 30001] + N4[pay_new 40001] + N5[pay_fee_new 50001] + N6[done: aaa-001→10001] + end + O1 --> M1 --> N1 + O1 --> N6 + O2 --> M2 --> N2 + O3 --> M3 --> N3 + O4 --> M4 --> N4 + O5 --> M5 --> N5 +``` + + +### 5.7 幂等与失败 + +| 场景 | 处理 | +|---|---| +| 已在 `mig_bi_order_done` | 抓单 SQL 自动排除,不重复拷 | +| 单步异常 | 整单回滚;业务 `*_new` 不留半成品 | +| 记 FAIL | 另开短事务写 `status=FAIL`(可选);否则下轮会再抓到 | +| 人工重跑某单 | 先按 `new_bill_id` 删该单 `*_new` 行 + 删 `done`,再跑任务 | +| 内存 map | 仅本单事务内有效;不落 `mig_bi_map_*` | + +### 5.8 积压监控 + +```sql +SELECT + (SELECT COUNT(*) FROM prc_order_bill) AS old_cnt, + (SELECT COUNT(*) FROM mig_bi_order_done WHERE status='SUCCESS') AS done_cnt, + (SELECT COUNT(*) FROM prc_order_bill_new) AS new_cnt; +``` + +`old_cnt - done_cnt` 为积压;趋近 0 且抽查一致后再进二期。 + +### 5.9 已实现类(一期) + +```text +com.bms.price.task.UuidToNewSyncRunner +com.bms.price.migration.service.IUuidToNewSyncService +com.bms.price.migration.service.impl.UuidToNewSyncServiceImpl +com.bms.price.migration.mapper.UuidToNewSyncMapper +mapper/price/UuidToNewSyncMapper.xml +com.bms.price.config.UuidToNewProperties +``` + +配置(`application.yml`,默认关闭): + +```yaml +bms: + mig: + uuid-to-new: + enabled: false + batch-size: 200 + thread-count: 2 # 1~4;4æ ¸8G 冲刺建议 2~3 + cron: "0/30 * * * * ?" +``` + +批内多线程:先一次抓单,再分给 N 个工作线程;每单 `INSERT IGNORE` 占坑 `mig_bi_order_done(RUNNING)`,避免重复处理。 + +启用前:先在测库执行 `20261114_uuid_to_bi_shadow_tables.sql`,再把 `enabled` 改 `true`。 + +业务 Domain / 现网读写路径一期**不动**。 + + + + +### 5.10 监控菜单 + +- 页面:`monitor/uuidToNew/index` +- 菜单:合并在 `20261114_uuid_to_bi_shadow_tables.sql` 文件末尾 +- API:`GET /monitor/uuidToNew/progress`、`GET /monitor/uuidToNew/jobLog/list`、`POST /monitor/uuidToNew/enabled` +- 开关权限:`monitor:uuidToNew:switch`(见 `20261114_uuid_to_bi_shadow_tables.sql` 末尾菜单段) + +## 6. 一期 / 二期 + +**一期(不影响现网)** + +1. 测库执行 DDL +2. ~~实现 Job/Service/Mapper~~(已完成,见 §5.9) +3. 测库开启 `bms.mig.uuid-to-new.enabled=true` 并看 `mig_bi_job_log` +4. 不改业务 Domain,不做 RENAME + +**二期切换** + +1. lag=0 并校验 +2. 发布 Long 代码 +3. `RENAME`:`xxx` → `xxx_uuid_bak`,`xxx_new` → `xxx` +4. 冒烟后删备份与 `mig_bi_*` + +## 7. 风险与验收 + +风险:磁盘约翻倍;仅任务写 `*_new`;一单事务内内存 map 丢失则整单回滚重试。 + +验收:DDL 成功;任务稳定;done 追上订单量;抽查一致;旧功能正常。 diff --git a/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_tables.sql b/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_tables.sql new file mode 100644 index 00000000..a12a2ba9 --- /dev/null +++ b/bms-back/sql/uuid_to_bi_shadow/20261114_uuid_to_bi_shadow_tables.sql @@ -0,0 +1,651 @@ +-- 方案文档:20261114_uuid_to_bi_shadow_dev.md +-- 含:阴影表 DDL + 监控菜单 +-- 阴影表 DDL:字段结构对齐【测试库】47.106.220.100:3307/bms +-- 全字段复制;仅 UUID(varchar36) 相关列改为 bigint;不加 old_* +-- UUID 追踪仅 mig_bi_order_done;无 mig_bi_map_*;无外键;旧表不动 +-- 注意:测试库 prc_pay_bill_fee 已有 id AI 主键,fee_id 只改 bigint 不设 AI +SET NAMES utf8mb4; + + +-- ========== 控制表 ========== +DROP TABLE IF EXISTS `mig_bi_job_log`; +DROP TABLE IF EXISTS `mig_bi_order_done`; + +CREATE TABLE `mig_bi_order_done` ( + `old_bill_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原订单UUID', + `new_bill_id` bigint NOT NULL COMMENT '新订单bigint', + `order_no` varchar(64) DEFAULT NULL COMMENT '订单号', + `status` varchar(20) NOT NULL DEFAULT 'SUCCESS' COMMENT 'SUCCESS/FAIL/SKIP', + `fail_reason` varchar(500) DEFAULT NULL COMMENT '失败原因', + `synced_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '同步时间', + `updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`old_bill_id`), + UNIQUE KEY `uk_mig_bi_order_new` (`new_bill_id`), + KEY `idx_mig_bi_order_synced` (`synced_at`), + KEY `idx_mig_bi_order_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='已处理订单UUID进度(唯一 UUID 追踪表)'; + +CREATE TABLE `mig_bi_job_log` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', + `batch_no` varchar(64) NOT NULL COMMENT '批次号', + `fetched` int NOT NULL DEFAULT 0 COMMENT '抓取数', + `success` int NOT NULL DEFAULT 0 COMMENT '成功数', + `fail` int NOT NULL DEFAULT 0 COMMENT '失败数', + `skip` int NOT NULL DEFAULT 0 COMMENT '跳过数', + `started_at` datetime NOT NULL COMMENT '开始时间', + `finished_at` datetime DEFAULT NULL COMMENT '结束时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`id`), + KEY `idx_mig_bi_job_started` (`started_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='同步任务批次日志'; + + +-- ========== 业务阴影表(结构对齐测试库)========== + + +DROP TABLE IF EXISTS `prc_order_bill_new`; + +CREATE TABLE IF NOT EXISTS `prc_order_bill_new` ( + `bill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '计费ID', + `bill_key` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'MD5(order_no)', + `order_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单号', + `tracking_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '运单号', + `transfer_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转单号', + `belong_saler` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属销售员', + `belong_saler_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '销售员编码', + `settlement_man` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员', + `settlement_man_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员编码', + `service_man` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客服员', + `service_man_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客服员编码', + `customer_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '客户ID', + `customer_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户代码', + `customer_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户名称', + `pieces` int(11) DEFAULT NULL COMMENT '件数', + `actual_weight` decimal(10,3) DEFAULT NULL COMMENT '实际重量KG', + `receive_weight` decimal(10,3) DEFAULT NULL COMMENT '收货重KG(上游TAB_ORDER.WEIGHT)', + `volume` decimal(12,4) DEFAULT NULL COMMENT '体积m3', + `volume_weight` decimal(10,3) DEFAULT NULL COMMENT '材积重KG', + `pkg_length` decimal(10,2) DEFAULT NULL COMMENT '长CM', + `pkg_width` decimal(10,2) DEFAULT NULL COMMENT '宽CM', + `pkg_height` decimal(10,2) DEFAULT NULL COMMENT '高CM', + `shipper_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人', + `consignee_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人', + `dest_postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的邮编', + `warehouse_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '仓库', + `order_time` datetime DEFAULT NULL COMMENT '下单时间', + `order_status` int(11) DEFAULT NULL COMMENT '上游订单状态', + `bl_pickup` tinyint(4) DEFAULT NULL COMMENT '是否收货:0否/1是', + `pickup_date` datetime DEFAULT NULL COMMENT '收货时间', + `order_remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单备注', + `product_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品代码', + `product_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称', + `country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '国家二字码', + `country_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '国家名称', + `weight` decimal(10,3) DEFAULT NULL COMMENT '计费重量KG', + `biz_date` datetime NOT NULL COMMENT '计费时间', + `tms_id` bigint(20) DEFAULT NULL COMMENT '多租户系统ID', + `product_floor_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '渠道编码', + `product_floor` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '渠道名称', + `price_set_id` bigint(20) DEFAULT NULL COMMENT '价格套餐ID', + `price_set_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '套餐编码', + `price_set_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '套餐名称', + `set_type` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '计价类型', + `bind_priority` int(11) DEFAULT NULL COMMENT '客户套餐优先级', + `customer_priceset_id` bigint(20) DEFAULT NULL COMMENT '客户套餐绑定ID', + `psv_id` bigint(20) DEFAULT NULL COMMENT '套餐版本ID,对应 prc_price_set_version.psv_id', + `psd_id` bigint(20) DEFAULT NULL COMMENT '套餐明细ID,对应 prc_price_set_detail.psd_id', + `weight_min` decimal(10,4) DEFAULT NULL COMMENT '重量段下限', + `weight_max` decimal(10,4) DEFAULT NULL COMMENT '重量段上限', + `weight_label` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '重量段展示', + `effective_date` date DEFAULT NULL COMMENT '产品明细生效日', + `charge_weight` decimal(10,3) DEFAULT NULL COMMENT '计费重量', + `freight_rate` decimal(10,4) DEFAULT NULL COMMENT '运费单价', + `base_freight_rate` decimal(10,4) DEFAULT NULL COMMENT '折扣前单价', + `discount_percent` decimal(5,2) DEFAULT NULL COMMENT '折扣百分数', + `registration_fee` decimal(10,2) DEFAULT NULL COMMENT '挂号费', + `freight_amount` decimal(10,2) DEFAULT NULL COMMENT '运费金额', + `surcharge_amount` decimal(10,2) DEFAULT NULL COMMENT '附加费合计', + `income_total` decimal(10,2) DEFAULT NULL COMMENT '应收总额', + `pay_total` decimal(10,2) DEFAULT NULL COMMENT '应付总额', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '币种', + `ar_bill_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNBILLED' COMMENT '应收账单状态:UNBILLED未出账/PARTIAL部分出账/BILLED已出账', + `customer_active_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户生效状态:ACTIVE正常 MISSING不存在 DISABLED停用', + `credit_arrears_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '额度欠费状态:NORMAL正常 ARREARS欠费', + `refund_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE' COMMENT '退费状态:NONE未退费/REFUNDED已退费', + `billing_fail_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '计费失败状态:0成功/1销售价未匹配产品/2销售价未匹配国家/3销售价未匹配重量段/23产品价未匹配产品/24产品价未匹配国家/25产品价未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败/26未计费。仅0/1/2/3/23/24/25写入应收主表,仅0写入应收费用', + `error_message` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + `round_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式', + `created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建人', + `create_time` datetime DEFAULT NULL, + `updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新人', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`bill_id`) USING BTREE, + UNIQUE KEY `uk_bill_order` (`order_no`) USING BTREE, + UNIQUE KEY `uk_bill_key` (`bill_key`) USING BTREE, + KEY `idx_bill_customer` (`customer_code`,`biz_date`) USING BTREE, + KEY `idx_bill_product` (`product_code`,`country_code`) USING BTREE, + KEY `idx_bill_ar_status` (`customer_id`,`currency`,`ar_bill_status`,`biz_date`) USING BTREE, + KEY `idx_bill_ar_async_cursor` (`customer_id`,`currency`,`ar_bill_status`,`create_time`,`bill_id`) USING BTREE, + KEY `idx_order_bill_transfer_no` (`transfer_no`) USING BTREE, + KEY `idx_order_bill_customer_active` (`customer_active_status`) USING BTREE, + KEY `idx_order_bill_credit_arrears` (`credit_arrears_status`) USING BTREE, + KEY `idx_order_bill_psv` (`psv_id`) USING BTREE, + KEY `idx_order_bill_psd` (`psd_id`) USING BTREE, + KEY `idx_ar_order_bill_status_scope` (`customer_id`,`currency`,`ar_bill_status`,`biz_date`,`bill_id`) USING BTREE, + KEY `idx_order_bill_refund_status` (`refund_status`) USING BTREE, + KEY `idx_bill_biz_date_report` (`biz_date`,`bill_id`) USING BTREE, + KEY `idx_order_bill_billing_fail_status` (`billing_fail_status`) USING BTREE, + KEY `idx_order_bill_tracking_no` (`tracking_no`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='订单信息主表'; + + +DROP TABLE IF EXISTS `prc_order_bill_ext_new`; + +CREATE TABLE IF NOT EXISTS `prc_order_bill_ext_new` ( + `bill_id` bigint NOT NULL COMMENT '计费订单ID', + `order_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单号', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `upstream_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'guid', + `line_no` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '线路', + `package_num` int(11) DEFAULT NULL COMMENT '包裹数量', + `actual_volume` decimal(10,3) DEFAULT NULL COMMENT '实际体积重', + `cp_type` tinyint(4) DEFAULT NULL COMMENT '线路产品类型 1全程 2干线 3尾程', + `consignee_mobile` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人手机', + `order_returnsign` char(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '退回标志,默认N表示不退回,Y标表示退回。中邮可以忽略该属性', + `trade_type` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '交易类型,值为ZYXT', + `duty_type` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关税类型,值为DDU或DDP', + `battery_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电池类型代码,联系货代提供', + `consignee_companyname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件公司名,如有最好填写', + `consignee_address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件地址街道,必填', + `consignee_telephone` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件电话,必填', + `consignee_state` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '州/省', + `consignee_city` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '城市', + `consignee_suburb` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件区,选填', + `consignee_passportno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件护照号,选填', + `consignee_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮箱,选填', + `consignee_taxno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人税号', + `consignee_taxnotype` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人税号类型', + `consignee_taxnocountry` varchar(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人税号国家', + `consignee_streetno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '街道号', + `consignee_doorno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '门牌号', + `shipper_taxnotype` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '税号类型,邮政产品可选值:IOSS,NO - IOSS,OTHER;DHL可选值:SDT、VAT、FTZ、DAN、EOR、CNP、EIN等(类型说明参照文档底部“DHL发件人税号类型”)', + `shipper_taxno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人税号', + `shipper_taxnocountry` varchar(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人税号国家,用国家二字码', + `customer_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户ID,必填', + `product_imagepath` text COLLATE utf8mb4_unicode_ci COMMENT '图片地址,多图片地址用分号隔开', + `order_transactionurl` text COLLATE utf8mb4_unicode_ci COMMENT '产品销售地址', + `order_cargoamount` decimal(12,5) DEFAULT NULL COMMENT '选填;用于DHL/FEDEX运费;或用于白关申报(订单实际金额,特殊渠道使用);或其他用途', + `cargo_type` char(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '包裹类型,P代表包裹,D代表文件,B代表PAK袋', + `shipper_companyname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人公司名,选填', + `shipper_address1` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人地址1,选填', + `shipper_address2` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人地址2,选填', + `shipper_address3` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人地址3,选填', + `shipper_city` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人城市,选填', + `shipper_state` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人州,选填', + `shipper_postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人邮编,选填', + `shipper_country` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人国家,选填', + `shipper_telephone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人电话,选填', + `shipper_suburb` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人区,选填', + `shipper_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人邮箱,选填', + `shipper_passportno` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人护照号,选填', + `import_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商代码', + `import_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商名称', + `import_companyname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商公司名称', + `import_address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商地址', + `import_address2` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商地址2', + `import_address3` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商地址3', + `import_telephone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商电话', + `import_email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商邮箱', + `import_postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商邮编', + `import_city` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商城市', + `import_state` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商州省', + `import_country` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '进口商国家', + `buyerid` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '买家ID', + `create_at` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人', + `pay_type` tinyint(4) DEFAULT NULL COMMENT '付款方式 0-预付 1-到付', + `line_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '线路产品编号', + `line_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '线路产品名称', + `product_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '上游产品代码', + `product_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '上游产品名称', + `product_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品类型(全称产品/全称B2B/尾程产品)', + `line_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '物流模式(国际快递/邮政小包)', + `cargo_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货物类型(普货/敏感/特货)', + `order_insurance` decimal(12,5) DEFAULT NULL COMMENT '保险金额', + `product_sum_price` decimal(12,5) DEFAULT NULL COMMENT '商品总价值', + `product_sum` decimal(18,6) DEFAULT NULL COMMENT '商品总件数', + `shipper_doorno` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发件人门牌号', + `tariff_payment_account` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '关税支付账号', + `topayment` decimal(12,5) DEFAULT NULL COMMENT '代收货款', + `product_floor` text COLLATE utf8mb4_unicode_ci COMMENT '渠道名称', + `latest_trajectory` text COLLATE utf8mb4_unicode_ci COMMENT '订单轨迹', + `way_bill` text COLLATE utf8mb4_unicode_ci COMMENT '电子面单', + `bl_baging` tinyint(4) DEFAULT NULL COMMENT '是否装箱 0 未装箱 1 已装箱', + `bag_date` datetime DEFAULT NULL COMMENT '装箱时间', + `org_order_status` tinyint(4) DEFAULT NULL COMMENT '渠道状态 0-未下单 1-获取中 2-获取成功 3-获取失败', + `status_description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '状态描述,对当前轨迹状态的详细说明', + `zz_date` datetime DEFAULT NULL COMMENT '中转时间', + `shipping_date` datetime DEFAULT NULL COMMENT '出货时间', + `order_type` decimal(18,6) DEFAULT NULL COMMENT '0-中性面单 1-尾程面单', + `receiving_photo` text COLLATE utf8mb4_unicode_ci COMMENT '收货照片', + `freight` decimal(10,3) DEFAULT NULL COMMENT '应收总运费', + `is_receivable` tinyint(4) DEFAULT NULL COMMENT '是否已经生成应收账单[0.未生成,1.已生成]', + `is_meet` tinyint(4) DEFAULT NULL COMMENT '是否已经生成应付账单[0.未生成,1.已生成]', + `bill_no_receivable` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '应收账单编号', + `bill_no_meet` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '应付账单编号', + `country_eingth_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收件人英文国家名称', + `is_receivable_fee` tinyint(4) DEFAULT NULL COMMENT '是否已计算应收费用0-未计算 1-已计算', + `settlement_fee` decimal(10,3) DEFAULT NULL COMMENT '应收运费', + `product_id` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '中转产品ID', + `service_freight` decimal(10,3) DEFAULT NULL COMMENT '服务商运费', + `is_pay_fee` tinyint(4) DEFAULT NULL COMMENT '是否已计算应付费用0-未计算 1-已计算', + `has_added_surcharge` tinyint(4) DEFAULT NULL COMMENT '是否已经添加过附加费 0未添加 1 已添加', + `yf_fee` decimal(10,3) DEFAULT NULL COMMENT '应付运费', + `yf_totail_fee` decimal(10,3) DEFAULT NULL COMMENT '应付总运费', + `trajectory_status` tinyint(4) DEFAULT NULL COMMENT '轨迹状态:1.已收货、2.查询失败、3.转运中、4.派送中、5.投递失败、6.退仓、7.异常、8.已签收', + `cargo_type_name` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货物类型中文', + `service_code` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属服务商编号', + `three_code` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '三字码', + `volume_height_old` decimal(10,2) DEFAULT NULL COMMENT '高,单位 CM', + `volume_length_old` decimal(10,2) DEFAULT NULL COMMENT '长,单位 CM', + `volume_width_old` decimal(10,2) DEFAULT NULL COMMENT '宽,单位 CM', + `register_man_old` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收货人', + `bag_no_old` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱号', + `air_no_old` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '航空主单号', + `lading_id` bigint(20) DEFAULT NULL COMMENT '应付提单ID', + `box_sizi_old` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '箱子规格', + `baging_man_old` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '装箱人', + `retrieval_man_old` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '出货人', + `invoice_amount_old` text COLLATE utf8mb4_unicode_ci COMMENT '申报总价值,必填', + `invoice_title_old` text COLLATE utf8mb4_unicode_ci COMMENT '英文品名,必填', + `invoice_weight_old` text COLLATE utf8mb4_unicode_ci COMMENT '单件重', + `sku_old` text COLLATE utf8mb4_unicode_ci COMMENT '中文品名', + `hs_code_old` text COLLATE utf8mb4_unicode_ci COMMENT '海关编码', + `register_date_old` datetime DEFAULT NULL COMMENT '收件时间', + `invoice_currency_old` text COLLATE utf8mb4_unicode_ci COMMENT '申报币种', + `trajectory_sub_state` tinyint(4) DEFAULT NULL COMMENT '轨迹子状态', + `sign_in_time` datetime DEFAULT NULL COMMENT '签收时间', + `warehouse_time` datetime DEFAULT NULL COMMENT '仓库操作完成,已安排出库时间', + `declaration_time` datetime DEFAULT NULL COMMENT '送仓报关时间', + `release_time` datetime DEFAULT NULL COMMENT '报关放行时间', + `station_time` datetime DEFAULT NULL COMMENT '货站收货时间', + `takeoff_time` datetime DEFAULT NULL COMMENT '航班起飞时间', + `landing_time` datetime DEFAULT NULL COMMENT '航班落地时间', + `clearance_and_delivery_time` datetime DEFAULT NULL COMMENT '已通知清关行提货时间', + `supplier_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '生产销售供应商', + `credit_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '社会信用代码', + `other_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '其他ID', + `platform_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '平台代码', + `sync_shipment_status` tinyint(4) DEFAULT NULL COMMENT '物流同步状态, 0:未同步, 1: 已同步', + `update_weight` decimal(10,3) DEFAULT NULL COMMENT '客户更新重量', + `other_bag_no` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '其它箱号', + `bag_flag` tinyint(4) DEFAULT NULL COMMENT '装箱标记,1: 已装箱 0:未装箱', + `online_time` datetime DEFAULT NULL COMMENT '上网时间', + `outbound_time` datetime DEFAULT NULL COMMENT '出库时间', + `air_confirm_date` datetime DEFAULT NULL COMMENT '主单号确认时间', + `shipment_type` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单配送类型', + `track_query_time` datetime DEFAULT NULL COMMENT '轨迹抓取时间', + `air_confirm_time` datetime DEFAULT NULL COMMENT '航班已配载时间', + `package_type` tinyint(4) DEFAULT NULL COMMENT '0:软包;1:硬包', + `confirm_weight` decimal(10,3) DEFAULT NULL COMMENT '客户确认重', + `update_by` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人', + `customer_pickup_site` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '取货地点', + `barcode_number` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '面单条形码', + `tracking_reflect_status` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '轨迹映射状态', + `remote_postcode` tinyint(4) DEFAULT NULL COMMENT '是否偏远邮编', + `clearance_time` datetime DEFAULT NULL COMMENT 'CLEARANCE_TIME', + `arrive_stock_time` datetime DEFAULT NULL COMMENT 'ARRIVE_STOCK_TIME', + `departure_time` datetime DEFAULT NULL COMMENT 'DEPARTURE_TIME', + `first_deliver_time` datetime DEFAULT NULL COMMENT 'FIRST_DELIVER_TIME', + `hs_code_tax_count` decimal(10,2) DEFAULT NULL COMMENT 'HS_CODE_TAX_COUNT', + `hs_code_tax_amount` decimal(10,4) DEFAULT NULL COMMENT 'HS_CODE_TAX_AMOUNT', + `hs_code_tax_currency` varchar(8) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'HS_CODE_TAX_CURRENCY', + `invoice_url` text COLLATE utf8mb4_unicode_ci COMMENT 'INVOICE_URL', + `clearance_no` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'CLEARANCE_NO', + `push_weight` decimal(10,3) DEFAULT NULL COMMENT 'PUSH_WEIGHT', + `internal_check_time` datetime DEFAULT NULL COMMENT 'INTERNAL_CHECK_TIME', + `air_delay_time` datetime DEFAULT NULL COMMENT 'AIR_DELAY_TIME', + `first_landing_time` datetime DEFAULT NULL COMMENT 'FIRST_LANDING_TIME', + `second_takeoff_time` datetime DEFAULT NULL COMMENT 'SECOND_TAKEOFF_TIME', + `des_port_check_time` datetime DEFAULT NULL COMMENT 'DES_PORT_CHECK_TIME', + `stay_remark` text COLLATE utf8mb4_unicode_ci COMMENT 'STAY_REMARK', + `miss_box_remark` text COLLATE utf8mb4_unicode_ci COMMENT 'MISS_BOX_REMARK', + `product_channel_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'PRODUCT_CHANNEL_ID', + `customer_track_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'CUSTOMER_TRACK_ID', + `register_site` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'REGISTER_SITE', + `package_type_financial` tinyint(4) DEFAULT NULL COMMENT '财务包裹类型,软硬包', + `mrn` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'MRN', + `product_floor_code` text COLLATE utf8mb4_unicode_ci COMMENT '渠道编码', + `order_status` tinyint(4) DEFAULT NULL COMMENT '订单状态', + `pickup_date` datetime DEFAULT NULL COMMENT '收件时间', + `bl_pickup` tinyint(4) DEFAULT NULL COMMENT '是否收件 0未收件 1已收件', + `dimensional_weight_factor` decimal(10,3) DEFAULT NULL COMMENT '计费重', + PRIMARY KEY (`bill_id`) USING BTREE, + UNIQUE KEY `uk_order_bill_ext_order` (`order_no`) USING BTREE, + KEY `idx_order_bill_ext_channel` (`product_channel_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='订单计费扩展表'; + + +DROP TABLE IF EXISTS `prc_income_bill_new`; + +CREATE TABLE IF NOT EXISTS `prc_income_bill_new` ( + `income_bill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '应收主表ID', + `bill_id` bigint NOT NULL COMMENT '计费订单ID', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT 'CNY' COMMENT '币种', + `product_amount` decimal(18,4) DEFAULT NULL COMMENT '产品价合计', + `redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价合计', + `sales_amount` decimal(18,4) DEFAULT NULL COMMENT '销售价合计(当前计费结果)', + `income_total` decimal(18,4) DEFAULT NULL COMMENT '应收合计(当前等于销售价)', + `refund_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE' COMMENT '是否退货:NONE未退货/REFUNDED已退货', + `belong_saler` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员/销售员', + `belong_saler_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '销售员编码', + `settlement_man` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员', + `settlement_man_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员编码', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`income_bill_id`) USING BTREE, + UNIQUE KEY `uk_income_bill_order_bill` (`bill_id`) USING BTREE, + KEY `idx_income_bill_saler` (`belong_saler`) USING BTREE, + KEY `idx_income_bill_settlement` (`settlement_man`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应收主表(订单计费与应收费用中间层,与订单一对一)'; + + +DROP TABLE IF EXISTS `prc_income_bill_fee_new`; + +CREATE TABLE IF NOT EXISTS `prc_income_bill_fee_new` ( + `fee_id` bigint NOT NULL AUTO_INCREMENT COMMENT '费用明细ID', + `bill_id` bigint NOT NULL COMMENT '计费ID', + `income_bill_id` bigint DEFAULT NULL COMMENT '应收主表ID', + `fee_type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用类型(字典bms_fee_type)', + `fee_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '费用名称', + `sc_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '附加费配置ID', + `amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '金额', + `product_amount` decimal(18,4) DEFAULT NULL COMMENT '产品价', + `redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '币种', + `round_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'HALF_UP' COMMENT '取值方式', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + `ar_bill_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'UNBILLED' COMMENT '是否生成账单状态:UNBILLED未生成/BILLED已生成', + `manual_flag` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否手动增加:0否 1是', + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`fee_id`) USING BTREE, + UNIQUE KEY `uk_bill_fee_item` (`bill_id`,`fee_type`,`sc_id`) USING BTREE, + KEY `idx_bill_fee_type` (`fee_type`) USING BTREE, + KEY `idx_income_fee_income_bill` (`income_bill_id`) USING BTREE, + KEY `idx_income_fee_ar_status` (`ar_bill_status`) USING BTREE, + KEY `idx_income_fee_bill_ar_status` (`bill_id`,`ar_bill_status`,`fee_id`) USING BTREE, + KEY `idx_income_fee_bill_status_amount` (`bill_id`,`ar_bill_status`,`amount`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应收费用明细'; + + +DROP TABLE IF EXISTS `prc_pay_bill_new`; + +CREATE TABLE IF NOT EXISTS `prc_pay_bill_new` ( + `pay_bill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '应付主表ID', + `bill_id` bigint NOT NULL COMMENT '计费订单ID', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT 'CNY' COMMENT '币种', + `product_amount` decimal(12,4) DEFAULT NULL COMMENT '产品价合计', + `redline_amount` decimal(12,4) DEFAULT NULL COMMENT '红线价合计', + `cost_amount` decimal(12,4) DEFAULT NULL COMMENT '成本价合计(当前计费结果)', + `pay_total` decimal(12,4) DEFAULT NULL COMMENT '应付合计(当前等于成本价)', + `amount_a` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT 'A段合计', + `amount_b` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT 'B段合计', + `amount_c` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT 'C段合计', + `amount_d` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT 'D段合计', + `belong_saler` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员/销售员', + `belong_saler_code` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '销售员编码', + `settlement_man` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员', + `settlement_man_code` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '结算员编码', + `create_by` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`pay_bill_id`) USING BTREE, + UNIQUE KEY `uk_pay_bill_order_bill` (`bill_id`) USING BTREE, + KEY `idx_pay_bill_saler` (`belong_saler`) USING BTREE, + KEY `idx_pay_bill_settlement` (`settlement_man`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应付主表(订单计费与应付费用中间层,与订单一对一)'; + + +DROP TABLE IF EXISTS `prc_pay_bill_fee_new`; + +CREATE TABLE IF NOT EXISTS `prc_pay_bill_fee_new` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', + `fee_id` bigint NOT NULL COMMENT '费用明细ID', + `bill_id` bigint NOT NULL COMMENT '订单主表ID', + `pay_bill_id` bigint DEFAULT NULL COMMENT '应付主表ID', + `fee_type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用类型(字典bms_fee_type)', + `sc_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '附加费配置ID', + `fee_level` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '费用所属阶段:1、a段(头程仓储);2、b段(提单运输);3、c段(清关);4、d段(尾程)', + `amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '金额', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CNY' COMMENT '币种', + `round_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'HALF_UP' COMMENT '取值方式', + `remark` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注', + `fee_source` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'CALC' COMMENT 'CALC计算 LADING提单分摊 MANUAL人工', + `source_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源数据id', + `is_deleted` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否删除:0未删除 1已删除', + `statement_line_id` bigint(20) DEFAULT NULL COMMENT '已进入的应付账单行ID,空表示未出账', + `settle_supplier_id` bigint(20) DEFAULT NULL COMMENT '结算供应商ID,空表示还没标明付款对象', + `settle_supplier_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '结算供应商名称', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_pay_bill_fee_item` (`bill_id`,`fee_type`,`sc_id`) USING BTREE, + KEY `idx_pay_bill_fee_type` (`fee_type`) USING BTREE, + KEY `idx_pay_fee_pay_bill` (`pay_bill_id`) USING BTREE, + KEY `idx_pay_fee_settle` (`settle_supplier_id`,`statement_line_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应付费用明细'; + + +DROP TABLE IF EXISTS `ar_bill_detail_new`; + +CREATE TABLE IF NOT EXISTS `ar_bill_detail_new` ( + `detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '账单订单明细ID', + `ar_bill_id` bigint(20) NOT NULL COMMENT '应收账单ID', + `prc_order_bill_id` bigint NOT NULL COMMENT '来源计费订单ID', + `detail_no` int(11) NOT NULL COMMENT '账单内顺序号', + `order_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单号快照', + `tracking_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '运单号快照', + `transfer_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转单号快照', + `receive_time` datetime DEFAULT NULL COMMENT '收件或入库时间', + `product_code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品代码快照', + `product_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品名称快照', + `goods_description` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '货物描述快照', + `country_code` char(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的国家二字码', + `country_name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的国家名称', + `dest_postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '目的邮编', + `actual_weight` decimal(12,3) DEFAULT NULL COMMENT '实际重量KG', + `receive_weight` decimal(10,3) DEFAULT NULL COMMENT 'prc_order_bill.receive_weight收货重来源快照', + `volume_weight` decimal(12,3) DEFAULT NULL COMMENT '材积重量KG', + `charge_weight` decimal(12,3) DEFAULT NULL COMMENT '计费重量KG', + `freight_rate` decimal(10,4) DEFAULT NULL COMMENT '运费单价快照', + `registration_fee` decimal(10,2) DEFAULT NULL COMMENT '处理费快照', + `pkg_length` decimal(10,2) DEFAULT NULL COMMENT '长度CM', + `pkg_width` decimal(10,2) DEFAULT NULL COMMENT '宽度CM', + `pkg_height` decimal(10,2) DEFAULT NULL COMMENT '高度CM', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '币种快照', + `total_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '订单费用合计', + `product_amount` decimal(18,4) DEFAULT NULL COMMENT '产品价合计快照', + `redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价合计快照', + `billing_fail_status` tinyint(4) DEFAULT NULL COMMENT '计费失败状态快照:0成功/1未匹配产品/2未匹配国家/3未匹配重量段', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单账单备注', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`detail_id`) USING BTREE, + UNIQUE KEY `uk_ar_bill_detail_source` (`ar_bill_id`,`prc_order_bill_id`) USING BTREE, + UNIQUE KEY `uk_ar_bill_detail_no` (`ar_bill_id`,`detail_no`) USING BTREE, + KEY `idx_ar_bill_detail_source` (`prc_order_bill_id`) USING BTREE, + KEY `idx_ar_bill_detail_bill_transfer_no` (`ar_bill_id`,`transfer_no`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应收账单订单快照'; + + +DROP TABLE IF EXISTS `ar_bill_detail_fee_new`; + +CREATE TABLE IF NOT EXISTS `ar_bill_detail_fee_new` ( + `detail_fee_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '账单费用明细ID', + `detail_id` bigint(20) NOT NULL COMMENT '账单订单明细ID', + `prc_income_bill_fee_id` bigint NOT NULL COMMENT '来源收入费用ID', + `fee_no` int(11) NOT NULL COMMENT '订单内费用顺序号', + `fee_type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用类型快照', + `fee_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '费用名称快照', + `sc_id` bigint(20) DEFAULT NULL COMMENT '附加费配置ID快照', + `quantity` decimal(12,3) DEFAULT NULL COMMENT '计费数量', + `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '计费单位', + `unit_price` decimal(15,4) DEFAULT NULL COMMENT '费用单价', + `amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '费用金额', + `product_amount` decimal(18,4) DEFAULT NULL COMMENT '产品价快照', + `redline_amount` decimal(18,4) DEFAULT NULL COMMENT '红线价快照', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '币种', + `calculation_formula` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '计算公式或说明', + `round_mode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '取舍规则', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '费用备注', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`detail_fee_id`) USING BTREE, + UNIQUE KEY `uk_ar_detail_prc_income_fee` (`detail_id`,`prc_income_bill_fee_id`) USING BTREE, + KEY `idx_ar_detail_fee_type` (`fee_type`) USING BTREE, + KEY `idx_ar_detail_prc_income_fee` (`prc_income_bill_fee_id`) USING BTREE, + KEY `idx_ar_detail_fee_order` (`detail_id`,`fee_no`,`detail_fee_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应收账单费用快照'; + + +DROP TABLE IF EXISTS `pay_bill_line_new`; + +CREATE TABLE IF NOT EXISTS `pay_bill_line_new` ( + `line_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '账单行ID', + `bill_id` bigint(20) NOT NULL COMMENT '应付账单ID', + `fee_id` bigint DEFAULT NULL COMMENT '应付费用明细ID', + `order_bill_id` bigint DEFAULT NULL COMMENT '订单ID', + `order_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单号', + `lading_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '提单号', + `lading_fee_id` bigint(20) DEFAULT NULL COMMENT '留在主单上的费用', + `fee_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '费用名称', + `fee_source` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'CALC计算 LADING提单分摊 MANUAL人工', + `fee_level` varchar(8) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '费用阶段 1A 2B 3C 4D', + `amount` decimal(12,4) NOT NULL COMMENT '金额快照', + `supplier_amount` decimal(12,4) DEFAULT NULL COMMENT '对方金额', + `diff_amount` decimal(12,4) DEFAULT NULL COMMENT '差额,对方减我方', + `diff_status` varchar(20) CHARACTER SET utf8mb4 NOT NULL DEFAULT 'NONE' COMMENT 'NONE无差异 OPEN待处理 ACCEPT_OURS接受我方 ACCEPT_THEIRS接受对方', + `tax_rate` decimal(8,4) NOT NULL DEFAULT '0.0000' COMMENT '税率,0.06 表示 6%', + `amount_net` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT '不含税金额', + `tax_amount` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT '税额', + `base_amount` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT '本位币金额,人民币', + `currency` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '币种', + `price_date` date DEFAULT NULL COMMENT '取价日', + `version_no` int(11) DEFAULT NULL COMMENT '价卡版本', + `remote_flag` char(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Y偏远 N非偏远', + `density_level` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'HIGH MEDIUM LOW', + `line_source` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'ORDER订单费用 LADING主单费用', + PRIMARY KEY (`line_id`), + KEY `idx_pay_bill_line_bill` (`bill_id`), + KEY `idx_pay_bill_line_order` (`order_bill_id`), + KEY `idx_pay_bill_line_lading_fee` (`lading_fee_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应付账单费用明细'; + + +DROP TABLE IF EXISTS `prc_billing_task_new`; + +CREATE TABLE IF NOT EXISTS `prc_billing_task_new` ( + `task_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID', + `transfer_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '转单号', + `tracking_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '运单号', + `order_no` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单号', + `customer_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '客户代码', + `bill_type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '计费类型:PENDING待计费 REBILL重计费 REFUND退费', + `success_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'WAITING' COMMENT '是否计费成功:WAITING待处理 SUCCESS成功 FAILED失败', + `bill_time` datetime DEFAULT NULL COMMENT '计费时间', + `bill_id` bigint DEFAULT NULL COMMENT '计费订单ID', + `fail_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败码', + `error_message` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`task_id`) USING BTREE, + KEY `idx_billing_task_transfer` (`transfer_no`) USING BTREE, + KEY `idx_billing_task_tracking` (`tracking_no`) USING BTREE, + KEY `idx_billing_task_order` (`order_no`) USING BTREE, + KEY `idx_billing_task_status` (`success_status`,`create_time`) USING BTREE, + KEY `idx_billing_task_type` (`bill_type`) USING BTREE, + KEY `idx_billing_task_fail_code` (`fail_code`) USING BTREE, + KEY `idx_billing_task_customer` (`customer_code`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='计费任务表'; + + +DROP TABLE IF EXISTS `ar_bill_generate_task_new`; + +CREATE TABLE IF NOT EXISTS `ar_bill_generate_task_new` ( + `task_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '生成任务ID', + `task_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '生成任务编号', + `ar_bill_id` bigint(20) NOT NULL COMMENT '应收账单ID', + `selection_mode` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '选择模式:EXPLICIT=手动选择,ALL_MATCHED=全选查询结果', + `request_payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '生成请求快照JSON', + `snapshot_create_time` datetime DEFAULT NULL COMMENT '来源订单创建时间快照', + `last_source_bill_id` bigint DEFAULT NULL COMMENT '来源订单游标', + `total_count` int(11) NOT NULL DEFAULT '-1' COMMENT '待处理订单数,-1表示尚未统计', + `processed_count` int(11) NOT NULL DEFAULT '0' COMMENT '已处理订单数', + `success_count` int(11) NOT NULL DEFAULT '0' COMMENT '成功订单数', + `total_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '当前生成金额', + `status` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '任务状态:WAITING=待处理,RUNNING=生成中,SUCCESS=生成成功,FAILED=生成失败,CANCEL_REQUESTED=取消中,CANCELED=已取消', + `error_message` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '错误信息', + `request_token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '请求幂等标识', + `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建用户ID', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `started_at` datetime DEFAULT NULL COMMENT '开始时间', + `heartbeat_time` datetime DEFAULT NULL COMMENT '最近心跳时间', + `finished_at` datetime DEFAULT NULL COMMENT '完成时间', + PRIMARY KEY (`task_id`) USING BTREE, + UNIQUE KEY `uk_ar_bill_generate_task_no` (`task_no`) USING BTREE, + UNIQUE KEY `uk_ar_bill_generate_request_token` (`request_token`) USING BTREE, + KEY `idx_ar_bill_generate_status` (`status`,`create_time`) USING BTREE, + KEY `idx_ar_bill_generate_bill` (`ar_bill_id`) USING BTREE, + KEY `idx_ar_bill_generate_creator` (`create_user_id`,`status`,`create_time`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='应收账单异步生成任务'; + + +-- ========== 监控菜单(系统监控) ========== + +SET @monitorParent := ( + SELECT menu_id FROM sys_menu + WHERE menu_type = 'M' AND (path = 'monitor' OR menu_name = N'系统监控') + ORDER BY menu_id LIMIT 1 +); + +INSERT INTO sys_menu ( + menu_id, menu_name, parent_id, order_num, path, component, query, route_name, + is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark +) +SELECT 2360, N'UUID阴影同步', IFNULL(@monitorParent, 2), 12, + 'uuidToNew', 'monitor/uuidToNew/index', '', 'UuidToNew', + 1, 0, 'C', '0', '0', 'monitor:uuidToNew:list', 'chart', + 'admin', sysdate(), N'查看 mig_bi_job_log 与同步积压' +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2360 OR perms = 'monitor:uuidToNew:list'); + +INSERT INTO sys_menu ( + menu_id, menu_name, parent_id, order_num, path, component, + is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time +) +SELECT 2361, N'查询', 2360, 1, '', NULL, + 1, 0, 'F', '0', '0', 'monitor:uuidToNew:list', '#', 'admin', sysdate() +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2361); + +INSERT INTO sys_menu ( + menu_id, menu_name, parent_id, order_num, path, component, + is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark +) +SELECT 2362, N'开关', 2360, 2, '', NULL, + 1, 0, 'F', '0', '0', 'monitor:uuidToNew:switch', '#', 'admin', sysdate(), N'打开/关闭连续同步' +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2362 OR perms = 'monitor:uuidToNew:switch'); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 1, m.menu_id + FROM (SELECT 2360 AS menu_id UNION SELECT 2361 UNION SELECT 2362) m + WHERE EXISTS (SELECT 1 FROM sys_menu x WHERE x.menu_id = m.menu_id) + AND NOT EXISTS ( + SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id + ); diff --git a/bms-front/src/api/monitor/uuidToNew.js b/bms-front/src/api/monitor/uuidToNew.js new file mode 100644 index 00000000..754a9f32 --- /dev/null +++ b/bms-front/src/api/monitor/uuidToNew.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +export function getUuidToNewProgress() { + return request({ + url: '/monitor/uuidToNew/progress', + method: 'get' + }) +} + +export function listUuidToNewJobLog(query) { + return request({ + url: '/monitor/uuidToNew/jobLog/list', + method: 'get', + params: query + }) +} + +/** 打开/关闭;开启时可带 threadCount/batchSize */ +export function setUuidToNewEnabled(payload) { + const data = typeof payload === 'boolean' + ? { enabled: payload } + : { + enabled: !!payload.enabled, + threadCount: payload.threadCount, + batchSize: payload.batchSize + } + return request({ + url: '/monitor/uuidToNew/enabled', + method: 'post', + data + }) +} + +/** 仅关闭时修改线程/批次 */ +export function updateUuidToNewConfig(payload) { + return request({ + url: '/monitor/uuidToNew/config', + method: 'post', + data: { + threadCount: payload.threadCount, + batchSize: payload.batchSize + } + }) +} diff --git a/bms-front/src/views/monitor/uuidToNew/index.vue b/bms-front/src/views/monitor/uuidToNew/index.vue new file mode 100644 index 00000000..c52247c5 --- /dev/null +++ b/bms-front/src/views/monitor/uuidToNew/index.vue @@ -0,0 +1,329 @@ + + + + +