feat: add signature quality period trends

This commit is contained in:
hectorzhao
2026-08-09 22:54:32 +08:00
parent 608662a054
commit 35de17a2d4
11 changed files with 385 additions and 30 deletions
+58 -5
View File
@@ -316,11 +316,13 @@ async sendQuality(date?: string) {
}
async signatureQuality(query: SignatureQualityQuery) {
const day = qualityBusinessDay(query.date);
// The selected date is an inclusive business cutoff, so a 30-day window starts 29 full days earlier.
const thirtyDayStartAt = new Date(day.startAt.getTime() - 29 * 24 * 60 * 60 * 1000);
const page = positiveInteger(query.page, 1);
const pageSize = Math.min(50, positiveInteger(query.pageSize, 10));
const keyword = query.keyword?.trim() || null;
const keywordPattern = keyword ? `%${keyword}%` : null;
const summaries = await this.prisma.$queryRaw<Array<{
const [summaries, selectedDaySignatureRows] = await Promise.all([this.prisma.$queryRaw<Array<{
signatureId: string;
signatureName: string;
tenantId: string;
@@ -351,7 +353,7 @@ async signatureQuality(query: SignatureQualityQuery) {
END AS arrival_ms
FROM "SmsMessageRecord" message
WHERE message."signatureId" IS NOT NULL
AND message."queuedAt" >= ${day.startAt}
AND message."queuedAt" >= ${thirtyDayStartAt}
AND message."queuedAt" < ${day.endAt}
)
SELECT
@@ -413,7 +415,13 @@ async signatureQuality(query: SignatureQualityQuery) {
ORDER BY total DESC, signature.name
LIMIT ${pageSize}
OFFSET ${(page - 1) * pageSize}
`);
`), this.prisma.$queryRaw<Array<{ count: number }>>(Prisma.sql`
SELECT COUNT(DISTINCT message."signatureId")::integer AS count
FROM "SmsMessageRecord" message
WHERE message."signatureId" IS NOT NULL
AND message."queuedAt" >= ${day.startAt}
AND message."queuedAt" < ${day.endAt}
`)]);
const signatureIds = summaries.map((item) => item.signatureId);
const drainageBreakdowns = signatureIds.length === 0
? []
@@ -486,7 +494,7 @@ async signatureQuality(query: SignatureQualityQuery) {
) failed_receipt ON TRUE
WHERE message."signatureId" IN (${Prisma.join(signatureIds)})
AND submit."submitStatus" IN ('accepted', 'rejected', 'timeout')
AND COALESCE(submit."submittedAt", submit."createdAt") >= ${day.startAt}
AND COALESCE(submit."submittedAt", submit."createdAt") >= ${thirtyDayStartAt}
AND COALESCE(submit."submittedAt", submit."createdAt") < ${day.endAt}
), classified AS (
SELECT
@@ -564,11 +572,54 @@ async signatureQuality(query: SignatureQualityQuery) {
))::integer AS "averageArrivalMs"
FROM "SmsMessageRecord" message
WHERE message."signatureId" IN (${Prisma.join(signatureIds)})
AND message."queuedAt" >= ${day.startAt}
AND message."queuedAt" >= ${thirtyDayStartAt}
AND message."queuedAt" < ${day.endAt}
GROUP BY message."signatureId", COALESCE(NULLIF(message.carrier, ''), 'unknown')
ORDER BY message."signatureId", COUNT(*) DESC, carrier
`);
const dailyBreakdowns = signatureIds.length === 0
? []
: await this.prisma.$queryRaw<Array<{
signatureId: string;
channelId: string;
channelName: string;
carrier: string;
date: string;
businessMessageCount: number;
deliveredMessageCount: number;
deliveryRate: number;
}>>(Prisma.sql`
SELECT
message."signatureId" AS "signatureId",
COALESCE(message."channelId", 'unassigned') AS "channelId",
COALESCE(channel.name, '未分配通道') AS "channelName",
COALESCE(NULLIF(message.carrier, ''), 'unknown') AS carrier,
TO_CHAR((message."queuedAt" AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD') AS date,
COUNT(*)::integer AS "businessMessageCount",
COUNT(*) FILTER (
WHERE message.status = 'delivered'
OR message."receiptStatus" = 'delivered'
)::integer AS "deliveredMessageCount",
ROUND(
COUNT(*) FILTER (
WHERE message.status = 'delivered'
OR message."receiptStatus" = 'delivered'
) * 100.0 / COUNT(*),
1
)::double precision AS "deliveryRate"
FROM "SmsMessageRecord" message
LEFT JOIN "SmsChannel" channel ON channel.id = message."channelId"
WHERE message."signatureId" IN (${Prisma.join(signatureIds)})
AND message."queuedAt" >= ${thirtyDayStartAt}
AND message."queuedAt" < ${day.endAt}
GROUP BY
message."signatureId",
COALESCE(message."channelId", 'unassigned'),
COALESCE(channel.name, '未分配通道'),
COALESCE(NULLIF(message.carrier, ''), 'unknown'),
TO_CHAR((message."queuedAt" AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD')
ORDER BY message."signatureId", date, "channelId", carrier
`);
const items = summaries.map(({ rowCount: _rowCount, ...summary }) => {
const signatureDrainageBreakdowns = drainageBreakdowns.filter((item) => item.signatureId === summary.signatureId);
const signatureBreakdowns = aggregateChannelCarrierRows(signatureDrainageBreakdowns);
@@ -578,10 +629,12 @@ async signatureQuality(query: SignatureQualityQuery) {
carrierOverview: carrierOverview.filter((item) => item.signatureId === summary.signatureId),
breakdowns: signatureBreakdowns,
drainageBreakdowns: signatureDrainageBreakdowns,
dailyBreakdowns: dailyBreakdowns.filter((item) => item.signatureId === summary.signatureId),
};
});
return {
date: day.key,
selectedDaySignatureCount: selectedDaySignatureRows[0]?.count ?? 0,
items,
total: summaries[0]?.rowCount ?? 0,
page,