通知修正
This commit is contained in:
@@ -120,6 +120,56 @@ export function createAdminRoutes(context) {
|
||||
})).filter(item => item.code && item.name && item.quota > 0);
|
||||
}
|
||||
|
||||
function systemPublications(db) {
|
||||
const examName = examId => db.exams.find(item => item.id === examId)?.name || '未知考试';
|
||||
const schoolName = schoolId => db.schools.find(item => item.id === schoolId)?.name || '未知学校';
|
||||
const view = (record, sourceType, category, title, publishedAt, summary) => ({
|
||||
id: record.id,
|
||||
sourceType,
|
||||
category,
|
||||
title,
|
||||
summary,
|
||||
author: '系统自动发布',
|
||||
publishedAt,
|
||||
visible: record.payload?.publicVisible !== false,
|
||||
status: record.payload?.publicVisible === false ? 'hidden' : 'visible'
|
||||
});
|
||||
const plans = admissionRecords(db, 'plan').filter(item => item.status === 'approved').map(item => view(
|
||||
item,
|
||||
'plan',
|
||||
'招生计划',
|
||||
`${examName(item.examId)} · ${schoolName(item.schoolId)}招生计划公示`,
|
||||
item.payload?.reviewedAt || item.updatedAt,
|
||||
'审核通过后由系统生成,当前页面仅控制是否在公开通知目录显示。'
|
||||
));
|
||||
const qualifications = admissionRecords(db, 'qualification_publication').filter(item => item.status === 'published').map(item => view(
|
||||
item,
|
||||
'qualification',
|
||||
'指标资格',
|
||||
`${examName(item.examId)} · ${schoolName(item.schoolId)}指标分配资格公示`,
|
||||
item.payload?.publishedAt || item.updatedAt,
|
||||
'资格确认完成后由系统生成,内容随资格确认结果更新。'
|
||||
));
|
||||
const admissions = admissionRecords(db, 'setting').filter(item => item.status === 'completed' && item.payload?.autoPublish !== false).map(item => view(
|
||||
item,
|
||||
'admission',
|
||||
'录取名单',
|
||||
`${examName(item.examId)}最终录取名单`,
|
||||
item.payload?.completedAt || item.updatedAt,
|
||||
'录取结束后由系统生成,内容取自最终录取结果。'
|
||||
));
|
||||
const cutoffs = admissionRecords(db, 'cutoff_publication').filter(item => item.status === 'published' && admissionSetting(db, item.examId)?.payload?.autoPublish !== false).map(item => view(
|
||||
item,
|
||||
'cutoff',
|
||||
'录取分数线',
|
||||
`${examName(item.examId)}录取分数线`,
|
||||
item.payload?.publishedAt || item.updatedAt,
|
||||
'录取结束后由系统生成,内容取自各招生类别最低录取分数。'
|
||||
));
|
||||
return [...plans, ...qualifications, ...admissions, ...cutoffs]
|
||||
.sort((left, right) => new Date(right.publishedAt) - new Date(left.publishedAt));
|
||||
}
|
||||
|
||||
async function handleAdmin(request, response, pathname) {
|
||||
if (!pathname.startsWith('/api/admin/')) return false;
|
||||
const user = await requireUser(request, response, 'admin');
|
||||
@@ -176,7 +226,7 @@ export function createAdminRoutes(context) {
|
||||
if (status.complete) {
|
||||
const published = admissionRecords(nextDb, 'qualification_publication', setting.examId).find(item => item.schoolId === user.schoolId);
|
||||
const publication = published || { id: uid('qualification_publication'), kind: 'qualification_publication', examId: setting.examId, userId: user.id, schoolId: user.schoolId, createdAt: now };
|
||||
Object.assign(publication, { status: 'published', updatedAt: now, payload: { publishedAt: now, rows: status.rows } });
|
||||
Object.assign(publication, { status: 'published', updatedAt: now, payload: { ...publication.payload, publishedAt: now, rows: status.rows } });
|
||||
records.push(publication);
|
||||
}
|
||||
await database.saveAdmissionRecords(records, logAction(db, user, '批量确认指标分配资格', `${qualifications.length} 人 · ${body.eligible ? '有资格' : '无资格'}${status.complete ? ' · 全校已自动公示' : ''}`));
|
||||
@@ -203,7 +253,7 @@ export function createAdminRoutes(context) {
|
||||
if (status.complete) {
|
||||
const published = admissionRecords(nextDb, 'qualification_publication', setting.examId).find(item => item.schoolId === user.schoolId);
|
||||
const publication = published || { id: uid('qualification_publication'), kind: 'qualification_publication', examId: setting.examId, userId: user.id, schoolId: user.schoolId, createdAt: now };
|
||||
Object.assign(publication, { status: 'published', updatedAt: now, payload: { publishedAt: now, rows: status.rows } });
|
||||
Object.assign(publication, { status: 'published', updatedAt: now, payload: { ...publication.payload, publishedAt: now, rows: status.rows } });
|
||||
records.push(publication);
|
||||
}
|
||||
await database.saveAdmissionRecords(records, logAction(db, user, '确认指标分配资格', `${account.candidateNumber} · ${body.eligible ? '有资格' : '无资格'}${status.complete ? ' · 全校已自动公示' : ''}`));
|
||||
@@ -301,7 +351,7 @@ export function createAdminRoutes(context) {
|
||||
if (admissionRecords(db, 'placement', exam.id).some(item => item.schoolId === school.id && item.status !== 'withdrawn')) return sendError(response, 409, '已经产生投档记录,不能再修改该校本轮招生计划');
|
||||
const now = nowIso();
|
||||
const plan = existing || { id: uid('plan'), kind: 'plan', examId: exam.id, schoolId: school.id, createdAt: now };
|
||||
Object.assign(plan, { userId: user.id, status: 'approved', updatedAt: now, payload: { categories, note: cleanText(body.note, 500), submittedBy: user.displayName, reviewedBy: user.displayName, reviewedAt: now, reviewNote: '超级管理员代上传并审核通过' } });
|
||||
Object.assign(plan, { userId: user.id, status: 'approved', updatedAt: now, payload: { ...plan.payload, categories, note: cleanText(body.note, 500), submittedBy: user.displayName, reviewedBy: user.displayName, reviewedAt: now, reviewNote: '超级管理员代上传并审核通过' } });
|
||||
await database.saveAdmissionRecord(plan, logAction(db, user, '代上传招生计划', `${school.name} · ${exam.name}`));
|
||||
await cache.invalidate('public');
|
||||
return sendJson(response, existing ? 200 : 201, { ok: true, plan });
|
||||
@@ -344,7 +394,7 @@ export function createAdminRoutes(context) {
|
||||
const cutoffRows = admissionCutoffRows(completedDb, setting.examId);
|
||||
const existingCutoff = admissionRecords(db, 'cutoff_publication', setting.examId)[0];
|
||||
const cutoffPublication = existingCutoff || { id: uid('cutoff_publication'), kind: 'cutoff_publication', examId: setting.examId, userId: user.id, schoolId: null, createdAt: now };
|
||||
Object.assign(cutoffPublication, { status: 'published', updatedAt: now, payload: { publishedAt: now, rows: cutoffRows } });
|
||||
Object.assign(cutoffPublication, { status: 'published', updatedAt: now, payload: { ...cutoffPublication.payload, publishedAt: now, rows: cutoffRows } });
|
||||
await database.saveAdmissionRecords([setting, ...admitted, ...notifications, cutoffPublication], logAction(db, user, '结束录取并发布结果与分数线', `${setting.examId} · ${admitted.length} 人`));
|
||||
return sendJson(response, 200, { ok: true, admittedCount: admitted.length, publicRows: publicAdmissionRows(completedDb, setting.examId), cutoffRows });
|
||||
}
|
||||
@@ -1327,7 +1377,7 @@ export function createAdminRoutes(context) {
|
||||
const notices = db.notices
|
||||
.sort((a, b) => new Date(b.publishAt || b.createdAt) - new Date(a.publishAt || a.createdAt))
|
||||
.map(noticeForClient);
|
||||
return sendJson(response, 200, { ok: true, notices });
|
||||
return sendJson(response, 200, { ok: true, notices, publications: systemPublications(db) });
|
||||
}
|
||||
if (request.method === 'POST' && pathname === '/api/admin/notices') {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
@@ -1339,8 +1389,24 @@ export function createAdminRoutes(context) {
|
||||
const notice = { id: uid('notice'), title, summary: cleanText(body.summary, 260) || contentText.slice(0, 80), content, category: cleanText(body.category, 30) || '通知公告', pinned: Boolean(body.pinned), status: body.status === 'draft' ? 'draft' : 'published', publishAt: body.status === 'draft' ? null : nowIso(), createdAt: nowIso(), author: user.displayName };
|
||||
const log = logAction(db, user, notice.status === 'published' ? '发布通知' : '保存通知草稿', notice.title);
|
||||
await database.createNotice(notice, log);
|
||||
await cache.invalidate('public');
|
||||
return sendJson(response, 201, { ok: true, notice: noticeForClient(notice) });
|
||||
}
|
||||
const publicationMatch = pathname.match(/^\/api\/admin\/publications\/(plan|qualification|admission|cutoff)\/([^/]+)$/);
|
||||
if (request.method === 'PATCH' && publicationMatch) {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
const kindByType = { plan: 'plan', qualification: 'qualification_publication', admission: 'setting', cutoff: 'cutoff_publication' };
|
||||
const sourceType = publicationMatch[1];
|
||||
const record = admissionRecords(db, kindByType[sourceType]).find(item => item.id === publicationMatch[2]);
|
||||
if (!record) return sendError(response, 404, '系统公示不存在');
|
||||
const body = await readJson(request);
|
||||
if (typeof body.visible !== 'boolean') return sendError(response, 400, '请明确设置是否显示');
|
||||
record.payload = { ...record.payload, publicVisible: body.visible };
|
||||
record.updatedAt = nowIso();
|
||||
await database.saveAdmissionRecord(record, logAction(db, user, body.visible ? '显示系统公示' : '隐藏系统公示', `${sourceType} · ${record.id}`));
|
||||
await cache.invalidate('public');
|
||||
return sendJson(response, 200, { ok: true, publication: systemPublications({ ...db, admissionRecords: db.admissionRecords.map(item => item.id === record.id ? record : item) }).find(item => item.id === record.id && item.sourceType === sourceType) });
|
||||
}
|
||||
const noticeMatch = pathname.match(/^\/api\/admin\/notices\/([^/]+)$/);
|
||||
if (request.method === 'PATCH' && noticeMatch) {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
@@ -1360,6 +1426,7 @@ export function createAdminRoutes(context) {
|
||||
}
|
||||
const log = logAction(db, user, '更新通知', `${notice.title} · ${notice.status}`);
|
||||
await database.updateNotice(notice, log);
|
||||
await cache.invalidate('public');
|
||||
return sendJson(response, 200, { ok: true, notice: noticeForClient(notice) });
|
||||
}
|
||||
if (request.method === 'POST' && pathname === '/api/admin/results/cache/refresh') {
|
||||
|
||||
Reference in New Issue
Block a user