feat: 作业纸

This commit is contained in:
R524809
2026-10-23 16:52:47 +08:00
parent 83a90407b0
commit c090675255
14 changed files with 532 additions and 3 deletions
@@ -11,6 +11,11 @@ import { PrismaService } from '../src/database/prisma.service';
import { JwtTokenService } from '../src/auth/jwt-token.service';
import { ASSET_STORAGE } from '../src/assets/qiniu.gateway';
import { AssetService } from '../src/assets/asset.service';
import { seedPaperSheets } from '../src/worksheets/paper-sheet-seed';
import {
PAPER_SHEET_PATH,
PAPER_SHEET_PRESETS,
} from '../src/worksheets/paper-sheet-presets';
import { SiteService } from '../src/site/site.service';
import { testConfig } from './test-config';
@@ -707,6 +712,147 @@ test(
);
},
);
await context.test(
'纸张拆分:预演不写库、草稿幂等、独立发布/收藏/下载及历史兼容',
async () => {
const parent = await db.worksheet.create({
data: {
id: 'paper-sheet',
title: '作业纸',
subtitle: '',
categoryId,
path: `${PAPER_SHEET_PATH}?id=paper-sheet`,
ageMin: 3,
ageMax: 12,
difficulty: 1,
previewAssetId: previewId,
status: 'active',
downloads: 123n,
likes: 9n,
},
});
await db.worksheetGrade.create({
data: { worksheetId: parent.id, grade: 0 },
});
const dry = await seedPaperSheets(app.get(PrismaService), true);
assert.equal(
dry.changes.filter((row) => row.action === 'insert-draft')
.length,
10,
);
assert.equal(
await db.worksheet.count({
where: { rendererKey: 'paper-sheet' },
}),
0,
);
await seedPaperSheets(app.get(PrismaService), false);
assert.ok(
(
await seedPaperSheets(app.get(PrismaService), false)
).changes.every((row) => row.action === 'unchanged'),
);
assert.deepEqual(
await db.worksheet.findUnique({ where: { id: parent.id } }),
parent,
);
for (const preset of PAPER_SHEET_PRESETS) {
const draft = await db.worksheet.findUniqueOrThrow({
where: { id: preset.id },
});
assert.equal(draft.status, 'draft');
assert.equal(draft.firstPublishedAt, null);
assert.equal(draft.downloads, 0n);
assert.equal(draft.likes, 0n);
assert.equal(
(await request(`/worksheets/${preset.id}`)).status,
404,
);
}
const input = {
id: 'paper-mi',
title: '米字格',
subtitle: '',
categoryId,
path: `${PAPER_SHEET_PATH}?id=paper-mi`,
ageMin: 3,
ageMax: 12,
difficulty: 1,
previewAssetId: previewId,
tags: ['米字格'],
status: 'active',
};
assert.equal(
(
await request(
'/admin/worksheets/publish',
tokens.admin,
'POST',
{
...input,
path: `${PAPER_SHEET_PATH}?id=paper-tian`,
},
)
).status,
400,
);
assert.equal(
(
await request(
'/admin/worksheets/publish',
tokens.admin,
'POST',
input,
)
).status,
201,
);
const detail = (await request('/worksheets/paper-mi')).body
.data;
assert.deepEqual(detail.rendering, {
rendererKey: 'paper-sheet',
paperType: 'mi',
});
assert.equal(detail.renderConfig, undefined);
assert.equal(
(await request('/me/favorites/paper-mi', tokens.b, 'PUT'))
.status,
200,
);
assert.equal(
(
await request(
'/me/favorites/paper-sheet',
tokens.b,
'PUT',
)
).status,
200,
);
const download = await request(
'/me/downloads',
tokens.b,
'POST',
{ worksheetId: 'paper-mi', exportId: randomUUID() },
);
assert.equal(download.status, 201);
const saved = await db.worksheet.findUniqueOrThrow({
where: { id: 'paper-mi' },
});
assert.equal(saved.downloads, 1n);
assert.equal(saved.likes, 1n);
const old = await db.worksheet.findUniqueOrThrow({
where: { id: parent.id },
});
assert.equal(old.downloads, 123n);
assert.equal(old.likes, 10n);
assert.ok(
await db.favorite.findFirst({
where: { userId: ids.b, worksheetId: parent.id },
}),
);
},
);
await context.test(
'注销删除本人明细、撤销旧 JWT,其余用户不受影响',
async () => {
+67
View File
@@ -0,0 +1,67 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import {
PAPER_SHEET_PATH,
PAPER_SHEET_PRESETS,
paperSheetPreset,
publicPaperSheetRendering,
} from '../src/worksheets/paper-sheet-presets';
import { parseWorksheetExport } from '../src/imports/worksheet-import';
import { worksheetFixture } from './worksheet-import.fixture';
test('独立纸张:固定注册、路由归属、导入配置和公开参数一致', () => {
assert.equal(
new Set(PAPER_SHEET_PRESETS.map((entry) => entry.id)).size,
10,
);
for (const entry of PAPER_SHEET_PRESETS) {
const path = `${PAPER_SHEET_PATH}?id=${entry.id}`;
const config = paperSheetPreset(entry.id, path)!;
const row = parseWorksheetExport(
JSON.stringify([{ ...worksheetFixture, _id: entry.id, path }]),
).rows[0]!.data;
assert.equal(row.rendererKey, 'paper-sheet');
assert.deepEqual(row.renderConfig, { paperType: entry.paperType });
assert.deepEqual(
publicPaperSheetRendering({
id: entry.id,
path,
...config,
renderConfig: {
...config.renderConfig,
privateKey: 'must-not-leak',
},
}),
{ rendererKey: 'paper-sheet', paperType: entry.paperType },
);
assert.throws(() =>
paperSheetPreset(entry.id, `${PAPER_SHEET_PATH}?id=paper-sheet`),
);
assert.equal(
publicPaperSheetRendering({
id: entry.id,
path,
...config,
renderConfig: { paperType: 'unknown' },
}),
undefined,
);
assert.equal(
publicPaperSheetRendering({
id: entry.id,
path,
...config,
rendererKey: 'unknown',
}),
undefined,
);
}
assert.equal(
paperSheetPreset('paper-sheet', `${PAPER_SHEET_PATH}?id=paper-sheet`),
undefined,
);
assert.equal(
paperSheetPreset('paper-unregistered', '/arbitrary'),
undefined,
);
});