Files
memind/health-observation-draft-store.mjs
T
john 2baf29b3ae feat(health): complete P0 health channel — baseline engine, page-data, MindSpace UI
Deliver encrypted health zone, observation API, baseline maturity pipeline,
page-data bindings, and H5/WeChat channel integration for health P0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:04:57 +08:00

58 lines
1.8 KiB
JavaScript

export const HEALTH_DRAFT_TTL_MS = 24 * 60 * 60 * 1000;
export function createDraftKey({ userId, sourceRef, metricSet, now = Date.now() } = {}) {
const ref = String(sourceRef ?? 'manual').slice(0, 64);
return `${String(userId)}:${metricSet}:${ref}:${now}`;
}
export function createInMemoryHealthObservationDraftStore() {
const rowsByUser = new Map();
let nextId = 1;
return {
async findByKey(userId, draftKey) {
const rows = rowsByUser.get(String(userId)) ?? [];
return rows.find((row) => row.draftKey === draftKey && row.status === 'pending') ?? null;
},
async findPendingBySourceRef(userId, sourceRef) {
const rows = rowsByUser.get(String(userId)) ?? [];
const now = Date.now();
return (
rows.find(
(row) =>
row.status === 'pending'
&& row.sourceRef === sourceRef
&& row.expiresAt > now,
) ?? null
);
},
async insert(userId, draft) {
const row = {
id: nextId++,
userId: String(userId),
draftKey: draft.draftKey,
metricSet: draft.metricSet,
extracted: draft.extracted,
validation: draft.validation,
status: 'pending',
channel: draft.channel ?? 'h5',
sourceRef: draft.sourceRef ?? null,
createdAt: draft.createdAt ?? Date.now(),
expiresAt: draft.expiresAt ?? Date.now() + HEALTH_DRAFT_TTL_MS,
committedAt: null,
};
const list = rowsByUser.get(row.userId) ?? [];
list.unshift(row);
rowsByUser.set(row.userId, list);
return row;
},
async update(userId, draftKey, patch) {
const rows = rowsByUser.get(String(userId)) ?? [];
const index = rows.findIndex((row) => row.draftKey === draftKey);
if (index < 0) return null;
rows[index] = { ...rows[index], ...patch };
return rows[index];
},
};
}