forked from yoone/API
1
0
Fork 0
API/src/service/product.service.ts

477 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Provide } from '@midwayjs/core';
import { In, Like, Not, Repository } from 'typeorm';
import { Product } from '../entity/product.entity';
import { Category } from '../entity/category.entity';
import { paginate } from '../utils/paginate.util';
import { PaginationParams } from '../interface';
import {
CreateCategoryDTO,
CreateFlavorsDTO,
CreateProductDTO,
CreateStrengthDTO,
UpdateCategoryDTO,
UpdateFlavorsDTO,
UpdateProductDTO,
UpdateStrengthDTO,
} from '../dto/product.dto';
import {
CategoryPaginatedResponse,
FlavorsPaginatedResponse,
ProductPaginatedResponse,
StrengthPaginatedResponse,
} from '../dto/reponse.dto';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { WpProduct } from '../entity/wp_product.entity';
import { Variation } from '../entity/variation.entity';
import { Strength } from '../entity/strength.entity';
import { Flavors } from '../entity/flavors.entity';
@Provide()
export class ProductService {
@InjectEntityModel(Product)
productModel: Repository<Product>;
@InjectEntityModel(Category)
categoryModel: Repository<Category>;
@InjectEntityModel(Strength)
strengthModel: Repository<Strength>;
@InjectEntityModel(Flavors)
flavorsModel: Repository<Flavors>;
@InjectEntityModel(WpProduct)
wpProductModel: Repository<WpProduct>;
@InjectEntityModel(Variation)
variationModel: Repository<Variation>;
// async findProductsByName(name: string): Promise<Product[]> {
// const where: any = {};
// const nameFilter = name ? name.split(' ').filter(Boolean) : [];
// if (nameFilter.length > 0) {
// const nameConditions = nameFilter.map(word => Like(`%${word}%`));
// where.name = And(...nameConditions);
// }
// if(name){
// where.nameCn = Like(`%${name}%`)
// }
// where.sku = Not(IsNull());
// // 查询 SKU 不为空且 name 包含关键字的产品,最多返回 50 条
// return this.productModel.find({
// where,
// take: 50,
// });
// }
async findProductsByName(name: string): Promise<Product[]> {
const nameFilter = name ? name.split(' ').filter(Boolean) : [];
const query = this.productModel.createQueryBuilder('product');
// 保证 sku 不为空
query.where('product.sku IS NOT NULL');
if (nameFilter.length > 0 || name) {
const params: Record<string, string> = {};
const conditions: string[] = [];
// 英文名关键词全部匹配AND
if (nameFilter.length > 0) {
const nameConds = nameFilter.map((word, index) => {
const key = `name${index}`;
params[key] = `%${word}%`;
return `product.name LIKE :${key}`;
});
conditions.push(`(${nameConds.join(' AND ')})`);
}
// 中文名模糊匹配
if (name) {
params['nameCn'] = `%${name}%`;
conditions.push(`product.nameCn LIKE :nameCn`);
}
// 英文名关键词匹配 OR 中文名匹配
query.andWhere(`(${conditions.join(' OR ')})`, params);
}
query.take(50);
return await query.getMany();
}
async findProductBySku(sku: string): Promise<Product> {
return this.productModel.findOne({
where: {
sku,
},
});
}
async getProductList(
pagination: PaginationParams,
name?: string,
categoryId?: number
): Promise<ProductPaginatedResponse> {
const nameFilter = name ? name.split(' ').filter(Boolean) : [];
const qb = this.productModel
.createQueryBuilder('product')
.leftJoin(Category, 'category', 'category.id = product.categoryId')
.leftJoin(Strength, 'strength', 'strength.id = product.strengthId')
.leftJoin(Flavors, 'flavors', 'flavors.id = product.flavorsId')
.select([
'product.id as id',
'product.name as name',
'product.nameCn as nameCn',
'product.description as description',
'product.humidity as humidity',
'product.sku as sku',
'product.createdAt as createdAt',
'product.updatedAt as updatedAt',
'category.name AS categoryName',
'strength.name AS strengthName',
'flavors.name AS flavorsName',
]);
// 模糊搜索 name支持多个关键词
nameFilter.forEach((word, index) => {
qb.andWhere(`product.name LIKE :name${index}`, {
[`name${index}`]: `%${word}%`,
});
});
// 分类过滤
if (categoryId) {
qb.andWhere('product.categoryId = :categoryId', { categoryId });
}
// 分页
qb.skip((pagination.current - 1) * pagination.pageSize).take(
pagination.pageSize
);
// 执行查询
const items = await qb.getRawMany();
const total = await qb.getCount();
return {
items,
total,
...pagination,
};
}
async createProduct(createProductDTO: CreateProductDTO): Promise<Product> {
const { name, description, categoryId, strengthId, flavorsId, humidity } =
createProductDTO;
const isExit = await this.productModel.findOne({
where: {
categoryId,
strengthId,
flavorsId,
humidity,
},
});
if (isExit) throw new Error('产品已存在');
const product = new Product();
product.name = name;
product.description = description;
product.categoryId = categoryId;
product.strengthId = strengthId;
product.flavorsId = flavorsId;
product.humidity = humidity;
const categoryKey = (
await this.categoryModel.findOne({ where: { id: categoryId } })
).unique_key;
const strengthKey = (
await this.strengthModel.findOne({ where: { id: strengthId } })
).unique_key;
const flavorsKey = (
await this.flavorsModel.findOne({ where: { id: flavorsId } })
).unique_key;
product.sku = `${categoryKey}-${flavorsKey}-${strengthKey}-${humidity}`;
return await this.productModel.save(product);
}
async updateProduct(
id: number,
updateProductDTO: UpdateProductDTO
): Promise<Product> {
// 确认产品是否存在
const product = await this.productModel.findOneBy({ id });
if (!product) {
throw new Error(`产品 ID ${id} 不存在`);
}
// 更新产品
await this.productModel.update(id, updateProductDTO);
// 返回更新后的产品
return await this.productModel.findOneBy({ id });
}
async updateProductNameCn(id: number, nameCn: string): Promise<Product> {
// 确认产品是否存在
const product = await this.productModel.findOneBy({ id });
if (!product) {
throw new Error(`产品 ID ${id} 不存在`);
}
// 更新产品
await this.productModel.update(id, { nameCn });
// 返回更新后的产品
return await this.productModel.findOneBy({ id });
}
async deleteProduct(id: number): Promise<boolean> {
// 检查产品是否存在
const product = await this.productModel.findOneBy({ id });
if (!product) {
throw new Error(`产品 ID ${id} 不存在`);
}
const productSku = product.sku;
// 查询 wp_product 表中是否存在与该 SKU 关联的产品
const wpProduct = await this.wpProductModel
.createQueryBuilder('wp_product')
.where('JSON_CONTAINS(wp_product.constitution, :sku)', {
sku: JSON.stringify({ sku: productSku }),
})
.getOne();
if (wpProduct) {
throw new Error('无法删除请先删除关联的WP产品');
}
const variation = await this.variationModel
.createQueryBuilder('variation')
.where('JSON_CONTAINS(variation.constitution, :sku)', {
sku: JSON.stringify({ sku: productSku }),
})
.getOne();
if (variation) {
console.log(variation);
throw new Error('无法删除请先删除关联的WP变体');
}
// 删除产品
const result = await this.productModel.delete(id);
return result.affected > 0; // `affected` 表示删除的行数
}
async hasProductsInCategory(categoryId: number): Promise<boolean> {
const count = await this.productModel.count({
where: { categoryId },
});
return count > 0;
}
async hasCategory(name: string, id?: string): Promise<boolean> {
const where: any = { name };
if (id) where.id = Not(id);
const count = await this.categoryModel.count({
where,
});
return count > 0;
}
async getCategoryList(
pagination: PaginationParams,
name?: string
): Promise<CategoryPaginatedResponse> {
const where: any = {};
if (name) {
where.name = Like(`%${name}%`);
}
return await paginate(this.categoryModel, { pagination, where });
}
async getCategoryAll(): Promise<CategoryPaginatedResponse> {
return await this.categoryModel.find();
}
async createCategory(
createCategoryDTO: CreateCategoryDTO
): Promise<Category> {
const { name, unique_key } = createCategoryDTO;
const category = new Category();
category.name = name;
category.unique_key = unique_key;
return await this.categoryModel.save(category);
}
async updateCategory(id: number, updateCategory: UpdateCategoryDTO) {
// 确认产品是否存在
const category = await this.categoryModel.findOneBy({ id });
if (!category) {
throw new Error(`产品分类 ID ${id} 不存在`);
}
// 更新产品
await this.categoryModel.update(id, updateCategory);
// 返回更新后的产品
return await this.categoryModel.findOneBy({ id });
}
async deleteCategory(id: number): Promise<boolean> {
// 检查产品是否存在
const category = await this.categoryModel.findOneBy({ id });
if (!category) {
throw new Error(`产品分类 ID ${id} 不存在`);
}
// 删除产品
const result = await this.categoryModel.delete(id);
return result.affected > 0; // `affected` 表示删除的行数
}
async hasProductsInFlavors(flavorsId: number): Promise<boolean> {
const count = await this.productModel.count({
where: { flavorsId },
});
return count > 0;
}
async hasFlavors(name: string, id?: string): Promise<boolean> {
const where: any = { name };
if (id) where.id = Not(id);
const count = await this.flavorsModel.count({
where,
});
return count > 0;
}
async getFlavorsList(
pagination: PaginationParams,
name?: string
): Promise<FlavorsPaginatedResponse> {
const where: any = {};
if (name) {
where.name = Like(`%${name}%`);
}
return await paginate(this.flavorsModel, { pagination, where });
}
async getFlavorsAll(): Promise<FlavorsPaginatedResponse> {
return await this.flavorsModel.find();
}
async createFlavors(createFlavorsDTO: CreateFlavorsDTO): Promise<Flavors> {
const { name, unique_key } = createFlavorsDTO;
const flavors = new Flavors();
flavors.name = name;
flavors.unique_key = unique_key;
return await this.flavorsModel.save(flavors);
}
async updateFlavors(id: number, updateFlavors: UpdateFlavorsDTO) {
// 确认产品是否存在
const flavors = await this.flavorsModel.findOneBy({ id });
if (!flavors) {
throw new Error(`口味 ID ${id} 不存在`);
}
// 更新产品
await this.flavorsModel.update(id, updateFlavors);
// 返回更新后的产品
return await this.flavorsModel.findOneBy({ id });
}
async deleteFlavors(id: number): Promise<boolean> {
// 检查产品是否存在
const flavors = await this.flavorsModel.findOneBy({ id });
if (!flavors) {
throw new Error(`口味 ID ${id} 不存在`);
}
// 删除产品
const result = await this.flavorsModel.delete(id);
return result.affected > 0; // `affected` 表示删除的行数
}
async hasProductsInStrength(strengthId: number): Promise<boolean> {
const count = await this.productModel.count({
where: { strengthId },
});
return count > 0;
}
async hasStrength(name: string, id?: string): Promise<boolean> {
const where: any = { name };
if (id) where.id = Not(id);
const count = await this.strengthModel.count({
where,
});
return count > 0;
}
async getStrengthList(
pagination: PaginationParams,
name?: string
): Promise<StrengthPaginatedResponse> {
const where: any = {};
if (name) {
where.name = Like(`%${name}%`);
}
return await paginate(this.strengthModel, { pagination, where });
}
async getStrengthAll(): Promise<StrengthPaginatedResponse> {
return await this.strengthModel.find();
}
async createStrength(
createStrengthDTO: CreateStrengthDTO
): Promise<Strength> {
const { name, unique_key } = createStrengthDTO;
const strength = new Strength();
strength.name = name;
strength.unique_key = unique_key;
return await this.strengthModel.save(strength);
}
async updateStrength(id: number, updateStrength: UpdateStrengthDTO) {
// 确认产品是否存在
const strength = await this.strengthModel.findOneBy({ id });
if (!strength) {
throw new Error(`口味 ID ${id} 不存在`);
}
// 更新产品
await this.strengthModel.update(id, updateStrength);
// 返回更新后的产品
return await this.strengthModel.findOneBy({ id });
}
async deleteStrength(id: number): Promise<boolean> {
// 检查产品是否存在
const strength = await this.strengthModel.findOneBy({ id });
if (!strength) {
throw new Error(`口味 ID ${id} 不存在`);
}
// 删除产品
const result = await this.flavorsModel.delete(id);
return result.affected > 0; // `affected` 表示删除的行数
}
async batchSetSku(skus: { productId: number; sku: string }[]) {
// 提取所有 sku
const skuList = skus.map(item => item.sku);
// 检查是否存在重复 sku
const existingProducts = await this.productModel.find({
where: { sku: In(skuList) },
});
if (existingProducts.length > 0) {
const existingSkus = existingProducts.map(product => product.sku);
throw new Error(`以下 SKU 已存在: ${existingSkus.join(', ')}`);
}
// 遍历检查产品 ID 是否存在,并更新 sku
for (const { productId, sku } of skus) {
const product = await this.productModel.findOne({
where: { id: productId },
});
if (!product) {
throw new Error(`产品 ID '${productId}' 不存在`);
}
product.sku = sku;
await this.productModel.save(product);
}
return `成功更新 ${skus.length} 个 sku`;
}
}