From 3545633f9e6b3c4cc2e5592d30a349855a99e903 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Thu, 27 Nov 2025 19:04:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=A8=A1=E6=9D=BF):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现模板的增删改查功能,包括模板实体、DTO、服务和控制器 添加模板渲染服务用于动态生成产品SKU 在product服务中集成模板服务用于SKU生成 添加模板数据填充器初始化默认模板 --- src/config/config.default.ts | 2 + src/controller/locale.controller.ts | 36 ++++++++ src/controller/template.controller.ts | 115 ++++++++++++++++++++++++++ src/db/seeds/template.seeder.ts | 36 ++++++++ src/dto/product.dto.ts | 25 +++++- src/dto/template.dto.ts | 22 +++++ src/entity/template.entity.ts | 43 ++++++++++ src/service/product.service.ts | 20 ++++- src/service/template.service.ts | 112 +++++++++++++++++++++++++ 9 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 src/controller/locale.controller.ts create mode 100644 src/controller/template.controller.ts create mode 100644 src/db/seeds/template.seeder.ts create mode 100644 src/dto/template.dto.ts create mode 100644 src/entity/template.entity.ts create mode 100644 src/service/template.service.ts diff --git a/src/config/config.default.ts b/src/config/config.default.ts index 2976b37..d15df9f 100644 --- a/src/config/config.default.ts +++ b/src/config/config.default.ts @@ -33,6 +33,7 @@ import { Subscription } from '../entity/subscription.entity'; import { Site } from '../entity/site.entity'; import { Dict } from '../entity/dict.entity'; import { DictItem } from '../entity/dict_item.entity'; +import { Template } from '../entity/template.entity'; import DictSeeder from '../db/seeds/dict.seeder'; export default { @@ -75,6 +76,7 @@ export default { Site, Dict, DictItem, + Template ], synchronize: true, logging: false, diff --git a/src/controller/locale.controller.ts b/src/controller/locale.controller.ts new file mode 100644 index 0000000..e1910ad --- /dev/null +++ b/src/controller/locale.controller.ts @@ -0,0 +1,36 @@ + +import { Controller, Get, Inject, Param } from '@midwayjs/core'; +import { DictService } from '../service/dict.service'; + +/** + * 国际化语言包 + */ +@Controller('/locales') +export class LocaleController { + @Inject() + dictService: DictService; + + /** + * 根据语言获取所有翻译项 + * @param lang 语言代码,例如 zh-CN, en-US + * @returns 返回一个包含所有翻译键值对的 JSON 对象 + */ + @Get('/:lang') + async getLocale(@Param('lang') lang: string) { + // 根据语言代码查找对应的字典 + const dict = await this.dictService.getDict({ name: lang }, ['items']); + + // 如果字典不存在,则返回空对象 + if (!dict) { + return {}; + } + + // 将字典项转换为 key-value 对象 + const locale = dict.items.reduce((acc, item) => { + acc[item.name] = item.title; + return acc; + }, {}); + + return locale; + } +} diff --git a/src/controller/template.controller.ts b/src/controller/template.controller.ts new file mode 100644 index 0000000..3590370 --- /dev/null +++ b/src/controller/template.controller.ts @@ -0,0 +1,115 @@ +import { Inject, Controller, Get, Post, Put, Del, Body, Param } from '@midwayjs/core'; +import { TemplateService } from '../service/template.service'; +import { successResponse, errorResponse } from '../utils/response.util'; +import { CreateTemplateDTO, UpdateTemplateDTO } from '../dto/template.dto'; +import { ApiOkResponse, ApiTags } from '@midwayjs/swagger'; +import { Template } from '../entity/template.entity'; +import { BooleanRes } from '../dto/reponse.dto'; + +/** + * @controller TemplateController 模板管理 + */ +@ApiTags('Template') +@Controller('/template') +export class TemplateController { + @Inject() + templateService: TemplateService; + + /** + * @summary 获取模板列表 + * @description 获取所有可用模板的列表 + */ + @ApiOkResponse({ type: [Template], description: '成功获取模板列表' }) + @Get('/') + async getTemplateList() { + try { + // 调用服务层获取列表 + const data = await this.templateService.getTemplateList(); + // 返回成功响应 + return successResponse(data); + } catch (error) { + // 返回错误响应 + return errorResponse(error.message); + } + } + + /** + * @summary 根据名称获取模板 + * @description 通过模板的唯一名称查找特定模板 + * @param name 模板名称 + */ + @ApiOkResponse({ type: Template, description: '成功获取模板' }) + @Get('/:name') + async getTemplateByName(@Param('name') name: string) { + try { + // 调用服务层获取单个模板 + const data = await this.templateService.getTemplateByName(name); + // 返回成功响应 + return successResponse(data); + } catch (error) { + // 返回错误响应 + return errorResponse(error.message); + } + } + + /** + * @summary 创建新模板 + * @description 创建一个新的模板,用于后续的字符串生成 + * @param templateData 模板数据 + */ + @ApiOkResponse({ type: Template, description: '成功创建模板' }) + @Post('/') + async createTemplate(@Body() templateData: CreateTemplateDTO) { + try { + // 调用服务层创建模板 + const data = await this.templateService.createTemplate(templateData); + // 返回成功响应 + return successResponse(data); + } catch (error) { + // 返回错误响应 + return errorResponse(error.message); + } + } + + /** + * @summary 更新现有模板 + * @description 根据模板 ID 更新一个现有模板的内容 + * @param id 模板 ID + * @param templateData 模板数据 + */ + @ApiOkResponse({ type: Template, description: '成功更新模板' }) + @Put('/:id') + async updateTemplate( + @Param('id') id: number, + @Body() templateData: UpdateTemplateDTO + ) { + try { + // 调用服务层更新模板 + const data = await this.templateService.updateTemplate(id, templateData); + // 返回成功响应 + return successResponse(data); + } catch (error) { + // 返回错误响应 + return errorResponse(error.message); + } + } + + /** + * @summary 删除模板 + * @description 根据模板 ID 删除一个模板 + * @param id 模板 ID + */ + @ApiOkResponse({ type: BooleanRes, description: '成功删除模板' }) + @Del('/:id') + async deleteTemplate(@Param('id') id: number) { + try { + // 调用服务层删除模板 + const data = await this.templateService.deleteTemplate(id); + // 返回成功响应 + return successResponse(data); + } catch (error) { + // 返回错误响应 + return errorResponse(error.message); + } + } +} diff --git a/src/db/seeds/template.seeder.ts b/src/db/seeds/template.seeder.ts new file mode 100644 index 0000000..f7ce0bc --- /dev/null +++ b/src/db/seeds/template.seeder.ts @@ -0,0 +1,36 @@ +import { Seeder, SeederFactoryManager } from 'typeorm-extension'; +import { DataSource } from 'typeorm'; +import { Template } from '../../entity/template.entity'; + +/** + * @class TemplateSeeder + * @description 模板数据填充器,用于在数据库初始化时插入默认的模板数据。 + */ +export default class TemplateSeeder implements Seeder { + /** + * @method run + * @description 执行数据填充操作。如果 product_sku 模板不存在,则创建它。 + * @param {DataSource} dataSource - 数据源实例,用于获取 repository。 + * @param {SeederFactoryManager} factoryManager - Seeder 工厂管理器。 + */ + public async run( + dataSource: DataSource, + factoryManager: SeederFactoryManager + ): Promise { + // 获取 Template 实体的 repository + const templateRepository = dataSource.getRepository(Template); + + // 检查名为 'product_sku' 的模板是否已存在 + const existingTemplate = await templateRepository.findOne({ + where: { name: 'product_sku' }, + }); + + // 如果模板不存在,则创建并保存 + if (!existingTemplate) { + const template = new Template(); + template.name = 'product_sku'; + template.value = '{{brand}}-{{flavor}}-{{strength}}-{{humidity}}'; + await templateRepository.save(template); + } + } +} diff --git a/src/dto/product.dto.ts b/src/dto/product.dto.ts index 6616a82..035e8d9 100644 --- a/src/dto/product.dto.ts +++ b/src/dto/product.dto.ts @@ -27,16 +27,35 @@ export class CreateProductDTO { @Rule(RuleType.string()) description: string; + @ApiProperty({ description: '产品 SKU', required: false }) + @Rule(RuleType.string()) + sku?: string; + @ApiProperty({ description: '品牌', type: DictItemDTO }) - @Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() })) + @Rule( + RuleType.object().keys({ + title: RuleType.string().required(), + name: RuleType.string(), + }) + ) brand: DictItemDTO; @ApiProperty({ description: '规格', type: DictItemDTO }) - @Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() })) + @Rule( + RuleType.object().keys({ + title: RuleType.string().required(), + name: RuleType.string(), + }) + ) strength: DictItemDTO; @ApiProperty({ description: '口味', type: DictItemDTO }) - @Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() })) + @Rule( + RuleType.object().keys({ + title: RuleType.string().required(), + name: RuleType.string(), + }) + ) flavor: DictItemDTO; @ApiProperty() diff --git a/src/dto/template.dto.ts b/src/dto/template.dto.ts new file mode 100644 index 0000000..e74296c --- /dev/null +++ b/src/dto/template.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@midwayjs/swagger'; +import { Rule, RuleType } from '@midwayjs/validate'; + +export class CreateTemplateDTO { + @ApiProperty({ description: '模板名称', required: true }) + @Rule(RuleType.string().required()) + name: string; + + @ApiProperty({ description: '模板内容', required: true }) + @Rule(RuleType.string().required()) + value: string; +} + +export class UpdateTemplateDTO { + @ApiProperty({ description: '模板名称', required: true }) + @Rule(RuleType.string().required()) + name: string; + + @ApiProperty({ description: '模板内容', required: true }) + @Rule(RuleType.string().required()) + value: string; +} diff --git a/src/entity/template.entity.ts b/src/entity/template.entity.ts new file mode 100644 index 0000000..b44ad6c --- /dev/null +++ b/src/entity/template.entity.ts @@ -0,0 +1,43 @@ +import { ApiProperty } from '@midwayjs/swagger'; +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; + +@Entity('template') +export class Template { + @ApiProperty({ type: 'number' }) + @PrimaryGeneratedColumn() + id: number; + + @ApiProperty({ type: 'string' }) + @Column({ unique: true }) + name: string; + + @ApiProperty({ type: 'string' }) + @Column('text') + value: string; + + @ApiProperty({ nullable: true ,name:"描述"}) + @Column('text',{nullable: true,comment: "描述"}) + description?: string; + + @ApiProperty({ + example: '2022-12-12 11:11:11', + description: '创建时间', + required: true, + }) + @CreateDateColumn() + createdAt: Date; + + @ApiProperty({ + example: '2022-12-12 11:11:11', + description: '更新时间', + required: true, + }) + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/src/service/product.service.ts b/src/service/product.service.ts index 0f8b72d..8bb4678 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -25,12 +25,16 @@ import { Variation } from '../entity/variation.entity'; import { Dict } from '../entity/dict.entity'; import { DictItem } from '../entity/dict_item.entity'; import { Context } from '@midwayjs/koa'; +import { TemplateService } from './template.service'; @Provide() export class ProductService { @Inject() ctx: Context; + @Inject() + templateService: TemplateService; + @InjectEntityModel(Product) productModel: Repository; @@ -209,7 +213,7 @@ export class ProductService { } async createProduct(createProductDTO: CreateProductDTO): Promise { - const { name, description, brand, flavor, strength, humidity } = + const { name, description, brand, flavor, strength, humidity, sku } = createProductDTO; // 获取或创建品牌、口味、规格 @@ -251,8 +255,18 @@ export class ProductService { product.description = description; product.attributes = attributesToMatch; - // 生成 SKU - product.sku = `${brandItem.name}-${flavorItem.name}-${strengthItem.name}-${humidityItem.name}`; + // 如果用户提供了 sku,则直接使用;否则,通过模板引擎生成 + if (sku) { + product.sku = sku; + } else { + // 生成 SKU + product.sku = await this.templateService.render('product_sku', { + brand: brandItem.name, + flavor: flavorItem.name, + strength: strengthItem.name, + humidity: humidityItem.name, + }); + } // 保存产品 return await this.productModel.save(product); diff --git a/src/service/template.service.ts b/src/service/template.service.ts new file mode 100644 index 0000000..45c512c --- /dev/null +++ b/src/service/template.service.ts @@ -0,0 +1,112 @@ +import { Provide } from '@midwayjs/core'; +import { InjectEntityModel } from '@midwayjs/typeorm'; +import { Repository } from 'typeorm'; +import { Template } from '../entity/template.entity'; +import { CreateTemplateDTO, UpdateTemplateDTO } from '../dto/template.dto'; + +/** + * @service TemplateService 模板服务 + */ +@Provide() +export class TemplateService { + // 注入 Template 实体模型 + @InjectEntityModel(Template) + templateModel: Repository