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
This commit is contained in:
@@ -60,7 +60,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -316,10 +315,14 @@ public class SendService {
|
|||||||
}
|
}
|
||||||
return targets;
|
return targets;
|
||||||
case "tag":
|
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 "rule":
|
||||||
case "combined":
|
return resolveRuleTarget(tenantCode, targetValue, channelType, channelStrategy);
|
||||||
return resolveCombinedTarget(tenantCode, targetValue, channelType, channelStrategy);
|
|
||||||
default:
|
default:
|
||||||
throw new BusinessException("不支持的目标类型: " + targetType);
|
throw new BusinessException("不支持的目标类型: " + targetType);
|
||||||
}
|
}
|
||||||
@@ -408,132 +411,30 @@ public class SendService {
|
|||||||
return Collections.singletonList(target);
|
return Collections.singletonList(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SendTarget> resolveTagTargetFromValue(String tenantCode, Map<String, Object> targetValue,
|
|
||||||
ChannelType channelType) {
|
|
||||||
List<Long> 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<Long> extractTagIds(Map<String, Object> targetValue) {
|
|
||||||
List<Long> 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<SendTarget> resolveTagTarget(String tenantCode, Long tagId, ChannelType channelType, String scope) {
|
private List<SendTarget> resolveTagTarget(String tenantCode, Long tagId, ChannelType channelType, String scope) {
|
||||||
return resolveTagTarget(tenantCode, Collections.singletonList(tagId), channelType, scope, "union");
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SendTarget> resolveTagTarget(String tenantCode, List<Long> tagIds, ChannelType channelType,
|
|
||||||
String scope, String innerLogic) {
|
|
||||||
List<SendTarget> targets = new ArrayList<>();
|
List<SendTarget> targets = new ArrayList<>();
|
||||||
String channelTypeStr = channelType.name().toLowerCase();
|
|
||||||
boolean union = !"intersection".equals(innerLogic);
|
|
||||||
|
|
||||||
if ("all".equals(scope) || "conversation".equals(scope)) {
|
if ("all".equals(scope) || "conversation".equals(scope)) {
|
||||||
Set<Long> conversationIds = union ? new HashSet<>() : null;
|
List<ConversationTagBinding> convBindings = conversationTagBindingMapper.selectList(
|
||||||
if (!union) {
|
new LambdaQueryWrapper<ConversationTagBinding>()
|
||||||
conversationIds = selectConversationIdsByAllTags(tenantCode, tagIds);
|
.eq(ConversationTagBinding::getTenantCode, tenantCode)
|
||||||
} else {
|
.eq(ConversationTagBinding::getTagId, tagId));
|
||||||
for (Long tagId : tagIds) {
|
for (ConversationTagBinding binding : convBindings) {
|
||||||
conversationIds.addAll(selectConversationIdsByTag(tenantCode, tagId));
|
targets.addAll(resolveConversationTarget(tenantCode, binding.getConversationId(), channelType,
|
||||||
}
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
Collections.singletonMap("strategy", "primary")));
|
Collections.singletonMap("strategy", "primary")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("all".equals(scope) || "person".equals(scope)) {
|
if ("all".equals(scope) || "person".equals(scope)) {
|
||||||
Set<Long> personIds = union ? new HashSet<>() : null;
|
List<PersonTagBinding> personBindings = personTagBindingMapper.selectList(
|
||||||
if (!union) {
|
new LambdaQueryWrapper<PersonTagBinding>()
|
||||||
personIds = selectPersonIdsByAllTags(tenantCode, tagIds);
|
.eq(PersonTagBinding::getTenantCode, tenantCode)
|
||||||
} else {
|
.eq(PersonTagBinding::getTagId, tagId));
|
||||||
for (Long tagId : tagIds) {
|
for (PersonTagBinding binding : personBindings) {
|
||||||
personIds.addAll(selectPersonIdsByTag(tenantCode, tagId));
|
targets.addAll(resolvePersonTarget(tenantCode, binding.getPersonId(), channelType));
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Long personId : personIds) {
|
|
||||||
targets.addAll(resolvePersonTarget(tenantCode, personId, channelType));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Long> selectConversationIdsByTag(String tenantCode, Long tagId) {
|
|
||||||
return conversationTagBindingMapper.selectList(
|
|
||||||
new LambdaQueryWrapper<ConversationTagBinding>()
|
|
||||||
.eq(ConversationTagBinding::getTenantCode, tenantCode)
|
|
||||||
.eq(ConversationTagBinding::getTagId, tagId))
|
|
||||||
.stream()
|
|
||||||
.map(ConversationTagBinding::getConversationId)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Long> selectConversationIdsByAllTags(String tenantCode, List<Long> tagIds) {
|
|
||||||
Set<Long> ids = null;
|
|
||||||
for (Long tagId : tagIds) {
|
|
||||||
Set<Long> current = selectConversationIdsByTag(tenantCode, tagId);
|
|
||||||
if (ids == null) {
|
|
||||||
ids = new HashSet<>(current);
|
|
||||||
} else {
|
|
||||||
ids.retainAll(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids == null ? Collections.emptySet() : ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Long> selectPersonIdsByTag(String tenantCode, Long tagId) {
|
|
||||||
return personTagBindingMapper.selectList(
|
|
||||||
new LambdaQueryWrapper<PersonTagBinding>()
|
|
||||||
.eq(PersonTagBinding::getTenantCode, tenantCode)
|
|
||||||
.eq(PersonTagBinding::getTagId, tagId))
|
|
||||||
.stream()
|
|
||||||
.map(PersonTagBinding::getPersonId)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Long> selectPersonIdsByAllTags(String tenantCode, List<Long> tagIds) {
|
|
||||||
Set<Long> ids = null;
|
|
||||||
for (Long tagId : tagIds) {
|
|
||||||
Set<Long> current = selectPersonIdsByTag(tenantCode, tagId);
|
|
||||||
if (ids == null) {
|
|
||||||
ids = new HashSet<>(current);
|
|
||||||
} else {
|
|
||||||
ids.retainAll(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids == null ? Collections.emptySet() : ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SendTarget> resolveRuleTarget(String tenantCode, Map<String, Object> targetValue,
|
private List<SendTarget> resolveRuleTarget(String tenantCode, Map<String, Object> targetValue,
|
||||||
ChannelType channelType, Map<String, Object> channelStrategy) {
|
ChannelType channelType, Map<String, Object> channelStrategy) {
|
||||||
RuleTargetValue ruleTarget = parseRuleTargetValue(targetValue);
|
RuleTargetValue ruleTarget = parseRuleTargetValue(targetValue);
|
||||||
@@ -554,125 +455,6 @@ public class SendService {
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private List<SendTarget> resolveCombinedTarget(String tenantCode, Map<String, Object> targetValue,
|
|
||||||
ChannelType channelType, Map<String, Object> 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<List<SendTarget>> groups = new ArrayList<>();
|
|
||||||
for (Object item : (List<?>) conditionsObj) {
|
|
||||||
if (!(item instanceof Map)) {
|
|
||||||
throw new BusinessException("combined 条件格式错误");
|
|
||||||
}
|
|
||||||
Map<String, Object> condition = (Map<String, Object>) item;
|
|
||||||
List<SendTarget> 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<String, Object> value = (Map<String, Object>) 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<SendTarget> resolveCombinedCondition(String tenantCode, String type, Map<String, Object> value,
|
|
||||||
ChannelType channelType, Map<String, Object> 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<SendTarget> unionTargets(List<List<SendTarget>> groups) {
|
|
||||||
Map<String, SendTarget> merged = new LinkedHashMap<>();
|
|
||||||
for (List<SendTarget> group : groups) {
|
|
||||||
for (SendTarget target : group) {
|
|
||||||
merged.put(targetKey(target), target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ArrayList<>(merged.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SendTarget> intersectTargets(List<List<SendTarget>> groups) {
|
|
||||||
List<SendTarget> result = new ArrayList<>();
|
|
||||||
Map<String, SendTarget> firstGroup = new LinkedHashMap<>();
|
|
||||||
for (SendTarget target : groups.get(0)) {
|
|
||||||
firstGroup.put(targetKey(target), target);
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, SendTarget> 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<String, Object> targetValue) {
|
private RuleTargetValue parseRuleTargetValue(Map<String, Object> targetValue) {
|
||||||
if (targetValue == null) {
|
if (targetValue == null) {
|
||||||
throw new BusinessException("rule 目标缺少 rules");
|
throw new BusinessException("rule 目标缺少 rules");
|
||||||
@@ -1170,7 +952,7 @@ public class SendService {
|
|||||||
|
|
||||||
private SendMessageResult doSendAttempt(SendRecord record, ChannelAdapter adapter, ChannelAccount account) {
|
private SendMessageResult doSendAttempt(SendRecord record, ChannelAdapter adapter, ChannelAccount account) {
|
||||||
SendMessageRequest messageRequest = new SendMessageRequest();
|
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.setReceiverId(record.getPersonId() != null ? String.valueOf(record.getPersonId()) : null);
|
||||||
messageRequest.setContentType(record.getContentType());
|
messageRequest.setContentType(record.getContentType());
|
||||||
messageRequest.setContent(JsonUtils.fromJson(record.getContentBody(), Map.class));
|
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) {
|
private void markAccountUnhealthy(ChannelAccount account) {
|
||||||
if (account == null || account.getAccountType() == null || account.getAccountType() != 3) {
|
if (account == null || account.getAccountType() == null || account.getAccountType() != 3) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user