Files

101 lines
3.1 KiB
TypeScript

import { BadRequestException } from '@nestjs/common';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Allow } from 'class-validator';
import type {
ContentStatus,
UpdateContentStatusRequest,
} from '@doodle/contracts';
import {
WorksheetQueryDto,
parseWorksheetQuery,
} from '../worksheets/worksheet-query.dto';
import { contentStatus } from './content-policy';
/** 管理列表才接受 status;公开接口不复用此 DTO,避免开放草稿查询开关。 */
export class AdminWorksheetQueryDto extends WorksheetQueryDto {
@Allow()
@ApiPropertyOptional({
enum: ['draft', 'active', 'hidden'],
description: '省略查询全部状态',
})
declare status?: string;
}
/** 首期只提供状态写入,不开放未经白名单校验的渲染配置编辑。 */
export class UpdateContentStatusDto implements UpdateContentStatusRequest {
@Allow()
@ApiProperty({ enum: ['draft', 'active', 'hidden'] })
declare status: ContentStatus;
}
/** 资源管理列表仅支持分页、所属题型和状态,不隐式套用题型年龄/年级条件。 */
export class AdminResourceQueryDto {
@Allow()
@ApiPropertyOptional({
type: 'integer',
default: 1,
minimum: 1,
maximum: 1000000,
})
declare page?: string;
@Allow()
@ApiPropertyOptional({
type: 'integer',
default: 20,
minimum: 1,
maximum: 100,
})
declare pageSize?: string;
@Allow()
@ApiPropertyOptional({ maxLength: 128 })
declare worksheetId?: string;
@Allow()
@ApiPropertyOptional({ enum: ['draft', 'active', 'hidden'] })
declare status?: string;
}
/** 服务层同样检查字段白名单,防止非 HTTP 调用绕过状态参数限制。 */
export function parseAdminWorksheetQuery(input: AdminWorksheetQueryDto) {
const { status, ...query } = input;
return {
query: parseWorksheetQuery(query),
status: status === undefined ? undefined : contentStatus(status),
};
}
/** 严格解析资源分页和过滤;拒绝重复/未知字段,避免管理查询静默扩大范围。 */
export function parseAdminResourceQuery(input: AdminResourceQueryDto) {
if (
Object.keys(input).some(
(key) =>
!['page', 'pageSize', 'worksheetId', 'status'].includes(key),
)
)
throw new BadRequestException();
const { page, pageSize } = parseWorksheetQuery({
...(input.page === undefined ? {} : { page: input.page }),
...(input.pageSize === undefined ? {} : { pageSize: input.pageSize }),
});
const worksheetId = input.worksheetId;
if (
worksheetId !== undefined &&
(typeof worksheetId !== 'string' ||
!worksheetId.trim() ||
[...worksheetId].length > 128 ||
worksheetId.includes('\0'))
)
throw new BadRequestException();
return {
page,
pageSize,
worksheetId,
status:
input.status === undefined
? undefined
: contentStatus(input.status),
};
}