diff --git a/src/constants/error-code.constant.ts b/src/constants/error-code.constant.ts index cb820259..87e8ba50 100644 --- a/src/constants/error-code.constant.ts +++ b/src/constants/error-code.constant.ts @@ -2,6 +2,7 @@ export enum ErrorCodeEnum { NoContentCanBeModified = 1000, SlugNotAvailable = 10000, + MaxCountLimit = 10001, CommentDisabled = 30000, CommentTooDeep = 30001, ServerlessError = 80000, @@ -13,6 +14,7 @@ export enum ErrorCodeEnum { export const ErrorCode = Object.freeze>( { [ErrorCodeEnum.SlugNotAvailable]: ['slug 不可用', 400], + [ErrorCodeEnum.MaxCountLimit]: ['已达到最大数量限制', 400], [ErrorCodeEnum.BanInDemo]: ['Demo 模式下此操作不可用', 400], [ErrorCodeEnum.MasterLost]: ['站点主人信息已丢失', 500], [ErrorCodeEnum.CommentDisabled]: ['全站评论已关闭', 403], diff --git a/src/modules/page/page.controller.ts b/src/modules/page/page.controller.ts index 91589f3b..943dd50b 100644 --- a/src/modules/page/page.controller.ts +++ b/src/modules/page/page.controller.ts @@ -19,6 +19,7 @@ import { TextMacroService } from '~/processors/helper/helper.macro.service' import { MongoIdDto } from '~/shared/dto/id.dto' import { PagerDto } from '~/shared/dto/pager.dto' +import { PageReorderDto } from './page.dto' import { PageModel, PartialPageModel } from './page.model' import { PageService } from './page.service' @@ -41,9 +42,7 @@ export class PageController { limit: size, page, select, - sort: sortBy - ? { [sortBy]: sortOrder || -1 } - : { order: -1, modified: -1 }, + sort: sortBy ? { [sortBy]: sortOrder || -1 } : { order: -1 }, }, ) } @@ -105,6 +104,28 @@ export class PageController { return } + @Patch('/reorder') + @Auth() + async reorder(@Body() body: PageReorderDto) { + const { seq } = body + const orders = seq.map(($) => $.order) + const uniq = new Set(orders) + if (uniq.size !== orders.length) { + throw new UnprocessableEntityException('order must be unique') + } + const tasks = seq.map(({ id, order }) => { + return this.pageService.model.updateOne( + { + _id: id, + }, + { + order, + }, + ) + }) + await Promise.all(tasks) + } + @Delete('/:id') @Auth() async deletePage(@Param() params: MongoIdDto) { diff --git a/src/modules/page/page.dto.ts b/src/modules/page/page.dto.ts new file mode 100644 index 00000000..7519410c --- /dev/null +++ b/src/modules/page/page.dto.ts @@ -0,0 +1,15 @@ +import { Type } from 'class-transformer' +import { IsInt, IsMongoId, Min, ValidateNested } from 'class-validator' + +class Seq { + @IsMongoId() + id: string + @IsInt() + @Min(1) + order: number +} +export class PageReorderDto { + @Type(() => Seq) + @ValidateNested() + seq: Seq[] +} diff --git a/src/modules/page/page.service.ts b/src/modules/page/page.service.ts index 0606edbf..965d2e67 100644 --- a/src/modules/page/page.service.ts +++ b/src/modules/page/page.service.ts @@ -4,8 +4,10 @@ import slugify from 'slugify' import { Injectable } from '@nestjs/common' +import { BizException } from '~/common/exceptions/biz.exception' import { NoContentCanBeModifiedException } from '~/common/exceptions/no-content-canbe-modified.exception' import { BusinessEvents, EventScope } from '~/constants/business-event.constant' +import { ErrorCodeEnum } from '~/constants/error-code.constant' import { EventManagerService } from '~/processors/helper/helper.event.service' import { ImageService } from '~/processors/helper/helper.image.service' import { TextMacroService } from '~/processors/helper/helper.macro.service' @@ -29,6 +31,14 @@ export class PageService { } public async create(doc: PageModel) { + const count = await this.model.countDocuments({}) + if (count >= 10) { + throw new BizException(ErrorCodeEnum.MaxCountLimit) + } + // `0` or `undefined` or `null` + if (!doc.order) { + doc.order = count + 1 + } const res = await this.model.create({ ...doc, slug: slugify(doc.slug),