diff --git a/src/controller/product.controller.ts b/src/controller/product.controller.ts index 4489514..890a0cb 100644 --- a/src/controller/product.controller.ts +++ b/src/controller/product.controller.ts @@ -750,4 +750,31 @@ export class ProductController { return errorResponse(error?.message || error); } } + + // 获取所有产品,支持按品牌过滤 + @ApiOkResponse({ description: '获取所有产品', type: ProductListRes }) + @Get('/all') + async getAllProducts(@Query('brand') brand?: string) { + try { + const data = await this.productService.getAllProducts(brand); + return successResponse(data); + } catch (error) { + return errorResponse(error?.message || error); + } + } + + // 获取按属性分组的产品,默认按强度划分 + @ApiOkResponse({ description: '获取按属性分组的产品' }) + @Get('/grouped') + async getGroupedProducts( + @Query('brand') brand?: string, + @Query('attribute') attribute: string = 'strength' + ) { + try { + const data = await this.productService.getProductsGroupedByAttribute(brand, attribute); + return successResponse(data); + } catch (error) { + return errorResponse(error?.message || error); + } + } } diff --git a/src/dto/product.dto.ts b/src/dto/product.dto.ts index ca3623c..6ef0be8 100644 --- a/src/dto/product.dto.ts +++ b/src/dto/product.dto.ts @@ -311,6 +311,8 @@ export interface ProductWhereFilter { updatedAtStart?: string; // 更新时间范围结束 updatedAtEnd?: string; + // TODO 使用 attributes 过滤 + attributes?: Record; } /** diff --git a/src/service/product.service.ts b/src/service/product.service.ts index be3d578..9ad03b7 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -393,6 +393,41 @@ export class ProductService { qb.andWhere('product.updatedAt <= :whereUpdatedAtEnd', { whereUpdatedAtEnd: new Date(query.where.updatedAtEnd) }); } + // 处理属性过滤 + const attributeFilters = query.where?.attributes || {}; + Object.entries(attributeFilters).forEach(([attributeName, value], index) => { + if (value === 'hasValue') { + // 如果值为'hasValue',则过滤出具有该属性的产品 + qb.andWhere(qb => { + const subQuery = qb + .subQuery() + .select('product_attributes_dict_item.productId') + .from('product_attributes_dict_item', 'product_attributes_dict_item') + .innerJoin('dict_item', 'dict_item', 'product_attributes_dict_item.dictItemId = dict_item.id') + .innerJoin('dict', 'dict', 'dict_item.dictId = dict.id') + .where('dict.name = :attributeName', { + attributeName, + }) + .getQuery(); + return 'product.id IN ' + subQuery; + }); + } else if (typeof value === 'number' || !isNaN(Number(value))) { + // 如果值是数字,则过滤出该属性等于该值的产品 + const attributeValueId = Number(value); + qb.andWhere(qb => { + const subQuery = qb + .subQuery() + .select('product_attributes_dict_item.productId') + .from('product_attributes_dict_item', 'product_attributes_dict_item') + .where('product_attributes_dict_item.dictItemId = :attributeValueId', { + attributeValueId, + }) + .getQuery(); + return 'product.id IN ' + subQuery; + }); + } + }); + // 品牌过滤(向后兼容) if (brandId) { qb.andWhere(qb => { @@ -2076,4 +2111,111 @@ export class ProductService { return unifiedProduct; } + + /** + * 获取所有产品,支持按品牌过滤 + * @param brand 品牌名称 + * @returns 所有符合条件的产品 + */ + async getAllProducts(brand?: string): Promise<{ items: Product[], total: number }> { + const qb = this.productModel + .createQueryBuilder('product') + .leftJoinAndSelect('product.attributes', 'attribute') + .leftJoinAndSelect('attribute.dict', 'dict') + .leftJoinAndSelect('product.category', 'category'); + + // 按品牌过滤 + if (brand) { + // 先获取品牌对应的字典项 + const brandDict = await this.dictModel.findOne({ where: { name: 'brand' } }); + if (brandDict) { + // 查找品牌名称对应的字典项(支持标题和名称匹配) + const brandItem = await this.dictItemModel.findOne({ + where: [ + { + title: brand, + dict: { id: brandDict.id } + }, + { + name: brand, + dict: { id: brandDict.id } + } + ] + }); + + if (brandItem) { + qb.andWhere(qb => { + const subQuery = qb + .subQuery() + .select('product_attributes_dict_item.productId') + .from('product_attributes_dict_item', 'product_attributes_dict_item') + .where('product_attributes_dict_item.dictItemId = :brandId', { + brandId: brandItem.id, + }) + .getQuery(); + return 'product.id IN ' + subQuery; + }); + } + } + } + + // 根据类型填充组成信息 + const items = await qb.getMany(); + for (const product of items) { + if (product.type === 'single') { + // 单品不持久化组成,这里仅返回一个基于 SKU 的虚拟组成 + const component = new ProductStockComponent(); + component.productId = product.id; + component.sku = product.sku; + component.quantity = 1; + product.components = [component]; + } else { + // 混装商品返回持久化的 SKU 组成 + product.components = await this.productStockComponentModel.find({ + where: { productId: product.id }, + }); + } + + // 确保属性按强度正确划分,只保留强度相关的属性 + // 这里根据需求,如果需要可以进一步过滤或重组属性 + } + + return { + items, + total: items.length + }; + } + + /** + * 获取产品按属性值分组,支持按强度划分 + * @param brand 品牌名称 + * @returns 按属性值分组的产品 + */ + async getProductsGroupedByAttribute(brand?: string, attributeName: string = 'strength'): Promise<{ [key: string]: Product[] }> { + // 首先获取所有产品 + const { items } = await this.getAllProducts(brand); + + // 按指定属性分组 + const groupedProducts: { [key: string]: Product[] } = {}; + + items.forEach(product => { + // 获取产品的指定属性值 + const attribute = product.attributes.find(attr => attr.dict.name === attributeName); + if (attribute) { + const attributeValue = attribute.title || attribute.name; + if (!groupedProducts[attributeValue]) { + groupedProducts[attributeValue] = []; + } + groupedProducts[attributeValue].push(product); + } else { + // 如果没有该属性,放入未分组 + if (!groupedProducts['未分组']) { + groupedProducts['未分组'] = []; + } + groupedProducts['未分组'].push(product); + } + }); + + return groupedProducts; + } }