diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java index 29a739ee..48293da1 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java @@ -27,6 +27,7 @@ import com.bms.common.enums.BusinessType; import com.bms.common.utils.MessageUtils; import com.bms.common.utils.StringUtils; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcPriceSetUser; import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetDetail; @@ -386,4 +387,30 @@ public class PriceSetController extends BaseController { return toAjax(priceSetService.deleteCustomerByIds(customerPricesetIds)); } + + @PreAuthorize("@ss.hasPermi('price:set:list')") + @GetMapping("/viewer/list") + public TableDataInfo viewerList(PrcPriceSetUser query) + { + startPage(); + List list = priceSetService.selectViewerList(query); + return getDataTable(list); + } + + @PreAuthorize("@ss.hasPermi('price:set:edit')") + @Log(title = "Package viewer bind", businessType = BusinessType.INSERT) + @PostMapping("/viewer") + public AjaxResult addViewer(@RequestBody PrcPriceSetUser row) + { + row.setCreateBy(getUsername()); + return toAjax(priceSetService.insertViewer(row)); + } + + @PreAuthorize("@ss.hasPermi('price:set:edit')") + @Log(title = "Package viewer bind", businessType = BusinessType.DELETE) + @DeleteMapping("/viewer/{psuIds}") + public AjaxResult removeViewer(@PathVariable Long[] psuIds) + { + return toAjax(priceSetService.deleteViewerByIds(psuIds)); + } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java index 8f260747..ccac8224 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java @@ -42,6 +42,8 @@ public class PrcPriceSet extends BaseEntity private List details; private List versions; private List customers; + /** viewers explicitly bound to this package */ + private List viewers; /** async save task id (not persisted) */ private String saveTaskId; /** Y: ignored on save; library pull only via /detail/pullFromLibrary */ diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetUser.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetUser.java new file mode 100644 index 00000000..6e6dc368 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetUser.java @@ -0,0 +1,21 @@ +package com.bms.price.domain; + +import com.bms.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** Package viewer bind: prc_price_set_user */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PrcPriceSetUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private Long psuId; + private Long priceSetId; + private Long userId; + /** login account */ + private String userName; + /** display only, not a table column */ + private String nickName; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetUserMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetUserMapper.java new file mode 100644 index 00000000..337a4232 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetUserMapper.java @@ -0,0 +1,20 @@ +package com.bms.price.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.bms.price.domain.PrcPriceSetUser; + +public interface PrcPriceSetUserMapper +{ + PrcPriceSetUser selectPrcPriceSetUserById(Long psuId); + + List selectPrcPriceSetUserList(PrcPriceSetUser query); + + int insertPrcPriceSetUser(PrcPriceSetUser row); + + int deletePrcPriceSetUserByIds(Long[] psuIds); + + int deleteByPriceSetId(Long priceSetId); + + int countBySetAndUser(@Param("priceSetId") Long priceSetId, @Param("userId") Long userId); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java index 08a19764..7ef854b1 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java @@ -9,6 +9,7 @@ import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetCheckItem; import com.bms.price.domain.PrcPriceSetDetail; import com.bms.price.domain.PrcPriceSetSaveTask; +import com.bms.price.domain.PrcPriceSetUser; /** * 价格套餐 服务层 @@ -91,6 +92,12 @@ public interface IPrcPriceSetService public int deleteCustomerByIds(Long[] customerPricesetIds); + public List selectViewerList(PrcPriceSetUser query); + + public int insertViewer(PrcPriceSetUser row); + + public int deleteViewerByIds(Long[] psuIds); + /** chain expire_time = next version effective_time, then refresh 0/3/1/2 */ public int refreshAllVersionStatus(); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java index 96cf9507..05d0b0b7 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java @@ -18,6 +18,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import com.bms.common.annotation.DataScope; import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; import com.bms.common.utils.MessageUtils; @@ -29,6 +30,7 @@ import com.bms.price.domain.BmsProductCountry; import com.bms.price.domain.PrcCustomerPriceSet; import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.domain.PrcPriceSet; +import com.bms.price.domain.PrcPriceSetUser; import com.bms.price.domain.PrcPriceSetCheckItem; import com.bms.price.domain.PrcPriceSetDetail; import com.bms.price.domain.PrcPriceSetProduct; @@ -42,9 +44,12 @@ import com.bms.price.mapper.PrcCustomerPriceSetMapper; import com.bms.price.mapper.PrcPriceSetDetailMapper; import com.bms.price.mapper.PrcPriceSetMapper; import com.bms.price.mapper.PrcPriceSetProductMapper; +import com.bms.price.mapper.PrcPriceSetUserMapper; import com.bms.price.mapper.PrcPriceSetVersionMapper; import com.bms.price.mapper.PrcSurchargeMapper; import com.bms.price.mapper.PrcSurchargeRelMapper; +import com.bms.common.core.domain.entity.SysUser; +import com.bms.system.mapper.SysUserMapper; import com.bms.price.service.IBmsProductService; import com.bms.price.service.IPrcPriceExcelFileService; import com.bms.price.service.IPrcPriceSetService; @@ -98,6 +103,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService @Autowired private PrcCustomerPriceSetMapper customerMapper; + @Autowired + private PrcPriceSetUserMapper priceSetUserMapper; + + @Autowired + private SysUserMapper sysUserMapper; + @Autowired private BmsProductCountryMapper productCountryMapper; @@ -132,9 +143,19 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return set; } + /** + * Price-set list data scope: bound customer's sales user (same as customer list), + * package creator, or explicit viewer bind. Super admin skips filter in DataScopeAspect. + */ @Override + @DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", + permission = "price:set:list") public List selectPrcPriceSetList(PrcPriceSet set) { + if (set != null) + { + set.getParams().put("viewerUserId", SecurityUtils.getUserId()); + } List list = setMapper.selectPrcPriceSetList(set); for (PrcPriceSet item : list) { @@ -2223,6 +2244,49 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService return customerMapper.deletePrcCustomerPriceSetByIds(customerPricesetIds); } + @Override + public List selectViewerList(PrcPriceSetUser query) + { + return priceSetUserMapper.selectPrcPriceSetUserList(query); + } + + @Override + @Transactional + public int insertViewer(PrcPriceSetUser row) + { + if (row == null || row.getPriceSetId() == null || row.getUserId() == null) + { + throw new ServiceException("price set user bind requires priceSetId and userId"); + } + requireSet(row.getPriceSetId()); + if (priceSetUserMapper.countBySetAndUser(row.getPriceSetId(), row.getUserId()) > 0) + { + throw new ServiceException("user already bound to this price set"); + } + SysUser user = sysUserMapper.selectUserById(row.getUserId()); + if (user == null) + { + throw new ServiceException("user not found"); + } + row.setUserName(user.getUserName()); + if (StringUtils.isEmpty(row.getCreateBy())) + { + row.setCreateBy(SecurityUtils.getUsername()); + } + return priceSetUserMapper.insertPrcPriceSetUser(row); + } + + @Override + @Transactional + public int deleteViewerByIds(Long[] psuIds) + { + if (psuIds == null || psuIds.length == 0) + { + return 0; + } + return priceSetUserMapper.deletePrcPriceSetUserByIds(psuIds); + } + private void replaceChildren(PrcPriceSet set) { saveOrUpdateVersion(set); @@ -2989,6 +3053,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService PrcCustomerPriceSet cquery = new PrcCustomerPriceSet(); cquery.setPriceSetId(priceSetId); set.setCustomers(customerMapper.selectPrcCustomerPriceSetList(cquery)); + PrcPriceSetUser uquery = new PrcPriceSetUser(); + uquery.setPriceSetId(priceSetId); + set.setViewers(priceSetUserMapper.selectPrcPriceSetUserList(uquery)); } private void fillVersionChildren(PrcPriceSetVersion version) diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetMapper.xml index 283a10b3..716e4462 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetMapper.xml @@ -81,6 +81,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ) ) + + + AND ( + EXISTS ( + SELECT 1 + FROM prc_customer_price_set cps + INNER JOIN ctr_customer c + ON c.customer_id = cps.customer_id + AND ifnull(c.del_flag, '0') = '0' + INNER JOIN sys_user su + ON su.del_flag = '0' + AND (su.user_name = c.sales_user_id + OR su.user_id = c.sales_user_id + 0) + WHERE cps.price_set_id = s.price_set_id + ${params.dataScope} + ) + OR EXISTS ( + SELECT 1 + FROM sys_user su + WHERE su.del_flag = '0' + AND su.user_name = s.created_by + ${params.dataScope} + ) + + OR EXISTS ( + SELECT 1 + FROM prc_price_set_user psu + WHERE psu.price_set_id = s.price_set_id + AND psu.user_id = #{params.viewerUserId} + ) + + ) + order by s.price_set_id desc diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetUserMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetUserMapper.xml new file mode 100644 index 00000000..19f33d05 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetUserMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + select psu.psu_id, psu.price_set_id, psu.user_id, psu.user_name, + u.nick_name, psu.created_by, psu.create_time + from prc_price_set_user psu + left join sys_user u on u.user_id = psu.user_id + + + + + + + + + + insert into prc_price_set_user (price_set_id, user_id, user_name, created_by, create_time) + values (#{priceSetId}, #{userId}, #{userName}, #{createBy}, sysdate()) + + + + delete from prc_price_set_user where psu_id in + #{id} + + + + delete from prc_price_set_user where price_set_id = #{priceSetId} + + diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql index bff5eae5..a63f3ba3 100644 --- a/bms-back/sql/bms_init.sql +++ b/bms-back/sql/bms_init.sql @@ -802,6 +802,23 @@ CREATE TABLE `prc_customer_price_set` ( CONSTRAINT `fk_customer_price_set` FOREIGN KEY (`price_set_id`) REFERENCES `prc_price_set` (`price_set_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户套餐表-客户某时间段内使用的套餐'; +-- ---------------------------- +-- 表结构:`prc_price_set_user` +-- ---------------------------- +CREATE TABLE `prc_price_set_user` ( + `psu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', + `price_set_id` bigint(20) NOT NULL COMMENT '价格套餐id', + `user_id` bigint(20) NOT NULL COMMENT '用户id', + `user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '登录账号', + `created_by` varchar(64) DEFAULT '' COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`psu_id`), + UNIQUE KEY `uk_psu_set_user` (`price_set_id`,`user_id`), + KEY `idx_psu_user` (`user_id`), + KEY `idx_psu_set` (`price_set_id`), + CONSTRAINT `fk_psu_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='套餐可见用户绑定'; + -- ---------------------------- -- 表结构:`prc_income_bill` -- ---------------------------- diff --git a/bms-back/sql/price/merge_price_upgrade_20261018.sql b/bms-back/sql/price/merge_price_upgrade_20261018.sql new file mode 100644 index 00000000..9ccf75f1 --- /dev/null +++ b/bms-back/sql/price/merge_price_upgrade_20261018.sql @@ -0,0 +1,927 @@ +-- ============================================================================= +-- Merged price upgrade script +-- Sources (in order): +-- - unify_price_type_version.sql +-- - prc_price_set_is_del_alter.sql +-- - prc_price_set_product_effect_alter.sql +-- - prc_price_excel_file.sql +-- - prc_price_excel_psv_alter.sql +-- - prc_order_bill_fail_snapshot_alter.sql +-- - order_bill_query_menu.sql +-- - order_bill_rebill_menu.sql +-- Safe to re-run where original scripts used information_schema guards. +-- Run once on target DB. Backup first if production. +-- Generated: 2026-10-18 +-- ============================================================================= + +SET NAMES utf8mb4; +SET @db := DATABASE(); + +-- --------------------------------------------------------------------------- +-- 1) unify PRODUCT / REDLINE / SALES into version + product/detail +-- source: unify_price_type_version.sql +-- --------------------------------------------------------------------------- + +-- Unify PRODUCT / REDLINE / SALES into prc_price_set_version + product/detail +-- Run once on existing DB. Safe-ish: checks columns via information_schema patterns where needed. +-- price_type: PRODUCT | REDLINE | SALES + +-- ========== 1) prc_price_set_version: price_type + soft FK ========== + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_version ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT产品价/REDLINE红线价/SALES销售价'' AFTER price_set_id', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'price_type' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +UPDATE prc_price_set_version SET price_type = 'SALES' WHERE price_type IS NULL OR price_type = ''; + +-- drop FK fk_psv_set if exists +SET @sql := ( + SELECT IF(COUNT(*) > 0, + 'ALTER TABLE prc_price_set_version DROP FOREIGN KEY fk_psv_set', + 'SELECT 1') + FROM information_schema.TABLE_CONSTRAINTS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND CONSTRAINT_NAME = 'fk_psv_set' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- price_set_id nullable +ALTER TABLE prc_price_set_version MODIFY COLUMN price_set_id bigint(20) DEFAULT NULL COMMENT '价格套餐id(弱关联;PRODUCT/REDLINE为空)'; + +-- generated key for uniqueness (NULL price_set_id -> 0) +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_version ADD COLUMN price_set_id_key bigint(20) GENERATED ALWAYS AS (IFNULL(price_set_id,0)) STORED COMMENT ''唯一键辅助:空price_set_id视为0'' AFTER price_set_id', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND COLUMN_NAME = 'price_set_id_key' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- replace unique keys +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_version DROP INDEX uk_psv_set_no', 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_set_no' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_version DROP INDEX uk_psv_set_seq', 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_set_seq' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_version ADD UNIQUE KEY uk_psv_type_set_no (price_type, price_set_id_key, version_no)', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_type_set_no' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_version ADD UNIQUE KEY uk_psv_type_set_seq (price_type, price_set_id_key, version_seq)', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'uk_psv_type_set_seq' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_version ADD KEY idx_psv_type_eff (price_type, status, effective_time)', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_version' AND INDEX_NAME = 'idx_psv_type_eff' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- ========== 2) prc_price_set_product: price_type, drop price_set_id ========== +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT/REDLINE/SALES'' AFTER psp_id', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'price_type' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +UPDATE prc_price_set_product psp +INNER JOIN prc_price_set_version v ON v.psv_id = psp.psv_id +SET psp.price_type = IFNULL(NULLIF(v.price_type,''), 'SALES') +WHERE psp.price_type IS NULL OR psp.price_type = '' OR psp.price_type = 'SALES'; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP FOREIGN KEY fk_psp_set', 'SELECT 1') + FROM information_schema.TABLE_CONSTRAINTS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND CONSTRAINT_NAME = 'fk_psp_set' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_product DROP COLUMN price_set_id', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'price_set_id' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_product ADD KEY idx_psp_type_psv (price_type, psv_id)', 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'idx_psp_type_psv' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- ========== 3) prc_price_set_detail: price_type, catalog cols, drop price_set_id ========== +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN price_type varchar(20) NOT NULL DEFAULT ''SALES'' COMMENT ''价格类型 PRODUCT/REDLINE/SALES'' AFTER psd_id', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'price_type' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +UPDATE prc_price_set_detail d +INNER JOIN prc_price_set_version v ON v.psv_id = d.psv_id +SET d.price_type = IFNULL(NULLIF(v.price_type,''), 'SALES') +WHERE d.psv_id IS NOT NULL; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN size_limit varchar(1000) DEFAULT NULL COMMENT ''尺寸限制'' AFTER fv_registration_fee', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'size_limit' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN last_mile varchar(500) DEFAULT NULL COMMENT ''尾程派送说明'' AFTER size_limit', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'last_mile' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN lead_time varchar(100) DEFAULT NULL COMMENT ''参考时效'' AFTER last_mile', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'lead_time' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_detail DROP FOREIGN KEY fk_psd_set', 'SELECT 1') + FROM information_schema.TABLE_CONSTRAINTS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND CONSTRAINT_NAME = 'fk_psd_set' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, 'ALTER TABLE prc_price_set_detail DROP COLUMN price_set_id', 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'price_set_id' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, 'ALTER TABLE prc_price_set_detail ADD KEY idx_psd_type_psv (price_type, psv_id)', 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND INDEX_NAME = 'idx_psd_type_psv' LIMIT 1 +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- ========== 4) Migrate PRODUCT catalog -> version snapshot ========== +-- Create one version per distinct effective_date group from active product rows (status in 0,1), +-- collapsed into global versions ordered by effective_date. + +DROP TEMPORARY TABLE IF EXISTS tmp_product_version_map; +CREATE TEMPORARY TABLE tmp_product_version_map ( + old_product_id bigint(20) NOT NULL PRIMARY KEY, + new_psv_id bigint(20) NOT NULL, + new_psp_id bigint(20) DEFAULT NULL +); + +DROP TEMPORARY TABLE IF EXISTS tmp_product_eff; +CREATE TEMPORARY TABLE tmp_product_eff AS +SELECT DISTINCT effective_date +FROM product +WHERE IFNULL(is_del,'0') = '0' +ORDER BY IFNULL(effective_date, '1970-01-01'); + +-- Build PRODUCT versions from distinct effective dates (empty catalog skipped) +INSERT INTO prc_price_set_version ( + price_set_id, price_type, version_no, version_seq, effective_time, expire_time, status, + price_build_mode, created_by, create_time +) +SELECT + NULL, + 'PRODUCT', + CONCAT('V', (@seq := @seq + 1)), + @seq, + e.effective_date, + NULL, + CASE + WHEN e.effective_date IS NOT NULL AND e.effective_date > NOW() THEN '0' + ELSE '1' + END, + 'IMPORT', + 'migrate', + NOW() +FROM tmp_product_eff e +CROSS JOIN (SELECT @seq := IFNULL((SELECT MAX(version_seq) FROM prc_price_set_version WHERE price_type='PRODUCT' AND IFNULL(price_set_id,0)=0), 0)) s +WHERE NOT EXISTS ( + SELECT 1 FROM prc_price_set_version v + WHERE v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0) = 0 +) +ORDER BY IFNULL(e.effective_date, '1970-01-01'); + +-- If versions already exist (re-run), skip product migrate body when PRODUCT versions already present with products +SET @product_migrated := ( + SELECT COUNT(*) FROM prc_price_set_product WHERE price_type = 'PRODUCT' +); + +-- Chain expire_time for PRODUCT versions +UPDATE prc_price_set_version v +LEFT JOIN prc_price_set_version n + ON n.price_type = 'PRODUCT' AND IFNULL(n.price_set_id,0)=0 AND n.version_seq = v.version_seq + 1 +SET v.expire_time = n.effective_time, + v.status = CASE + WHEN n.psv_id IS NOT NULL AND n.effective_time IS NOT NULL AND n.effective_time <= NOW() THEN '2' + WHEN v.effective_time IS NOT NULL AND v.effective_time > NOW() THEN '0' + WHEN v.effective_time IS NULL OR v.effective_time <= NOW() THEN '1' + ELSE v.status + END +WHERE v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0)=0; + +-- Map each product row to version by effective_date match (only if not yet migrated) +INSERT INTO prc_price_set_product ( + price_type, psv_id, product_code, product_name, service_level, cargo_attr, dest_region, + update_note, tax_mode, source_product_id, source_sheet, create_time +) +SELECT + 'PRODUCT', + v.psv_id, + p.product_code, + p.product_name, + p.service_level, + p.cargo_attr, + p.dest_region, + p.update_note, + IFNULL(p.tax_mode, 'TAX_INCLUDED'), + p.product_id, + p.source_sheet, + IFNULL(p.create_time, NOW()) +FROM product p +INNER JOIN prc_price_set_version v + ON v.price_type = 'PRODUCT' AND IFNULL(v.price_set_id,0)=0 + AND ( + (p.effective_date IS NULL AND v.effective_time IS NULL) + OR p.effective_date = v.effective_time + ) +WHERE IFNULL(p.is_del,'0') = '0' + AND @product_migrated = 0 + AND NOT EXISTS ( + SELECT 1 FROM prc_price_set_product x + WHERE x.psv_id = v.psv_id AND x.product_code = p.product_code + ); + +INSERT INTO tmp_product_version_map (old_product_id, new_psv_id, new_psp_id) +SELECT p.product_id, psp.psv_id, psp.psp_id +FROM product p +INNER JOIN prc_price_set_product psp + ON psp.price_type = 'PRODUCT' AND psp.source_product_id = p.product_id; + +INSERT INTO prc_price_set_detail ( + price_type, psv_id, psp_id, product_code, product_name, country_code, country_name, + zone_id, zone_code, zone_name, weight_label, weight_min, weight_max, product_country_id, + fv_value, fv_registration_fee, size_limit, last_mile, lead_time, round_mode, + effective_date, expire_date, create_time +) +SELECT + 'PRODUCT', + m.new_psv_id, + m.new_psp_id, + c.product_code, + p.product_name, + c.country_code, + c.country_name, + c.zone_id, + c.zone_code, + c.zone_name, + CASE + WHEN c.weight_min IS NULL AND c.weight_max IS NULL THEN NULL + ELSE CONCAT( + IFNULL(TRIM(TRAILING '0' FROM TRIM(TRAILING '.' FROM CAST(c.weight_min AS CHAR))), ''), + ' NOW() THEN '0' + ELSE '1' + END, + 'IMPORT', + 'migrate', + NOW() +FROM tmp_redline_eff e +CROSS JOIN (SELECT @rseq := IFNULL((SELECT MAX(version_seq) FROM prc_price_set_version WHERE price_type='REDLINE' AND IFNULL(price_set_id,0)=0), 0)) s +WHERE NOT EXISTS ( + SELECT 1 FROM prc_price_set_version v + WHERE v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0) = 0 +) +ORDER BY IFNULL(e.effective_date, '1970-01-01'); + +SET @redline_migrated := ( + SELECT COUNT(*) FROM prc_price_set_product WHERE price_type = 'REDLINE' +); + +UPDATE prc_price_set_version v +LEFT JOIN prc_price_set_version n + ON n.price_type = 'REDLINE' AND IFNULL(n.price_set_id,0)=0 AND n.version_seq = v.version_seq + 1 +SET v.expire_time = n.effective_time, + v.status = CASE + WHEN n.psv_id IS NOT NULL AND n.effective_time IS NOT NULL AND n.effective_time <= NOW() THEN '2' + WHEN v.effective_time IS NOT NULL AND v.effective_time > NOW() THEN '0' + WHEN v.effective_time IS NULL OR v.effective_time <= NOW() THEN '1' + ELSE v.status + END +WHERE v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0)=0; + +INSERT INTO prc_price_set_product ( + price_type, psv_id, product_code, product_name, service_level, cargo_attr, dest_region, + update_note, tax_mode, source_product_id, source_sheet, create_time +) +SELECT + 'REDLINE', + v.psv_id, + p.product_code, + p.product_name, + p.service_level, + p.cargo_attr, + p.dest_region, + p.update_note, + IFNULL(p.tax_mode, 'TAX_INCLUDED'), + p.product_id, + p.source_sheet, + IFNULL(p.create_time, NOW()) +FROM redline_product p +INNER JOIN prc_price_set_version v + ON v.price_type = 'REDLINE' AND IFNULL(v.price_set_id,0)=0 + AND ( + (p.effective_date IS NULL AND v.effective_time IS NULL) + OR p.effective_date = v.effective_time + ) +WHERE IFNULL(p.is_del,'0') = '0' + AND @redline_migrated = 0 + AND NOT EXISTS ( + SELECT 1 FROM prc_price_set_product x + WHERE x.psv_id = v.psv_id AND x.product_code = p.product_code + ); + +INSERT INTO tmp_redline_version_map (old_product_id, new_psv_id, new_psp_id) +SELECT p.product_id, psp.psv_id, psp.psp_id +FROM redline_product p +INNER JOIN prc_price_set_product psp + ON psp.price_type = 'REDLINE' AND psp.source_product_id = p.product_id; + +INSERT INTO prc_price_set_detail ( + price_type, psv_id, psp_id, product_code, product_name, country_code, country_name, + zone_id, zone_code, zone_name, weight_label, weight_min, weight_max, product_country_id, + fv_value, fv_registration_fee, size_limit, last_mile, lead_time, round_mode, + effective_date, expire_date, create_time +) +SELECT + 'REDLINE', + m.new_psv_id, + m.new_psp_id, + c.product_code, + p.product_name, + c.country_code, + c.country_name, + c.zone_id, + c.zone_code, + c.zone_name, + CASE + WHEN c.weight_min IS NULL AND c.weight_max IS NULL THEN NULL + ELSE CONCAT( + IFNULL(TRIM(TRAILING '0' FROM TRIM(TRAILING '.' FROM CAST(c.weight_min AS CHAR))), ''), + ' 0, + 'ALTER TABLE prc_price_set_product DROP INDEX uk_psp_psv_code', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'uk_psp_psv_code' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD COLUMN active_product_code varchar(20) GENERATED ALWAYS AS (CASE WHEN IFNULL(is_del,''0'') = ''0'' THEN product_code ELSE NULL END) STORED COMMENT ''未删除时等于product_code,用于唯一约束''', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'active_product_code' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD UNIQUE KEY uk_psp_psv_active_code (psv_id, active_product_code)', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'uk_psp_psv_active_code' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- --------------------------------------------------------------------------- +-- 3) product/detail effective_time expire_time status sync from version +-- source: prc_price_set_product_effect_alter.sql +-- --------------------------------------------------------------------------- + +-- product effective/expire time and status; detail status; detail dates keep time + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD COLUMN effective_time datetime DEFAULT NULL COMMENT ''生效时间'' AFTER tax_mode', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'effective_time' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD COLUMN expire_time datetime DEFAULT NULL COMMENT ''失效时间(空=长期)'' AFTER effective_time', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'expire_time' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_product ADD COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_time', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'status' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- If status was added before expire_time, move status after expire_time when both exist +SET @has_status := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'status' +); +SET @has_expire := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'expire_time' +); +SET @sql := ( + SELECT IF(@has_status > 0 AND @has_expire > 0, + 'ALTER TABLE prc_price_set_product MODIFY COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_time', + 'SELECT 1') +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +ALTER TABLE prc_price_set_detail + MODIFY COLUMN effective_date datetime DEFAULT NULL COMMENT '生效时间', + MODIFY COLUMN expire_date datetime DEFAULT NULL COMMENT '失效时间(空=长期)'; + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_set_detail ADD COLUMN status char(1) NOT NULL DEFAULT ''0'' COMMENT ''状态 0草稿 3待生效 1已生效 2已失效'' AFTER expire_date', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_detail' AND COLUMN_NAME = 'status' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +UPDATE prc_price_set_product p +INNER JOIN prc_price_set_version v ON v.psv_id = p.psv_id +SET p.effective_time = v.effective_time, + p.expire_time = v.expire_time, + p.status = v.status +WHERE p.price_type IN ('PRODUCT', 'REDLINE') + AND v.price_set_id IS NULL; + +UPDATE prc_price_set_detail d +INNER JOIN prc_price_set_version v ON v.psv_id = d.psv_id +SET d.effective_date = v.effective_time, + d.expire_date = v.expire_time, + d.status = v.status +WHERE d.price_type IN ('PRODUCT', 'REDLINE') + AND v.price_set_id IS NULL; + +-- --------------------------------------------------------------------------- +-- 4) create prc_price_excel_file +-- source: prc_price_excel_file.sql +-- --------------------------------------------------------------------------- + +-- ------------------------------------------------------------ +-- 导入价表 Excel 附件记录 +-- 关联:prc_price_excel_file.psv_id -> prc_price_set_version.psv_id +-- ------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS prc_price_excel_file ( + pef_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', + excel_type varchar(20) NOT NULL COMMENT '价表类型 PRODUCT/REDLINE/SALES', + psv_id bigint(20) DEFAULT NULL COMMENT '价格版本id prc_price_set_version.psv_id', + effective_time datetime NOT NULL COMMENT '生效时间', + file_name varchar(255) NOT NULL COMMENT '文件原始名称', + file_path varchar(500) NOT NULL COMMENT '文件存储路径', + file_size bigint(20) DEFAULT 0 COMMENT '文件大小(字节)', + content_type varchar(100) DEFAULT '' COMMENT 'MIME', + file_extension varchar(20) DEFAULT '' COMMENT '扩展名', + oss_id varchar(64) DEFAULT NULL, + sheet_names varchar(1000) DEFAULT NULL, + remark varchar(500) DEFAULT NULL, + created_by varchar(64) DEFAULT '', + create_time datetime DEFAULT NULL, + updated_by varchar(64) DEFAULT '', + update_time datetime DEFAULT NULL, + PRIMARY KEY (pef_id), + KEY idx_pef_type_eff (excel_type, effective_time), + KEY idx_pef_psv (psv_id), + KEY idx_pef_create_time (create_time) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='导入价表Excel附件'; + +-- --------------------------------------------------------------------------- +-- 5) excel_file.psv_id backfill + drop product.pef_id +-- source: prc_price_excel_psv_alter.sql +-- --------------------------------------------------------------------------- + +-- Move Excel attachment link: product.pef_id -> excel_file.psv_id + +SET @sql := ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_price_excel_file ADD COLUMN psv_id bigint(20) DEFAULT NULL COMMENT ''价格版本id prc_price_set_version.psv_id'' AFTER excel_type, ADD KEY idx_pef_psv (psv_id)', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_excel_file' AND COLUMN_NAME = 'psv_id' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @has_psp_pef := ( + SELECT COUNT(*) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'pef_id' +); + +SET @sql := IF(@has_psp_pef > 0, + 'UPDATE prc_price_excel_file f INNER JOIN (SELECT pef_id, MIN(psv_id) AS psv_id FROM prc_price_set_product WHERE pef_id IS NOT NULL GROUP BY pef_id) x ON x.pef_id = f.pef_id SET f.psv_id = IFNULL(f.psv_id, x.psv_id)', + 'SELECT 1'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, + 'ALTER TABLE prc_price_set_product DROP INDEX idx_psp_pef_id', + 'SELECT 1') + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND INDEX_NAME = 'idx_psp_pef_id' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql := ( + SELECT IF(COUNT(*) > 0, + 'ALTER TABLE prc_price_set_product DROP COLUMN pef_id', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_price_set_product' AND COLUMN_NAME = 'pef_id' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- --------------------------------------------------------------------------- +-- 6) order bill fail snapshot + income refund_status +-- source: prc_order_bill_fail_snapshot_alter.sql +-- --------------------------------------------------------------------------- + +-- idempotent: order bill fail snapshot + income refund_status + +SET @sql = ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_order_bill ADD COLUMN error_message varchar(1000) NULL COMMENT \'失败原因\' AFTER billing_fail_status', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'error_message' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_order_bill ADD COLUMN order_status int NULL COMMENT \'上游订单状态\' AFTER order_time', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'order_status' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_order_bill ADD COLUMN bl_pickup tinyint NULL COMMENT \'是否收货:0否/1是\' AFTER order_status', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'bl_pickup' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_order_bill ADD COLUMN pickup_date datetime NULL COMMENT \'收货时间\' AFTER bl_pickup', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_order_bill' AND COLUMN_NAME = 'pickup_date' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +SET @sql = ( + SELECT IF(COUNT(*) = 0, + 'ALTER TABLE prc_income_bill ADD COLUMN refund_status varchar(20) NOT NULL DEFAULT \'NONE\' COMMENT \'是否退货:NONE未退货/REFUNDED已退货\' AFTER income_total', + 'SELECT 1') + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'prc_income_bill' AND COLUMN_NAME = 'refund_status' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +ALTER TABLE prc_order_bill + MODIFY COLUMN product_code varchar(20) NULL COMMENT '产品代码', + MODIFY COLUMN weight decimal(10,3) NULL COMMENT '计费重量KG', + MODIFY COLUMN billing_fail_status tinyint NOT NULL DEFAULT 0 COMMENT '计费失败状态:0成功/1未匹配产品/2未匹配国家/3未匹配重量段/4未绑定套餐/5无生效套餐版本/6已生成应收账单/7产品档案缺失/8未命中价格/9分区无价格/10邮编必填/11邮编未匹配分区/12上游订单不存在/13上游订单号为空/14上游客户为空/15上游产品为空/16上游重量无效/17计费时间为空/18退费订单无法解析/19客户代码为空/20客户已停用/21运费为空/22未知失败。仅0/1/2/3写入应收主表,仅0写入应收费用'; + +-- --------------------------------------------------------------------------- +-- 7) order bill query menu (2340/2341) +-- source: order_bill_query_menu.sql +-- --------------------------------------------------------------------------- + +-- Move order source back under system monitor, add read-only order bill query under billing center + +SET @monitorParent := ( + SELECT menu_id FROM sys_menu + WHERE menu_type = 'M' AND (path = 'monitor' OR menu_name = '系统监控') + ORDER BY menu_id + LIMIT 1 +); + +UPDATE sys_menu + SET parent_id = @monitorParent, + menu_name = '订单数据源', + order_num = 9, + path = 'orderSource', + component = 'monitor/orderSource/index', + route_name = 'OrderSource', + visible = '0', + status = '0', + update_by = 'admin', + update_time = sysdate() + WHERE @monitorParent IS NOT NULL + AND menu_type = 'C' + AND (menu_id = 2325 OR perms = 'monitor:orderSource:list'); + +SET @billParent := ( + SELECT parent_id FROM sys_menu + WHERE menu_type = 'C' AND component = 'price/billing/bill' + ORDER BY menu_id + LIMIT 1 +); + +SET @orderBillMenuExists := ( + SELECT COUNT(*) FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill:list' +); + +UPDATE sys_menu + SET order_num = order_num + 1 + WHERE @billParent IS NOT NULL + AND @orderBillMenuExists = 0 + AND parent_id = @billParent + AND menu_type = 'C'; + +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 2340, '订单列表查询', @billParent, 1, + 'orderBill', 'price/orderBill/index', '', 'OrderBillQuery', + 1, 0, 'C', '0', '0', 'price:orderBill:list', 'list', 'admin', sysdate(), + '查询 prc_order_bill 全部订单,只读' + FROM DUAL + WHERE @billParent IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2340 OR perms = 'price:orderBill: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 2341, '查询', 2340, 1, '', NULL, + 1, 0, 'F', '0', '0', 'price:orderBill:list', '#', 'admin', sysdate() + FROM DUAL + WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2341); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 1, m.menu_id + FROM (SELECT 2340 AS menu_id UNION SELECT 2341) m + WHERE NOT EXISTS ( + SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id + ); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT DISTINCT rm.role_id, 2340 + FROM sys_role_menu rm + JOIN sys_menu src ON src.menu_id = rm.menu_id + WHERE src.menu_type = 'C' AND src.component = 'price/billing/bill' + AND NOT EXISTS ( + SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2340 + ); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT DISTINCT rm.role_id, 2341 + FROM sys_role_menu rm + WHERE rm.menu_id = 2340 + AND NOT EXISTS ( + SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2341 + ); + +-- --------------------------------------------------------------------------- +-- 8) order bill rebill menu (2342, depends on 2340) +-- source: order_bill_rebill_menu.sql +-- --------------------------------------------------------------------------- + +-- Order list query: rebill button, same upstream sync as order data source + +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 2342, '重新计费', 2340, 2, '', NULL, + 1, 0, 'F', '0', '0', 'price:orderBill:rebill', '#', 'admin', sysdate(), + '订单列表按单号或选中行重走上游计费' + FROM DUAL + WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2342 OR perms = 'price:orderBill:rebill'); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT 1, 2342 + FROM DUAL + WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu WHERE role_id = 1 AND menu_id = 2342); + +INSERT INTO sys_role_menu (role_id, menu_id) +SELECT DISTINCT rm.role_id, 2342 + FROM sys_role_menu rm + WHERE rm.menu_id = 2340 + AND NOT EXISTS ( + SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = 2342 + ); + diff --git a/bms-back/sql/price/prc_price_set_user.sql b/bms-back/sql/price/prc_price_set_user.sql new file mode 100644 index 00000000..e136c884 --- /dev/null +++ b/bms-back/sql/price/prc_price_set_user.sql @@ -0,0 +1,16 @@ +-- Price set viewer bind +SET NAMES utf8mb4; + +CREATE TABLE IF NOT EXISTS prc_price_set_user ( + psu_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', + price_set_id bigint(20) NOT NULL COMMENT '价格套餐id', + user_id bigint(20) NOT NULL COMMENT '用户id', + user_name varchar(64) NOT NULL DEFAULT '' COMMENT '登录账号', + created_by varchar(64) DEFAULT '' COMMENT '创建人', + create_time datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (psu_id), + UNIQUE KEY uk_psu_set_user (price_set_id, user_id), + KEY idx_psu_user (user_id), + KEY idx_psu_set (price_set_id), + CONSTRAINT fk_psu_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='套餐可见用户绑定'; diff --git a/bms-front/src/api/price/set.js b/bms-front/src/api/price/set.js index 7bbe9b52..0a819332 100644 --- a/bms-front/src/api/price/set.js +++ b/bms-front/src/api/price/set.js @@ -412,3 +412,26 @@ export function delSetCustomer(ids) { method: 'delete' }) } + +export function listSetViewer(query) { + return request({ + url: '/price/set/viewer/list', + method: 'get', + params: query + }) +} + +export function addSetViewer(data) { + return request({ + url: '/price/set/viewer', + method: 'post', + data: data + }) +} + +export function delSetViewer(ids) { + return request({ + url: '/price/set/viewer/' + ids, + method: 'delete' + }) +} diff --git a/bms-front/src/views/price/billing/bill.vue b/bms-front/src/views/price/billing/bill.vue index 325f93c1..b35179de 100644 --- a/bms-front/src/views/price/billing/bill.vue +++ b/bms-front/src/views/price/billing/bill.vue @@ -334,7 +334,7 @@ const columns = [ { label: '下单时间', prop: 'orderTime', align: 'center', width: 170, parseTime: true }, { label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true }, { label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true }, - // { label: '客服员', prop: 'serviceMan', minWidth: 100, showOverflowTooltip: true }, + { label: '客服员', prop: 'serviceMan', minWidth: 100, showOverflowTooltip: true }, { label: '操作', prop: 'action', align: 'center', width: 240, fixed: 'right', slot: 'action', showInToolbar: false } ] diff --git a/bms-front/src/views/price/orderBill/index.vue b/bms-front/src/views/price/orderBill/index.vue index 0768d9e4..6ba7dfa9 100644 --- a/bms-front/src/views/price/orderBill/index.vue +++ b/bms-front/src/views/price/orderBill/index.vue @@ -79,16 +79,6 @@ - @@ -106,7 +96,6 @@ const tableRef = ref() const syncDialogRef = ref() const logDrawerRef = ref() const batchLoading = ref(false) -const rebillingKey = ref(null) const tableHeight = ref(480) let tableResizeObserver = null @@ -130,7 +119,7 @@ const columns = [ { label: '提货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true }, { label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true }, { label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true }, - { label: '操作', prop: 'action', align: 'center', width: 120, fixed: 'right', slot: 'action', showInToolbar: false } + { label: '客服员', prop: 'serviceMan', minWidth: 100, showOverflowTooltip: true } ] const orderStatusMap = { @@ -256,27 +245,6 @@ function toSyncRow(row) { } } -function handleRebillOne(row) { - if (!toSyncRow(row).pickupDate) { - ElMessage.warning('该订单没有计费时间,无法重新计费') - return - } - ElMessageBox.confirm('确认重新计费该订单?', '二次确认', { type: 'warning' }).then(() => { - rebillingKey.value = row.billId - return syncOrderSourceSelected([toSyncRow(row)]) - }).then(res => { - const data = (res && res.data) || {} - if ((data.ok || 0) > 0 && !(data.fail || 0)) { - ElMessage.success('重新计费成功') - } else { - const detail = data.details && data.details[0] - ElMessage.error((detail && detail.errorMessage) || '重新计费失败') - } - tableRef.value?.refresh?.() - }).catch(() => {}).finally(() => { - rebillingKey.value = null - }) -} function handleBatchRebill(selection) { const rows = selection || [] diff --git a/bms-front/src/views/price/set/components/detail/SetUserTable.vue b/bms-front/src/views/price/set/components/detail/SetUserTable.vue new file mode 100644 index 00000000..5f92ac18 --- /dev/null +++ b/bms-front/src/views/price/set/components/detail/SetUserTable.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/bms-front/src/views/price/set/detail.vue b/bms-front/src/views/price/set/detail.vue index a9211e30..d5376fe6 100644 --- a/bms-front/src/views/price/set/detail.vue +++ b/bms-front/src/views/price/set/detail.vue @@ -20,6 +20,8 @@ + +