Files
ctms/frontend/src/views/admin/loginActivitySources.ts
T
Cheng Zhou d5279b124f
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
release(main): 同步 dev 最新候选改动
2026-07-16 17:15:50 +08:00

57 lines
2.0 KiB
TypeScript

import type { UserLoginActivity } from "../../types/api";
export type DisplayLoginActivity = UserLoginActivity & { grouped_count: number };
type ActivityStatus = NonNullable<UserLoginActivity["activity_status"]>;
const activityTime = (value: string) => new Date(value).getTime();
export const activitySourceKey = (item: UserLoginActivity) => {
if (!item.login_ip) return `session:${item.id}`;
return [item.client_type, item.login_ip].join("|");
};
export const groupLoginActivitiesBySource = (
activities: UserLoginActivity[],
resolveStatus: (item: UserLoginActivity) => ActivityStatus,
): DisplayLoginActivity[] => {
const grouped = new Map<
string,
{ row: DisplayLoginActivity; firstLoginAt: string; hasOnlineSession: boolean }
>();
activities.forEach((item) => {
const key = activitySourceKey(item);
const status = resolveStatus(item);
const existing = grouped.get(key);
if (!existing) {
grouped.set(key, {
row: { ...item, activity_status: status, grouped_count: 1 },
firstLoginAt: item.login_at,
hasOnlineSession: status === "ONLINE",
});
return;
}
existing.row.grouped_count += 1;
existing.hasOnlineSession ||= status === "ONLINE";
if (activityTime(item.login_at) < activityTime(existing.firstLoginAt)) {
existing.firstLoginAt = item.login_at;
}
if (activityTime(item.last_seen_at) > activityTime(existing.row.last_seen_at)) {
const groupedCount = existing.row.grouped_count;
existing.row = { ...item, activity_status: status, grouped_count: groupedCount };
}
});
return Array.from(grouped.values())
.map(({ row, firstLoginAt, hasOnlineSession }) => ({
...row,
login_at: firstLoginAt,
activity_status: hasOnlineSession ? "ONLINE" : row.activity_status,
ended_at: hasOnlineSession ? null : row.ended_at,
end_reason: hasOnlineSession ? null : row.end_reason,
}))
.sort((left, right) => activityTime(right.last_seen_at) - activityTime(left.last_seen_at));
};