修改套餐价格
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# Price set version tables
|
||||
|
||||
## Wizard (2 steps)
|
||||
|
||||
1. Basic info + view bound customers (read-only)
|
||||
2. Edit product prices (library pull on next)
|
||||
|
||||
- No manual effective_time input.
|
||||
- Draft version: status=0, effective_time null until activate.
|
||||
- Activate: sets effective_time=now; previous version expires. Does not change package header status.
|
||||
- Copy as new version: `POST /price/set/version/copy/{psvId}` inserts new draft `prc_price_set_version` and copies `prc_price_set_detail` (+ surcharge snapshots) from source psv_id.
|
||||
- Audit submit/approve/reject removed from UI save path.
|
||||
|
||||
## Graph
|
||||
|
||||
```
|
||||
prc_price_set
|
||||
-> prc_price_set_version draft / active / expired
|
||||
-> prc_price_set_detail product prices (psv_id)
|
||||
-> prc_price_set_version_surcharge
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Version: 0 draft, 1 active, 2 expired. Active when effective_time <= now and (expire_time null or > now).
|
||||
|
||||
## SQL
|
||||
|
||||
1. `price_set_version.sql`
|
||||
2. `price_set_version_eff_nullable.sql` (allow null `prc_price_set.effective_date` and `prc_price_set_version.effective_time` for drafts)
|
||||
|
||||
## Billing
|
||||
|
||||
Match by price_set_id; multi-version quote should filter active psv_id by bill time.
|
||||
@@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS prc_price_set (
|
||||
default_discount DECIMAL(5,2) DEFAULT NULL COMMENT '默认折扣百分数 1-99,仅 DISCOUNT',
|
||||
default_type CHAR(1) DEFAULT NULL COMMENT '默认套餐 Y=默认',
|
||||
calculate_sign CHAR(1) DEFAULT NULL COMMENT '试算标记 Y=外部试算套餐',
|
||||
effective_date DATE NOT NULL COMMENT '生效日期',
|
||||
effective_date DATE DEFAULT NULL COMMENT 'effective date (null until activate)',
|
||||
expire_date DATE DEFAULT NULL COMMENT '失效日期(空=长期)',
|
||||
status CHAR(1) NOT NULL DEFAULT 'D' COMMENT 'D草稿 A审核中 E生效 R驳回 S停用',
|
||||
tms_id BIGINT(20) DEFAULT NULL COMMENT '多租户系统ID',
|
||||
@@ -46,17 +46,23 @@ CREATE TABLE IF NOT EXISTS prc_price_set_detail (
|
||||
zd_id BIGINT(20) DEFAULT NULL COMMENT 'legacy zone division id',
|
||||
wsd_id BIGINT(20) DEFAULT NULL COMMENT '重量段明细id',
|
||||
weight_label VARCHAR(50) DEFAULT NULL COMMENT '重量段展示',
|
||||
weight_min DECIMAL(10,3) DEFAULT NULL COMMENT '重量段最小值KG(含)',
|
||||
weight_max DECIMAL(10,3) DEFAULT NULL COMMENT '重量段最大值KG(含)',
|
||||
product_country_id BIGINT(20) DEFAULT NULL COMMENT '关联 bms_product_country.detail_id',
|
||||
fv_value DECIMAL(10,4) DEFAULT NULL COMMENT '运费单价 RMB/KG',
|
||||
fv_registration_fee DECIMAL(10,2) DEFAULT NULL COMMENT '挂号费 RMB/票',
|
||||
discount_value DECIMAL(5,2) DEFAULT NULL COMMENT '明细折扣百分数,DISCOUNT 可覆盖默认',
|
||||
round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||
pr_id INT(11) DEFAULT NULL COMMENT '关联价格规则id',
|
||||
effective_date DATE DEFAULT NULL COMMENT '生效日期',
|
||||
expire_date DATE DEFAULT NULL COMMENT '失效日期(空=长期)',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psd_id),
|
||||
INDEX idx_psd_set (price_set_id),
|
||||
INDEX idx_psd_product (price_set_id, product_code, country_code),
|
||||
INDEX idx_psd_weight_match (price_set_id, product_code, country_code, weight_min, weight_max),
|
||||
INDEX idx_psd_eff (price_set_id, effective_date, expire_date),
|
||||
INDEX idx_psd_product_country (product_country_id),
|
||||
CONSTRAINT fk_psd_set FOREIGN KEY (price_set_id) REFERENCES prc_price_set (price_set_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐明细-固定价/折扣范围';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Prevent duplicate FIXED details within one version (same product_country snapshot row)
|
||||
-- MySQL allows multiple NULLs in UNIQUE, so DISCOUNT rows (product_country_id null) are OK.
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- Clean exact duplicates before adding unique key (keep min psd_id)
|
||||
DELETE d1 FROM prc_price_set_detail d1
|
||||
INNER JOIN prc_price_set_detail d2
|
||||
ON d1.psv_id = d2.psv_id
|
||||
AND d1.product_country_id = d2.product_country_id
|
||||
AND d1.psd_id > d2.psd_id
|
||||
WHERE d1.psv_id IS NOT NULL
|
||||
AND d1.product_country_id IS NOT NULL;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'uk_psd_psv_product_country'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD UNIQUE KEY uk_psd_psv_product_country (psv_id, product_country_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- prc_price_set_detail: weight_min/weight_max + effective_date/expire_date
|
||||
-- same types as bms_product_country
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='weight_min');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN weight_min DECIMAL(10,3) DEFAULT NULL COMMENT ''重量段最小值KG(含)'' AFTER weight_label', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='weight_max');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN weight_max DECIMAL(10,3) DEFAULT NULL COMMENT ''重量段最大值KG(含)'' AFTER weight_min', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='effective_date');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN effective_date DATE DEFAULT NULL COMMENT ''生效日期'' AFTER pr_id', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND COLUMN_NAME='expire_date');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD COLUMN expire_date DATE DEFAULT NULL COMMENT ''失效日期(空=长期)'' AFTER effective_date', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND INDEX_NAME='idx_psd_weight_match');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_weight_match (price_set_id, product_code, country_code, weight_min, weight_max)', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='prc_price_set_detail' AND INDEX_NAME='idx_psd_eff');
|
||||
SET @sql := IF(@has=0, 'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_eff (price_set_id, effective_date, expire_date)', 'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 回填: 从 bms_product_country 带入重量段与有效期
|
||||
UPDATE prc_price_set_detail d
|
||||
INNER JOIN bms_product_country c ON c.detail_id = d.product_country_id
|
||||
SET
|
||||
d.weight_min = IFNULL(d.weight_min, c.weight_min),
|
||||
d.weight_max = IFNULL(d.weight_max, c.weight_max),
|
||||
d.effective_date = IFNULL(d.effective_date, c.effective_date),
|
||||
d.expire_date = IFNULL(d.expire_date, c.expire_date)
|
||||
WHERE d.product_country_id IS NOT NULL;
|
||||
@@ -0,0 +1,152 @@
|
||||
-- 价格套餐版本中间表 + 版本附加费
|
||||
-- 关系:
|
||||
-- prc_price_set
|
||||
-- -> prc_price_set_version (步骤2: 版本号自动生成, 生效时间)
|
||||
-- -> prc_price_set_detail (步骤3: 产品价格, 通过 psv_id)
|
||||
-- -> prc_price_set_version_surcharge (psd_id 关联明细)
|
||||
-- 可重复执行
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS prc_price_set_version (
|
||||
psv_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '套餐版本id',
|
||||
price_set_id BIGINT(20) NOT NULL COMMENT '价格套餐id',
|
||||
version_no VARCHAR(20) NOT NULL COMMENT '版本号 V1/V2,自动+1',
|
||||
version_seq INT(11) NOT NULL COMMENT '版本序号,用于自动+1',
|
||||
effective_time DATETIME DEFAULT NULL COMMENT 'version start time (null until activate)',
|
||||
expire_time DATETIME DEFAULT NULL COMMENT '失效时间(空=长期;下一版本启动时写入该启动时间)',
|
||||
status CHAR(1) NOT NULL DEFAULT '0' COMMENT '0草稿 1生效 2失效',
|
||||
created_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
updated_by VARCHAR(64) DEFAULT '' COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psv_id),
|
||||
UNIQUE KEY uk_psv_set_no (price_set_id, version_no),
|
||||
UNIQUE KEY uk_psv_set_seq (price_set_id, version_seq),
|
||||
INDEX idx_psv_set_eff (price_set_id, effective_time),
|
||||
INDEX idx_psv_status (status),
|
||||
CONSTRAINT fk_psv_set FOREIGN KEY (price_set_id) REFERENCES prc_price_set (price_set_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐版本';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS prc_price_set_version_surcharge (
|
||||
psvs_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '版本附加费id',
|
||||
psv_id BIGINT(20) NOT NULL COMMENT '套餐版本id',
|
||||
psd_id BIGINT(20) NOT NULL COMMENT '套餐明细id',
|
||||
sc_id BIGINT(20) DEFAULT NULL COMMENT '来源附加费配置id',
|
||||
product_code VARCHAR(20) DEFAULT NULL COMMENT '产品代码(与明细一致)',
|
||||
sc_name VARCHAR(100) NOT NULL COMMENT '附加费名称快照',
|
||||
fee_type VARCHAR(30) NOT NULL DEFAULT 'SURCHARGE' COMMENT '费用类型(字典bms_fee_type)',
|
||||
charge_type VARCHAR(20) NOT NULL COMMENT '计费方式 FIXED固定金额/WEIGHT按重量/RATE按比例',
|
||||
charge_value DECIMAL(18,6) NOT NULL COMMENT '计费数值快照',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种',
|
||||
round_mode VARCHAR(20) NOT NULL DEFAULT 'HALF_UP' COMMENT '取值方式(字典bms_round_mode)',
|
||||
country_code CHAR(2) DEFAULT NULL COMMENT '适用国家ISO2(空=全部)',
|
||||
sc_note VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
created_by VARCHAR(64) DEFAULT '' COMMENT '创建人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
updated_by VARCHAR(64) DEFAULT '' COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (psvs_id),
|
||||
INDEX idx_psvs_psv (psv_id),
|
||||
INDEX idx_psvs_psd (psd_id),
|
||||
INDEX idx_psvs_product (psv_id, product_code),
|
||||
INDEX idx_psvs_sc (sc_id),
|
||||
CONSTRAINT fk_psvs_psv FOREIGN KEY (psv_id) REFERENCES prc_price_set_version (psv_id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_psvs_psd FOREIGN KEY (psd_id) REFERENCES prc_price_set_detail (psd_id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格套餐版本附加费快照';
|
||||
|
||||
|
||||
-- expire_time: previous version expire = next version effective_time (start)
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'expire_time'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_version ADD COLUMN expire_time DATETIME DEFAULT NULL COMMENT ''失效时间(空=长期;下一版本启动时写入该启动时间)'' AFTER effective_time',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- backfill: expire_time = next version start time (by version_seq)
|
||||
UPDATE prc_price_set_version cur
|
||||
INNER JOIN prc_price_set_version nxt
|
||||
ON nxt.price_set_id = cur.price_set_id
|
||||
AND nxt.version_seq = cur.version_seq + 1
|
||||
SET cur.expire_time = nxt.effective_time;
|
||||
|
||||
-- prc_price_set_detail.psv_id
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'psv_id'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD COLUMN psv_id BIGINT(20) DEFAULT NULL COMMENT ''套餐版本id'' AFTER price_set_id',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_psv'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD INDEX idx_psd_psv (psv_id)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND CONSTRAINT_NAME = 'fk_psd_psv'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_detail ADD CONSTRAINT fk_psd_psv FOREIGN KEY (psv_id) REFERENCES prc_price_set_version (psv_id) ON DELETE CASCADE',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 已建表补关联: 附加费.psd_id -> 套餐明细
|
||||
DELETE FROM prc_price_set_version_surcharge
|
||||
WHERE psd_id IS NULL
|
||||
OR NOT EXISTS (
|
||||
SELECT 1 FROM prc_price_set_detail d WHERE d.psd_id = prc_price_set_version_surcharge.psd_id
|
||||
);
|
||||
|
||||
SET @sql := CONCAT(
|
||||
'ALTER TABLE prc_price_set_version_surcharge MODIFY COLUMN psd_id BIGINT(20) NOT NULL COMMENT ''套餐明细id'''
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @has := (
|
||||
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version_surcharge' AND CONSTRAINT_NAME = 'fk_psvs_psd'
|
||||
);
|
||||
SET @sql := IF(@has = 0,
|
||||
'ALTER TABLE prc_price_set_version_surcharge ADD CONSTRAINT fk_psvs_psd FOREIGN KEY (psd_id) REFERENCES prc_price_set_detail (psd_id) ON DELETE CASCADE',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 回填: 每个已有套餐生成一条 V1
|
||||
INSERT INTO prc_price_set_version (
|
||||
price_set_id, version_no, version_seq, effective_time, status, created_by, create_time
|
||||
)
|
||||
SELECT
|
||||
s.price_set_id,
|
||||
'V1',
|
||||
1,
|
||||
COALESCE(CAST(s.effective_date AS DATETIME), s.create_time, NOW()),
|
||||
CASE
|
||||
WHEN s.status = 'S' THEN '2'
|
||||
WHEN s.expire_date IS NOT NULL AND s.expire_date <= CURDATE() THEN '2'
|
||||
WHEN s.status = 'E' AND (s.effective_date IS NULL OR s.effective_date <= CURDATE()) THEN '1'
|
||||
ELSE '0'
|
||||
END,
|
||||
IFNULL(s.created_by, ''),
|
||||
NOW()
|
||||
FROM prc_price_set s
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM prc_price_set_version v WHERE v.price_set_id = s.price_set_id
|
||||
);
|
||||
|
||||
-- 将已有明细绑定到同一套餐的 V1
|
||||
UPDATE prc_price_set_detail d
|
||||
INNER JOIN prc_price_set_version v
|
||||
ON v.price_set_id = d.price_set_id AND v.version_seq = 1
|
||||
SET d.psv_id = v.psv_id
|
||||
WHERE d.psv_id IS NULL;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- Draft package / version: effective dates nullable until activate
|
||||
SET NAMES utf8mb4;
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- prc_price_set.effective_date
|
||||
SET @nullable := (
|
||||
SELECT IS_NULLABLE FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set' AND COLUMN_NAME = 'effective_date'
|
||||
);
|
||||
SET @sql := IF(@nullable = 'NO',
|
||||
'ALTER TABLE prc_price_set MODIFY COLUMN effective_date DATE DEFAULT NULL COMMENT ''effective date (null until version activate)''',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- prc_price_set_version.effective_time
|
||||
SET @nullable := (
|
||||
SELECT IS_NULLABLE FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'effective_time'
|
||||
);
|
||||
SET @sql := IF(@nullable = 'NO',
|
||||
'ALTER TABLE prc_price_set_version MODIFY COLUMN effective_time DATETIME DEFAULT NULL COMMENT ''version start time (null until activate)''',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
Reference in New Issue
Block a user