2baf29b3ae
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>
53 lines
1.8 KiB
JavaScript
53 lines
1.8 KiB
JavaScript
export function createInMemoryHealthObservationStore() {
|
|
const rowsByUser = new Map();
|
|
const dedupIndex = new Map();
|
|
let nextId = 1;
|
|
|
|
return {
|
|
listUserIds() {
|
|
return [...rowsByUser.keys()];
|
|
},
|
|
async list(userId, { limit = 50 } = {}) {
|
|
const rows = rowsByUser.get(String(userId)) ?? [];
|
|
return rows.filter((row) => row.deletedAt == null).slice(0, limit);
|
|
},
|
|
async findByDedupKey(userId, dedupKey) {
|
|
const id = dedupIndex.get(`${String(userId)}|${dedupKey}`);
|
|
if (!id) return null;
|
|
const rows = rowsByUser.get(String(userId)) ?? [];
|
|
return rows.find((row) => row.id === id) ?? null;
|
|
},
|
|
async insert(userId, observation) {
|
|
if (!observation?.confirmed) {
|
|
const error = Object.assign(new Error('健康观测必须确认后才能保存'), {
|
|
code: 'health_unconfirmed',
|
|
});
|
|
throw error;
|
|
}
|
|
const row = {
|
|
id: nextId++,
|
|
userId: String(userId),
|
|
observedAt: observation.observedAt,
|
|
metricType: observation.metricType,
|
|
valueNum: observation.valueNum ?? null,
|
|
valueText: observation.valueText ?? null,
|
|
unit: observation.unit ?? null,
|
|
context: observation.context ?? null,
|
|
source: observation.source ?? 'manual',
|
|
sourceRef: observation.sourceRef ?? null,
|
|
qualityFlag: observation.qualityFlag ?? 'ok',
|
|
deletedAt: observation.deletedAt ?? null,
|
|
note: observation.note ?? null,
|
|
createdAt: observation.createdAt ?? Date.now(),
|
|
};
|
|
if (observation.dedupKey) {
|
|
dedupIndex.set(`${row.userId}|${observation.dedupKey}`, row.id);
|
|
}
|
|
const list = rowsByUser.get(row.userId) ?? [];
|
|
list.unshift(row);
|
|
rowsByUser.set(row.userId, list);
|
|
return row;
|
|
},
|
|
};
|
|
}
|