新增内容引擎文章发布接口与详情页

This commit is contained in:
amb
2026-09-11 17:19:09 +08:00
parent ba6aab722e
commit 726485dccb
16 changed files with 958 additions and 102 deletions
+78
View File
@@ -0,0 +1,78 @@
const { migrate } = require('../src/database/migrate');
const { getDb, closeDb } = require('../src/config/database');
const Article = require('../src/models/Article');
const { ContentIngestService, ContentIngestError } = require('../src/services/ContentIngestService');
const suffix = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
const taskId = `ingest-test-${suffix}`;
let contentId;
const buildPayload = ({ version = 1, hash = 'a'.repeat(64), title = '内容发布服务测试' } = {}) => ({
request_id: `request-${version}`,
source_system: 'amaba-content-core',
source_task_id: taskId,
source_version_id: `version-${version}`,
source_version_number: version,
content_hash: hash,
site_profile: 'ai',
publish_mode: 'publish',
article: {
title,
body_plain: '第一段测试正文。\n\n第二段测试正文。',
summary: '测试摘要。',
slug: `ingest-test-${suffix}`,
keywords: ['测试'],
content_type: 'methodology',
author_name: '阿美巴',
},
quality: { human_confirmed: true, confirmed_at: '2026-09-11T09:00:00+08:00' },
provenance: { knowledge_refs: [] },
semantic: { entities: [], questions: [], facts: [], suggested_schema_type: 'Article' },
});
beforeAll(() => migrate());
afterAll(() => {
if (contentId) {
getDb().prepare('DELETE FROM integration_publications WHERE content_id = ?').run(contentId);
Article.delete(contentId);
}
closeDb();
});
describe('内容判断引擎发布服务', () => {
test('首次发布、重复幂等请求、哈希冲突与新版本更新', async () => {
const firstKey = `${taskId}:version-1:ai`;
const first = await ContentIngestService.upsert(buildPayload(), firstKey);
contentId = first.body.publication_id;
expect(first.statusCode).toBe(201);
expect(first.body.status).toBe('published');
expect(first.body.revision).toBe(1);
const repeated = await ContentIngestService.upsert(buildPayload(), firstKey);
expect(repeated.statusCode).toBe(200);
expect(repeated.body.publication_id).toBe(contentId);
expect(repeated.body.revision).toBe(1);
await expect(ContentIngestService.upsert(buildPayload({ hash: 'b'.repeat(64) }), firstKey))
.rejects.toMatchObject({ code: 'CONTENT_CONFLICT', status: 409 });
const updated = await ContentIngestService.upsert(
buildPayload({ version: 2, hash: 'c'.repeat(64), title: '内容发布服务测试(更新)' }),
`${taskId}:version-2:ai`,
);
expect(updated.statusCode).toBe(200);
expect(updated.body.publication_id).toBe(contentId);
expect(updated.body.revision).toBe(2);
expect(updated.body.status).toBe('published');
});
test('拒绝错误站点身份与未人工确认的内容', () => {
const wrongSite = buildPayload();
wrongSite.site_profile = 'human';
expect(() => ContentIngestService.validate(wrongSite, 'invalid-site')).toThrow(ContentIngestError);
const notConfirmed = buildPayload();
notConfirmed.quality.human_confirmed = false;
expect(() => ContentIngestService.validate(notConfirmed, 'not-confirmed')).toThrow(ContentIngestError);
});
});