feat(api): 支持外部客户端创建作品和更新版本

This commit is contained in:
yuzhe
2026-07-21 18:28:21 +08:00
parent b42f46c182
commit e4d1d3bcea
13 changed files with 418 additions and 56 deletions

View File

@@ -10,7 +10,7 @@ export const imagesRepository = {
WHERE note_id = ?${versionNumber ? ' AND version_number = ?' : ''} ORDER BY order_index ASC, id ASC`, versionNumber ? [noteId, versionNumber] : [noteId]);
return rows.map(toImage);
},
async createMany(noteId: number, images: { url: string; width: number; height: number; storageProvider: 'local' | 'tencent_cos'; storageKey: string }[], versionNumber = 1, existing?: QueryContext): Promise<number[]> {
async createMany(noteId: number, images: { url: string; width: number; height: number; storageProvider: 'local' | 'tencent_cos' | 'external'; storageKey: string }[], versionNumber = 1, existing?: QueryContext): Promise<number[]> {
const insert = async (tx: QueryContext) => {
const ids: number[] = [];
for (let index = 0; index < images.length; index += 1) {

View File

@@ -2,7 +2,7 @@ import { database } from '../database.js';
import type { Note, NoteListQuery, ReviewStatus } from '../../shared/types.js';
interface NoteRow {
id: number; collection_id: number; title: string; description: string; tags: string;
id: number; collection_id: number; external_id: string | null; title: string; description: string; tags: string;
review_status: ReviewStatus; version_number: number; created_at: string;
image_count: number | string; annotation_count: number | string; comment_count: number | string; cover_url: string | null;
}
@@ -18,17 +18,22 @@ function toNote(row: NoteRow): Note {
}
const select = `
SELECT n.id, n.collection_id, n.title, n.description, n.tags, n.review_status, n.version_number, n.created_at,
(SELECT COUNT(*) FROM images i WHERE i.note_id = n.id AND i.version_number = n.version_number) AS image_count,
(SELECT COUNT(*) FROM annotations a JOIN images i ON i.id = a.image_id WHERE i.note_id = n.id AND i.version_number = n.version_number) AS annotation_count,
(SELECT COUNT(*) FROM work_comments wc WHERE wc.note_id = n.id) AS comment_count,
(SELECT url FROM images WHERE note_id = n.id AND version_number = n.version_number ORDER BY order_index, id LIMIT 1) AS cover_url
FROM notes n`;
SELECT n.id, n.collection_id, n.external_id, n.title, n.description, n.tags, n.review_status, n.version_number, n.created_at,
COALESCE(ic.image_count, 0) AS image_count,
COALESCE(ac.annotation_count, 0) AS annotation_count,
COALESCE(cc.comment_count, 0) AS comment_count,
cover.url AS cover_url
FROM notes n
LEFT JOIN (SELECT note_id, version_number, COUNT(*) AS image_count FROM images GROUP BY note_id, version_number) ic ON ic.note_id = n.id AND ic.version_number = n.version_number
LEFT JOIN (SELECT i.note_id, i.version_number, COUNT(a.id) AS annotation_count FROM images i LEFT JOIN annotations a ON a.image_id = i.id GROUP BY i.note_id, i.version_number) ac ON ac.note_id = n.id AND ac.version_number = n.version_number
LEFT JOIN (SELECT note_id, COUNT(*) AS comment_count FROM work_comments GROUP BY note_id) cc ON cc.note_id = n.id
LEFT JOIN images cover ON cover.note_id = n.id AND cover.version_number = n.version_number AND cover.order_index = 0`;
export const notesRepository = {
async list(query: NoteListQuery = {}): Promise<Note[]> {
const conditions: string[] = []; const params: unknown[] = [];
if (query.collectionId) { conditions.push('n.collection_id = ?'); params.push(query.collectionId); }
if (query.externalId?.trim()) { conditions.push('n.external_id = ?'); params.push(query.externalId.trim()); }
if (query.status) { conditions.push('n.review_status = ?'); params.push(query.status); }
if (query.q?.trim()) { conditions.push('(n.title LIKE ? OR n.description LIKE ?)'); params.push(`%${query.q.trim()}%`, `%${query.q.trim()}%`); }
if (query.tag?.trim()) { conditions.push('n.tags LIKE ?'); params.push(`%"${query.tag.trim()}"%`); }
@@ -43,6 +48,10 @@ export const notesRepository = {
const row = await database.one<NoteRow>(`${select} WHERE n.id = ?`, [id]);
return row ? toNote(row) : null;
},
async findByExternalId(collectionId: number, externalId: string): Promise<Note | null> {
const row = await database.one<NoteRow>(`${select} WHERE n.collection_id = ? AND n.external_id = ?`, [collectionId, externalId]);
return row ? toNote(row) : null;
},
async create(title: string, description: string, collectionId: number, tags: string[]): Promise<number> {
return database.insertId('INSERT INTO notes (title, description, collection_id, tags, review_status) VALUES (?, ?, ?, ?, ?)', [title, description, collectionId, JSON.stringify(tags), 'pending']);
},