mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: content manager
This commit is contained in:
4 files changed
+85
-14
No files matched your search
@@ -223,7 +223,8 @@ export interface ProblemConfig {
|
||||
hackable?: boolean;
|
||||
}
|
||||
|
||||
export type Content = string | Record<string, string>;
|
||||
export type LocalizedContent = ({ id: string, name: string, lang: string } | { from: string, name: string, lang: string })[];
|
||||
export type Content = string | LocalizedContent;
|
||||
|
||||
export interface Document {
|
||||
_id: ObjectId;
|
||||
@@ -241,7 +242,7 @@ declare module './model/problem' {
|
||||
docId: number;
|
||||
pid: string;
|
||||
title: string;
|
||||
content: ({ id: string, name: string, lang: string } | { from: string, name: string, lang: string })[];
|
||||
content: LocalizedContent;
|
||||
nSubmit: number;
|
||||
nAccept: number;
|
||||
tag: string[];
|
||||
@@ -678,6 +679,11 @@ export interface ContestBalloonDoc {
|
||||
sentAt?: Date;
|
||||
}
|
||||
|
||||
export interface ContentDoc {
|
||||
_id: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
declare module './service/db' {
|
||||
interface Collections {
|
||||
'blacklist': BlacklistDoc;
|
||||
@@ -689,6 +695,7 @@ declare module './service/db' {
|
||||
'document.status': StatusDocBase & {
|
||||
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
|
||||
}[keyof DocStatusType];
|
||||
'content': ContentDoc;
|
||||
'discussion.history': DiscussionHistoryDoc;
|
||||
'user': Udoc;
|
||||
'user.preference': UserPreferenceDoc;
|
||||
|
||||
@@ -19,6 +19,7 @@ type NormalArrayKeys<O, P = any> = Exclude<ArrayKeys<O, P>, Symbol>;
|
||||
|
||||
export const coll = db.collection('document');
|
||||
export const collStatus = db.collection('document.status');
|
||||
export const collContent = db.collection('content');
|
||||
|
||||
export const TYPE_PROBLEM: 10 = 10;
|
||||
export const TYPE_PROBLEM_SOLUTION: 11 = 11;
|
||||
@@ -51,13 +52,13 @@ export interface DocStatusType {
|
||||
}
|
||||
|
||||
export async function add<T extends keyof DocType, K extends DocType[T]['docId']>(
|
||||
domainId: string, content: Content, owner: number,
|
||||
domainId: string, content: DocType[T]['content'], owner: number,
|
||||
docType: T, docId: K,
|
||||
parentType?: DocType[T]['parentType'], parentId?: DocType[T]['parentId'],
|
||||
args?: Partial<DocType[T]>,
|
||||
): Promise<K>;
|
||||
export async function add<T extends keyof DocType>(
|
||||
domainId: string, content: Content, owner: number,
|
||||
domainId: string, content: DocType[T]['content'], owner: number,
|
||||
docType: T, docId: null,
|
||||
parentType?: DocType[T]['parentType'], parentId?: DocType[T]['parentId'],
|
||||
args?: Partial<DocType[T]>,
|
||||
@@ -425,6 +426,29 @@ export async function revSetStatus<T extends keyof DocStatusType>(
|
||||
return res.value;
|
||||
}
|
||||
|
||||
export async function getContent(contentId: string) {
|
||||
return await collContent.findOne({ _id: contentId });
|
||||
}
|
||||
|
||||
export async function getContents(contentIds: string[]) {
|
||||
return await collContent.find({ _id: { $in: contentIds } }).toArray();
|
||||
}
|
||||
|
||||
export async function addContent(content: string) {
|
||||
const id = String.random(32);
|
||||
await collContent.insertOne({ _id: id, content });
|
||||
return id;
|
||||
}
|
||||
|
||||
export async function setContent(contentId: string, content: string) {
|
||||
const res = await collContent.updateOne({ _id: contentId }, { $set: { content } });
|
||||
return !!res.matchedCount;
|
||||
}
|
||||
|
||||
export async function deleteContent(contentId: string) {
|
||||
return await collContent.deleteOne({ _id: contentId });
|
||||
}
|
||||
|
||||
export async function apply(ctx: Context) {
|
||||
ctx.on('domain/delete', (domainId) => Promise.all([
|
||||
coll.deleteMany({ domainId }),
|
||||
@@ -463,6 +487,7 @@ export async function apply(ctx: Context) {
|
||||
|
||||
global.Hydro.model.document = {
|
||||
coll,
|
||||
collContent,
|
||||
collStatus,
|
||||
|
||||
add,
|
||||
@@ -494,6 +519,11 @@ global.Hydro.model.document = {
|
||||
setStatus,
|
||||
setMultiStatus,
|
||||
setSub,
|
||||
getContent,
|
||||
getContents,
|
||||
addContent,
|
||||
setContent,
|
||||
deleteContent,
|
||||
|
||||
TYPE_CONTEST,
|
||||
TYPE_CONTEST_CLARIFICATION,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Logger, size, streamToBuffer } from '@hydrooj/utils/lib/utils';
|
||||
import { Context } from '../context';
|
||||
import { FileUploadError, ProblemNotFoundError, ValidationError } from '../error';
|
||||
import type {
|
||||
Document, ProblemConfigFile, ProblemDict, ProblemStatusDoc, User,
|
||||
Document, LocalizedContent, ProblemConfigFile, ProblemDict, ProblemStatusDoc, User,
|
||||
} from '../interface';
|
||||
import { parseConfig } from '../lib/testdataConfig';
|
||||
import * as bus from '../service/bus';
|
||||
@@ -90,8 +90,6 @@ export class ProblemModel {
|
||||
'reference', 'maintainer',
|
||||
];
|
||||
|
||||
static collContent = db.collection('problem.content');
|
||||
|
||||
static default = {
|
||||
_id: new ObjectId(),
|
||||
domainId: 'system',
|
||||
@@ -135,15 +133,29 @@ export class ProblemModel {
|
||||
};
|
||||
|
||||
static async add(
|
||||
domainId: string, pid: string = '', title: string, content: ProblemDoc['content'], owner: number,
|
||||
tag: string[] = [], meta: ProblemCreateOptions = {},
|
||||
domainId: string, pid: string = '', title: string,
|
||||
content: { content?: string, from?: string, lang: string, name: string }[] | string,
|
||||
owner: number, tag: string[] = [], meta: ProblemCreateOptions = {},
|
||||
) {
|
||||
let c: LocalizedContent;
|
||||
if (typeof content === 'string') {
|
||||
c.push({ id: await document.addContent(content), name: 'default', lang: 'en' });
|
||||
} else {
|
||||
for (const entry of content) {
|
||||
if (entry.from) {
|
||||
c.push(entry as any);
|
||||
} else {
|
||||
const id = await document.addContent(entry.content);
|
||||
c.push({ id, name: entry.name, lang: entry.lang });
|
||||
}
|
||||
}
|
||||
}
|
||||
const [doc] = await ProblemModel.getMulti(domainId, {})
|
||||
.sort({ docId: -1 }).limit(1).project({ docId: 1 })
|
||||
.toArray();
|
||||
const result = await ProblemModel.addWithId(
|
||||
domainId, (doc?.docId || 0) + 1, pid,
|
||||
title, content, owner, tag, meta,
|
||||
title, c, owner, tag, meta,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
@@ -191,7 +203,7 @@ export class ProblemModel {
|
||||
}
|
||||
|
||||
static async getContent(contentIds: string[]) {
|
||||
const content = await ProblemModel.collContent.find({ _id: { $in: contentIds } }).toArray();
|
||||
const content = await document.getContents(contentIds);
|
||||
return Object.fromEntries(content.map((i) => [i._id.toString(), i.content]));
|
||||
}
|
||||
|
||||
@@ -219,7 +231,10 @@ export class ProblemModel {
|
||||
return document.getMultiStatus(domainId, document.TYPE_PROBLEM, query);
|
||||
}
|
||||
|
||||
static async edit(domainId: string, _id: number, $set: Partial<ProblemDoc>): Promise<ProblemDoc> {
|
||||
static async edit(
|
||||
domainId: string, _id: number,
|
||||
$set: (Partial<ProblemDoc> & ({ content?: { content: string }[] })),
|
||||
): Promise<ProblemDoc> {
|
||||
const delpid = $set.pid === '';
|
||||
const ddoc = await DomainModel.get(domainId);
|
||||
if (delpid) {
|
||||
@@ -229,6 +244,23 @@ export class ProblemModel {
|
||||
$set.sort = sortable($set.pid, ddoc.namespaces);
|
||||
}
|
||||
await bus.parallel('problem/before-edit', $set);
|
||||
if ($set.content) {
|
||||
const update = $set.content instanceof Array ? $set.content : [{ content: $set.content, lang: 'en', name: 'default' }];
|
||||
const existing = await document.get(domainId, document.TYPE_PROBLEM, _id);
|
||||
if (!existing) throw new ProblemNotFoundError(domainId, _id);
|
||||
const contents = existing.content;
|
||||
const final = [];
|
||||
for (const entry of update) {
|
||||
if ('id' in entry && contents.find((i) => i.id === entry.id)) {
|
||||
await document.setContent(entry.id, entry.content);
|
||||
final.push({ id: entry.id, name: entry.name, lang: entry.lang });
|
||||
} else {
|
||||
const cid = await document.addContent(entry.content);
|
||||
final.push({ id: cid, name: entry.name, lang: entry.lang });
|
||||
}
|
||||
}
|
||||
$set.content = final;
|
||||
}
|
||||
const result = await document.set(domainId, document.TYPE_PROBLEM, _id, $set, delpid ? { pid: '' } : undefined);
|
||||
await bus.emit('problem/edit', result);
|
||||
return result;
|
||||
@@ -542,7 +574,9 @@ export class ProblemModel {
|
||||
const docId = overridePid
|
||||
? (await ProblemModel.edit(domainId, overridePid, {
|
||||
title: pdoc.title.trim(),
|
||||
content: overrideContent || pdoc.content.toString() || 'No content',
|
||||
content: [{
|
||||
id: '', name: 'default', content: overrideContent || pdoc.content.toString() || 'No content', lang: 'en',
|
||||
}],
|
||||
tag,
|
||||
difficulty: pdoc.difficulty,
|
||||
})).docId
|
||||
|
||||
@@ -68,7 +68,7 @@ export interface EventMap extends KnownHandlerEvents {
|
||||
'discussion/before-add': (payload: Partial<DiscussionDoc>) => VoidReturn
|
||||
'discussion/add': (payload: Partial<DiscussionDoc>) => VoidReturn
|
||||
|
||||
'problem/before-add': (domainId: string, content: string, owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn
|
||||
'problem/before-add': (domainId: string, content: ProblemDoc['content'], owner: number, docId: number, doc: Partial<ProblemDoc>) => VoidReturn
|
||||
'problem/add': (doc: Partial<ProblemDoc>, docId: number) => VoidReturn
|
||||
'problem/before-edit': (doc: Partial<ProblemDoc>) => VoidReturn
|
||||
'problem/edit': (doc: ProblemDoc) => VoidReturn
|
||||
|
||||
Reference in new issue
Block a user