This commit is contained in:
2026-07-21 12:49:59 +08:00 Unverified
parent 97bbe07789
commit ae528b20e9
24 changed files with 770 additions and 30 deletions
+69 -3
View File
@@ -35,6 +35,7 @@ export const relationalTables = [
'workflow_steps',
'workflow_instances',
'workflow_actions',
'admission_records',
'audit_logs'
];
@@ -44,7 +45,7 @@ function validateState(state, source = '数据库') {
'testCenters', 'testRooms', 'centerChangeRequests', 'centerChangeRooms',
'admissionNumberRules', 'arrangementPlans',
'numberRules', 'candidateAccountBatches', 'candidateAccountBatchItems',
'workflows', 'workflowInstances', 'workflowActions', 'auditLogs'
'workflows', 'workflowInstances', 'workflowActions', 'admissionRecords', 'auditLogs'
];
if (!state || typeof state !== 'object' || collections.some(name => !Array.isArray(state[name]))) {
throw new Error(`${source}中的应用数据格式无效`);
@@ -59,7 +60,7 @@ export function buildSeedOperations(state) {
const nullable = value => value == null || value === '' ? null : value;
add(
'UPDATE schema_metadata SET schema_version = 17, app_version = ?, self_registration_enabled = ?, created_at = ? WHERE id = 1',
'UPDATE schema_metadata SET schema_version = 18, app_version = ?, self_registration_enabled = ?, created_at = ? WHERE id = 1',
Number(state.meta?.version || 1), state.settings?.selfRegistrationEnabled ? 1 : 0,
state.meta?.createdAt || new Date().toISOString()
);
@@ -335,6 +336,16 @@ export function buildSeedOperations(state) {
);
}
for (const record of state.admissionRecords) {
add(
`INSERT INTO admission_records (
id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
record.id, record.kind, record.examId, nullable(record.userId), nullable(record.schoolId), record.status,
JSON.stringify(record.payload || {}), record.createdAt, record.updatedAt
);
}
for (const log of state.auditLogs) {
add(
'INSERT INTO audit_logs (id, actor_id, action, detail, created_at) VALUES (?, ?, ?, ?, ?)',
@@ -507,6 +518,9 @@ function stateFromRows(rows) {
postalCode: row.postal_code || '',
guardianName: row.guardian_name || '',
guardianPhone: row.guardian_phone || '',
specialtyTypes: (() => { try { return JSON.parse(row.specialty_types || '[]'); } catch { return []; } })(),
specialtyCertificate: row.specialty_certificate || '',
policyEligibility: row.policy_eligibility || '',
profileCompleted: Boolean(row.profile_completed),
status: row.status,
reviewNote: row.review_note || '',
@@ -738,6 +752,17 @@ function stateFromRows(rows) {
toAssigneeId: row.to_assignee_id,
createdAt: row.created_at
})),
admissionRecords: rows.admissionRecords.map(row => ({
id: row.id,
kind: row.kind,
examId: row.exam_id,
userId: row.user_id || null,
schoolId: row.school_id || null,
status: row.status,
payload: (() => { try { return JSON.parse(row.payload_json || '{}'); } catch { return {}; } })(),
createdAt: row.created_at,
updatedAt: row.updated_at
})),
auditLogs: rows.auditLogs.map(row => ({
id: row.id,
actorId: row.actor_id,
@@ -779,6 +804,7 @@ function readSqliteRows(connection) {
workflowSteps: connection.prepare('SELECT * FROM workflow_steps ORDER BY workflow_id, position, id').all(),
workflowInstances: connection.prepare('SELECT * FROM workflow_instances ORDER BY created_at DESC, id').all(),
workflowActions: connection.prepare('SELECT * FROM workflow_actions ORDER BY created_at, id').all(),
admissionRecords: connection.prepare('SELECT * FROM admission_records ORDER BY created_at, id').all(),
auditLogs: connection.prepare('SELECT * FROM audit_logs ORDER BY created_at DESC, id DESC').all()
};
}
@@ -815,6 +841,7 @@ async function readMysqlRows(connection) {
workflowSteps: await query('SELECT * FROM workflow_steps ORDER BY workflow_id, position, id'),
workflowInstances: await query('SELECT * FROM workflow_instances ORDER BY created_at DESC, id'),
workflowActions: await query('SELECT * FROM workflow_actions ORDER BY created_at, id'),
admissionRecords: await query('SELECT * FROM admission_records ORDER BY created_at, id'),
auditLogs: await query('SELECT * FROM audit_logs ORDER BY created_at DESC, id DESC')
};
}
@@ -925,6 +952,7 @@ function createRepository({ client, location, read, transaction, close }) {
province_code = ?, province_name = ?, city_code = ?, city_name = ?, district_code = ?, district_name = ?, address = ?,
school_id = ?, class_id = ?, emergency_contact = ?, emergency_phone = ?, status = ?, review_note = ?,
native_place = ?, birth_date = ?, ethnicity = ?, postal_code = ?, guardian_name = ?, guardian_phone = ?,
specialty_types = ?, specialty_certificate = ?, policy_eligibility = ?,
profile_completed = ?, reviewed_at = ?, reviewer_id = ?, updated_at = ?
WHERE id = ?`,
profile.name, optional(profile.gender), profile.idNumber, profile.phone, optional(profile.email),
@@ -933,7 +961,8 @@ function createRepository({ client, location, read, transaction, close }) {
optional(profile.address), optional(profile.schoolId),
optional(profile.classId), optional(profile.emergencyContact), optional(profile.emergencyPhone), profile.status,
optional(profile.reviewNote), optional(profile.nativePlace), optional(profile.birthDate), optional(profile.ethnicity),
optional(profile.postalCode), optional(profile.guardianName), optional(profile.guardianPhone), profile.profileCompleted ? 1 : 0, optional(profile.reviewedAt),
optional(profile.postalCode), optional(profile.guardianName), optional(profile.guardianPhone), JSON.stringify(profile.specialtyTypes || []),
optional(profile.specialtyCertificate), optional(profile.policyEligibility), profile.profileCompleted ? 1 : 0, optional(profile.reviewedAt),
optional(profile.reviewerId), profile.updatedAt, profile.id
),
operation('UPDATE users SET display_name = ? WHERE id = ?', displayName, profile.userId)
@@ -1186,6 +1215,43 @@ function createRepository({ client, location, read, transaction, close }) {
auditOperation(log)
]);
},
async createAdmissionSchoolAccount(user, log) {
await transaction([
operation(
`INSERT INTO users (
id, username, password_hash, role, admin_level, school_id, class_id, active, display_name, created_at
) VALUES (?, ?, ?, 'admission_school', NULL, ?, NULL, ?, ?, ?)`,
user.id, user.username, user.passwordHash, user.schoolId, user.active === false ? 0 : 1, user.displayName, user.createdAt
),
auditOperation(log)
]);
},
async saveAdmissionRecord(record, log = null) {
const operations = [operation('DELETE FROM admission_records WHERE id = ?', record.id), operation(
`INSERT INTO admission_records (
id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
record.id, record.kind, record.examId, optional(record.userId), optional(record.schoolId), record.status,
JSON.stringify(record.payload || {}), record.createdAt, record.updatedAt
)];
if (log) operations.push(auditOperation(log));
await transaction(operations);
},
async saveAdmissionRecords(records, log = null) {
const operations = [];
for (const record of records) {
operations.push(operation('DELETE FROM admission_records WHERE id = ?', record.id));
operations.push(operation(
`INSERT INTO admission_records (
id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
record.id, record.kind, record.examId, optional(record.userId), optional(record.schoolId), record.status,
JSON.stringify(record.payload || {}), record.createdAt, record.updatedAt
));
}
if (log) operations.push(auditOperation(log));
await transaction(operations);
},
async saveSchool(school, isNew, log) {
const change = isNew
? operation(