feat(dict): 改进字典项名称格式化为kebab-case并更新唯一约束
重构字典服务以统一格式化名称,将字典项名称中的空格、下划线和点转换为中划线并转为小写 更新字典项实体,移除name字段的唯一约束,改为组合索引(name, dict) 添加根据字典名称获取字典项的API端点 更新数据迁移和种子数据以匹配新的名称格式
This commit is contained in:
parent
e4fc195b8d
commit
10b42eca7a
|
|
@ -131,9 +131,13 @@ export class DictController {
|
|||
* @param dictId 字典ID (可选)
|
||||
*/
|
||||
@Get('/items')
|
||||
async getDictItems(@Query('dictId') dictId?: number) {
|
||||
async getDictItems(
|
||||
@Query('dictId') dictId?: number,
|
||||
@Query('name') name?: string,
|
||||
@Query('title') title?: string,
|
||||
) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.getDictItems(dictId);
|
||||
return this.dictService.getDictItems({ dictId, name, title });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,4 +172,14 @@ export class DictController {
|
|||
// 调用服务层方法
|
||||
return this.dictService.deleteDictItem(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典名称获取字典项列表
|
||||
* @param name 字典名称
|
||||
*/
|
||||
@Get('/items-by-name')
|
||||
async getDictItemsByDictName(@Query('name') name: string) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.getDictItemsByDictName(name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class UpdateDictItemUniqueConstraint1764569947170 implements MigrationInterface {
|
||||
name = 'UpdateDictItemUniqueConstraint1764569947170'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`productId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`isPackage\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`externalOrderId\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`externalProductId\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`externalVariationId\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`subtotal\` decimal(10,2) NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`subtotal_tax\` decimal(10,2) NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`total\` decimal(10,2) NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`total_tax\` decimal(10,2) NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`price\` decimal(10,2) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`productId\` int NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`isPackage\` tinyint NOT NULL DEFAULT 0`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`siteId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`siteId\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` CHANGE \`sku\` \`sku\` varchar(255) NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`siteId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`siteId\` int NULL`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`siteId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`siteId\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` CHANGE \`sku\` \`sku\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`siteId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`siteId\` int NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`isPackage\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`productId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`price\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`total_tax\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`total\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`subtotal_tax\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`subtotal\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`externalVariationId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`externalProductId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` DROP COLUMN \`externalOrderId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`isPackage\` tinyint NOT NULL DEFAULT '0'`);
|
||||
await queryRunner.query(`ALTER TABLE \`order_item_original\` ADD \`productId\` int NOT NULL`);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,103 +1,114 @@
|
|||
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
|
||||
import { Seeder } from 'typeorm-extension';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { Dict } from '../../entity/dict.entity';
|
||||
import { DictItem } from '../../entity/dict_item.entity';
|
||||
|
||||
export default class DictSeeder implements Seeder {
|
||||
/**
|
||||
* 格式化名称为 kebab-case
|
||||
* @param name 需要格式化的名称
|
||||
* @returns 格式化后的名称
|
||||
*/
|
||||
private formatName(name: string): string {
|
||||
// 将空格、下划线、点统一转换成中划线,并转为小写
|
||||
return String(name).replace(/[_\s.]+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
public async run(
|
||||
dataSource: DataSource,
|
||||
factoryManager: SeederFactoryManager
|
||||
): Promise<any> {
|
||||
const dictRepository = dataSource.getRepository(Dict);
|
||||
const dictItemRepository = dataSource.getRepository(DictItem);
|
||||
|
||||
const flavorsData = [
|
||||
{ id: 1, title: 'Bellini Mini', name: 'Bellini Mini' },
|
||||
{ id: 2, title: 'Max Polarmint', name: 'Max Polarmint' },
|
||||
{ id: 3, title: 'Blueberry', name: 'Blueberry' },
|
||||
{ id: 4, title: 'Citrus', name: 'Citrus' },
|
||||
{ id: 5, title: 'Wintergreen', name: 'Wintergreen' },
|
||||
{ id: 6, title: 'COOL MINT', name: 'COOL MINT' },
|
||||
{ id: 7, title: 'JUICY PEACH', name: 'JUICY PEACH' },
|
||||
{ id: 8, title: 'ORANGE', name: 'ORANGE' },
|
||||
{ id: 9, title: 'PEPPERMINT', name: 'PEPPERMINT' },
|
||||
{ id: 10, title: 'SPEARMINT', name: 'SPEARMINT' },
|
||||
{ id: 11, title: 'STRAWBERRY', name: 'STRAWBERRY' },
|
||||
{ id: 12, title: 'WATERMELON', name: 'WATERMELON' },
|
||||
{ id: 13, title: 'COFFEE', name: 'COFFEE' },
|
||||
{ id: 14, title: 'LEMONADE', name: 'LEMONADE' },
|
||||
{ id: 15, title: 'apple mint', name: 'apple mint' },
|
||||
{ id: 16, title: 'PEACH', name: 'PEACH' },
|
||||
{ id: 17, title: 'Mango', name: 'Mango' },
|
||||
{ id: 18, title: 'ICE WINTERGREEN', name: 'ICE WINTERGREEN' },
|
||||
{ id: 19, title: 'Pink Lemonade', name: 'Pink Lemonade' },
|
||||
{ id: 20, title: 'Blackcherry', name: 'Blackcherry' },
|
||||
{ id: 21, title: 'fresh mint', name: 'fresh mint' },
|
||||
{ id: 22, title: 'Strawberry Lychee', name: 'Strawberry Lychee' },
|
||||
{ id: 23, title: 'Passion Fruit', name: 'Passion Fruit' },
|
||||
{ id: 24, title: 'Banana lce', name: 'Banana lce' },
|
||||
{ id: 25, title: 'Bubblegum', name: 'Bubblegum' },
|
||||
{ id: 26, title: 'Mango lce', name: 'Mango lce' },
|
||||
{ id: 27, title: 'Grape lce', name: 'Grape lce' },
|
||||
{ title: 'Bellini', name: 'bellini' },
|
||||
{ title: 'Max Polarmint', name: 'max-polarmint' },
|
||||
{ title: 'Blueberry', name: 'blueberry' },
|
||||
{ title: 'Citrus', name: 'citrus' },
|
||||
{ title: 'Wintergreen', name: 'wintergreen' },
|
||||
{ title: 'COOL MINT', name: 'cool-mint' },
|
||||
{ title: 'JUICY PEACH', name: 'juicy-peach' },
|
||||
{ title: 'ORANGE', name: 'orange' },
|
||||
{ title: 'PEPPERMINT', name: 'peppermint' },
|
||||
{ title: 'SPEARMINT', name: 'spearmint' },
|
||||
{ title: 'STRAWBERRY', name: 'strawberry' },
|
||||
{ title: 'WATERMELON', name: 'watermelon' },
|
||||
{ title: 'COFFEE', name: 'coffee' },
|
||||
{ title: 'LEMONADE', name: 'lemonade' },
|
||||
{ title: 'apple mint', name: 'apple-mint' },
|
||||
{ title: 'PEACH', name: 'peach' },
|
||||
{ title: 'Mango', name: 'mango' },
|
||||
{ title: 'ICE WINTERGREEN', name: 'ice-wintergreen' },
|
||||
{ title: 'Pink Lemonade', name: 'pink-lemonade' },
|
||||
{ title: 'Blackcherry', name: 'blackcherry' },
|
||||
{ title: 'fresh mint', name: 'fresh-mint' },
|
||||
{ title: 'Strawberry Lychee', name: 'strawberry-lychee' },
|
||||
{ title: 'Passion Fruit', name: 'passion-fruit' },
|
||||
{ title: 'Banana lce', name: 'banana-lce' },
|
||||
{ title: 'Bubblegum', name: 'bubblegum' },
|
||||
{ title: 'Mango lce', name: 'mango-lce' },
|
||||
{ title: 'Grape lce', name: 'grape-lce' },
|
||||
{ title: 'apple', name: 'apple' },
|
||||
{ title: 'grape', name: 'grape' },
|
||||
{ title: 'cherry', name: 'cherry' },
|
||||
{ title: 'lemon', name: 'lemon' },
|
||||
{ title: 'razz', name: 'razz' },
|
||||
{ title: 'pineapple', name: 'pineapple' },
|
||||
{ title: 'berry', name: 'berry' },
|
||||
{ title: 'fruit', name: 'fruit' },
|
||||
{ title: 'mint', name: 'mint' },
|
||||
{ title: 'menthol', name: 'menthol' },
|
||||
];
|
||||
|
||||
const brandsData = [
|
||||
{ id: 1, title: 'Yoone', name: 'YOONE' },
|
||||
{ id: 2, title: 'White Fox', name: 'WHITE_FOX' },
|
||||
{ id: 3, title: 'ZYN', name: 'ZYN' },
|
||||
{ id: 4, title: 'Zonnic', name: 'ZONNIC' },
|
||||
{ id: 5, title: 'Zolt', name: 'ZOLT' },
|
||||
{ id: 6, title: 'Velo', name: 'VELO' },
|
||||
{ id: 7, title: 'Lucy', name: 'LUCY' },
|
||||
{ id: 8, title: 'EGP', name: 'EGP' },
|
||||
{ id: 9, title: 'Bridge', name: 'BRIDGE' },
|
||||
{ id: 10, title: 'ZEX', name: 'ZEX' },
|
||||
{ id: 11, title: 'Sesh', name: 'Sesh' },
|
||||
{ id: 12, title: 'Pablo', name: 'Pablo' },
|
||||
{ title: 'Yoone', name: 'yoone' },
|
||||
{ title: 'White Fox', name: 'white-fox' },
|
||||
{ title: 'ZYN', name: 'zyn' },
|
||||
{ title: 'Zonnic', name: 'zonnic' },
|
||||
{ title: 'Zolt', name: 'zolt' },
|
||||
{ title: 'Velo', name: 'velo' },
|
||||
{ title: 'Lucy', name: 'lucy' },
|
||||
{ title: 'EGP', name: 'egp' },
|
||||
{ title: 'Bridge', name: 'bridge' },
|
||||
{ title: 'ZEX', name: 'zex' },
|
||||
{ title: 'Sesh', name: 'sesh' },
|
||||
{ title: 'Pablo', name: 'pablo' },
|
||||
];
|
||||
|
||||
const strengthsData = [
|
||||
{ id: 1, title: '3MG', name: '3MG' },
|
||||
{ id: 2, title: '9MG', name: '9MG' },
|
||||
{ id: 3, title: '2MG', name: '2MG' },
|
||||
{ id: 4, title: '4MG', name: '4MG' },
|
||||
{ id: 5, title: '12MG', name: '12MG' },
|
||||
{ id: 6, title: '18MG', name: '18MG' },
|
||||
{ id: 7, title: '6MG', name: '6MG' },
|
||||
{ id: 8, title: '16.5MG', name: '16.5MG' },
|
||||
{ id: 9, title: '6.5MG', name: '6.5MG' },
|
||||
{ id: 10, title: '30MG', name: '30MG' },
|
||||
{ title: '3MG', name: '3mg' },
|
||||
{ title: '9MG', name: '9mg' },
|
||||
{ title: '2MG', name: '2mg' },
|
||||
{ title: '4MG', name: '4mg' },
|
||||
{ title: '12MG', name: '12mg' },
|
||||
{ title: '18MG', name: '18mg' },
|
||||
{ title: '6MG', name: '6mg' },
|
||||
{ title: '16.5MG', name: '16-5mg' },
|
||||
{ title: '6.5MG', name: '6-5mg' },
|
||||
{ title: '30MG', name: '30mg' },
|
||||
];
|
||||
|
||||
// 在插入新数据前,不清空旧数据,改为如果不存在则创建
|
||||
// await dictItemRepository.query('DELETE FROM `dict_item`');
|
||||
// await dictRepository.query('DELETE FROM `dict`');
|
||||
// // 重置自增 ID
|
||||
// await dictItemRepository.query('ALTER TABLE `dict_item` AUTO_INCREMENT = 1');
|
||||
// await dictRepository.query('ALTER TABLE `dict` AUTO_INCREMENT = 1');
|
||||
const nonFlavorTokensData = ['slim', 'pouches', 'pouch', 'mini', 'dry'].map(item => ({ title: item, name: item }));
|
||||
|
||||
// 初始化语言字典
|
||||
const locales = [
|
||||
{ name: 'zh-CN', title: '简体中文' },
|
||||
{ name: 'en-US', title: 'English' },
|
||||
{ name: 'zh-cn', title: '简体中文' },
|
||||
{ name: 'en-us', title: 'English' },
|
||||
];
|
||||
|
||||
for (const locale of locales) {
|
||||
let dict = await dictRepository.findOne({ where: { name: locale.name } });
|
||||
if (!dict) {
|
||||
dict = await dictRepository.save(locale);
|
||||
}
|
||||
await this.createOrFindDict(dictRepository, locale);
|
||||
}
|
||||
|
||||
// 添加示例翻译条目
|
||||
const zhDict = await dictRepository.findOne({ where: { name: 'zh-CN' } });
|
||||
const enDict = await dictRepository.findOne({ where: { name: 'en-US' } });
|
||||
const zhDict = await dictRepository.findOne({ where: { name: 'zh-cn' } });
|
||||
const enDict = await dictRepository.findOne({ where: { name: 'en-us' } });
|
||||
|
||||
const translations = [
|
||||
{ name: 'common.save', zh: '保存', en: 'Save' },
|
||||
{ name: 'common.cancel', zh: '取消', en: 'Cancel' },
|
||||
{ name: 'common.success', zh: '操作成功', en: 'Success' },
|
||||
{ name: 'common.failure', zh: '操作失败', en: 'Failure' },
|
||||
{ name: 'common-save', zh: '保存', en: 'Save' },
|
||||
{ name: 'common-cancel', zh: '取消', en: 'Cancel' },
|
||||
{ name: 'common-success', zh: '操作成功', en: 'Success' },
|
||||
{ name: 'common-failure', zh: '操作失败', en: 'Failure' },
|
||||
];
|
||||
|
||||
for (const t of translations) {
|
||||
|
|
@ -114,40 +125,56 @@ export default class DictSeeder implements Seeder {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const brandDict = await dictRepository.save({ title: '品牌', name: 'brand' });
|
||||
const flavorDict = await dictRepository.save({ title: '口味', name: 'flavor' });
|
||||
const strengthDict = await dictRepository.save({ title: '强度', name: 'strength' });
|
||||
const brandDict = await this.createOrFindDict(dictRepository, { title: '品牌', name: 'brand' });
|
||||
const flavorDict = await this.createOrFindDict(dictRepository, { title: '口味', name: 'flavor' });
|
||||
const strengthDict = await this.createOrFindDict(dictRepository, { title: '强度', name: 'strength' });
|
||||
const nonFlavorTokensDict = await this.createOrFindDict(dictRepository, { title: '非口味关键词', name: 'non-flavor-tokens' });
|
||||
|
||||
// 遍历品牌数据
|
||||
for (const brand of brandsData) {
|
||||
// 保存字典项,并关联到品牌字典
|
||||
await dictItemRepository.save({
|
||||
title: brand.title,
|
||||
name: brand.name,
|
||||
dict: brandDict,
|
||||
});
|
||||
}
|
||||
await this.seedDictItems(dictItemRepository, brandDict, brandsData);
|
||||
|
||||
// 遍历口味数据
|
||||
for (const flavor of flavorsData) {
|
||||
// 保存字典项,并关联到口味字典
|
||||
await dictItemRepository.save({
|
||||
title: flavor.title,
|
||||
name: flavor.name,
|
||||
dict: flavorDict,
|
||||
});
|
||||
}
|
||||
await this.seedDictItems(dictItemRepository, flavorDict, flavorsData);
|
||||
|
||||
// 遍历强度数据
|
||||
for (const strength of strengthsData) {
|
||||
// 保存字典项,并关联到强度字典
|
||||
await dictItemRepository.save({
|
||||
title: strength.title,
|
||||
name: strength.name,
|
||||
dict: strengthDict,
|
||||
});
|
||||
await this.seedDictItems(dictItemRepository, strengthDict, strengthsData);
|
||||
|
||||
// 遍历非口味关键词数据
|
||||
await this.seedDictItems(dictItemRepository, nonFlavorTokensDict, nonFlavorTokensData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或查找字典
|
||||
* @param repo DictRepository
|
||||
* @param dictInfo 字典信息
|
||||
* @returns Dict 实例
|
||||
*/
|
||||
private async createOrFindDict(repo: any, dictInfo: { title: string; name: string }): Promise<Dict> {
|
||||
// 格式化 name
|
||||
const formattedName = this.formatName(dictInfo.name);
|
||||
let dict = await repo.findOne({ where: { name: formattedName } });
|
||||
if (!dict) {
|
||||
// 如果字典不存在,则使用格式化后的 name 创建新字典
|
||||
dict = await repo.save({ title: dictInfo.title, name: formattedName });
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充字典项
|
||||
* @param repo DictItemRepository
|
||||
* @param dict 字典实例
|
||||
* @param items 字典项数组
|
||||
*/
|
||||
private async seedDictItems(repo: any, dict: Dict, items: { title: string; name: string }[]): Promise<void> {
|
||||
for (const item of items) {
|
||||
// 格式化 name
|
||||
const formattedName = this.formatName(item.name);
|
||||
const existingItem = await repo.findOne({ where: { name: formattedName, dict: { id: dict.id } } });
|
||||
if (!existingItem) {
|
||||
// 如果字典项不存在,则使用格式化后的 name 创建新字典项
|
||||
await repo.save({ ...item, name: formattedName, dict });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,23 @@ export class CreateProductDTO {
|
|||
@ApiProperty({ description: '商品类型', enum: ['simple', 'bundle'], default: 'simple', required: false })
|
||||
@Rule(RuleType.string().valid('simple', 'bundle').default('simple'))
|
||||
type?: string;
|
||||
|
||||
// 中文注释:仅当 type 为 'bundle' 时,才需要提供 components
|
||||
@ApiProperty({ description: '产品组成', type: 'array', required: false })
|
||||
@Rule(
|
||||
RuleType.array()
|
||||
.items(
|
||||
RuleType.object({
|
||||
sku: RuleType.string().required(),
|
||||
quantity: RuleType.number().required(),
|
||||
})
|
||||
)
|
||||
.when('type', {
|
||||
is: 'bundle',
|
||||
then: RuleType.array().required(),
|
||||
})
|
||||
)
|
||||
components?: { sku: string; quantity: number }[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
|
|
@ -17,6 +18,7 @@ import {
|
|||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
@Index(['name', 'dict'], { unique: true })
|
||||
export class DictItem {
|
||||
// 主键
|
||||
@PrimaryGeneratedColumn()
|
||||
|
|
@ -29,7 +31,7 @@ export class DictItem {
|
|||
@Column({ comment: '字典项中文名称', nullable: true })
|
||||
titleCN: string;
|
||||
// 唯一标识
|
||||
@Column({ unique: true, comment: '字典唯一标识名称' })
|
||||
@Column({ comment: '字典唯一标识名称' })
|
||||
name: string;
|
||||
|
||||
// 字典项值
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export class Product {
|
|||
@Column({ default: 0 })
|
||||
stock: number;
|
||||
|
||||
@ManyToMany(() => DictItem, {
|
||||
@ManyToMany(() => DictItem, dictItem => dictItem.products, {
|
||||
cascade: true,
|
||||
})
|
||||
@JoinTable()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ export class DictService {
|
|||
@InjectEntityModel(DictItem)
|
||||
dictItemModel: Repository<DictItem>;
|
||||
|
||||
// 格式化名称为 kebab-case
|
||||
private formatName(name: string): string {
|
||||
return String(name).replace(/[_\s.]+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
// 生成并返回字典的XLSX模板
|
||||
getDictXLSXTemplate() {
|
||||
// 定义表头
|
||||
|
|
@ -43,7 +48,7 @@ export class DictService {
|
|||
// 创建要保存的字典实体数组
|
||||
const dicts = data.map((row: any) => {
|
||||
const dict = new Dict();
|
||||
dict.name = row.name;
|
||||
dict.name = this.formatName(row.name);
|
||||
dict.title = row.title;
|
||||
return dict;
|
||||
});
|
||||
|
|
@ -76,7 +81,7 @@ export class DictService {
|
|||
|
||||
const items = data.map((row: any) => {
|
||||
const item = new DictItem();
|
||||
item.name = row.name;
|
||||
item.name = this.formatName(row.name);
|
||||
item.title = row.title;
|
||||
item.titleCN = row.titleCN; // 保存中文名称
|
||||
item.value = row.value;
|
||||
|
|
@ -106,13 +111,16 @@ export class DictService {
|
|||
// 创建新字典
|
||||
async createDict(createDictDTO: CreateDictDTO) {
|
||||
const dict = new Dict();
|
||||
dict.name = createDictDTO.name;
|
||||
dict.name = this.formatName(createDictDTO.name);
|
||||
dict.title = createDictDTO.title;
|
||||
return this.dictModel.save(dict);
|
||||
}
|
||||
|
||||
// 更新字典
|
||||
async updateDict(id: number, updateDictDTO: UpdateDictDTO) {
|
||||
if (updateDictDTO.name) {
|
||||
updateDictDTO.name = this.formatName(updateDictDTO.name);
|
||||
}
|
||||
await this.dictModel.update(id, updateDictDTO);
|
||||
return this.dictModel.findOneBy({ id });
|
||||
}
|
||||
|
|
@ -127,10 +135,23 @@ export class DictService {
|
|||
}
|
||||
|
||||
// 获取字典项列表,支持按 dictId 过滤
|
||||
async getDictItems(dictId?: number) {
|
||||
// 如果提供了 dictId,则只返回该字典下的项
|
||||
async getDictItems(params: { dictId?: number; name?: string; title?: string; }) {
|
||||
const { dictId, name, title } = params;
|
||||
const where: any = {};
|
||||
|
||||
if (dictId) {
|
||||
return this.dictItemModel.find({ where: { dict: { id: dictId } } });
|
||||
where.dict = { id: dictId };
|
||||
}
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
}
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
|
||||
// 如果提供了 dictId,则只返回该字典下的项
|
||||
if (params.dictId) {
|
||||
return this.dictItemModel.find({ where });
|
||||
}
|
||||
// 否则,返回所有字典项
|
||||
return this.dictItemModel.find();
|
||||
|
|
@ -143,7 +164,7 @@ export class DictService {
|
|||
throw new Error('指定的字典不存在');
|
||||
}
|
||||
const item = new DictItem();
|
||||
item.name = createDictItemDTO.name;
|
||||
item.name = this.formatName(createDictItemDTO.name);
|
||||
item.title = createDictItemDTO.title;
|
||||
item.titleCN = createDictItemDTO.titleCN; // 保存中文名称
|
||||
item.dict = dict;
|
||||
|
|
@ -152,6 +173,9 @@ export class DictService {
|
|||
|
||||
// 更新字典项
|
||||
async updateDictItem(id: number, updateDictItemDTO: UpdateDictItemDTO) {
|
||||
if (updateDictItemDTO.name) {
|
||||
updateDictItemDTO.name = this.formatName(updateDictItemDTO.name);
|
||||
}
|
||||
await this.dictItemModel.update(id, updateDictItemDTO);
|
||||
return this.dictItemModel.findOneBy({ id });
|
||||
}
|
||||
|
|
@ -161,4 +185,16 @@ export class DictService {
|
|||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
// 根据字典名称获取字典项列表
|
||||
async getDictItemsByDictName(dictName: string) {
|
||||
// 查找字典
|
||||
const dict = await this.dictModel.findOne({ where: { name: dictName } });
|
||||
// 如果字典不存在,则返回空数组
|
||||
if (!dict) {
|
||||
return [];
|
||||
}
|
||||
// 返回该字典下的所有字典项
|
||||
return this.dictItemModel.find({ where: { dict: { id: dict.id } } });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,8 +374,12 @@ export class WpProductService {
|
|||
|
||||
// 数据转换
|
||||
const items = rawResult.reduce((acc, row) => {
|
||||
// 在累加器中查找当前产品
|
||||
let product = acc.find(p => p.id === row.id);
|
||||
// 如果产品不存在,则创建新产品
|
||||
if (!product) {
|
||||
// 从原始产品列表中查找,以获取 'site' 关联数据
|
||||
const originalProduct = products.find(p => p.id === row.id);
|
||||
product = {
|
||||
...Object.keys(row)
|
||||
.filter(key => !key.startsWith('variation_'))
|
||||
|
|
@ -384,6 +388,8 @@ export class WpProductService {
|
|||
return obj;
|
||||
}, {}),
|
||||
variations: [],
|
||||
// 附加 'site' 对象
|
||||
site: originalProduct.site,
|
||||
};
|
||||
acc.push(product);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue