feat: 增强管理端工作台用户活跃榜能力

- 新增用户活跃榜接口、筛选与导出能力

- 支持按智能体过滤排行榜并补充前后端测试
This commit is contained in:
2026-05-10 17:06:22 +08:00
parent 8d07b306e5
commit 516d43ce7d
14 changed files with 868 additions and 126 deletions

View File

@@ -487,15 +487,17 @@ public class ChatAnalyticalDBRepository {
* @param startDate 开始日期,包含
* @param endDate 结束日期,不包含
* @param tenantId 租户 ID空表示全局
* @param assistantId 智能体 ID空表示全部
* @param limit 返回条数
* @return 排行列表
*/
public List<ChatActiveUserRank> queryActiveUserRanks(LocalDate startDate,
LocalDate endDate,
BigInteger tenantId,
int limit) {
BigInteger assistantId,
Integer limit) {
assertAvailable();
int safeLimit = Math.max(limit, 1);
Integer safeLimit = limit == null ? null : Math.max(limit, 1);
List<Object> args = new java.util.ArrayList<>();
StringBuilder sql = new StringBuilder();
sql.append("SELECT agg.user_id AS user_id, ")
@@ -511,6 +513,10 @@ public class ChatAnalyticalDBRepository {
.append("WHERE agg.stat_date >= toDate(?) AND agg.stat_date < toDate(?)");
args.add(startDate.toString());
args.add(endDate.toString());
if (assistantId != null) {
sql.append(" AND agg.assistant_id = ?");
args.add(assistantId);
}
appendOptionalTenantFilter(sql, args, tenantId, "s.tenant_id");
sql.append(" GROUP BY agg.user_id")
.append(") agg ")
@@ -520,9 +526,11 @@ public class ChatAnalyticalDBRepository {
appendOptionalTenantFilter(sql, args, tenantId, "tenant_id");
sql.append(" GROUP BY user_id")
.append(") snapshot ON snapshot.user_id = agg.user_id ")
.append("ORDER BY agg.session_total DESC, agg.message_total DESC, agg.user_id ASC ")
.append("LIMIT ?");
args.add(safeLimit);
.append("ORDER BY agg.session_total DESC, agg.message_total DESC, agg.user_id ASC ");
if (safeLimit != null) {
sql.append("LIMIT ?");
args.add(safeLimit);
}
return analyticalDBOperations.query(
sql.toString(),

View File

@@ -94,13 +94,15 @@ public interface ChatDashboardQueryService {
* @param startDate 开始日期,包含当天
* @param endDate 结束日期,不包含当天
* @param tenantId 租户 ID空表示全局
* @param assistantId 智能体 ID空表示全部
* @param limit 返回条数
* @return 活跃用户排行
*/
List<ChatActiveUserRank> queryActiveUserRanks(LocalDate startDate,
LocalDate endDate,
BigInteger tenantId,
int limit);
BigInteger assistantId,
Integer limit);
/**
* 当前分析库是否可用。

View File

@@ -141,6 +141,7 @@ public class ChatDashboardQueryServiceImpl implements ChatDashboardQueryService
* @param startDate 开始日期,包含当天
* @param endDate 结束日期,不包含当天
* @param tenantId 租户 ID空表示全局
* @param assistantId 智能体 ID空表示全部
* @param limit 返回条数
* @return 活跃用户排行
*/
@@ -148,11 +149,12 @@ public class ChatDashboardQueryServiceImpl implements ChatDashboardQueryService
public List<ChatActiveUserRank> queryActiveUserRanks(LocalDate startDate,
LocalDate endDate,
BigInteger tenantId,
int limit) {
BigInteger assistantId,
Integer limit) {
if (!available()) {
return Collections.emptyList();
}
return analyticalDBRepository.queryActiveUserRanks(startDate, endDate, tenantId, limit);
return analyticalDBRepository.queryActiveUserRanks(startDate, endDate, tenantId, assistantId, limit);
}
/**

View File

@@ -104,6 +104,28 @@ public class ChatAnalyticalDBRepositoryTest {
Assert.assertTrue(operations.lastQuerySql.contains("(agg.assistant_id IN (?) OR agg.assistant_id IS NULL)"));
}
/**
* 验证用户活跃榜支持按智能体过滤,且导出场景可不带 limit。
*/
@Test
public void shouldSupportAssistantFilterAndUnlimitedUserRanks() {
RecordingAnalyticalDBOperations operations = new RecordingAnalyticalDBOperations();
ChatAnalyticalDBRepository repository = newRepository(operations);
repository.queryActiveUserRanks(
LocalDate.of(2026, 4, 1),
LocalDate.of(2026, 4, 8),
BigInteger.ONE,
BigInteger.TEN,
null
);
Assert.assertNotNull(operations.lastQuerySql);
Assert.assertTrue(operations.lastQuerySql.contains("AND agg.assistant_id = ?"));
Assert.assertTrue(operations.lastQuerySql.contains("ORDER BY agg.session_total DESC, agg.message_total DESC, agg.user_id ASC"));
Assert.assertFalse(operations.lastQuerySql.contains("LIMIT ?"));
}
/**
* 构造仓储实例。
*