feat(模板): 添加模板管理功能及相关服务
实现模板的增删改查功能,包括模板实体、DTO、服务和控制器 添加模板渲染服务用于动态生成产品SKU 在product服务中集成模板服务用于SKU生成 添加模板数据填充器初始化默认模板
This commit is contained in:
parent
0809840507
commit
3545633f9e
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<any> {
|
||||
// 获取 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<Product>;
|
||||
|
||||
|
|
@ -209,7 +213,7 @@ export class ProductService {
|
|||
}
|
||||
|
||||
async createProduct(createProductDTO: CreateProductDTO): Promise<Product> {
|
||||
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,则直接使用;否则,通过模板引擎生成
|
||||
if (sku) {
|
||||
product.sku = sku;
|
||||
} else {
|
||||
// 生成 SKU
|
||||
product.sku = `${brandItem.name}-${flavorItem.name}-${strengthItem.name}-${humidityItem.name}`;
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<Template>;
|
||||
|
||||
/**
|
||||
* 获取所有模板的列表
|
||||
* @returns {Promise<Template[]>} 模板实体数组
|
||||
*/
|
||||
async getTemplateList(): Promise<Template[]> {
|
||||
// 使用 find 方法查询所有模板
|
||||
return this.templateModel.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据唯一名称获取模板
|
||||
* @param {string} name - 模板名称
|
||||
* @returns {Promise<Template>} 模板实体
|
||||
*/
|
||||
async getTemplateByName(name: string): Promise<Template> {
|
||||
// 使用 findOne 方法按名称查询模板
|
||||
return this.templateModel.findOne({ where: { name } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新模板
|
||||
* @param {CreateTemplateDTO} templateData - 创建模板所需的数据
|
||||
* @returns {Promise<Template>} 创建后的模板实体
|
||||
*/
|
||||
async createTemplate(templateData: CreateTemplateDTO): Promise<Template> {
|
||||
// 创建一个新的模板实例
|
||||
const template = new Template();
|
||||
// 设置模板的名称和值
|
||||
template.name = templateData.name;
|
||||
template.value = templateData.value;
|
||||
// 保存新模板到数据库
|
||||
return this.templateModel.save(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 更新一个现有模板
|
||||
* @param {number} id - 模板 ID
|
||||
* @param {UpdateTemplateDTO} templateData - 更新模板所需的数据
|
||||
* @returns {Promise<Template>} 更新后的模板实体
|
||||
*/
|
||||
async updateTemplate(
|
||||
id: number,
|
||||
templateData: UpdateTemplateDTO
|
||||
): Promise<Template> {
|
||||
// 首先根据 ID 查找模板
|
||||
const template = await this.templateModel.findOneBy({ id });
|
||||
// 如果模板不存在,则抛出错误
|
||||
if (!template) {
|
||||
throw new Error(`模板 ID ${id} 不存在`);
|
||||
}
|
||||
// 更新模板的名称和值
|
||||
template.name = templateData.name;
|
||||
template.value = templateData.value;
|
||||
// 保存更新后的模板到数据库
|
||||
return this.templateModel.save(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 删除一个模板
|
||||
* @param {number} id - 模板 ID
|
||||
* @returns {Promise<boolean>} 如果删除成功则返回 true,否则返回 false
|
||||
*/
|
||||
async deleteTemplate(id: number): Promise<boolean> {
|
||||
// 执行删除操作
|
||||
const result = await this.templateModel.delete(id);
|
||||
// 如果影响的行数大于 0,则表示删除成功
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板名称和数据对象渲染最终字符串
|
||||
* @param {string} name - 模板名称
|
||||
* @param {Record<string, any>} data - 用于替换占位符的数据对象
|
||||
* @returns {Promise<string>} 渲染后的字符串
|
||||
*/
|
||||
async render(name: string, data: Record<string, any>): Promise<string> {
|
||||
// 根据名称获取模板
|
||||
const template = await this.getTemplateByName(name);
|
||||
// 如果模板不存在,则抛出错误
|
||||
if (!template) {
|
||||
throw new Error(`模板 '${name}' 不存在`);
|
||||
}
|
||||
|
||||
// 获取模板的原始内容
|
||||
let rendered = template.value;
|
||||
// 遍历数据对象,替换模板中的占位符
|
||||
for (const key in data) {
|
||||
// 创建一个正则表达式来匹配 {{key}}
|
||||
const regex = new RegExp(`{{${key}}}`, 'g');
|
||||
// 执行替换操作
|
||||
rendered = rendered.replace(regex, data[key]);
|
||||
}
|
||||
|
||||
// 返回渲染后的字符串
|
||||
return rendered;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue