Add demo mode factory reset

- Align the backend seeded default surgery template with the report editor's default report content.

- Add backend demo defaults for the default template, Kimi provider, and Xunfei speech proxy configuration.

- Change system reset into a super-admin demo mode factory reset that clears reports, audit logs, files, custom templates, and non-default users.

- Keep only the default admin, manager, doctor, and default surgery template after demo reset.

- Replace the old local-only reset all data button with a two-confirmation backend reset flow.

- Add tests covering demo default alignment and database-backed demo reset behavior.

- Update docs to describe demo mode reset semantics and production credential cautions.
This commit is contained in:
2026-05-02 02:52:30 +08:00
parent bc235b2358
commit 911b96b883
17 changed files with 361 additions and 85 deletions

View File

@@ -1,6 +1,11 @@
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import argon2 from 'argon2';
import {
DEMO_DEFAULT_REPORT_CONTENT,
DEMO_SYSTEM_SETTINGS,
DEMO_TEMPLATE_ID,
} from '../src/demo/demo-defaults.js';
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is required to seed the database');
@@ -12,16 +17,6 @@ const prisma = new PrismaClient({
}),
});
const defaultTemplateContent = `
<h1 style="text-align:center;">手术记录</h1>
<p>患者姓名:<span class="field-value" data-bind="patientName" contenteditable="true"></span></p>
<p>住院号:<span class="field-value" data-bind="hospitalId" contenteditable="true"></span></p>
<p>手术名称:<span class="field-value" data-bind="title" contenteditable="true"></span></p>
<div class="ai-region" data-ai-id="手术步骤" data-ai-title="手术步骤">
<div class="ai-content"><p>请在此处填写手术步骤。</p></div>
</div>
`;
const main = async () => {
const tenant = await prisma.tenant.upsert({
where: { code: 'default' },
@@ -94,14 +89,22 @@ const main = async () => {
});
const defaultTemplate = await prisma.template.upsert({
where: { id: 'tpl_default_surgery' },
update: {},
where: { id: DEMO_TEMPLATE_ID },
update: {
name: '腹腔镜胆囊切除术报告',
description: '标准手术记录模板',
content: DEMO_DEFAULT_REPORT_CONTENT,
fields: [],
scope: 'DEPARTMENT',
ownerDepartmentId: surgeryDepartment.id,
ownerUserId: null,
},
create: {
id: 'tpl_default_surgery',
id: DEMO_TEMPLATE_ID,
tenantId: tenant.id,
name: '腹腔镜胆囊切除术报告',
description: '标准手术记录模板',
content: defaultTemplateContent,
content: DEMO_DEFAULT_REPORT_CONTENT,
fields: [],
scope: 'DEPARTMENT',
ownerDepartmentId: surgeryDepartment.id,
@@ -128,6 +131,25 @@ const main = async () => {
},
});
const existingSystemSettings = await prisma.systemSetting.findFirst({
where: { tenantId: tenant.id, scope: 'global', departmentId: null, key: 'systemSettings' },
});
if (existingSystemSettings) {
await prisma.systemSetting.update({
where: { id: existingSystemSettings.id },
data: { value: DEMO_SYSTEM_SETTINGS },
});
} else {
await prisma.systemSetting.create({
data: {
tenantId: tenant.id,
scope: 'global',
key: 'systemSettings',
value: DEMO_SYSTEM_SETTINGS,
},
});
}
await prisma.auditLog.create({
data: {
tenantId: tenant.id,