Merge branch 'test' into feauture-20261104

# Conflicts:
#	bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcProfitReportServiceImpl.java
This commit is contained in:
liuminghui
2026-11-05 16:38:53 +08:00
73 changed files with 5246 additions and 830 deletions
+3 -1
View File
@@ -355,7 +355,7 @@ CREATE TABLE `ctr_customer` (
`customer_code_dw` varchar(50) DEFAULT '' COMMENT '对外代码',
`credit_code` varchar(18) DEFAULT '' COMMENT '统一社会信用代码',
`customer_type` char(1) DEFAULT '0' COMMENT '业务类型(0直客 1同行 2集运)',
`area_type` varchar(32) DEFAULT NULL COMMENT '区域类型(字典 data_area_type)',
`customer_area` varchar(32) DEFAULT NULL COMMENT '客户区域(字典 customer_area)',
`is_enable` char(1) DEFAULT '0' COMMENT '启用状态(0正常 1停用)',
`sales_user_id` varchar(64) DEFAULT NULL COMMENT '业务员系统用户ID(sys_user.user_id)',
`source_sales_code` varchar(255) DEFAULT NULL COMMENT '客户同步源业务员工号(内部字段)',
@@ -1220,6 +1220,7 @@ CREATE TABLE `prc_price_set` (
`price_set_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '报价名称',
`price_set_note` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`set_type` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'FIXED' COMMENT '计价类型 FIXED=固定价 DISCOUNT=折扣',
`set_category` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'PACKAGE' COMMENT '分类 PACKAGE=普通套餐 EXCLUSIVE=专属报价',
`default_discount` decimal(5,2) DEFAULT NULL COMMENT '默认折扣百分数 1-99,仅 DISCOUNT',
`default_type` char(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '默认套餐 Y=默认',
`calculate_sign` char(1) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '试算标记 Y=外部试算套餐',
@@ -1233,6 +1234,7 @@ CREATE TABLE `prc_price_set` (
UNIQUE KEY `uk_price_set_code` (`price_set_code`),
KEY `idx_ps_status` (`status`),
KEY `idx_ps_type` (`set_type`),
KEY `idx_ps_category` (`set_category`),
KEY `idx_ps_tms` (`tms_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='价格方案表-报价基本资料';
@@ -1,11 +0,0 @@
-- ctr_customer.area_type:客户区域类型,取值来自字典 data_area_type(可重复执行)
SET @col_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer' AND COLUMN_NAME = 'area_type'
);
SET @sql := IF(@col_exists = 0,
'ALTER TABLE `ctr_customer` ADD COLUMN `area_type` varchar(32) DEFAULT NULL COMMENT ''区域类型(字典 data_area_type)'' AFTER `customer_type`',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
@@ -0,0 +1,173 @@
-- Unified migration: ctr_customer.area_type -> customer_area and data_area_type -> customer_area.
-- Target: MySQL 5.7.x; run the whole file against the intended database in one connection.
-- Confirm both migration_status and dictionary_migration_status report READY.
-- Conflicting columns or dictionary values are preserved and reported for manual review.
SET @customer_table_exists := (
SELECT COUNT(1) FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer'
);
SET @customer_area_old_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer' AND COLUMN_NAME = 'area_type'
);
SET @customer_area_new_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer' AND COLUMN_NAME = 'customer_area'
);
SET @customer_area_conflict_count := 0;
-- If both columns exist, require matching non-NULL values before merging and dropping the old column.
SET @customer_area_check_sql := IF(
@customer_area_old_exists = 1 AND @customer_area_new_exists = 1,
'SELECT COUNT(1) INTO @customer_area_conflict_count FROM `ctr_customer` WHERE `area_type` IS NOT NULL AND `customer_area` IS NOT NULL AND BINARY `area_type` <> BINARY `customer_area`',
'SELECT 0 INTO @customer_area_conflict_count'
);
PREPARE customer_area_stmt FROM @customer_area_check_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
-- In the no-conflict dual-column state, copy values that only exist in the old column.
SET @customer_area_merge_sql := IF(
@customer_area_old_exists = 1 AND @customer_area_new_exists = 1 AND @customer_area_conflict_count = 0,
'UPDATE `ctr_customer` SET `customer_area` = `area_type` WHERE `customer_area` IS NULL AND `area_type` IS NOT NULL',
'SELECT 1'
);
PREPARE customer_area_stmt FROM @customer_area_merge_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
-- Drop the legacy column only after its values have been merged without conflicts.
SET @customer_area_drop_sql := IF(
@customer_area_old_exists = 1 AND @customer_area_new_exists = 1 AND @customer_area_conflict_count = 0,
'ALTER TABLE `ctr_customer` DROP COLUMN `area_type`',
'SELECT 1'
);
PREPARE customer_area_stmt FROM @customer_area_drop_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
-- Rename the existing column, or create the new column when neither name exists.
SET @customer_area_rename_sql := CASE
WHEN @customer_table_exists = 1 AND @customer_area_old_exists = 1 AND @customer_area_new_exists = 0 THEN
'ALTER TABLE `ctr_customer` CHANGE COLUMN `area_type` `customer_area` varchar(32) DEFAULT NULL COMMENT ''客户区域(字典 customer_area)'' AFTER `customer_type`'
WHEN @customer_table_exists = 1 AND @customer_area_old_exists = 0 AND @customer_area_new_exists = 0 THEN
'ALTER TABLE `ctr_customer` ADD COLUMN `customer_area` varchar(32) DEFAULT NULL COMMENT ''客户区域(字典 customer_area)'' AFTER `customer_type`'
ELSE 'SELECT 1'
END;
PREPARE customer_area_stmt FROM @customer_area_rename_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
SET @customer_area_old_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer' AND COLUMN_NAME = 'area_type'
);
SET @customer_area_new_exists := (
SELECT COUNT(1) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ctr_customer' AND COLUMN_NAME = 'customer_area'
);
SELECT CASE
WHEN @customer_table_exists = 0 THEN 'ERROR: ctr_customer table does not exist in the selected database'
WHEN @customer_area_old_exists = 0 AND @customer_area_new_exists = 1 THEN 'READY'
WHEN @customer_area_conflict_count > 0 THEN 'BLOCKED: resolve conflicting values and rerun this full script'
ELSE 'ERROR: expected customer_area and no area_type; inspect the table before continuing'
END AS migration_status,
@customer_area_conflict_count AS conflicting_rows;
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'ctr_customer'
AND COLUMN_NAME IN ('area_type', 'customer_area')
ORDER BY ORDINAL_POSITION;
-- When migration_status is BLOCKED, inspect these rows, resolve each value manually, then rerun this file.
SET @customer_area_conflict_rows_sql := IF(
@customer_area_conflict_count > 0,
'SELECT `customer_id`, `customer_code`, `area_type`, `customer_area` FROM `ctr_customer` WHERE `area_type` IS NOT NULL AND `customer_area` IS NOT NULL AND BINARY `area_type` <> BINARY `customer_area` ORDER BY `customer_id`',
'SELECT ''No conflicting customer area values'' AS conflict_check'
);
PREPARE customer_area_stmt FROM @customer_area_conflict_rows_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
-- Rename the dictionary identifier and its values. If both dictionary types already exist,
-- or both dictionaries use the same value, stop and report instead of creating ambiguity.
SET @customer_area_old_dict_exists := (
SELECT COUNT(1) FROM `sys_dict_type` WHERE `dict_type` = 'data_area_type'
);
SET @customer_area_new_dict_exists := (
SELECT COUNT(1) FROM `sys_dict_type` WHERE `dict_type` = 'customer_area'
);
SET @customer_area_dict_value_conflicts := (
SELECT COUNT(1)
FROM `sys_dict_data` old_data
JOIN `sys_dict_data` new_data ON old_data.`dict_value` = new_data.`dict_value`
WHERE old_data.`dict_type` = 'data_area_type'
AND new_data.`dict_type` = 'customer_area'
);
SET @customer_area_dict_data_sql := IF(
@customer_area_old_exists = 0 AND @customer_area_new_exists = 1
AND (@customer_area_old_dict_exists = 1 OR @customer_area_new_dict_exists = 1)
AND @customer_area_dict_value_conflicts = 0
AND NOT (@customer_area_old_dict_exists = 1 AND @customer_area_new_dict_exists = 1),
'UPDATE `sys_dict_data` SET `dict_type` = ''customer_area'' WHERE `dict_type` = ''data_area_type''',
'SELECT 1'
);
PREPARE customer_area_stmt FROM @customer_area_dict_data_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
SET @customer_area_dict_type_sql := CASE
WHEN @customer_area_old_exists = 0 AND @customer_area_new_exists = 1
AND @customer_area_old_dict_exists = 1 AND @customer_area_new_dict_exists = 0
AND @customer_area_dict_value_conflicts = 0 THEN
'UPDATE `sys_dict_type` SET `dict_type` = ''customer_area'', `dict_name` = ''客户区域'', `update_time` = NOW() WHERE `dict_type` = ''data_area_type'''
WHEN @customer_area_old_exists = 0 AND @customer_area_new_exists = 1
AND @customer_area_old_dict_exists = 0 AND @customer_area_new_dict_exists = 1
AND @customer_area_dict_value_conflicts = 0 THEN
'UPDATE `sys_dict_type` SET `dict_name` = ''客户区域'', `update_time` = NOW() WHERE `dict_type` = ''customer_area'''
ELSE 'SELECT 1'
END;
PREPARE customer_area_stmt FROM @customer_area_dict_type_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
SET @customer_area_old_dict_exists := (
SELECT COUNT(1) FROM `sys_dict_type` WHERE `dict_type` = 'data_area_type'
);
SET @customer_area_new_dict_exists := (
SELECT COUNT(1) FROM `sys_dict_type` WHERE `dict_type` = 'customer_area'
);
SELECT CASE
WHEN @customer_area_old_exists <> 0 OR @customer_area_new_exists <> 1 THEN 'BLOCKED: finish customer column migration first'
WHEN @customer_area_old_dict_exists = 0 AND @customer_area_new_dict_exists = 1
AND @customer_area_dict_value_conflicts = 0 THEN 'READY'
WHEN @customer_area_old_dict_exists = 1 AND @customer_area_new_dict_exists = 1 THEN 'BLOCKED: both dictionary types exist; merge them manually'
WHEN @customer_area_dict_value_conflicts > 0 THEN 'BLOCKED: dictionary values overlap; reconcile them manually'
ELSE 'MISSING: verify the customer area dictionary in the selected database'
END AS dictionary_migration_status,
@customer_area_dict_value_conflicts AS overlapping_values;
SELECT `dict_id`, `dict_name`, `dict_type`, `status`
FROM `sys_dict_type`
WHERE `dict_type` IN ('data_area_type', 'customer_area')
ORDER BY `dict_id`;
SELECT `dict_code`, `dict_type`, `dict_value`, `dict_label`, `status`
FROM `sys_dict_data`
WHERE `dict_type` IN ('data_area_type', 'customer_area')
ORDER BY `dict_type`, `dict_sort`, `dict_code`;
SET @customer_area_dict_conflict_rows_sql := IF(
@customer_area_dict_value_conflicts > 0,
'SELECT old_data.`dict_value`, old_data.`dict_label` AS old_label, new_data.`dict_label` AS new_label FROM `sys_dict_data` old_data JOIN `sys_dict_data` new_data ON old_data.`dict_value` = new_data.`dict_value` WHERE old_data.`dict_type` = ''data_area_type'' AND new_data.`dict_type` = ''customer_area'' ORDER BY old_data.`dict_value`',
'SELECT ''No overlapping dictionary values'' AS conflict_check'
);
PREPARE customer_area_stmt FROM @customer_area_dict_conflict_rows_sql;
EXECUTE customer_area_stmt;
DEALLOCATE PREPARE customer_area_stmt;
@@ -0,0 +1,32 @@
-- Exclusive quote: share prc_price_set via set_category
SET NAMES utf8mb4;
SET @col_exists := (
SELECT COUNT(1) FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'prc_price_set'
AND column_name = 'set_category'
);
SET @sql := IF(@col_exists = 0,
'ALTER TABLE prc_price_set ADD COLUMN set_category varchar(20) NOT NULL DEFAULT ''PACKAGE'' COMMENT ''分类 PACKAGE=普通套餐 EXCLUSIVE=专属报价'' AFTER set_type',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
UPDATE prc_price_set
SET set_category = 'PACKAGE'
WHERE set_category IS NULL OR set_category = '';
SET @idx_exists := (
SELECT COUNT(1) FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'prc_price_set'
AND index_name = 'idx_ps_category'
);
SET @sql := IF(@idx_exists = 0,
'ALTER TABLE prc_price_set ADD KEY idx_ps_category (set_category)',
'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
@@ -0,0 +1,59 @@
-- Exclusive quote module menus
SET NAMES utf8mb4;
-- Top-level directory
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 2600, N'专属报价', 0, 55, 'exclusive', NULL, '', '', 1, 0, 'M', '0', '0', '', 'money', 'admin', sysdate(), N'老板客户专属报价'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2600 OR (path = 'exclusive' AND parent_id = 0 AND menu_type = 'M'));
SET @exParentId := (SELECT menu_id FROM sys_menu WHERE menu_id = 2600 LIMIT 1);
SET @exParentId := IFNULL(@exParentId, (
SELECT menu_id FROM sys_menu WHERE path = 'exclusive' AND menu_type = 'M' AND parent_id = 0 ORDER BY menu_id ASC LIMIT 1
));
-- List page
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 2601, N'报价列表', @exParentId, 1, 'quote', 'price/exclusive/exclusiveQuote', '', 'ExclusiveQuote', 1, 0, 'C', '0', '0', 'price:exclusive:list', 'list', 'admin', sysdate(), N'专属报价列表'
FROM DUAL WHERE @exParentId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2601 OR perms = 'price:exclusive:list');
-- Hidden create / detail
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 2609, N'报价新建', @exParentId, 2, 'quoteCreate', 'price/exclusive/create', '', 'ExclusiveQuoteCreate', 1, 0, 'C', '1', '0', 'price:exclusive:add', '#', 'admin', sysdate(), N'专属报价新建/编辑'
FROM DUAL WHERE @exParentId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2609 OR path = 'quoteCreate');
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 2610, N'报价详情', @exParentId, 3, 'quoteDetail', 'price/exclusive/detail', '', 'ExclusiveQuoteDetail', 1, 0, 'C', '1', '0', 'price:exclusive:query', '#', 'admin', sysdate(), N'专属报价详情'
FROM DUAL WHERE @exParentId IS NOT NULL AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2610 OR path = 'quoteDetail');
-- Buttons under 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, remark)
SELECT 2602, N'查询', 2601, 1, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:query', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2602 OR (parent_id = 2601 AND perms = 'price:exclusive:query' AND menu_type = 'F'));
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 2603, N'新增', 2601, 2, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:add', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2603 OR (parent_id = 2601 AND perms = 'price:exclusive:add' AND menu_type = 'F'));
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 2604, N'修改', 2601, 3, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:edit', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2604 OR (parent_id = 2601 AND perms = 'price:exclusive:edit' AND menu_type = 'F'));
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 2605, N'删除', 2601, 4, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:remove', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2605 OR (parent_id = 2601 AND perms = 'price:exclusive:remove' AND menu_type = 'F'));
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 2606, N'启停/审核', 2601, 5, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:audit', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2606 OR (parent_id = 2601 AND perms = 'price:exclusive:audit' AND menu_type = 'F'));
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 2607, N'强制改价', 2601, 6, '', NULL, 1, 0, 'F', '0', '0', 'price:exclusive:forceEdit', '#', 'admin', sysdate(), N''
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2607 OR (parent_id = 2601 AND perms = 'price:exclusive:forceEdit' AND menu_type = 'F'));
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT 1, m.menu_id FROM (
SELECT 2600 AS menu_id UNION SELECT 2601 UNION SELECT 2602 UNION SELECT 2603
UNION SELECT 2604 UNION SELECT 2605 UNION SELECT 2606 UNION SELECT 2607
UNION SELECT 2609 UNION SELECT 2610
) m
WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);