招生计划、资格、录取、分数线及报到公示

成绩单 HMAC 防伪验真
录取通知书 HMAC 防伪验真
防伪码常量时间比较
生产环境文书密钥强度检查
旧文书防伪码完全兼容
This commit is contained in:
2026-07-22 19:12:38 +08:00 Unverified
parent 38e42ad734
commit 017cacc6f9
14 changed files with 824 additions and 6 deletions
@@ -0,0 +1,37 @@
using Eis.Infrastructure.Security;
namespace Eis.Infrastructure.Tests.Security;
public sealed class DocumentVerificationCodeServiceTests
{
private const string Secret = "test-document-verification-secret-32-characters";
private readonly DocumentVerificationCodeService _codes = new(DocumentVerificationOptions.FromSecret(Secret));
[Fact]
public void ScoreReportCode_MatchesLegacyNodeImplementation()
{
var results = new[]
{
new ScoreSignatureItem("math", 118, "2026-07-20T08:00:00.000Z", null),
new ScoreSignatureItem("chinese", 112, "2026-07-20T08:00:00.000Z", null)
};
var code = _codes.ScoreReportCode("registration_1", "candidate_1", "exam_1", results);
var reordered = _codes.ScoreReportCode("registration_1", "candidate_1", "exam_1", results.Reverse());
Assert.Equal("SR-38D987280CE4FBE78307B5F3", code);
Assert.Equal(code, reordered);
Assert.True(DocumentVerificationCodeService.SafeEquals(code, code.ToLowerInvariant()));
Assert.False(DocumentVerificationCodeService.SafeEquals(code, $"{code}0"));
}
[Fact]
public void AdmissionNoticeCode_MatchesLegacyNodeImplementation()
{
var code = _codes.AdmissionNoticeCode(
"placement_1", "candidate_1", "school_1", "exam_1", "general",
"AD01-EX-2026-000001", "2026-07-21T08:00:00.000Z");
Assert.Equal("AN-E907684020E21579B854C167", code);
}
}
@@ -0,0 +1,48 @@
import { DatabaseSync } from 'node:sqlite';
import { admissionNoticeCode } from '../../src/security/document-verification.mjs';
const databasePath = process.argv[2];
if (!databasePath) throw new Error('缺少 SQLite 测试数据库路径');
const database = new DatabaseSync(databasePath);
const rows = database.prepare(
"SELECT id, exam_id, user_id, school_id, payload_json, updated_at FROM admission_records WHERE kind = 'placement' ORDER BY id"
).all();
let row = rows.map(item => ({ ...item, payload: JSON.parse(item.payload_json || '{}') }))[0];
if (!row) {
const exam = database.prepare('SELECT id FROM exams ORDER BY id LIMIT 1').get();
const user = database.prepare("SELECT id FROM users WHERE role = 'candidate' ORDER BY id LIMIT 1").get();
const school = database.prepare('SELECT id FROM schools WHERE is_admission_school = 1 ORDER BY id LIMIT 1').get();
if (!exam || !user || !school) throw new Error('测试数据库缺少考试、考生或招生学校');
row = {
id: 'smoke_final_placement',
exam_id: exam.id,
user_id: user.id,
school_id: school.id,
updated_at: '2026-07-22T00:00:00.000Z',
payload: { categoryCode: 'general', categoryName: '普通生', noticeNumber: 'SMOKE-EXAM-000001' }
};
database.prepare(
"INSERT INTO admission_records (id, kind, exam_id, user_id, school_id, status, payload_json, created_at, updated_at) VALUES (?, 'placement', ?, ?, ?, 'final', ?, ?, ?)"
).run(row.id, row.exam_id, row.user_id, row.school_id, JSON.stringify(row.payload), row.updated_at, row.updated_at);
} else {
row.payload.noticeNumber ||= 'SMOKE-EXAM-000001';
row.updated_at = '2026-07-22T00:00:00.000Z';
database.prepare(
"UPDATE admission_records SET status = 'final', payload_json = ?, updated_at = ? WHERE id = ?"
).run(JSON.stringify(row.payload), row.updated_at, row.id);
}
database.close();
console.log(admissionNoticeCode(
process.env.DOCUMENT_VERIFICATION_SECRET,
{
id: row.id,
userId: row.user_id,
schoolId: row.school_id,
payload: row.payload,
updatedAt: row.updated_at
},
{ id: row.exam_id }
));