调研(08-11-internal-oa-usage-census-and-simplification): 固化采用度普查与减法决策
这个提交包含在:
+430
@@ -0,0 +1,430 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("node:path");
|
||||
|
||||
const MODULE_KEYS = Object.freeze([
|
||||
"portal_notifications",
|
||||
"workflow_approvals",
|
||||
"reimbursements_invoices",
|
||||
"payments_ap",
|
||||
"procurement",
|
||||
"contracts",
|
||||
"projects",
|
||||
"assets_inventory",
|
||||
"leave_wecom_history",
|
||||
"attendance_presence",
|
||||
"weekly_reports",
|
||||
"todos",
|
||||
"announcements",
|
||||
"budgets",
|
||||
"travel",
|
||||
"people_lifecycle",
|
||||
"payroll",
|
||||
"performance",
|
||||
"system_configuration",
|
||||
"integrations",
|
||||
]);
|
||||
|
||||
const SAFE_RESULT_KEYS = Object.freeze([
|
||||
"moduleKey",
|
||||
"totalRows",
|
||||
"created30d",
|
||||
"created90d",
|
||||
"activity30d",
|
||||
"activity90d",
|
||||
"latestActivityAt",
|
||||
"humanActors30d",
|
||||
"humanActors90d",
|
||||
"humanEvents30d",
|
||||
"humanEvents90d",
|
||||
"smokeEvents30d",
|
||||
"smokeEvents90d",
|
||||
"systemEvents30d",
|
||||
"systemEvents90d",
|
||||
"unknownEvents30d",
|
||||
"unknownEvents90d",
|
||||
"auditEvents30d",
|
||||
"auditEvents90d",
|
||||
"auditHumanActors30d",
|
||||
"auditHumanActors90d",
|
||||
"auditHumanEvents30d",
|
||||
"auditHumanEvents90d",
|
||||
"auditSmokeEvents30d",
|
||||
"auditSmokeEvents90d",
|
||||
"auditUnknownEvents30d",
|
||||
"auditUnknownEvents90d",
|
||||
"navigationMode",
|
||||
"effectiveFlagState",
|
||||
"queryStatus",
|
||||
]);
|
||||
|
||||
const USAGE_SQL = String.raw`
|
||||
WITH module_keys(module_key) AS (
|
||||
VALUES
|
||||
('portal_notifications'), ('workflow_approvals'),
|
||||
('reimbursements_invoices'), ('payments_ap'), ('procurement'),
|
||||
('contracts'), ('projects'), ('assets_inventory'),
|
||||
('leave_wecom_history'), ('attendance_presence'), ('weekly_reports'),
|
||||
('todos'), ('announcements'), ('budgets'), ('travel'),
|
||||
('people_lifecycle'), ('payroll'), ('performance'),
|
||||
('system_configuration'), ('integrations')
|
||||
), record_events(module_key, record_id, created_at, activity_at, actor_id, forced_kind) AS (
|
||||
SELECT 'portal_notifications', id::text, created_at, created_at, recipient_employee_id, 'system' FROM notifications
|
||||
UNION ALL SELECT 'workflow_approvals', id::text, created_at, updated_at, submitter_id, NULL FROM workflow_instances
|
||||
UNION ALL SELECT 'reimbursements_invoices', id::text, created_at, updated_at, owner_id, NULL FROM reimbursement_forms
|
||||
UNION ALL SELECT 'reimbursements_invoices', id::text, created_at, updated_at, uploaded_by_employee_id, NULL FROM invoice_pool
|
||||
UNION ALL SELECT 'reimbursements_invoices', id::text, created_at, created_at, NULL::uuid, 'system' FROM ocr_results
|
||||
UNION ALL SELECT 'payments_ap', id::text, created_at, updated_at, created_by, NULL FROM payment_batches
|
||||
UNION ALL SELECT 'payments_ap', id::text, created_at, created_at, uploaded_by, NULL FROM payment_batch_receipt_attachments
|
||||
UNION ALL SELECT 'procurement', id::text, created_at, updated_at, applicant_id, NULL FROM purchase_requests
|
||||
UNION ALL SELECT 'contracts', id::text, created_at, updated_at, owner_employee_id, NULL FROM contract_registry
|
||||
UNION ALL SELECT 'projects', id::text, created_at, updated_at, owner_employee_id, NULL FROM projects
|
||||
UNION ALL SELECT 'assets_inventory', id::text, created_at, updated_at, created_by, NULL FROM asset_cards
|
||||
UNION ALL SELECT 'assets_inventory', id::text, created_at, updated_at, created_by, NULL FROM inventory_checks
|
||||
UNION ALL SELECT 'leave_wecom_history', id::text, created_at, updated_at, owner_id, NULL FROM leave_requests
|
||||
UNION ALL SELECT 'leave_wecom_history', id::text, created_at, updated_at, applicant_employee_id, 'system' FROM wecom_approval_archives
|
||||
UNION ALL SELECT 'attendance_presence', id::text, created_at, updated_at, owner_id, NULL FROM attendance_corrections
|
||||
UNION ALL SELECT 'attendance_presence', id::text, created_at, updated_at, employee_id, 'system' FROM wecom_checkin_records
|
||||
UNION ALL SELECT 'attendance_presence', id::text, created_at, updated_at, updated_by, NULL FROM employee_presence
|
||||
UNION ALL SELECT 'weekly_reports', id::text, created_at, updated_at, employee_id, NULL FROM weekly_reports
|
||||
UNION ALL SELECT 'todos', id::text, created_at, updated_at, created_by, NULL FROM todo_items
|
||||
UNION ALL SELECT 'announcements', id::text, created_at, updated_at, created_by, NULL FROM portal_announcements
|
||||
UNION ALL SELECT 'announcements', id::text, created_at, COALESCE(acknowledged_at, created_at), actor_id, NULL FROM portal_announcement_acknowledgements
|
||||
UNION ALL SELECT 'budgets', id::text, created_at, updated_at, created_by, NULL FROM budget_pools
|
||||
UNION ALL SELECT 'travel', id::text, created_at, updated_at, applicant_employee_id, NULL FROM travel_requests
|
||||
UNION ALL SELECT 'travel', id::text, created_at, updated_at, created_by, NULL FROM travel_orders
|
||||
UNION ALL SELECT 'people_lifecycle', id::text, created_at, created_at, created_by, NULL FROM employee_lifecycle_events
|
||||
UNION ALL SELECT 'people_lifecycle', id::text, created_at, updated_at, created_by, NULL FROM employee_contracts
|
||||
UNION ALL SELECT 'people_lifecycle', id::text, created_at, updated_at, created_by, NULL FROM employee_trainings
|
||||
UNION ALL SELECT 'payroll', id::text, created_at, updated_at, created_by, NULL FROM payslips
|
||||
UNION ALL SELECT 'performance', id::text, created_at, updated_at, reviewer_employee_id, NULL FROM performance_reviews
|
||||
UNION ALL SELECT 'system_configuration', id::text, created_at, updated_at, updated_by, NULL FROM feature_flags
|
||||
UNION ALL SELECT 'system_configuration', id::text, created_at, updated_at, updated_by, NULL FROM cutover_scopes
|
||||
UNION ALL SELECT 'integrations', id::text, created_at, updated_at, created_by, 'system' FROM integration_outbox
|
||||
), classified_records AS (
|
||||
SELECT
|
||||
r.*,
|
||||
CASE
|
||||
WHEN r.forced_kind = 'system' THEN 'system'
|
||||
WHEN r.actor_id IS NULL OR e.id IS NULL THEN 'unknown'
|
||||
WHEN lower(split_part(e.email, '@', 1)) ~ '^(smoke[-_.]|oa[-_.]?smoke[-_.]?)'
|
||||
OR e.employee_no ~* '^(SMOKE|OSMOKE|WFULL|PRELAUNCH|LAUNCHAUTO|HRWDTEST)'
|
||||
OR e.name ~ '(测试|试运行)'
|
||||
THEN 'smoke'
|
||||
ELSE 'human'
|
||||
END AS actor_kind
|
||||
FROM record_events r
|
||||
LEFT JOIN employees e ON e.id = r.actor_id
|
||||
), audit_mapped AS (
|
||||
SELECT
|
||||
CASE
|
||||
WHEN action ~ '^(APPROVAL_|DELEGATION_|WORKFLOW_)' THEN 'workflow_approvals'
|
||||
WHEN action ~ '^(REIMBURSEMENT_|INVOICE_)' OR action = 'EXPORT_REIMBURSEMENTS' THEN 'reimbursements_invoices'
|
||||
WHEN action ~ '^PAYMENT_' OR action = 'EXPORT_AP_OPERATIONS' THEN 'payments_ap'
|
||||
WHEN action ~ '^PURCHASE_' THEN 'procurement'
|
||||
WHEN action ~ '^CONTRACT_' OR action = 'EXPORT_CONTRACTS_REGISTRY' THEN 'contracts'
|
||||
WHEN action ~ '^PROJECT_' THEN 'projects'
|
||||
WHEN action ~ '^(ASSET_|INVENTORY_)' THEN 'assets_inventory'
|
||||
WHEN action ~ '^(ATTENDANCE_|PRESENCE_)' THEN 'attendance_presence'
|
||||
WHEN action ~ '^PORTAL_ANNOUNCEMENT_' THEN 'announcements'
|
||||
WHEN action ~ '^BUDGET_' THEN 'budgets'
|
||||
WHEN action ~ '^TRAVEL_' THEN 'travel'
|
||||
WHEN action ~ '^(EMPLOYEE_|SYSTEM_USER|DEPARTMENT_)' THEN 'people_lifecycle'
|
||||
WHEN action ~ '^(CREATE_SALARY_|UPDATE_SALARY_|UPSERT_EMPLOYEE_SALARY_|GENERATE_PAYSLIPS|ISSUE_PAYSLIPS|UPDATE_PAYSLIP|EXPORT_PAYSLIPS)' THEN 'payroll'
|
||||
WHEN action ~ '^PERFORMANCE_' THEN 'performance'
|
||||
WHEN action ~ '^(FEATURE_FLAG_|WORKSPACE_)' THEN 'system_configuration'
|
||||
WHEN action ~ '^ERP_' THEN 'integrations'
|
||||
ELSE NULL
|
||||
END AS module_key,
|
||||
a.actor_id,
|
||||
a.created_at,
|
||||
CASE
|
||||
WHEN e.id IS NULL THEN 'unknown'
|
||||
WHEN lower(split_part(e.email, '@', 1)) ~ '^(smoke[-_.]|oa[-_.]?smoke[-_.]?)'
|
||||
OR e.employee_no ~* '^(SMOKE|OSMOKE|WFULL|PRELAUNCH|LAUNCHAUTO|HRWDTEST)'
|
||||
OR e.name ~ '(测试|试运行)'
|
||||
THEN 'smoke'
|
||||
ELSE 'human'
|
||||
END AS actor_kind
|
||||
FROM audit_logs a
|
||||
LEFT JOIN employees e ON e.id = a.actor_id
|
||||
), record_metrics AS (
|
||||
SELECT
|
||||
module_key,
|
||||
count(*)::int AS total_rows,
|
||||
count(*) FILTER (WHERE created_at >= now() - interval '30 days')::int AS created_30d,
|
||||
count(*) FILTER (WHERE created_at >= now() - interval '90 days')::int AS created_90d,
|
||||
count(*) FILTER (WHERE activity_at >= now() - interval '30 days')::int AS activity_30d,
|
||||
count(*) FILTER (WHERE activity_at >= now() - interval '90 days')::int AS activity_90d,
|
||||
max(activity_at) AS latest_activity_at,
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND activity_at >= now() - interval '30 days')::int AS human_actors_30d,
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND activity_at >= now() - interval '90 days')::int AS human_actors_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND activity_at >= now() - interval '30 days')::int AS human_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND activity_at >= now() - interval '90 days')::int AS human_events_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND activity_at >= now() - interval '30 days')::int AS smoke_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND activity_at >= now() - interval '90 days')::int AS smoke_events_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'system' AND activity_at >= now() - interval '30 days')::int AS system_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'system' AND activity_at >= now() - interval '90 days')::int AS system_events_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND activity_at >= now() - interval '30 days')::int AS unknown_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND activity_at >= now() - interval '90 days')::int AS unknown_events_90d
|
||||
FROM classified_records
|
||||
GROUP BY module_key
|
||||
), audit_metrics AS (
|
||||
SELECT
|
||||
module_key,
|
||||
count(*) FILTER (WHERE created_at >= now() - interval '30 days')::int AS audit_events_30d,
|
||||
count(*) FILTER (WHERE created_at >= now() - interval '90 days')::int AS audit_events_90d,
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '30 days')::int AS audit_human_actors_30d,
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '90 days')::int AS audit_human_actors_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '30 days')::int AS audit_human_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '90 days')::int AS audit_human_events_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND created_at >= now() - interval '30 days')::int AS audit_smoke_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND created_at >= now() - interval '90 days')::int AS audit_smoke_events_90d,
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND created_at >= now() - interval '30 days')::int AS audit_unknown_events_30d,
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND created_at >= now() - interval '90 days')::int AS audit_unknown_events_90d
|
||||
FROM audit_mapped
|
||||
WHERE module_key IS NOT NULL
|
||||
GROUP BY module_key
|
||||
)
|
||||
SELECT
|
||||
k.module_key AS "moduleKey",
|
||||
COALESCE(r.total_rows, 0) AS "totalRows",
|
||||
COALESCE(r.created_30d, 0) AS "created30d",
|
||||
COALESCE(r.created_90d, 0) AS "created90d",
|
||||
COALESCE(r.activity_30d, 0) AS "activity30d",
|
||||
COALESCE(r.activity_90d, 0) AS "activity90d",
|
||||
r.latest_activity_at AS "latestActivityAt",
|
||||
COALESCE(r.human_actors_30d, 0) AS "humanActors30d",
|
||||
COALESCE(r.human_actors_90d, 0) AS "humanActors90d",
|
||||
COALESCE(r.human_events_30d, 0) AS "humanEvents30d",
|
||||
COALESCE(r.human_events_90d, 0) AS "humanEvents90d",
|
||||
COALESCE(r.smoke_events_30d, 0) AS "smokeEvents30d",
|
||||
COALESCE(r.smoke_events_90d, 0) AS "smokeEvents90d",
|
||||
COALESCE(r.system_events_30d, 0) AS "systemEvents30d",
|
||||
COALESCE(r.system_events_90d, 0) AS "systemEvents90d",
|
||||
COALESCE(r.unknown_events_30d, 0) AS "unknownEvents30d",
|
||||
COALESCE(r.unknown_events_90d, 0) AS "unknownEvents90d",
|
||||
COALESCE(a.audit_events_30d, 0) AS "auditEvents30d",
|
||||
COALESCE(a.audit_events_90d, 0) AS "auditEvents90d",
|
||||
COALESCE(a.audit_human_actors_30d, 0) AS "auditHumanActors30d",
|
||||
COALESCE(a.audit_human_actors_90d, 0) AS "auditHumanActors90d",
|
||||
COALESCE(a.audit_human_events_30d, 0) AS "auditHumanEvents30d",
|
||||
COALESCE(a.audit_human_events_90d, 0) AS "auditHumanEvents90d",
|
||||
COALESCE(a.audit_smoke_events_30d, 0) AS "auditSmokeEvents30d",
|
||||
COALESCE(a.audit_smoke_events_90d, 0) AS "auditSmokeEvents90d",
|
||||
COALESCE(a.audit_unknown_events_30d, 0) AS "auditUnknownEvents30d",
|
||||
COALESCE(a.audit_unknown_events_90d, 0) AS "auditUnknownEvents90d"
|
||||
FROM module_keys k
|
||||
LEFT JOIN record_metrics r ON r.module_key = k.module_key
|
||||
LEFT JOIN audit_metrics a ON a.module_key = k.module_key
|
||||
ORDER BY k.module_key
|
||||
`;
|
||||
|
||||
const AUTH_SQL = String.raw`
|
||||
WITH classified AS (
|
||||
SELECT
|
||||
a.actor_id,
|
||||
a.created_at,
|
||||
CASE
|
||||
WHEN e.id IS NULL THEN 'unknown'
|
||||
WHEN lower(split_part(e.email, '@', 1)) ~ '^(smoke[-_.]|oa[-_.]?smoke[-_.]?)'
|
||||
OR e.employee_no ~* '^(SMOKE|OSMOKE|WFULL|PRELAUNCH|LAUNCHAUTO|HRWDTEST)'
|
||||
OR e.name ~ '(测试|试运行)'
|
||||
THEN 'smoke'
|
||||
ELSE 'human'
|
||||
END AS actor_kind
|
||||
FROM audit_logs a
|
||||
LEFT JOIN employees e ON e.id = a.actor_id
|
||||
WHERE a.action IN ('AUTH_LOGIN_SUCCESS', 'AUTH_EMAIL_LOGIN_SUCCESS', 'AUTH_OIDC_LOGIN_SUCCESS')
|
||||
)
|
||||
SELECT
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '30 days')::int AS "humanActors30d",
|
||||
count(DISTINCT actor_id) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '90 days')::int AS "humanActors90d",
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '30 days')::int AS "humanEvents30d",
|
||||
count(*) FILTER (WHERE actor_kind = 'human' AND created_at >= now() - interval '90 days')::int AS "humanEvents90d",
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND created_at >= now() - interval '30 days')::int AS "smokeEvents30d",
|
||||
count(*) FILTER (WHERE actor_kind = 'smoke' AND created_at >= now() - interval '90 days')::int AS "smokeEvents90d",
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND created_at >= now() - interval '30 days')::int AS "unknownEvents30d",
|
||||
count(*) FILTER (WHERE actor_kind = 'unknown' AND created_at >= now() - interval '90 days')::int AS "unknownEvents90d"
|
||||
FROM classified
|
||||
`;
|
||||
|
||||
function isSmokeLikeValue(value) {
|
||||
const text = String(value ?? "").trim();
|
||||
if (!text) return false;
|
||||
const lower = text.toLowerCase();
|
||||
const emailLocal = lower.includes("@") ? lower.split("@")[0] : lower;
|
||||
return (
|
||||
/^(smoke[-_.]|oa[-_.]?smoke[-_.]?)/u.test(emailLocal)
|
||||
|| /^(SMOKE|OSMOKE|WFULL|PRELAUNCH|LAUNCHAUTO|HRWDTEST)[A-Z0-9_-]*/iu.test(text)
|
||||
|| /测试|试运行/u.test(text)
|
||||
);
|
||||
}
|
||||
|
||||
function classifyFixtureActor(actor, forcedKind = null) {
|
||||
if (forcedKind === "system") return "system";
|
||||
if (!actor) return "unknown";
|
||||
if ([actor.employeeNo, actor.email, actor.name].some(isSmokeLikeValue)) return "smoke";
|
||||
return "human";
|
||||
}
|
||||
|
||||
function createReadOnlyAdapter(client) {
|
||||
return {
|
||||
featureFlag: {
|
||||
async findUnique({ where }) {
|
||||
const result = await client.query(
|
||||
"SELECT flag_key, override_mode, rollout_type, department_ids, note, version, created_at, created_by, updated_at, updated_by FROM feature_flags WHERE flag_key = $1",
|
||||
[where.flag_key],
|
||||
);
|
||||
return result.rows[0] ?? null;
|
||||
},
|
||||
async findMany({ where }) {
|
||||
const result = await client.query(
|
||||
"SELECT flag_key, override_mode, rollout_type, department_ids, note, version, created_at, created_by, updated_at, updated_by FROM feature_flags WHERE flag_key = ANY($1::text[]) ORDER BY flag_key ASC",
|
||||
[where.flag_key.in],
|
||||
);
|
||||
return result.rows;
|
||||
},
|
||||
},
|
||||
workspaceSetting: {
|
||||
async findFirst() {
|
||||
const result = await client.query(
|
||||
"SELECT profile, org_mode, primary_entry_mode, notification_strategy, device_priority, module_visibility_json, created_at, updated_at, updated_by FROM workspace_settings ORDER BY created_at ASC LIMIT 1",
|
||||
);
|
||||
return result.rows[0] ?? null;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function effectiveFlagState(item) {
|
||||
if (item.requiresDepartment && item.effectiveReason === "department_deferred") return "partial";
|
||||
return item.effectiveEnabled ? "on" : "off";
|
||||
}
|
||||
|
||||
function applyExposure(metrics, workspace, flags) {
|
||||
const flagState = new Map(flags.map((item) => [item.flagKey, effectiveFlagState(item)]));
|
||||
const moduleExposure = {
|
||||
portal_notifications: ["portal", "portal_home_v1"],
|
||||
workflow_approvals: ["workflowTasks", null],
|
||||
reimbursements_invoices: ["reimbursements", null],
|
||||
payments_ap: ["paymentBatches", "payment_batch_v1"],
|
||||
procurement: ["purchaseRequests", null],
|
||||
contracts: ["contractsRegistry", "project_linking_v1"],
|
||||
projects: ["projects", "project_domain_v1"],
|
||||
assets_inventory: ["assets", null],
|
||||
leave_wecom_history: ["leaveRequests", null],
|
||||
attendance_presence: ["attendance", "attendance_unified_v1"],
|
||||
weekly_reports: ["weeklyReports", "weekly_report_v1"],
|
||||
todos: ["todos", "todo_board_v1"],
|
||||
announcements: ["announcements", "announcements_v1"],
|
||||
budgets: ["budgets", null],
|
||||
travel: ["travelRequests", null],
|
||||
people_lifecycle: ["people", "employee_lifecycle_v1"],
|
||||
payroll: [null, null],
|
||||
performance: [null, null],
|
||||
system_configuration: ["featureFlags", null],
|
||||
integrations: [null, "erp_sync"],
|
||||
};
|
||||
|
||||
return metrics.map((metric) => {
|
||||
const [navigationKey, flagKey] = moduleExposure[metric.moduleKey] ?? [null, null];
|
||||
const navigationMode = navigationKey
|
||||
? workspace.moduleVisibility[navigationKey] ?? "unknown"
|
||||
: "no_navigation";
|
||||
const effectiveState = flagKey ? flagState.get(flagKey) ?? "unknown" : "not_applicable";
|
||||
return {
|
||||
...metric,
|
||||
latestActivityAt: metric.latestActivityAt?.toISOString?.() ?? metric.latestActivityAt ?? null,
|
||||
navigationMode,
|
||||
effectiveFlagState: effectiveState,
|
||||
queryStatus: "PASS",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function assertSafeResult(metrics) {
|
||||
if (!Array.isArray(metrics) || metrics.length !== MODULE_KEYS.length) {
|
||||
throw new Error("result_module_count_invalid");
|
||||
}
|
||||
const seen = new Set();
|
||||
for (const item of metrics) {
|
||||
if (!MODULE_KEYS.includes(item.moduleKey) || seen.has(item.moduleKey)) {
|
||||
throw new Error("result_module_key_invalid");
|
||||
}
|
||||
seen.add(item.moduleKey);
|
||||
const keys = Object.keys(item).sort();
|
||||
const expected = [...SAFE_RESULT_KEYS].sort();
|
||||
if (JSON.stringify(keys) !== JSON.stringify(expected)) {
|
||||
throw new Error("result_field_contract_invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const appRoot = path.resolve(process.env.USAGE_CENSUS_APP_ROOT || process.cwd());
|
||||
const { Client } = require(path.join(appRoot, "node_modules/pg"));
|
||||
const { getConfig } = require(path.join(appRoot, "src/config.js"));
|
||||
const { listFeatureFlags } = require(path.join(appRoot, "src/services/featureFlags/index.js"));
|
||||
const { getWorkspaceSetting } = require(path.join(appRoot, "src/services/workspaceSettings.js"));
|
||||
const config = getConfig();
|
||||
const client = new Client({ connectionString: process.env.DATABASE_URL });
|
||||
let inTransaction = false;
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
await client.query("BEGIN TRANSACTION READ ONLY");
|
||||
inTransaction = true;
|
||||
await client.query("SET LOCAL statement_timeout = '30s'");
|
||||
await client.query("SET LOCAL lock_timeout = '2s'");
|
||||
const readOnly = await client.query("SELECT current_setting('transaction_read_only') AS value");
|
||||
if (readOnly.rows[0]?.value !== "on") throw new Error("transaction_not_read_only");
|
||||
|
||||
const adapter = createReadOnlyAdapter(client);
|
||||
const [metricResult, authResult, flags, workspace] = await Promise.all([
|
||||
client.query(USAGE_SQL),
|
||||
client.query(AUTH_SQL),
|
||||
listFeatureFlags(adapter, config),
|
||||
getWorkspaceSetting(adapter),
|
||||
]);
|
||||
const metrics = applyExposure(metricResult.rows, workspace, flags);
|
||||
assertSafeResult(metrics);
|
||||
await client.query("COMMIT");
|
||||
inTransaction = false;
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
status: "PASS",
|
||||
transactionReadOnly: true,
|
||||
sourceBuildSha: config.buildSha,
|
||||
generatedAt: new Date().toISOString(),
|
||||
authentication: authResult.rows[0],
|
||||
modules: metrics,
|
||||
}, null, 2)}\n`);
|
||||
} catch {
|
||||
if (inTransaction) {
|
||||
try { await client.query("ROLLBACK"); } catch { /* fail closed */ }
|
||||
}
|
||||
process.stderr.write("[usage-census][FAILED] code=QUERY_FAILED\n");
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
await client.end().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AUTH_SQL,
|
||||
MODULE_KEYS,
|
||||
SAFE_RESULT_KEYS,
|
||||
USAGE_SQL,
|
||||
applyExposure,
|
||||
assertSafeResult,
|
||||
classifyFixtureActor,
|
||||
effectiveFlagState,
|
||||
isSmokeLikeValue,
|
||||
};
|
||||
|
||||
// `node -` names stdin as argv[1] and leaves require.main unset. Production
|
||||
// streams this task-scoped file through stdin so it never lands on the host.
|
||||
if (require.main === module || process.argv[1] === "-") {
|
||||
run();
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户