feat: add signature quality period trends
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user