This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
CREATE TABLE "ReportReadinessState" (
|
||||
"objectKey" TEXT PRIMARY KEY, "mask" INTEGER NOT NULL, "armed" BOOLEAN NOT NULL,
|
||||
"cycle" INTEGER NOT NULL DEFAULT 0, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')
|
||||
);
|
||||
CREATE TABLE "ReportNotificationHour" (
|
||||
"id" TEXT PRIMARY KEY, "tenantId" TEXT NOT NULL, "tenantName" TEXT NOT NULL,
|
||||
"hour" TIMESTAMP(3) NOT NULL, "revision" INTEGER NOT NULL DEFAULT 1,
|
||||
"signatureCount" INTEGER NOT NULL DEFAULT 0, "drainageCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
|
||||
UNIQUE ("tenantId", "hour")
|
||||
);
|
||||
CREATE INDEX "ReportNotificationHour_hour_idx" ON "ReportNotificationHour" ("hour" DESC);
|
||||
CREATE TABLE "ReportReadinessEvent" (
|
||||
"id" TEXT PRIMARY KEY, "hourId" TEXT NOT NULL REFERENCES "ReportNotificationHour"("id"),
|
||||
"objectKey" TEXT NOT NULL, "cycle" INTEGER NOT NULL, "tenantId" TEXT NOT NULL,
|
||||
"reportType" TEXT NOT NULL, "signatureId" TEXT NOT NULL, "drainageItemId" TEXT,
|
||||
"applicationId" TEXT, "applicationName" TEXT, "signatureName" TEXT NOT NULL,
|
||||
"targetName" TEXT NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
|
||||
UNIQUE ("objectKey", "cycle")
|
||||
);
|
||||
CREATE INDEX "ReportReadinessEvent_hour_idx" ON "ReportReadinessEvent" ("hourId", "createdAt", "id");
|
||||
CREATE TABLE "ReportNotificationRead" (
|
||||
"userId" TEXT NOT NULL, "hourId" TEXT NOT NULL REFERENCES "ReportNotificationHour"("id"),
|
||||
"revision" INTEGER NOT NULL, PRIMARY KEY ("userId", "hourId")
|
||||
);
|
||||
|
||||
CREATE FUNCTION cmpp_report_ready_mask(kind TEXT, signature_id TEXT, drainage_id TEXT) RETURNS INTEGER
|
||||
LANGUAGE sql STABLE AS $$
|
||||
SELECT COALESCE(sum(bit),0)::integer FROM (VALUES ('mobile',1),('unicom',2),('telecom',4)) AS carriers(name,bit)
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM "SmsChannel" c
|
||||
WHERE c.status='active' AND c."sendRegion"='全国'
|
||||
AND (CASE WHEN cardinality(c.carriers)>0 THEN carriers.name=ANY(c.carriers)
|
||||
ELSE c.carrier IN (carriers.name,'all') END)
|
||||
AND (SELECT t.status FROM "ChannelSignatureReportTask" t
|
||||
WHERE t."channelId"=c.id AND t."signatureId"=signature_id AND t."reportType"=kind
|
||||
AND (kind='signature' OR t."drainageItemId"=drainage_id)
|
||||
AND (t.carrier=carriers.name OR (t.carrier IS NULL AND t."approvalScope"='legacy_channel'))
|
||||
ORDER BY (t.carrier=carriers.name) DESC NULLS LAST, t."updatedAt" DESC, t.id DESC LIMIT 1)='approved'
|
||||
);
|
||||
$$;
|
||||
|
||||
-- Seed only state. Existing successes must never become historical unread notices.
|
||||
INSERT INTO "ReportReadinessState" ("objectKey",mask,armed)
|
||||
SELECT 'signature:'||id, mask, mask=0 FROM "SmsSignature" s
|
||||
CROSS JOIN LATERAL (SELECT cmpp_report_ready_mask('signature',s.id,NULL) AS mask) m;
|
||||
INSERT INTO "ReportReadinessState" ("objectKey",mask,armed)
|
||||
SELECT 'drainage:'||id, mask, mask=0 FROM "SmsDrainageInfo" d
|
||||
CROSS JOIN LATERAL (SELECT cmpp_report_ready_mask('drainage',d."signatureId",d.id) AS mask) m;
|
||||
|
||||
CREATE FUNCTION cmpp_report_readiness_before() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
DECLARE r "ChannelSignatureReportTask"; k TEXT; m INTEGER;
|
||||
BEGIN
|
||||
r := CASE WHEN TG_OP='DELETE' THEN OLD ELSE NEW END;
|
||||
IF TG_OP='UPDATE' AND (OLD."signatureId",OLD."drainageItemId",OLD."reportType") IS DISTINCT FROM
|
||||
(NEW."signatureId",NEW."drainageItemId",NEW."reportType") THEN
|
||||
RAISE EXCEPTION 'Reporting task identity is immutable';
|
||||
END IF;
|
||||
k := r."reportType"||':'||CASE WHEN r."reportType"='drainage' THEN r."drainageItemId" ELSE r."signatureId" END;
|
||||
IF k IS NULL THEN RETURN r; END IF;
|
||||
PERFORM pg_advisory_xact_lock(hashtextextended(k, 20260906));
|
||||
m := cmpp_report_ready_mask(r."reportType",r."signatureId",r."drainageItemId");
|
||||
INSERT INTO "ReportReadinessState" ("objectKey",mask,armed) VALUES(k,m,m=0) ON CONFLICT DO NOTHING;
|
||||
-- A configuration change can remove eligibility without changing a reporting task.
|
||||
UPDATE "ReportReadinessState" SET mask=m, armed=armed OR m=0 WHERE "objectKey"=k;
|
||||
RETURN r;
|
||||
END;
|
||||
$$;
|
||||
CREATE FUNCTION cmpp_report_readiness_after() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
DECLARE r "ChannelSignatureReportTask"; k TEXT; m INTEGER; st "ReportReadinessState";
|
||||
sig "SmsSignature"; tenant_name TEXT; app_name TEXT; target_name TEXT;
|
||||
app_id TEXT; hour_value TIMESTAMP(3); hour_id TEXT; next_cycle INTEGER;
|
||||
BEGIN
|
||||
r := CASE WHEN TG_OP='DELETE' THEN OLD ELSE NEW END;
|
||||
k := r."reportType"||':'||CASE WHEN r."reportType"='drainage' THEN r."drainageItemId" ELSE r."signatureId" END;
|
||||
IF k IS NULL THEN RETURN r; END IF;
|
||||
SELECT * INTO st FROM "ReportReadinessState" WHERE "objectKey"=k FOR UPDATE;
|
||||
m := cmpp_report_ready_mask(r."reportType",r."signatureId",r."drainageItemId");
|
||||
IF m=7 AND st.armed THEN
|
||||
SELECT * INTO sig FROM "SmsSignature" WHERE id=r."signatureId";
|
||||
IF sig.id IS NOT NULL THEN
|
||||
SELECT name INTO tenant_name FROM "Tenant" WHERE id=sig."tenantId";
|
||||
app_id:=sig."applicationId"; target_name:=sig.name;
|
||||
IF r."reportType"='drainage' THEN
|
||||
SELECT COALESCE(d."applicationId",sig."applicationId"), d.url INTO app_id,target_name
|
||||
FROM "SmsDrainageInfo" d WHERE id=r."drainageItemId";
|
||||
END IF;
|
||||
SELECT name INTO app_name FROM "SmsApplication" WHERE id=app_id;
|
||||
hour_value:=date_trunc('hour',timezone('UTC',statement_timestamp()));
|
||||
hour_id:=md5(sig."tenantId"||':'||hour_value::text);
|
||||
next_cycle:=st.cycle+1;
|
||||
INSERT INTO "ReportNotificationHour" (id,"tenantId","tenantName",hour,"signatureCount","drainageCount")
|
||||
VALUES(hour_id,sig."tenantId",tenant_name,hour_value,(r."reportType"='signature')::integer,(r."reportType"='drainage')::integer)
|
||||
ON CONFLICT ("tenantId",hour) DO UPDATE SET revision="ReportNotificationHour".revision+1,
|
||||
"signatureCount"="ReportNotificationHour"."signatureCount"+EXCLUDED."signatureCount",
|
||||
"drainageCount"="ReportNotificationHour"."drainageCount"+EXCLUDED."drainageCount",
|
||||
"updatedAt"=timezone('UTC',statement_timestamp()) RETURNING id INTO hour_id;
|
||||
INSERT INTO "ReportReadinessEvent" (id,"hourId","objectKey",cycle,"tenantId","reportType","signatureId","drainageItemId","applicationId","applicationName","signatureName","targetName")
|
||||
VALUES(md5(k||':'||next_cycle),hour_id,k,next_cycle,sig."tenantId",r."reportType",sig.id,r."drainageItemId",app_id,app_name,sig.name,target_name);
|
||||
UPDATE "ReportReadinessState" SET cycle=next_cycle,armed=false WHERE "objectKey"=k;
|
||||
END IF;
|
||||
END IF;
|
||||
UPDATE "ReportReadinessState" SET mask=m,armed=armed OR m=0,"updatedAt"=timezone('UTC',statement_timestamp()) WHERE "objectKey"=k;
|
||||
RETURN r;
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER report_readiness_before BEFORE INSERT OR UPDATE OR DELETE ON "ChannelSignatureReportTask"
|
||||
FOR EACH ROW EXECUTE FUNCTION cmpp_report_readiness_before();
|
||||
CREATE TRIGGER report_readiness_after AFTER INSERT OR UPDATE OR DELETE ON "ChannelSignatureReportTask"
|
||||
FOR EACH ROW EXECUTE FUNCTION cmpp_report_readiness_after();
|
||||
Reference in New Issue
Block a user