server/laf-cloud/functions/activityConfig/store.ts
2026-09-24 17:59:52 +08:00

64 lines
3.2 KiB
TypeScript

import cloud from "@lafjs/cloud";
import { createHash } from "crypto";
export const COLLECTION = "activityConfigs";
export const records = (): any => (cloud.mongo.db as any).collection(COLLECTION);
export const configId = (activityId: string, version: string) => createHash("sha256").update(JSON.stringify([activityId, version])).digest("hex");
/** Missing flags on legacy records retain the enabled behavior. */
export const isEnabled = (record: any): boolean => !!record && record.enabled !== false;
/** Published records are immutable. Runtime selection never uses a future publication. */
export async function publish(activityId: string, configVersion: string, effectiveFrom: number, config: any, enabled = true) {
if (typeof enabled !== "boolean") throw new Error("enabled 必须为布尔值");
if (typeof configVersion !== "string" || !/^[A-Za-z0-9_:-]{1,100}$/.test(configVersion) || !Number.isSafeInteger(effectiveFrom)
|| effectiveFrom <= Date.now()) throw new Error("配置版本必填,且只能发布未来生效的配置");
const row = {
_id: configId(activityId, configVersion), activityId, recordType: "version", configVersion,
status: "published", effectiveFrom, publishedAt: Date.now(), config,
enabled
};
const old = await records().findOne({ _id: row._id });
if (old) {
if (old.effectiveFrom !== effectiveFrom || JSON.stringify(old.config) !== JSON.stringify(config)
|| isEnabled(old) !== enabled) throw new Error("配置版本已存在,不可修改");
return old;
}
try { await records().insertOne(row); }
catch (error) {
const saved = await records().findOne({ _id: row._id });
if (!saved || saved.effectiveFrom !== effectiveFrom || JSON.stringify(saved.config) !== JSON.stringify(config)
|| isEnabled(saved) !== enabled) throw error;
return saved;
}
return row;
}
export async function latest(activityId: string, at: number, publishedBefore = at, extra: any = {}) {
const rows = await records().find({
...extra, activityId, recordType: "version", status: "published",
effectiveFrom: { $lte: at }, publishedAt: { $lte: publishedBefore }
})
.sort({ effectiveFrom: -1, publishedAt: -1, _id: 1 }).limit(2).toArray();
if (rows[1] && rows[0].effectiveFrom === rows[1].effectiveFrom && rows[0].publishedAt === rows[1].publishedAt)
throw new Error("配置生效时间冲突,请调整发布版本");
return rows[0] || null;
}
/** For new activity periods: never fall back past a disabled selected version. */
export async function latestEnabled(activityId: string, at: number, publishedBefore = at, extra: any = {}) {
const row = await latest(activityId, at, publishedBefore, extra);
return isEnabled(row) ? row : null;
}
export async function setupIndexes() {
await records().createIndex({ activityId: 1, configVersion: 1 }, {
unique: true,
partialFilterExpression: { recordType: "version" }, name: "activity_config_version"
});
await records().createIndex({ activityId: 1, recordType: 1, status: 1, effectiveFrom: -1, publishedAt: -1 });
await records().createIndex({ activityId: 1, "config.periodId": 1, publishedAt: -1 });
}
export default async function () { return { code: 0, msg: "internal module" }; }