feat: 事件异常摘要拼接与可盯状态

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-21 18:18:01 +08:00
co-authored by Cursor
parent c24d8c19c0
commit 1c742353b8
2 changed files with 162 additions and 0 deletions
@@ -0,0 +1,109 @@
package org.jeecg.modules.inquiry.domain;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 事件异常纯规则:摘要拼接、哪些六态还盯超时、换等待种类才重置计时。
* <p>
* 不写库、不发企微。工单六态不因事件异常改变。
*/
public final class EventExceptionRule {
private EventExceptionRule() {
}
/**
* 多次异常拼在一起;同一类型不重复。
*/
public static String appendSummary(String existing, String type) {
String add = StringUtils.trimToEmpty(type);
if (add.isEmpty()) {
return StringUtils.trimToEmpty(existing);
}
String cur = StringUtils.trimToEmpty(existing);
if (cur.isEmpty() || "-".equals(cur)) {
return add;
}
Set<String> parts = new LinkedHashSet<String>(Arrays.asList(cur.split(";")));
if (parts.contains(add)) {
return cur;
}
parts.add(add);
StringBuilder out = new StringBuilder();
for (String p : parts) {
String one = StringUtils.trimToEmpty(p);
if (one.isEmpty()) {
continue;
}
if (out.length() > 0) {
out.append(';');
}
out.append(one);
}
return out.toString();
}
/**
* 已成交 / 已关闭 / 转人工不再盯;未成交仍可能在等原因。
*/
public static boolean isWatchable(String status) {
if (StringUtils.isBlank(status)) {
return false;
}
if (TicketStatuses.DEAL.equals(status)
|| TicketStatuses.CLOSED.equals(status)
|| TicketStatuses.HANDOFF.equals(status)) {
return false;
}
return TicketStatuses.isKnown(status);
}
/**
* 等待种类变了才重置起点;同一种类重复 start 不改已过时间。
*/
public static boolean shouldResetClock(String currentKind, String newKind) {
String cur = StringUtils.trimToEmpty(currentKind);
String next = StringUtils.trimToEmpty(newKind);
if (next.isEmpty()) {
return false;
}
return !cur.equals(next);
}
/**
* 开始等待后的三列新值。
*/
public static EventWaitState applyStart(String currentKind, Date started, Integer alerted,
String newKind, Date now) {
String next = StringUtils.trimToEmpty(newKind);
if (next.isEmpty()) {
return new EventWaitState(currentKind, started, alerted);
}
if (!shouldResetClock(currentKind, next)) {
Date keep = started != null ? started : now;
int flag = alerted == null ? 0 : alerted.intValue();
return new EventWaitState(next, keep, flag);
}
return new EventWaitState(next, now, 0);
}
/**
* 当前等待三列。
*/
public static final class EventWaitState {
public final String kind;
public final Date startedAt;
public final int alerted;
public EventWaitState(String kind, Date startedAt, int alerted) {
this.kind = kind;
this.startedAt = startedAt;
this.alerted = alerted;
}
}
}
@@ -0,0 +1,53 @@
package org.jeecg.modules.inquiry.domain;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 事件异常纯规则:摘要拼接、可盯状态、换等待种类才重置计时。
*/
public class EventExceptionRuleTest {
@Test
public void appendKeepsBothTypes() {
assertEquals("航线响应超时", EventExceptionRule.appendSummary("", "航线响应超时"));
assertEquals("航线响应超时;销售响应超时",
EventExceptionRule.appendSummary("航线响应超时", "销售响应超时"));
assertEquals("航线响应超时", EventExceptionRule.appendSummary("航线响应超时", "航线响应超时"));
}
@Test
public void lostIsWatchableHandoffIsNot() {
assertTrue(EventExceptionRule.isWatchable("未成交"));
assertTrue(EventExceptionRule.isWatchable(TicketStatuses.INQUIRING));
assertFalse(EventExceptionRule.isWatchable("已成交"));
assertFalse(EventExceptionRule.isWatchable("已关闭"));
assertFalse(EventExceptionRule.isWatchable("转人工"));
}
@Test
public void sameKindDoesNotResetClock() {
assertFalse(EventExceptionRule.shouldResetClock("air_quote", "air_quote"));
assertTrue(EventExceptionRule.shouldResetClock("air_quote", "adopt"));
assertTrue(EventExceptionRule.shouldResetClock("", "product_quote"));
}
@Test
public void applyStartKeepsClockOnSameKind() {
java.util.Date first = new java.util.Date(1_000L);
java.util.Date now = new java.util.Date(2_000L);
EventExceptionRule.EventWaitState keep = EventExceptionRule.applyStart(
"air_quote", first, 1, "air_quote", now);
assertEquals("air_quote", keep.kind);
assertEquals(first, keep.startedAt);
assertEquals(1, keep.alerted);
EventExceptionRule.EventWaitState reset = EventExceptionRule.applyStart(
"air_quote", first, 1, "adopt", now);
assertEquals("adopt", reset.kind);
assertEquals(now, reset.startedAt);
assertEquals(0, reset.alerted);
}
}