feat: enforce signature-scoped drainage authorization before SMS submission

This commit is contained in:
hectorzhao
2026-09-10 13:29:04 +08:00
parent 5bcdbb2a03
commit 0c3f820cc9
35 changed files with 2769 additions and 791 deletions
+11 -2
View File
@@ -196,6 +196,10 @@ export class ChannelReportingService {
message."signatureId" AS signature_id,
message.carrier AS carrier,
message."drainageInfoId" AS drainage_info_id,
CASE WHEN COALESCE(submit."drainageGate", message."drainageGate") IS NULL THEN NULL ELSE ARRAY(
SELECT DISTINCT material_id FROM jsonb_array_elements(COALESCE(COALESCE(submit."drainageGate", message."drainageGate")->'targets', '[]'::jsonb)) target
CROSS JOIN LATERAL jsonb_array_elements_text(target->'materialIds') AS ids(material_id)
) END AS drainage_ids,
submit."submitStatus" AS submit_status,
COALESCE(submit."submittedAt", submit."createdAt") AS attempted_at,
CASE
@@ -239,6 +243,7 @@ export class ChannelReportingService {
AND receipt."receiptStatus" = 'undelivered'
) failed_receipt ON TRUE
WHERE submit."submitStatus" IN ('accepted', 'rejected', 'timeout')
AND NOT (COALESCE(submit."errorCode", '') LIKE 'DRN%' AND submit."firstWireSubmitAt" IS NULL)
AND submit."channelId" IN (${Prisma.join(channelIds)})
AND message."signatureId" IN (${Prisma.join(signatureIds)})
)
@@ -247,6 +252,7 @@ export class ChannelReportingService {
signature_id AS "signatureId",
carrier,
drainage_info_id AS "drainageInfoId",
drainage_ids AS "drainageIds",
COUNT(*) FILTER (
WHERE attempted_at >= ${day.startAt} AND attempted_at < ${day.endAt}
)::integer AS total,
@@ -272,7 +278,7 @@ export class ChannelReportingService {
)::integer AS "failureCount",
MAX(successful_at) FILTER (WHERE delivery_status = 'success') AS "lastSuccessfulSentAt"
FROM base
GROUP BY channel_id, signature_id, drainage_info_id, carrier
GROUP BY channel_id, signature_id, drainage_info_id, carrier, drainage_ids
`);
return tasks.map((task) => {
@@ -281,7 +287,10 @@ export class ChannelReportingService {
row.channelId === task.channelId &&
row.signatureId === task.signatureId &&
(!task.carrier || row.carrier === task.carrier) &&
((task.reportType ?? 'signature') === 'signature' || row.drainageInfoId === task.drainageItemId),
((task.reportType ?? 'signature') === 'signature' ||
(row.drainageIds
? row.drainageIds.includes(task.drainageItemId ?? '')
: row.drainageInfoId === task.drainageItemId)),
);
const deliveryStats = summarizeChannelReportDelivery(taskRows);
return {
+1
View File
@@ -592,6 +592,7 @@ export function deriveReceiptStatus(rowCount: number, successCount: number, fail
}
export type ChannelReportDeliveryRow = {
drainageIds?: string[] | null;
carrier: string | null;
channelId: string;
signatureId: string;