feat(channel/send): support all 4 binding types and validate send targets
- BindConversationAccountRequest: restrict bindingType to 1-4 - ConversationService.bindAccount: upsert binding by account+conversation+type with sortOrder and status=1 - SendService.selectSendAccount: prefer send-primary(1), fallback send-backup(2), status=1, tenant/channel match, sortOrder asc - SendService.resolveConversationTarget/resolvePersonTarget: verify account tenant, channel type and status; ensure person identity channel type matches for single chat
This commit is contained in:
@@ -2,6 +2,7 @@ package com.sino.mci.channel.dto;
|
|||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Range;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class BindConversationAccountRequest {
|
public class BindConversationAccountRequest {
|
||||||
@@ -10,6 +11,7 @@ public class BindConversationAccountRequest {
|
|||||||
private Long accountId;
|
private Long accountId;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Range(min = 1, max = 4)
|
||||||
private Integer bindingType;
|
private Integer bindingType;
|
||||||
|
|
||||||
private Integer sortOrder;
|
private Integer sortOrder;
|
||||||
|
|||||||
@@ -104,14 +104,11 @@ public class ConversationService {
|
|||||||
public void bindAccount(String tenantCode, Long conversationId, BindConversationAccountRequest request) {
|
public void bindAccount(String tenantCode, Long conversationId, BindConversationAccountRequest request) {
|
||||||
getConversation(tenantCode, conversationId);
|
getConversation(tenantCode, conversationId);
|
||||||
|
|
||||||
ChannelAccountConversation existing = bindingMapper.selectOne(
|
bindingMapper.delete(
|
||||||
new LambdaQueryWrapper<ChannelAccountConversation>()
|
new LambdaQueryWrapper<ChannelAccountConversation>()
|
||||||
.eq(ChannelAccountConversation::getAccountId, request.getAccountId())
|
.eq(ChannelAccountConversation::getAccountId, request.getAccountId())
|
||||||
.eq(ChannelAccountConversation::getConversationId, conversationId)
|
.eq(ChannelAccountConversation::getConversationId, conversationId)
|
||||||
.eq(ChannelAccountConversation::getBindingType, request.getBindingType()));
|
.eq(ChannelAccountConversation::getBindingType, request.getBindingType()));
|
||||||
if (existing != null) {
|
|
||||||
throw new BusinessException("该账号已绑定此会话的相同类型");
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelAccountConversation binding = new ChannelAccountConversation();
|
ChannelAccountConversation binding = new ChannelAccountConversation();
|
||||||
binding.setAccountId(request.getAccountId());
|
binding.setAccountId(request.getAccountId());
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import com.sino.mci.channel.common.dto.SendMessageResult;
|
|||||||
import com.sino.mci.channel.common.model.ChannelType;
|
import com.sino.mci.channel.common.model.ChannelType;
|
||||||
import com.sino.mci.channel.common.spi.ChannelAdapter;
|
import com.sino.mci.channel.common.spi.ChannelAdapter;
|
||||||
import com.sino.mci.channel.entity.ChannelAccount;
|
import com.sino.mci.channel.entity.ChannelAccount;
|
||||||
|
import com.sino.mci.channel.entity.ChannelAccountConversation;
|
||||||
import com.sino.mci.channel.entity.Conversation;
|
import com.sino.mci.channel.entity.Conversation;
|
||||||
|
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
|
||||||
import com.sino.mci.channel.repository.ChannelAccountMapper;
|
import com.sino.mci.channel.repository.ChannelAccountMapper;
|
||||||
import com.sino.mci.channel.repository.ConversationMapper;
|
import com.sino.mci.channel.repository.ConversationMapper;
|
||||||
import com.sino.mci.channel.service.AccountSelector;
|
import com.sino.mci.channel.service.AccountSelector;
|
||||||
@@ -80,6 +82,7 @@ public class SendService {
|
|||||||
private final SendRecordMapper sendRecordMapper;
|
private final SendRecordMapper sendRecordMapper;
|
||||||
private final ConversationMapper conversationMapper;
|
private final ConversationMapper conversationMapper;
|
||||||
private final ChannelAccountMapper accountMapper;
|
private final ChannelAccountMapper accountMapper;
|
||||||
|
private final ChannelAccountConversationMapper channelAccountConversationMapper;
|
||||||
private final ChannelIdentityMapper channelIdentityMapper;
|
private final ChannelIdentityMapper channelIdentityMapper;
|
||||||
private final PersonMapper personMapper;
|
private final PersonMapper personMapper;
|
||||||
private final TagMapper tagMapper;
|
private final TagMapper tagMapper;
|
||||||
@@ -332,6 +335,12 @@ public class SendService {
|
|||||||
if (accountId == null) {
|
if (accountId == null) {
|
||||||
throw new BusinessException("会话未配置发送账号: " + conversationId);
|
throw new BusinessException("会话未配置发送账号: " + conversationId);
|
||||||
}
|
}
|
||||||
|
ChannelAccount account = accountMapper.selectById(accountId);
|
||||||
|
if (account == null || !tenantCode.equals(account.getTenantCode())
|
||||||
|
|| !channelType.name().equalsIgnoreCase(account.getChannelType())
|
||||||
|
|| account.getStatus() == null || account.getStatus() != STATUS_ENABLED) {
|
||||||
|
throw new BusinessException("会话没有可用的发送账号: " + conversationId);
|
||||||
|
}
|
||||||
SendTarget target = new SendTarget();
|
SendTarget target = new SendTarget();
|
||||||
target.setConversationId(conversationId);
|
target.setConversationId(conversationId);
|
||||||
target.setPersonId(null);
|
target.setPersonId(null);
|
||||||
@@ -356,6 +365,14 @@ public class SendService {
|
|||||||
if (accountId == null) {
|
if (accountId == null) {
|
||||||
throw new BusinessException("租户未配置可用发送账号: channel=" + channelType.name().toLowerCase());
|
throw new BusinessException("租户未配置可用发送账号: channel=" + channelType.name().toLowerCase());
|
||||||
}
|
}
|
||||||
|
ChannelAccount account = accountMapper.selectById(accountId);
|
||||||
|
if (account == null || !tenantCode.equals(account.getTenantCode())
|
||||||
|
|| account.getStatus() == null || account.getStatus() != STATUS_ENABLED) {
|
||||||
|
throw new BusinessException("租户没有可用的发送账号: channel=" + channelType.name().toLowerCase());
|
||||||
|
}
|
||||||
|
if (!channelType.name().equalsIgnoreCase(account.getChannelType())) {
|
||||||
|
throw new BusinessException("单聊发送账号渠道类型与目标身份不一致");
|
||||||
|
}
|
||||||
SendTarget target = new SendTarget();
|
SendTarget target = new SendTarget();
|
||||||
target.setConversationId(null);
|
target.setConversationId(null);
|
||||||
target.setPersonId(personId);
|
target.setPersonId(personId);
|
||||||
@@ -675,7 +692,24 @@ public class SendService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Long selectSendAccount(String tenantCode, Long conversationId, ChannelType channelType, Map<String, Object> channelStrategy) {
|
private Long selectSendAccount(String tenantCode, Long conversationId, ChannelType channelType, Map<String, Object> channelStrategy) {
|
||||||
return accountSelector.selectSendAccount(tenantCode, conversationId, channelType, extractStrategy(channelStrategy));
|
List<Integer> bindingTypes = List.of(1, 2);
|
||||||
|
for (Integer bindingType : bindingTypes) {
|
||||||
|
List<ChannelAccountConversation> bindings = channelAccountConversationMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ChannelAccountConversation>()
|
||||||
|
.eq(ChannelAccountConversation::getConversationId, conversationId)
|
||||||
|
.eq(ChannelAccountConversation::getBindingType, bindingType)
|
||||||
|
.eq(ChannelAccountConversation::getStatus, STATUS_ENABLED)
|
||||||
|
.orderByAsc(ChannelAccountConversation::getSortOrder));
|
||||||
|
for (ChannelAccountConversation binding : bindings) {
|
||||||
|
ChannelAccount account = accountMapper.selectById(binding.getAccountId());
|
||||||
|
if (account != null && tenantCode.equals(account.getTenantCode())
|
||||||
|
&& channelType.name().equalsIgnoreCase(account.getChannelType())
|
||||||
|
&& account.getStatus() != null && account.getStatus() == STATUS_ENABLED) {
|
||||||
|
return account.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long selectDefaultAccount(String tenantCode, ChannelType channelType) {
|
private Long selectDefaultAccount(String tenantCode, ChannelType channelType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user