diff --git a/admin-web/src/views/Conversation.vue b/admin-web/src/views/Conversation.vue index 1b5ed4e..5db3984 100644 --- a/admin-web/src/views/Conversation.vue +++ b/admin-web/src/views/Conversation.vue @@ -220,7 +220,9 @@ function formatAccountName(id: number | undefined) { const filteredBindAccounts = computed(() => { const channelType = currentBindConversation.value?.channelType if (!channelType) return accounts.value - return accounts.value.filter((a: any) => a.channelType === channelType) + return accounts.value.filter((a: any) => + a.channelType?.toLowerCase() === channelType?.toLowerCase() + ) }) function formatConversationType(type: number | undefined) { @@ -337,7 +339,10 @@ function openBindAccount(row: any) { } async function handleBindAccount() { - if (!bindAccountForm.accountId || !currentBindConversation.value) return + if (!bindAccountForm.accountId || !currentBindConversation.value) { + ElMessage.warning('请选择账号') + return + } try { await bindConversationAccount(currentBindConversation.value.id, bindAccountForm) ElMessage.success('绑定成功') @@ -348,8 +353,8 @@ async function handleBindAccount() { } } -const currentConversationId = ref() +const currentConversationId = ref() const bindTagsVisible = ref(false) const bindTagsForm = reactive({ tagIds: [] as number[], diff --git a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java index 30577d8..65a227f 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java @@ -364,13 +364,9 @@ public class SendService { 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) { + if (!AccountSelector.isHealthy(account, tenantCode, channelType)) { throw new BusinessException("租户没有可用的发送账号: channel=" + channelType.name().toLowerCase()); } - if (!Objects.equals(channelType.name().toLowerCase(), account.getChannelType())) { - throw new BusinessException("单聊发送账号渠道类型与目标身份不一致"); - } SendTarget target = new SendTarget(); target.setConversationId(null); target.setPersonId(personId); diff --git a/backend/mci-server/src/test/java/com/sino/mci/channel/service/ConversationServiceTest.java b/backend/mci-server/src/test/java/com/sino/mci/channel/service/ConversationServiceTest.java index ed4a385..01a62bf 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/channel/service/ConversationServiceTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/channel/service/ConversationServiceTest.java @@ -12,6 +12,7 @@ import com.sino.mci.crm.repository.TagMapper; import com.sino.mci.exception.BusinessException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -81,7 +82,56 @@ class ConversationServiceTest { BindConversationAccountRequest request = createBindRequest(10L); conversationService.bindAccount("tenant", 1L, request); - verify(bindingMapper).insert(any(ChannelAccountConversation.class)); + ArgumentCaptor captor = ArgumentCaptor.forClass(ChannelAccountConversation.class); + verify(bindingMapper).insert(captor.capture()); + ChannelAccountConversation binding = captor.getValue(); + assertEquals(10L, binding.getAccountId()); + assertEquals(1L, binding.getConversationId()); + assertEquals(1, binding.getBindingType()); + assertEquals(0, binding.getSortOrder()); + assertEquals(1, binding.getStatus()); + } + + @Test + void bindAccount_defaultsSortOrderToZeroWhenNull() { + Conversation conversation = createConversation(); + ChannelAccount account = createAccount(10L, "wecom"); + + when(conversationMapper.selectById(1L)).thenReturn(conversation); + when(accountMapper.selectById(10L)).thenReturn(account); + + BindConversationAccountRequest request = createBindRequest(10L); + request.setSortOrder(null); + conversationService.bindAccount("tenant", 1L, request); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ChannelAccountConversation.class); + verify(bindingMapper).insert(captor.capture()); + assertEquals(0, captor.getValue().getSortOrder()); + } + + @Test + void bindAccount_rejectsConversationFromDifferentTenant() { + Conversation conversation = createConversation(); + conversation.setTenantCode("other-tenant"); + + when(conversationMapper.selectById(1L)).thenReturn(conversation); + + BindConversationAccountRequest request = createBindRequest(10L); + + BusinessException ex = assertThrows(BusinessException.class, + () -> conversationService.bindAccount("tenant", 1L, request)); + assertEquals("会话不存在", ex.getMessage()); + } + + @Test + void bindAccount_rejectsConversationNotFound() { + when(conversationMapper.selectById(1L)).thenReturn(null); + + BindConversationAccountRequest request = createBindRequest(10L); + + BusinessException ex = assertThrows(BusinessException.class, + () -> conversationService.bindAccount("tenant", 1L, request)); + assertEquals("会话不存在", ex.getMessage()); } private Conversation createConversation() { diff --git a/backend/mci-server/src/test/java/com/sino/mci/send/service/SendServiceResolveTargetTest.java b/backend/mci-server/src/test/java/com/sino/mci/send/service/SendServiceResolveTargetTest.java index 80e79d6..263459d 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/send/service/SendServiceResolveTargetTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/send/service/SendServiceResolveTargetTest.java @@ -183,7 +183,7 @@ class SendServiceResolveTargetTest { BusinessException ex = assertThrows(BusinessException.class, () -> sendService.resolveTestTargets("tenant", createPersonRequest())); - assertEquals("单聊发送账号渠道类型与目标身份不一致", ex.getMessage()); + assertEquals("租户没有可用的发送账号: channel=wecom", ex.getMessage()); } private SendTestRequest createConversationRequest(String strategy) {