From 3a8d51b0dfa11949753a149f404e063c540dce38 Mon Sep 17 00:00:00 2001 From: jinliang Date: Tue, 14 Jul 2026 17:44:48 +0800 Subject: [PATCH] fix(send): remove out-of-scope tag/combined-target logic and revert conversationId handling Task 5 (aeb14cf) correctly added scene/person-based automatic routing, but also introduced tag/combined-target features and changed doSendAttempt conversationId handling that were not in the task brief. This commit reverts those out-of-scope changes while keeping the scene/person routing and account-type selection that belong to Task 5: - Revert tag target resolution to single-tag logic (no tag_ids/inner_logic) - Remove combined target type and all helper methods - Revert doSendAttempt conversationId to use record.conversationId directly - Keep conversation_scene routing, person_type routing, and account-type helpers --- .../sino/mci/send/service/SendService.java | 269 ++---------------- 1 file changed, 20 insertions(+), 249 deletions(-) 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 97a5019..ffe7f82 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 @@ -60,7 +60,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -316,10 +315,14 @@ public class SendService { } return targets; case "tag": - return resolveTagTargetFromValue(tenantCode, targetValue, channelType); + Number tagId = (Number) targetValue.get("tag_id"); + if (tagId == null) { + throw new BusinessException("tag 目标缺少 tag_id"); + } + String scope = (String) targetValue.getOrDefault("scope", "all"); + return resolveTagTarget(tenantCode, tagId.longValue(), channelType, scope); case "rule": - case "combined": - return resolveCombinedTarget(tenantCode, targetValue, channelType, channelStrategy); + return resolveRuleTarget(tenantCode, targetValue, channelType, channelStrategy); default: throw new BusinessException("不支持的目标类型: " + targetType); } @@ -408,132 +411,30 @@ public class SendService { return Collections.singletonList(target); } - private List resolveTagTargetFromValue(String tenantCode, Map targetValue, - ChannelType channelType) { - List tagIds = extractTagIds(targetValue); - if (CollectionUtils.isEmpty(tagIds)) { - throw new BusinessException("tag 目标缺少 tag_id 或 tag_ids"); - } - String scope = (String) targetValue.getOrDefault("scope", "all"); - String innerLogic = (String) targetValue.getOrDefault("inner_logic", "union"); - return resolveTagTarget(tenantCode, tagIds, channelType, scope, innerLogic); - } - - @SuppressWarnings("unchecked") - private List extractTagIds(Map targetValue) { - List tagIds = new ArrayList<>(); - Object tagIdsObj = targetValue.get("tag_ids"); - if (tagIdsObj instanceof List) { - for (Object item : (List) tagIdsObj) { - Long id = toLong(item); - if (id != null) { - tagIds.add(id); - } - } - } - Object singleTagId = targetValue.get("tag_id"); - if (singleTagId != null) { - Long id = toLong(singleTagId); - if (id != null && !tagIds.contains(id)) { - tagIds.add(id); - } - } - return tagIds; - } - private List resolveTagTarget(String tenantCode, Long tagId, ChannelType channelType, String scope) { - return resolveTagTarget(tenantCode, Collections.singletonList(tagId), channelType, scope, "union"); - } - - private List resolveTagTarget(String tenantCode, List tagIds, ChannelType channelType, - String scope, String innerLogic) { List targets = new ArrayList<>(); - String channelTypeStr = channelType.name().toLowerCase(); - boolean union = !"intersection".equals(innerLogic); - if ("all".equals(scope) || "conversation".equals(scope)) { - Set conversationIds = union ? new HashSet<>() : null; - if (!union) { - conversationIds = selectConversationIdsByAllTags(tenantCode, tagIds); - } else { - for (Long tagId : tagIds) { - conversationIds.addAll(selectConversationIdsByTag(tenantCode, tagId)); - } - } - for (Long conversationId : conversationIds) { - Conversation conversation = conversationMapper.selectById(conversationId); - if (conversation == null || !tenantCode.equals(conversation.getTenantCode())) { - continue; - } - if (!channelTypeStr.equals(conversation.getChannelType())) { - continue; - } - targets.addAll(resolveConversationTarget(tenantCode, conversationId, channelType, + List convBindings = conversationTagBindingMapper.selectList( + new LambdaQueryWrapper() + .eq(ConversationTagBinding::getTenantCode, tenantCode) + .eq(ConversationTagBinding::getTagId, tagId)); + for (ConversationTagBinding binding : convBindings) { + targets.addAll(resolveConversationTarget(tenantCode, binding.getConversationId(), channelType, Collections.singletonMap("strategy", "primary"))); } } if ("all".equals(scope) || "person".equals(scope)) { - Set personIds = union ? new HashSet<>() : null; - if (!union) { - personIds = selectPersonIdsByAllTags(tenantCode, tagIds); - } else { - for (Long tagId : tagIds) { - personIds.addAll(selectPersonIdsByTag(tenantCode, tagId)); - } - } - for (Long personId : personIds) { - targets.addAll(resolvePersonTarget(tenantCode, personId, channelType)); + List personBindings = personTagBindingMapper.selectList( + new LambdaQueryWrapper() + .eq(PersonTagBinding::getTenantCode, tenantCode) + .eq(PersonTagBinding::getTagId, tagId)); + for (PersonTagBinding binding : personBindings) { + targets.addAll(resolvePersonTarget(tenantCode, binding.getPersonId(), channelType)); } } return targets; } - private Set selectConversationIdsByTag(String tenantCode, Long tagId) { - return conversationTagBindingMapper.selectList( - new LambdaQueryWrapper() - .eq(ConversationTagBinding::getTenantCode, tenantCode) - .eq(ConversationTagBinding::getTagId, tagId)) - .stream() - .map(ConversationTagBinding::getConversationId) - .collect(Collectors.toSet()); - } - - private Set selectConversationIdsByAllTags(String tenantCode, List tagIds) { - Set ids = null; - for (Long tagId : tagIds) { - Set current = selectConversationIdsByTag(tenantCode, tagId); - if (ids == null) { - ids = new HashSet<>(current); - } else { - ids.retainAll(current); - } - } - return ids == null ? Collections.emptySet() : ids; - } - - private Set selectPersonIdsByTag(String tenantCode, Long tagId) { - return personTagBindingMapper.selectList( - new LambdaQueryWrapper() - .eq(PersonTagBinding::getTenantCode, tenantCode) - .eq(PersonTagBinding::getTagId, tagId)) - .stream() - .map(PersonTagBinding::getPersonId) - .collect(Collectors.toSet()); - } - - private Set selectPersonIdsByAllTags(String tenantCode, List tagIds) { - Set ids = null; - for (Long tagId : tagIds) { - Set current = selectPersonIdsByTag(tenantCode, tagId); - if (ids == null) { - ids = new HashSet<>(current); - } else { - ids.retainAll(current); - } - } - return ids == null ? Collections.emptySet() : ids; - } - private List resolveRuleTarget(String tenantCode, Map targetValue, ChannelType channelType, Map channelStrategy) { RuleTargetValue ruleTarget = parseRuleTargetValue(targetValue); @@ -554,125 +455,6 @@ public class SendService { return targets; } - @SuppressWarnings("unchecked") - private List resolveCombinedTarget(String tenantCode, Map targetValue, - ChannelType channelType, Map channelStrategy) { - if (targetValue == null) { - throw new BusinessException("combined 目标缺少 conditions"); - } - Object conditionsObj = targetValue.get("conditions"); - if (!(conditionsObj instanceof List) || ((List) conditionsObj).isEmpty()) { - throw new BusinessException("combined 目标缺少 conditions"); - } - String logic = toRuleString(targetValue.getOrDefault("logic", "and")); - if (!"and".equals(logic) && !"or".equals(logic)) { - throw new BusinessException("combined 逻辑仅支持 and 或 or"); - } - - List> groups = new ArrayList<>(); - for (Object item : (List) conditionsObj) { - if (!(item instanceof Map)) { - throw new BusinessException("combined 条件格式错误"); - } - Map condition = (Map) item; - List group; - // 支持嵌套条件组:{ logic, conditions } - if (condition.containsKey("logic") && condition.containsKey("conditions")) { - group = resolveCombinedTarget(tenantCode, condition, channelType, channelStrategy); - } else { - String type = toRuleString(condition.get("type")); - Object valueObj = condition.get("value"); - if (!StringUtils.hasText(type) || !(valueObj instanceof Map)) { - throw new BusinessException("combined 条件缺少 type 或 value"); - } - Map value = (Map) valueObj; - group = resolveCombinedCondition(tenantCode, type, value, channelType, channelStrategy); - } - if (!group.isEmpty()) { - groups.add(group); - } - } - - if (groups.isEmpty()) { - return Collections.emptyList(); - } - if ("and".equals(logic)) { - return intersectTargets(groups); - } - return unionTargets(groups); - } - - private List resolveCombinedCondition(String tenantCode, String type, Map value, - ChannelType channelType, Map channelStrategy) { - switch (type) { - case "tag": - return resolveTagTargetFromValue(tenantCode, value, channelType); - case "conversation": - Number convId = (Number) value.get("conversation_id"); - if (convId == null) { - throw new BusinessException("combined conversation 条件缺少 conversation_id"); - } - return resolveConversationTarget(tenantCode, convId.longValue(), channelType, channelStrategy); - case "person": - Number personId = (Number) value.get("person_id"); - if (personId == null) { - throw new BusinessException("combined person 条件缺少 person_id"); - } - return resolvePersonTarget(tenantCode, personId.longValue(), channelType); - default: - throw new BusinessException("combined 不支持的条件类型: " + type); - } - } - - private List unionTargets(List> groups) { - Map merged = new LinkedHashMap<>(); - for (List group : groups) { - for (SendTarget target : group) { - merged.put(targetKey(target), target); - } - } - return new ArrayList<>(merged.values()); - } - - private List intersectTargets(List> groups) { - List result = new ArrayList<>(); - Map firstGroup = new LinkedHashMap<>(); - for (SendTarget target : groups.get(0)) { - firstGroup.put(targetKey(target), target); - } - for (Map.Entry entry : firstGroup.entrySet()) { - String key = entry.getKey(); - boolean inAll = true; - for (int i = 1; i < groups.size(); i++) { - boolean found = false; - for (SendTarget target : groups.get(i)) { - if (key.equals(targetKey(target))) { - found = true; - break; - } - } - if (!found) { - inAll = false; - break; - } - } - if (inAll) { - result.add(entry.getValue()); - } - } - return result; - } - - private String targetKey(SendTarget target) { - if (target.getPersonId() != null) { - return "P:" + target.getPersonId() + ":" + target.getChannelType(); - } - if (target.getConversationId() != null) { - return "C:" + target.getConversationId() + ":" + target.getChannelType(); - } - return "U:" + System.identityHashCode(target); - } - private RuleTargetValue parseRuleTargetValue(Map targetValue) { if (targetValue == null) { throw new BusinessException("rule 目标缺少 rules"); @@ -1170,7 +952,7 @@ public class SendService { private SendMessageResult doSendAttempt(SendRecord record, ChannelAdapter adapter, ChannelAccount account) { SendMessageRequest messageRequest = new SendMessageRequest(); - messageRequest.setConversationId(resolveChannelConversationId(record)); + messageRequest.setConversationId(record.getConversationId() != null ? String.valueOf(record.getConversationId()) : null); messageRequest.setReceiverId(record.getPersonId() != null ? String.valueOf(record.getPersonId()) : null); messageRequest.setContentType(record.getContentType()); messageRequest.setContent(JsonUtils.fromJson(record.getContentBody(), Map.class)); @@ -1198,17 +980,6 @@ public class SendService { } } - private String resolveChannelConversationId(SendRecord record) { - if (record.getConversationId() == null) { - return null; - } - Conversation conversation = conversationMapper.selectById(record.getConversationId()); - if (conversation != null && StringUtils.hasText(conversation.getChannelConversationId())) { - return conversation.getChannelConversationId(); - } - return String.valueOf(record.getConversationId()); - } - private void markAccountUnhealthy(ChannelAccount account) { if (account == null || account.getAccountType() == null || account.getAccountType() != 3) { return;