Skip to content

Commit d03a881

Browse files
committed
feat(meta-preset): add MetaPreset module with CRUD operations and built-in presets
- Introduced MetaPresetModule, MetaPresetController, and MetaPresetService to manage custom metadata presets. - Implemented DTOs for creating, updating, and querying presets, including validation. - Added built-in presets for AI participation, cover images, banners, SEO keywords, and article styles. - Updated app.module.ts and database models to integrate the new MetaPreset functionality. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 7285929 commit d03a881

File tree

7 files changed

+646
-0
lines changed

7 files changed

+646
-0
lines changed

apps/core/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { HelperModule as BizHelperModule } from './modules/helper/helper.module'
3939
import { InitModule } from './modules/init/init.module'
4040
import { LinkModule } from './modules/link/link.module'
4141
import { MarkdownModule } from './modules/markdown/markdown.module'
42+
import { MetaPresetModule } from './modules/meta-preset/meta-preset.module'
4243
import { NoteModule } from './modules/note/note.module'
4344
import { OptionModule } from './modules/option/option.module'
4445
import { PageModule } from './modules/page/page.module'
@@ -92,6 +93,7 @@ import { RedisModule } from './processors/redis/redis.module'
9293
HealthModule,
9394
LinkModule,
9495
MarkdownModule,
96+
MetaPresetModule,
9597
NoteModule,
9698
OptionModule,
9799
PageModule,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
Body,
3+
Delete,
4+
Get,
5+
Param,
6+
Patch,
7+
Post,
8+
Put,
9+
Query,
10+
} from '@nestjs/common'
11+
import { ApiController } from '~/common/decorators/api-controller.decorator'
12+
import { Auth } from '~/common/decorators/auth.decorator'
13+
import { MongoIdDto } from '~/shared/dto/id.dto'
14+
import {
15+
CreateMetaPresetDto,
16+
QueryMetaPresetDto,
17+
UpdateMetaPresetDto,
18+
UpdateOrderDto,
19+
} from './meta-preset.dto'
20+
import { MetaPresetService } from './meta-preset.service'
21+
22+
@ApiController({ path: 'meta-presets' })
23+
export class MetaPresetController {
24+
constructor(private readonly metaPresetService: MetaPresetService) {}
25+
26+
/**
27+
* 获取所有预设字段
28+
* 支持按 scope 过滤
29+
*/
30+
@Get('/')
31+
async getAll(@Query() query: QueryMetaPresetDto) {
32+
const { scope, enabledOnly } = query
33+
return this.metaPresetService.findAll(scope, enabledOnly)
34+
}
35+
36+
/**
37+
* 获取单个预设字段
38+
*/
39+
@Get('/:id')
40+
async getById(@Param() { id }: MongoIdDto) {
41+
return this.metaPresetService.findById(id)
42+
}
43+
44+
/**
45+
* 创建自定义预设字段
46+
*/
47+
@Post('/')
48+
@Auth()
49+
async create(@Body() dto: CreateMetaPresetDto) {
50+
return this.metaPresetService.create(dto)
51+
}
52+
53+
/**
54+
* 更新预设字段
55+
*/
56+
@Patch('/:id')
57+
@Auth()
58+
async update(@Param() { id }: MongoIdDto, @Body() dto: UpdateMetaPresetDto) {
59+
return this.metaPresetService.update(id, dto)
60+
}
61+
62+
/**
63+
* 删除预设字段
64+
*/
65+
@Delete('/:id')
66+
@Auth()
67+
async delete(@Param() { id }: MongoIdDto) {
68+
return this.metaPresetService.delete(id)
69+
}
70+
71+
/**
72+
* 批量更新排序
73+
*/
74+
@Put('/order')
75+
@Auth()
76+
async updateOrder(@Body() dto: UpdateOrderDto) {
77+
return this.metaPresetService.updateOrder(dto.ids)
78+
}
79+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { PartialType } from '@nestjs/mapped-types'
2+
import { Transform, Type } from 'class-transformer'
3+
import {
4+
IsArray,
5+
IsBoolean,
6+
IsEnum,
7+
IsMongoId,
8+
IsNotEmpty,
9+
IsNumber,
10+
IsOptional,
11+
IsString,
12+
ValidateNested,
13+
} from 'class-validator'
14+
import {
15+
MetaFieldOption,
16+
MetaFieldType,
17+
MetaPresetChild,
18+
MetaPresetScope,
19+
} from './meta-preset.model'
20+
21+
/**
22+
* 创建预设字段 DTO
23+
*/
24+
export class CreateMetaPresetDto {
25+
@IsString()
26+
@IsNotEmpty()
27+
key!: string
28+
29+
@IsString()
30+
@IsNotEmpty()
31+
label!: string
32+
33+
@IsEnum(MetaFieldType)
34+
type!: MetaFieldType
35+
36+
@IsOptional()
37+
@IsString()
38+
description?: string
39+
40+
@IsOptional()
41+
@IsString()
42+
placeholder?: string
43+
44+
@IsOptional()
45+
@IsEnum(MetaPresetScope)
46+
scope?: MetaPresetScope
47+
48+
@IsOptional()
49+
@IsArray()
50+
@ValidateNested({ each: true })
51+
@Type(() => MetaFieldOption)
52+
options?: MetaFieldOption[]
53+
54+
@IsOptional()
55+
@IsBoolean()
56+
allowCustomOption?: boolean
57+
58+
@IsOptional()
59+
@IsArray()
60+
@ValidateNested({ each: true })
61+
@Type(() => MetaPresetChild)
62+
children?: MetaPresetChild[]
63+
64+
@IsOptional()
65+
@IsNumber()
66+
order?: number
67+
68+
@IsOptional()
69+
@IsBoolean()
70+
enabled?: boolean
71+
}
72+
73+
/**
74+
* 更新预设字段 DTO
75+
*/
76+
export class UpdateMetaPresetDto extends PartialType(CreateMetaPresetDto) {}
77+
78+
/**
79+
* 查询预设字段 DTO
80+
*/
81+
export class QueryMetaPresetDto {
82+
@IsOptional()
83+
@IsEnum(MetaPresetScope)
84+
scope?: MetaPresetScope
85+
86+
@IsOptional()
87+
@Transform(({ value }) => value === 'true' || value === true)
88+
@IsBoolean()
89+
enabledOnly?: boolean
90+
}
91+
92+
/**
93+
* 批量更新排序 DTO
94+
*/
95+
export class UpdateOrderDto {
96+
@IsArray()
97+
@IsMongoId({ each: true })
98+
ids!: string[]
99+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { modelOptions, prop, Severity } from '@typegoose/typegoose'
2+
import { BaseModel } from '~/shared/model/base.model'
3+
import { Type } from 'class-transformer'
4+
import {
5+
IsArray,
6+
IsBoolean,
7+
IsEnum,
8+
IsNotEmpty,
9+
IsNumber,
10+
IsOptional,
11+
IsString,
12+
ValidateNested,
13+
} from 'class-validator'
14+
import { Schema } from 'mongoose'
15+
16+
/**
17+
* 元数据字段类型枚举
18+
*/
19+
export enum MetaFieldType {
20+
Text = 'text',
21+
Textarea = 'textarea',
22+
Number = 'number',
23+
Url = 'url',
24+
Select = 'select',
25+
MultiSelect = 'multi-select',
26+
Checkbox = 'checkbox',
27+
Tags = 'tags',
28+
Boolean = 'boolean',
29+
Object = 'object',
30+
}
31+
32+
/**
33+
* 适用范围枚举
34+
*/
35+
export enum MetaPresetScope {
36+
Post = 'post',
37+
Note = 'note',
38+
Both = 'both',
39+
}
40+
41+
/**
42+
* 字段选项(嵌入式)
43+
*/
44+
export class MetaFieldOption {
45+
@prop({ type: Schema.Types.Mixed, required: true })
46+
@IsNotEmpty()
47+
value!: any
48+
49+
@prop({ required: true })
50+
@IsString()
51+
@IsNotEmpty()
52+
label!: string
53+
54+
@prop({ default: false })
55+
@IsOptional()
56+
@IsBoolean()
57+
exclusive?: boolean
58+
}
59+
60+
/**
61+
* 子字段定义(用于 object 类型)
62+
*/
63+
export class MetaPresetChild {
64+
@prop({ required: true })
65+
@IsString()
66+
@IsNotEmpty()
67+
key!: string
68+
69+
@prop({ required: true })
70+
@IsString()
71+
@IsNotEmpty()
72+
label!: string
73+
74+
@prop({ required: true, enum: MetaFieldType })
75+
@IsEnum(MetaFieldType)
76+
type!: MetaFieldType
77+
78+
@prop()
79+
@IsOptional()
80+
@IsString()
81+
description?: string
82+
83+
@prop()
84+
@IsOptional()
85+
@IsString()
86+
placeholder?: string
87+
88+
@prop({ type: () => [MetaFieldOption], default: [] })
89+
@IsOptional()
90+
@IsArray()
91+
@ValidateNested({ each: true })
92+
@Type(() => MetaFieldOption)
93+
options?: MetaFieldOption[]
94+
}
95+
96+
/**
97+
* 元数据预设字段模型
98+
*/
99+
@modelOptions({
100+
options: { allowMixed: Severity.ALLOW, customName: 'MetaPreset' },
101+
schemaOptions: {
102+
timestamps: {
103+
createdAt: 'created',
104+
updatedAt: 'updated',
105+
},
106+
},
107+
})
108+
export class MetaPresetModel extends BaseModel {
109+
@prop({ required: true, unique: true })
110+
@IsString()
111+
@IsNotEmpty()
112+
key!: string
113+
114+
@prop({ required: true })
115+
@IsString()
116+
@IsNotEmpty()
117+
label!: string
118+
119+
@prop({ required: true, enum: MetaFieldType })
120+
@IsEnum(MetaFieldType)
121+
type!: MetaFieldType
122+
123+
@prop()
124+
@IsOptional()
125+
@IsString()
126+
description?: string
127+
128+
@prop()
129+
@IsOptional()
130+
@IsString()
131+
placeholder?: string
132+
133+
@prop({
134+
required: true,
135+
enum: MetaPresetScope,
136+
default: MetaPresetScope.Both,
137+
})
138+
@IsEnum(MetaPresetScope)
139+
scope!: MetaPresetScope
140+
141+
@prop({ type: () => [MetaFieldOption], default: [] })
142+
@IsOptional()
143+
@IsArray()
144+
@ValidateNested({ each: true })
145+
@Type(() => MetaFieldOption)
146+
options?: MetaFieldOption[]
147+
148+
@prop({ default: false })
149+
@IsOptional()
150+
@IsBoolean()
151+
allowCustomOption?: boolean
152+
153+
@prop({ type: () => [MetaPresetChild], default: [] })
154+
@IsOptional()
155+
@IsArray()
156+
@ValidateNested({ each: true })
157+
@Type(() => MetaPresetChild)
158+
children?: MetaPresetChild[]
159+
160+
@prop({ default: false })
161+
@IsBoolean()
162+
isBuiltin!: boolean
163+
164+
@prop({ default: 0 })
165+
@IsNumber()
166+
order!: number
167+
168+
@prop({ default: true })
169+
@IsBoolean()
170+
enabled!: boolean
171+
172+
updated?: Date
173+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Module } from '@nestjs/common'
2+
import { MetaPresetController } from './meta-preset.controller'
3+
import { MetaPresetService } from './meta-preset.service'
4+
5+
@Module({
6+
providers: [MetaPresetService],
7+
exports: [MetaPresetService],
8+
controllers: [MetaPresetController],
9+
})
10+
export class MetaPresetModule {}

0 commit comments

Comments
 (0)