From 0179a3594204ef1bf92c169fca589eca38235310 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Sun, 15 Nov 2026 09:01:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0UUID=E5=88=B0=E6=96=B0?= =?UTF-8?q?=E8=B4=B9=E7=94=A8=E8=A1=8C=E5=92=8C=E9=98=B4=E5=BD=B1=E5=90=8C?= =?UTF-8?q?=E6=AD=A5Mapper=E7=9A=84=E6=B3=A8=E9=87=8A=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=85=B3=E8=81=94=E8=A1=A8=E5=9B=9E=E5=86=99=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/domain/UuidToNewFeeRow.java | 2 +- .../migration/domain/UuidToNewIdHolder.java | 5 +- .../migration/mapper/UuidToNewSyncMapper.java | 17 +- .../impl/UuidToNewSyncServiceImpl.java | 42 +- .../mapper/price/UuidToNewSyncMapper.xml | 96 ++-- .../20261114_uuid_to_bi_shadow_dev.md | 447 +++++------------- .../20261114_uuid_to_bi_shadow_tables.sql | 144 +----- .../20261115_uuid_order_id_scheme.sql | 109 +++++ 8 files changed, 303 insertions(+), 559 deletions(-) create mode 100644 bms-back/sql/uuid_to_bi_shadow/20261115_uuid_order_id_scheme.sql 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 index b35e0986..dc3f5ed8 100644 --- 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 @@ -1,7 +1,7 @@ package com.bms.price.migration.domain; /** - * 旧费用行:用于逐行插入并建内存 Map + * 旧收入费用行:逐行插入 income *_new 并建内存 Map */ public class UuidToNewFeeRow { 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 index 5bd6af37..2a66eb41 100644 --- 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 @@ -1,14 +1,17 @@ package com.bms.price.migration.domain; /** - * 阴影插入时传参 / 接收自增主键 + * 阴影插入传参 / 接收自增主键、批次日志 */ public class UuidToNewIdHolder { private String oldBillId; private Long newBillId; + /** 旧 income_bill_id / fee_id(UUID) */ private String oldId; + /** 新 income_bill_id / fee_id(bigint) */ private Long newId; + /** 新 income_bill_id(费用行用) */ private Long newIncomeId; private String orderNo; private String status; 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 index 3b488ee4..a86494a3 100644 --- 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 @@ -8,7 +8,7 @@ import com.bms.price.migration.domain.UuidToNewIdHolder; import com.bms.price.migration.domain.UuidToNewProgressVo; /** - * UUID -> bigint 阴影同步 Mapper + * UUID -> bigint 阴影同步 Mapper(订单+income *_new + 关联表 order_id) */ public interface UuidToNewSyncMapper { @@ -39,13 +39,19 @@ public interface UuidToNewSyncMapper int insertIncomeFeeNew(UuidToNewIdHolder holder); - int insertArDetailNew(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + /** 回写现网关联表 order_id(= prc_order_bill_new.bill_id) */ - List selectArDetailFees(@Param("oldBillId") String oldBillId); + int updatePayBillOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); - int insertArDetailFeeNew(@Param("detailFeeId") Long detailFeeId, @Param("newFeeId") Long newFeeId); + int updatePayFeeOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); - int insertBillingTaskNew(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + int updateArDetailOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + int updatePayBillLineOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + int updateBillingTaskOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); + + int updateArGenTaskOrderId(@Param("oldBillId") String oldBillId, @Param("newBillId") Long newBillId); int insertJobLogStart(UuidToNewIdHolder holder); @@ -55,4 +61,3 @@ public interface UuidToNewSyncMapper List selectJobLogList(MigBiJobLog query); } - 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 index 2c3a71df..6ec92d74 100644 --- 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 @@ -27,7 +27,8 @@ import com.bms.price.migration.mapper.UuidToNewSyncMapper; import com.bms.price.migration.service.IUuidToNewSyncService; /** - * UUID -> bigint 阴影同步:占坑独立提交;一单一事务;批内可多线程。 + * UUID -> bigint 阴影同步:订单+income *_new + 关联表回写 order_id。 + * 占坑独立提交;一单一事务;批内可多线程。 */ @Service public class UuidToNewSyncServiceImpl implements IUuidToNewSyncService @@ -293,9 +294,6 @@ public class UuidToNewSyncServiceImpl implements IUuidToNewSyncService return SKIP; } - Map incomeMap = new HashMap(); - Map feeMap = new HashMap(); - UuidToNewIdHolder order = new UuidToNewIdHolder(); order.setOldBillId(oldBillId); syncMapper.insertOrderBillNew(order); @@ -313,7 +311,17 @@ public class UuidToNewSyncServiceImpl implements IUuidToNewSyncService syncMapper.updateOrderDoneSuccess(done); syncMapper.insertOrderExtNew(oldBillId, newBillId); + copyIncomeShadow(oldBillId, newBillId); + fillRelatedOrderIds(oldBillId, newBillId); + return SUCCESS; + } + /** + * 拷贝收入主表/费用到 *_new:income_bill_id、fee_id UUID -> bigint AI + */ + private void copyIncomeShadow(String oldBillId, Long newBillId) + { + Map incomeMap = new HashMap(); for (String oldIncomeId : syncMapper.selectIncomeIds(oldBillId)) { UuidToNewIdHolder h = new UuidToNewIdHolder(); @@ -347,23 +355,17 @@ public class UuidToNewSyncServiceImpl implements IUuidToNewSyncService { throw new IllegalStateException("insert income_fee_new no id for " + row.getFeeId()); } - feeMap.put(row.getFeeId(), h.getNewId()); } + } - 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); - } - - syncMapper.insertBillingTaskNew(oldBillId, newBillId); - return SUCCESS; + /** 现网关联表回写 order_id;旧 UUID 列不动 */ + private void fillRelatedOrderIds(String oldBillId, Long newBillId) + { + syncMapper.updatePayBillOrderId(oldBillId, newBillId); + syncMapper.updatePayFeeOrderId(oldBillId, newBillId); + syncMapper.updateArDetailOrderId(oldBillId, newBillId); + syncMapper.updatePayBillLineOrderId(oldBillId, newBillId); + syncMapper.updateBillingTaskOrderId(oldBillId, newBillId); + syncMapper.updateArGenTaskOrderId(oldBillId, newBillId); } } 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 index c717c329..a1d9a8f0 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/UuidToNewSyncMapper.xml @@ -234,71 +234,43 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where f.fee_id = #{oldId} - - 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} - + - + + update prc_pay_bill + set order_id = #{newBillId} + where 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} - + + update prc_pay_bill_fee + set order_id = #{newBillId} + where bill_id = #{oldBillId} + - - 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} - + + update ar_bill_detail + set order_id = #{newBillId} + where prc_order_bill_id = #{oldBillId} + + + + update pay_bill_line + set order_id = #{newBillId} + where order_bill_id = #{oldBillId} + + + + update prc_billing_task + set order_id = #{newBillId} + where bill_id = #{oldBillId} + + + + update ar_bill_generate_task + set order_id = #{newBillId} + where last_source_bill_id = #{oldBillId} + insert into mig_bi_job_log (batch_no, fetched, success, fail, skip, started_at, remark) 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 index 0c60dc5b..25288f40 100644 --- 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 @@ -1,381 +1,170 @@ -# UUID -> bigint 阴影同步方案 +# UUID -> bigint 阴影同步方案(order_id 方案) -> DDL:`20261114_uuid_to_bi_shadow_tables.sql` +> DDL:`20261114_uuid_to_bi_shadow_tables.sql`(订单+income 四张 + mig) +> 变更 SQL:`20261115_uuid_order_id_scheme.sql`(删多余 *_new(保留订单/收入)、关联表加 `order_id`) > 字段对齐**测试库** `47.106.220.100:3307/bms`(不是生产库)。 -> 测试库 `prc_pay_bill_fee` 已有 `id` 自增主键,故 `fee_id` 只改 bigint、不再设 AUTO_INCREMENT。 +> **代码已按 order_id 方案改完**(`UuidToNewSyncServiceImpl` / Mapper);部署前须先执行 `20261115_uuid_order_id_scheme.sql`。 ## 1. 目标 -旧表 + 旧代码继续服务。新建 `*_new` 阴影表与定时任务先把数据拷过去;追上后再切 Long 代码并 `RENAME`。 +旧表 + 旧代码继续服务。**建订单+income 四张阴影表**;其他关联 `prc_order_bill` 的表在**现网表**上增加 `order_id`(bigint),订单同步出新 `bill_id` 后回写各表 `order_id`。 + +### 为何改 + +旧方案为每张关联表都建 `*_new` 并复制行(含 AR `detail_id` 复用等),成本高、易冲突、与生产结构差异大。 +新方案:**订单阴影化 + 关联表只补 bigint 外键列**,旧 UUID 列保留给现网代码。 要点: -1. `*_new` = 旧表**全字段**复制,仅 UUID 主键/外键改为 bigint -2. **不加** `old_*` 等额外业务列 -3. 已处理 UUID **只记一张表** `mig_bi_order_done`(不建 `mig_bi_map_*`) -4. 子表 UUID→bigint 映射仅在「一单同步事务」内存中使用,不落库 -5. 一期不改业务代码、不做换表 +1. `prc_order_bill_new` / `prc_order_bill_ext_new`(`bill_id` bigint) +2. `prc_income_bill_new` / `prc_income_bill_fee_new`(`income_bill_id` / `fee_id` UUID -> bigint AI) +3. 保留 `mig_bi_order_done`、`mig_bi_job_log` +4. **不再建** pay/AR/billing 等 `*_new` +5. 关联表增加可空 `order_id`;同步成功后 `UPDATE ... SET order_id = new_bill_id` +6. 旧 UUID 列(`bill_id` / `prc_order_bill_id` 等)**不动**,现网仍用 UUID +7. 一期不切业务读写到 `*_new`(仍读旧表) ## 2. 命名 | 类型 | 命名 | 说明 | |---|---|---| -| 阴影业务表 | `{原表}_new` | 例:`prc_order_bill_new` | -| UUID 进度(唯一) | `mig_bi_order_done` | 已处理订单 UUID + 新 bill_id | -| 任务日志 | `mig_bi_job_log` | 批次统计(非 UUID 映射) | +| 订单阴影 | `prc_order_bill_new` / `prc_order_bill_ext_new` | 订单 bigint | +| 收入阴影 | `prc_income_bill_new` / `prc_income_bill_fee_new` | income/fee UUID -> bigint | +| 进度 | `mig_bi_order_done` | 旧订单 UUID → 新 `bill_id` | +| 批次日志 | `mig_bi_job_log` | 批次统计 | +| 关联补列 | `各表.order_id` | = `prc_order_bill_new.bill_id` | -## 3. 表范围与改动列 +## 3. 表范围 -### 3.1 核心阴影表 +### 3.1 阴影表(订单+income 共 4 张) -| 旧表 | 新表 | 仅改这些列 | +| 旧表 | 新表 | 改动 | |---|---|---| | `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) | +| `prc_order_bill_ext` | `prc_order_bill_ext_new` | `bill_id` -> bigint(=订单新 ID) | +| `prc_income_bill` | `prc_income_bill_new` | `income_bill_id` -> bigint AI;`bill_id`=新订单 | +| `prc_income_bill_fee` | `prc_income_bill_fee_new` | `fee_id` -> bigint AI;`income_bill_id`=新主表 | -其余业务字段与旧表一致。完整列定义见 SQL。 +### 3.2 现网表增加 `order_id`(income 现网表不加,甠 *_new) -### 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` | +| `prc_pay_bill` | `bill_id` varchar36 | `order_id` bigint | +| `prc_pay_bill_fee` | `bill_id` varchar36 | `order_id` bigint | +| `ar_bill_detail` | `prc_order_bill_id` varchar36 | `order_id` bigint | +| `pay_bill_line` | `order_bill_id` varchar36 | `order_id` bigint | +| `prc_billing_task` | `bill_id` varchar36 | `order_id` bigint | +| `ar_bill_generate_task` | `last_source_bill_id` varchar36 | `order_id` bigint | -### 3.3 本期不动 +说明: -`prc_quote*`;`ar_bill` / `pay_bill` 等已是 bigint 的账单主表。 +- `ar_bill_detail_fee` **不直接**挂订单,本期**不加** `order_id`(可经 `detail` 关联)。 +- `order_id` 允许 NULL;未同步订单为 NULL。 +- 建议索引:`KEY idx__order_id (order_id)`。 -### 3.4 控制表 +### 3.3 控制表(保留) -**mig_bi_order_done**(唯一 UUID 追踪表) +**mig_bi_order_done** -| 字段 | 类型 | 说明 | -|---|---|---| -| 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 | 时间 | +| 字段 | 说明 | +|---|---| +| old_bill_id | 原订单 UUID PK | +| new_bill_id | 新订单 bigint UK | +| order_no / status / fail_reason / synced_at / updated_at | 同旧 | -**mig_bi_job_log**:batch_no、fetched/success/fail/skip、started_at/finished_at、remark。 +**mig_bi_job_log**:批次 fetched/success/fail/skip 等,同旧。 -合计:控制 2 张 + 业务阴影 11 张。不建 `mig_bi_map_*`。 +### 3.4 不再建(若库中已有则 DROP) -## 4. 建表原则 +`prc_pay_bill_new`、`prc_pay_bill_fee_new`、 +`ar_bill_detail_new`、`ar_bill_detail_fee_new`、`pay_bill_line_new`、 +`prc_billing_task_new`、`ar_bill_generate_task_new` -1. 全字段复制旧表(对齐测试库结构) -2. 只改 UUID(varchar36)主键/外键为 bigint -3. 业务 `*_new` **不增加**任何额外列 -4. 已处理订单只写 `mig_bi_order_done`;子表映射不落库 -5. 不建指向旧表的外键 +## 4. 同步逻辑 -## 5. 定时同步逻辑 - -### 5.1 运行约定 +### 4.1 运行约定 | 项 | 约定 | |---|---| -| 入口类(拟) | `UuidToNewSyncRunner` / `UuidToNewSyncServiceImpl` | -| 调度 | 监控页开关;开启后连续跑批(无 cron) | -| 开关 | 监控页「连续同步」开关 / API `POST /monitor/uuidToNew/enabled` | -| 批次 | `bms.mig.uuid-to-new.batch-size`(建议 200) | -| 并发 | **单线程**:上一轮未结束不启下一轮 | -| 事务 | **一单一事务**;失败整单回滚 | -| 映射 | 内存 `Map`,事务结束即丢弃 | +| 调度 | 监控页开关;开后连续跑批 | +| 批次 | `batch-size`(建议 200) | +| 事务 | **一单一事务**(订单+income 插入 + 关联表 UPDATE order_id) | +| 失败 | 整单回滚;FAIL 落 `mig_bi_order_done`(独立事务),避免死循环 | +| Redis | 开关/线程/批次可持久;建议启动不自动开(可再讨) | -### 5.2 总流程图 +### 4.2 单单步骤 + +```text +1) claim mig_bi_order_done (RUNNING) +2) INSERT prc_order_bill_new <- 自旧订单;得 new_bill_id +3) UPDATE mig_bi_order_done -> SUCCESS + new_bill_id +4) INSERT prc_order_bill_ext_new (bill_id = new_bill_id) +5) INSERT prc_income_bill_new / prc_income_bill_fee_new(income/fee UUID -> bigint) +6) UPDATE 关联表 order_id = new_bill_id + WHERE 旧 UUID 列 = old_bill_id +7) return SUCCESS +``` + +不再:拷贝 pay/AR/billing 到 `*_new`。 + +### 4.3 总流程 ```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 + A[开关 ON] --> B[抓取未处理订单] + B --> C{fetched=0?} + C -->|是| D[空转睡眠] + C -->|否| E[逐单事务] + E --> F[INSERT order/ext/income *_new] + F --> G[UPDATE 各表 order_id] + G --> H[mig_bi_order_done SUCCESS] + H --> I[下一单/下一批] + D --> A ``` -### 5.3 单订单同步流程图(syncOneOrder) +### 4.4 例子 -```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] -``` +旧订单 `bill_id=aaa-001` 同步后: -### 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_*` | +| `prc_order_bill_new` | `bill_id=10001`(新) | +| `prc_order_bill_ext_new` | `bill_id=10001` | +| `prc_income_bill_new` | 新 `income_bill_id`;`bill_id=10001` | +| `prc_income_bill_fee_new` | 新 `fee_id`;挂新 income | +| `mig_bi_order_done` | old=`aaa-001` new=`10001` SUCCESS | +| `ar_bill_detail` | `prc_order_bill_id` 仍=`aaa-001`;`order_id=10001` | -### 5.8 积压监控 +## 5. SQL 文件 -```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; -``` +| 文件 | 用途 | +|---|---| +| `20261114_uuid_to_bi_shadow_tables.sql` | 新库/重装:mig + 订单/income 四张 + 菜单 | +| `20261115_uuid_order_id_scheme.sql` | 已执行过旧方案的库:DROP 多余 *_new;ADD `order_id` | +| `20261115_drop_pay_shadow_new.sql` | 历史:仅删应付 *_new(已被 20261115 方案覆盖) | -`old_cnt - done_cnt` 为积压;趋近 0 且抽查一致后再进二期。 +建议执行顺序(已有库):先 `20261115_uuid_order_id_scheme.sql`(不掉 order/ext_new);若缺少 order/ext_new 再跑主 DDL 中对应 CREATE。 -### 5.9 已实现类(一期) +## 6. 风险与注意 -```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 -``` +| 风险 | 说明 / 缓解 | +|---|---| +| 改现网表结构 | 仅 ADD 可空列 + 索引;需低峰;先测试库 | +| UPDATE 大表 | 按单 UPDATE;有 `bill_id`/订单列索引;避全表扫 | +| 双写 | 同事务内先插阴影再 UPDATE;失败回滚 | +| 切换 | 业务改读 `order_id` / `*_new` 属二期;本期不切 | +| 磁盘 | 仅订单两张阴影,比全量 `*_new` 小得多 | -配置(`application.yml`,默认关闭): +## 7. 验收 -```yaml -bms: - mig: - uuid-to-new: - enabled: false - batch-size: 200 - thread-count: 2 # 1~4;4æ ¸8G 冲刺建议 2~3 - cron: "0/30 * * * * ?" -``` +- [ ] 测试库执行 `20261115_uuid_order_id_scheme.sql` 成功 +- [ ] 仅存在 order/ext/income `*_new` + 两张 mig +- [ ] 6 张关联表有 `order_id`(不含 income) +- [ ] 同步 1 单:`*_new` 有行且关联表 `order_id`=新 ID +- [ ] 旧 UUID 列未被改 -批内多线程:先一次抓单,再分给 N 个工作线程;每单 `INSERT IGNORE` 占坑 `mig_bi_order_done(RUNNING)`,避免重复处理。 +## 8. 下一步 -启用前:先在测库执行 `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 追上订单量;抽查一致;旧功能正常。 +1. 测试库执行 `20261115_uuid_order_id_scheme.sql` +2. 部署后用监控页同步 1 单验收 +3. 确认 6 张表 `order_id` 已回写且旧 UUID 列未变 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 index 4e60ac04..804b7cf5 100644 --- 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 @@ -1,10 +1,8 @@ -- 方案文档: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 --- 应付阴影表(prc_pay_bill_new / prc_pay_bill_fee_new / pay_bill_line_new)暂不建,后续脚本补 +-- 含:控制表 + 订单阴影表 DDL + 监控菜单 +-- 方案变更(order_id):建 prc_order_bill_new / prc_order_bill_ext_new / prc_income_bill_new / prc_income_bill_fee_new +-- 保留 mig_bi_order_done / mig_bi_job_log;其他关联表加 order_id(见 20261115_uuid_order_id_scheme.sql) +-- 字段结构对齐【测试库】47.106.220.100:3307/bms SET NAMES utf8mb4; @@ -381,140 +379,6 @@ CREATE TABLE IF NOT EXISTS `prc_income_bill_fee_new` ( 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 `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 `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 := ( diff --git a/bms-back/sql/uuid_to_bi_shadow/20261115_uuid_order_id_scheme.sql b/bms-back/sql/uuid_to_bi_shadow/20261115_uuid_order_id_scheme.sql new file mode 100644 index 00000000..014be380 --- /dev/null +++ b/bms-back/sql/uuid_to_bi_shadow/20261115_uuid_order_id_scheme.sql @@ -0,0 +1,109 @@ +-- 方案文档:20261114_uuid_to_bi_shadow_dev.md +-- order_id 方案:删除多余 *_new(保留订单/收入);关联表增加 order_id(对应 prc_order_bill_new.bill_id) +-- 可重入(列已存在则跳过) +-- 注意:会改生产/测试库现网表结构(仅增列,不改旧 UUID 列) + +SET NAMES utf8mb4; + +-- ========== 0. 确保收入阴影表存在(若已误删可重建) ========== +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='应收主表(订单计费与应收费用中间层,与订单一对一)'; + +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='应收费用明细'; + +-- ========== 1. 删除不再使用的阴影表 ========== +DROP TABLE IF EXISTS `ar_bill_generate_task_new`; +DROP TABLE IF EXISTS `prc_billing_task_new`; +DROP TABLE IF EXISTS `pay_bill_line_new`; +DROP TABLE IF EXISTS `ar_bill_detail_fee_new`; +DROP TABLE IF EXISTS `ar_bill_detail_new`; +DROP TABLE IF EXISTS `prc_pay_bill_fee_new`; +DROP TABLE IF EXISTS `prc_pay_bill_new`; + +-- ========== 2. 关联表增加 order_id ========== +DROP PROCEDURE IF EXISTS `mig_add_order_id_col`; +DELIMITER $$ +CREATE PROCEDURE `mig_add_order_id_col`( + IN p_table VARCHAR(64), + IN p_after VARCHAR(64), + IN p_comment VARCHAR(255) +) +BEGIN + DECLARE cnt INT DEFAULT 0; + SELECT COUNT(*) INTO cnt + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = p_table + AND column_name = 'order_id'; + IF cnt = 0 THEN + SET @ddl = CONCAT( + 'ALTER TABLE `', p_table, '` ADD COLUMN `order_id` bigint DEFAULT NULL COMMENT ''', + REPLACE(p_comment, '''', ''), + ''' AFTER `', p_after, '`, ADD KEY `idx_', p_table, '_order_id` (`order_id`)' + ); + PREPARE stmt FROM @ddl; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END$$ +DELIMITER ; + +CALL mig_add_order_id_col('prc_pay_bill', 'bill_id', '对应 prc_order_bill_new.bill_id'); +CALL mig_add_order_id_col('prc_pay_bill_fee', 'bill_id', '对应 prc_order_bill_new.bill_id'); +CALL mig_add_order_id_col('ar_bill_detail', 'prc_order_bill_id', '对应 prc_order_bill_new.bill_id'); +CALL mig_add_order_id_col('pay_bill_line', 'order_bill_id', '对应 prc_order_bill_new.bill_id'); +CALL mig_add_order_id_col('prc_billing_task', 'bill_id', '对应 prc_order_bill_new.bill_id'); +CALL mig_add_order_id_col('ar_bill_generate_task', 'last_source_bill_id', '对应 prc_order_bill_new.bill_id'); + +DROP PROCEDURE IF EXISTS `mig_add_order_id_col`; + +-- 说明:同步一单成功后更新(旧 UUID 列不动): +-- UPDATE prc_pay_bill SET order_id=#{new} WHERE bill_id=#{oldUuid}; +-- UPDATE prc_pay_bill_fee SET order_id=#{new} WHERE bill_id=#{oldUuid}; +-- UPDATE ar_bill_detail SET order_id=#{new} WHERE prc_order_bill_id=#{oldUuid}; +-- UPDATE pay_bill_line SET order_id=#{new} WHERE order_bill_id=#{oldUuid}; +-- UPDATE prc_billing_task SET order_id=#{new} WHERE bill_id=#{oldUuid}; +-- UPDATE ar_bill_generate_task SET order_id=#{new} WHERE last_source_bill_id=#{oldUuid};