渠道管理增加应收关联

This commit is contained in:
wuweihua
2026-11-09 17:27:21 +08:00
parent ace1eb06c0
commit 2632e5aa78
10 changed files with 131 additions and 28 deletions
@@ -114,17 +114,18 @@ public class PayProductController extends BaseController
return success(payProductService.selectTemplateOptions(payType));
}
/** 渠道当前价格副本 */
/** 渠道当前价格副本;payType 默认应付 AP */
@PreAuthorize("@ss.hasAnyPermi('price:pay:query,data:serviceChannel:query,data:serviceChannel:edit')")
@GetMapping("/channel/{channelId}")
public AjaxResult channelPrice(@PathVariable String channelId)
public AjaxResult channelPrice(@PathVariable String channelId,
@RequestParam(value = "payType", required = false) String payType)
{
return success(payProductService.selectChannelPrice(channelId));
return success(payProductService.selectChannelPrice(channelId, payType));
}
/** 渠道只绑定产品编号,以后按有效期取价 */
/** 渠道只绑定产品编号,以后按有效期取价;支持应收 AR 与应付 AP */
@PreAuthorize("@ss.hasAnyPermi('price:pay:edit,data:serviceChannel:edit')")
@Log(title = "渠道应付产品", businessType = BusinessType.UPDATE)
@Log(title = "渠道产品绑定", businessType = BusinessType.UPDATE)
@PutMapping("/channel/{channelId}/bind")
public AjaxResult bindChannel(@PathVariable String channelId, @RequestBody ChannelProductMap link)
{
@@ -135,7 +136,7 @@ public class PayProductController extends BaseController
return success();
}
payProductService.bindChannel(channelId, link.getProductId(), link.getPriceSetId(), payType);
return success(payProductService.selectChannelPrice(channelId));
return success(payProductService.selectChannelPrice(channelId, payType));
}
/** 对照两个价格版本 */
@@ -15,6 +15,9 @@ public class ChannelProductMap
/** 渠道表主键 srv_service_channel.id */
private String channelId;
/** 渠道编码,对应 srv_service_channel.channel_code */
private String channelCode;
private Long productId;
/** 应收套餐 prc_price_set.price_set_id */
@@ -22,7 +22,7 @@ public interface PayProductMapper
Long selectTemplateId(@Param("dictValue") String dictValue);
List<PayProduct> selectTemplateOptions();
List<PayProduct> selectTemplateOptions(@Param("payType") String payType);
Integer selectMaxVersion(@Param("originId") Long originId);
@@ -47,10 +47,13 @@ public interface IPayProductService
/** 公共模板下拉。payType 为空时按应付 AP */
List<PayProduct> selectTemplateOptions(String payType);
/** 渠道绑定的产品。返回的 originId 是不变的产品编号,价格是当前有效版 */
/** 渠道绑定的产品。返回的 originId 是不变的产品编号,价格是当前有效版;默认应付 AP */
PayProduct selectChannelPrice(String channelId);
/** 只绑定产品编号,不产生新版本。payType 为空时按应付 AP */
/** 按收付类型查询渠道绑定产品,payType 为空时按应付 AP */
PayProduct selectChannelPrice(String channelId, String payType);
/** 只绑定产品编号,不产生新版本;同时落 channel_code。payType 为空时按应付 AP */
void bindChannel(String channelId, Long productId, Long priceSetId, String payType);
/** 取消渠道上指定收付类型的产品。payType 为空时按应付 AP */
@@ -35,6 +35,8 @@ import com.bms.price.domain.PayProductTerm;
import com.bms.price.domain.PaySaveCommand;
import com.bms.price.domain.PaySurcharge;
import com.bms.price.domain.PayWeightBand;
import com.bms.masterdata.domain.ServiceChannel;
import com.bms.masterdata.mapper.ServiceChannelMapper;
import com.bms.price.mapper.ChannelProductMapMapper;
import com.bms.price.mapper.PayProductMapper;
import com.bms.price.mapper.PayProductTermMapper;
@@ -63,6 +65,9 @@ public class PayProductServiceImpl implements IPayProductService
@Autowired
private ChannelProductMapMapper channelProductMapMapper;
@Autowired
private ServiceChannelMapper serviceChannelMapper;
@Autowired
private PayProductTermMapper payProductTermMapper;
@@ -276,21 +281,23 @@ public class PayProductServiceImpl implements IPayProductService
@Override
public List<PayProduct> selectTemplateOptions(String payType)
{
if (!PAY_TYPE_AP.equals(payTypeOf(payType)))
{
return Collections.emptyList();
}
return payProductMapper.selectTemplateOptions();
return payProductMapper.selectTemplateOptions(payTypeOf(payType));
}
@Override
public PayProduct selectChannelPrice(String channelId)
{
return selectChannelPrice(channelId, PAY_TYPE_AP);
}
@Override
public PayProduct selectChannelPrice(String channelId, String payType)
{
if (isBlank(channelId))
{
return null;
}
Long root = channelProductRoot(channelId);
Long root = channelProductRoot(channelId, payTypeOf(payType));
if (root == null)
{
return null;
@@ -937,10 +944,16 @@ public class PayProductServiceImpl implements IPayProductService
{
throw new ServiceException("请先保存渠道,再选择产品");
}
ServiceChannel channel = serviceChannelMapper.selectServiceChannelById(channelId);
if (channel == null || isBlank(channel.getChannelCode()))
{
throw new ServiceException("渠道不存在或渠道编码为空");
}
String type = payTypeOf(payType);
Long storedId = PAY_TYPE_AP.equals(type) ? resolveRoot(productId) : productId;
Long storedId = resolveRoot(productId);
ChannelProductMap row = new ChannelProductMap();
row.setChannelId(channelId);
row.setChannelCode(channel.getChannelCode());
row.setProductId(storedId);
row.setPriceSetId(priceSetId);
row.setPayType(type);
@@ -1035,10 +1048,15 @@ public class PayProductServiceImpl implements IPayProductService
return saved;
}
/** 渠道应付绑定记在映射表,产品编号是不变的根产品。 */
/** 渠道产品绑定记在映射表,产品编号是不变的根产品。 */
private Long channelProductRoot(String channelId)
{
ChannelProductMap link = channelProductMapMapper.selectByChannelAndType(channelId, PAY_TYPE_AP);
return channelProductRoot(channelId, PAY_TYPE_AP);
}
private Long channelProductRoot(String channelId, String payType)
{
ChannelProductMap link = channelProductMapMapper.selectByChannelAndType(channelId, payType);
if (link == null || link.getProductId() == null)
{
return null;
@@ -5,8 +5,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="com.bms.price.mapper.ChannelProductMapMapper">
<select id="selectByChannelAndType" resultType="com.bms.price.domain.ChannelProductMap">
select id, channel_id as channelId, product_id as productId, price_set_id as priceSetId,
pay_type as payType, create_by as createBy, create_time as createTime,
select id, channel_id as channelId, channel_code as channelCode, product_id as productId,
price_set_id as priceSetId, pay_type as payType, create_by as createBy, create_time as createTime,
update_by as updateBy, update_time as updateTime
from srv_channel_product_map
where channel_id = #{channelId} and pay_type = #{payType}
@@ -15,11 +15,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="upsert">
insert into srv_channel_product_map (
channel_id, product_id, price_set_id, pay_type, create_by, create_time, update_by, update_time
channel_id, channel_code, product_id, price_set_id, pay_type, create_by, create_time, update_by, update_time
) values (
#{channelId}, #{productId}, #{priceSetId}, #{payType}, #{createBy}, sysdate(), #{updateBy}, sysdate()
#{channelId}, #{channelCode}, #{productId}, #{priceSetId}, #{payType}, #{createBy}, sysdate(), #{updateBy}, sysdate()
)
on duplicate key update
channel_code = values(channel_code),
product_id = values(product_id),
price_set_id = values(price_set_id),
update_by = values(update_by),
@@ -212,7 +212,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="selectTemplateOptions" resultMap="PayProductResult">
select p.product_id, p.product_name, p.dict_value, p.sheet_name, p.supplier_name, p.effective_date,
select p.product_id, p.product_name, p.product_type, p.dict_value, p.sheet_name, p.supplier_name, p.effective_date,
p.currency, ifnull(cur.version_no, p.version_no) as version_no
from pay_product p
left join pay_product_term t on t.term_id = (
@@ -227,6 +227,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join pay_product cur on cur.product_id = t.product_id
where (p.channel_id is null or p.channel_id = '')
and p.origin_id is null
and ifnull(p.product_type, 'AP') = #{payType}
order by p.product_name
</select>
+22 -2
View File
@@ -4,7 +4,8 @@ SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS `srv_channel_product_map` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`channel_id` varchar(50) NOT NULL COMMENT '渠道ID,对应 srv_service_channel.id',
`product_id` bigint(20) NOT NULL COMMENT '产品ID,应付时为价卡产品编号',
`channel_code` varchar(64) DEFAULT NULL COMMENT '渠道编码,对应 srv_service_channel.channel_code',
`product_id` bigint(20) NOT NULL COMMENT '产品ID,应付/应收时为价卡产品编号',
`price_set_id` bigint(20) DEFAULT NULL COMMENT '应收套餐ID,对应 prc_price_set.price_set_id',
`pay_type` varchar(10) NOT NULL COMMENT '收付类型,字典 bms_pay_type:AR应收 AP应付',
`create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
@@ -14,7 +15,8 @@ CREATE TABLE IF NOT EXISTS `srv_channel_product_map` (
PRIMARY KEY (`id`),
UNIQUE KEY `uk_channel_pay_type` (`channel_id`, `pay_type`),
KEY `idx_channel_product_map_product` (`product_id`),
KEY `idx_channel_product_map_price_set` (`price_set_id`)
KEY `idx_channel_product_map_price_set` (`price_set_id`),
KEY `idx_channel_product_map_channel_code` (`channel_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='渠道产品关系映射';
SET @sql = (
@@ -30,6 +32,24 @@ PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql_channel_code = (
SELECT IF(
COUNT(*) = 0,
'ALTER TABLE srv_channel_product_map ADD COLUMN channel_code varchar(64) DEFAULT NULL COMMENT ''渠道编码,对应 srv_service_channel.channel_code'' AFTER channel_id, ADD KEY idx_channel_product_map_channel_code (channel_code)',
'SELECT 1'
)
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'srv_channel_product_map' AND COLUMN_NAME = 'channel_code'
);
PREPARE stmt FROM @sql_channel_code;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
UPDATE srv_channel_product_map m
INNER JOIN srv_service_channel c ON c.id = m.channel_id
SET m.channel_code = c.channel_code
WHERE m.channel_code IS NULL OR m.channel_code = '';
SET @copy_sql = (
SELECT IF(
COUNT(*) > 0,
+3 -2
View File
@@ -80,10 +80,11 @@ export function comparePay(leftId, rightId) {
})
}
export function getChannelPay(channelId) {
export function getChannelPay(channelId, payType = 'AP') {
return request({
url: '/price/pay/channel/' + channelId,
method: 'get'
method: 'get',
params: { payType }
})
}
@@ -149,6 +149,14 @@
<div class="pay-tip">{{ payTip }}</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="应收产品">
<el-select v-model="arOriginId" filterable clearable placeholder="选择已导入的应收产品" style="width: 280px" :disabled="viewing || !form.id" @change="onArOrigin">
<el-option v-for="item in arOptions" :key="item.productId" :label="payOptionLabel(item)" :value="item.productId" />
</el-select>
<div class="pay-tip">{{ arTip }}</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
@@ -175,9 +183,13 @@ const open = ref(false)
const viewing = ref(false)
const title = ref('')
const payOptions = ref([])
const arOptions = ref([])
const payOriginId = ref()
const arOriginId = ref()
const payProduct = ref(null)
const arProduct = ref(null)
const syncingPay = ref(false)
const syncingAr = ref(false)
const payTip = computed(() => {
if (viewing.value) {
if (payProduct.value?.versionNo) return `已绑定第 ${payProduct.value.versionNo} 版。`
@@ -187,6 +199,15 @@ const payTip = computed(() => {
if (payProduct.value?.versionNo) return `当前按有效期使用第 ${payProduct.value.versionNo} 版。历史订单按下单或计费时间取当时的版本。`
return '选择产品后即绑定,按应付 AP 保存。'
})
const arTip = computed(() => {
if (viewing.value) {
if (arProduct.value?.versionNo) return `已绑定第 ${arProduct.value.versionNo} 版。`
return arOriginId.value ? '已绑定应收产品。' : '未绑定应收产品。'
}
if (!form.value?.id) return '先保存渠道,再选择应收产品。渠道绑定的是产品编号,更新价格不会换编号。'
if (arProduct.value?.versionNo) return `当前按有效期使用第 ${arProduct.value.versionNo} 版。历史订单按下单或计费时间取当时的版本。`
return '选择产品后即绑定,按应收 AR 保存。'
})
const openOptions = [
{ label: '启用', value: '1' },
@@ -260,10 +281,14 @@ function emptyForm() {
function resetPay() {
syncingPay.value = true
syncingAr.value = true
payOriginId.value = undefined
arOriginId.value = undefined
payProduct.value = null
arProduct.value = null
nextTick(() => {
syncingPay.value = false
syncingAr.value = false
})
}
@@ -276,11 +301,14 @@ function loadPayOptions() {
listPayOptions('AP').then(res => {
payOptions.value = res.data || []
})
listPayOptions('AR').then(res => {
arOptions.value = res.data || []
})
}
function loadChannelPay(channelId) {
syncingPay.value = true
getChannelPay(channelId).then(res => {
getChannelPay(channelId, 'AP').then(res => {
payProduct.value = res.data || null
payOriginId.value = res.data?.originId || res.data?.productId
}).finally(() => {
@@ -290,6 +318,18 @@ function loadChannelPay(channelId) {
})
}
function loadChannelAr(channelId) {
syncingAr.value = true
getChannelPay(channelId, 'AR').then(res => {
arProduct.value = res.data || null
arOriginId.value = res.data?.originId || res.data?.productId
}).finally(() => {
nextTick(() => {
syncingAr.value = false
})
})
}
function handleAdd() {
viewing.value = false
form.value = emptyForm()
@@ -307,6 +347,7 @@ function handleUpdate(row) {
resetPay()
loadPayOptions()
loadChannelPay(row.id)
loadChannelAr(row.id)
title.value = '修改渠道'
open.value = true
nextTick(() => formRef.value && formRef.value.clearValidate())
@@ -320,6 +361,7 @@ function handleDetail(row) {
resetPay()
loadPayOptions()
loadChannelPay(row.id)
loadChannelAr(row.id)
title.value = '渠道详情'
open.value = true
nextTick(() => formRef.value && formRef.value.clearValidate())
@@ -339,6 +381,19 @@ function onPayOrigin(id) {
})
}
function onArOrigin(id) {
if (viewing.value || !form.value.id || syncingAr.value) return
if (!id) {
arProduct.value = null
bindChannelPay(form.value.id, null, 'AR')
return
}
bindChannelPay(form.value.id, id, 'AR').then(res => {
arProduct.value = res.data || null
arOriginId.value = res.data?.originId || id
})
}
function submitForm() {
if (viewing.value) return
formRef.value.validate(valid => {