import { Body, ContentType, Controller, Del, Files, Get, Inject, Param, Post, Put, Query, } from '@midwayjs/core'; import { Context } from '@midwayjs/koa'; import { ILogger } from '@midwayjs/logger'; import { ApiOkResponse } from '@midwayjs/swagger'; import { UnifiedSearchParamsDTO } from '../dto/api.dto'; import { SyncOperationResultDTO } from '../dto/batch.dto'; import { BatchDeleteProductDTO, BatchUpdateProductDTO, CreateCategoryDTO, CreateProductDTO, ProductWhereFilter, UpdateCategoryDTO, UpdateProductDTO } from '../dto/product.dto'; import { BooleanRes, ProductListRes, ProductRes, ProductsRes } from '../dto/reponse.dto'; import { BatchSyncProductToSiteDTO, SyncProductToSiteDTO, SyncProductToSiteResultDTO } from '../dto/site-sync.dto'; import { ProductService } from '../service/product.service'; import { errorResponse, successResponse } from '../utils/response.util'; @Controller('/product') export class ProductController { @Inject() productService: ProductService; @Inject() ctx: Context; @Inject() logger: ILogger; @ApiOkResponse({ description: '通过name搜索产品', type: ProductsRes, }) @Get('/search') async searchProducts(@Query('name') name: string) { try { // 调用服务获取产品数据 const products = await this.productService.findProductsByName(name); return successResponse(products); } catch (error) { return errorResponse(error.message || '获取数据失败'); } } @ApiOkResponse({ type: ProductRes, }) @Get('/sku/:sku') async productBySku(@Param('sku') sku: string) { try { // 调用服务获取产品数据 const product = await this.productService.findProductBySku(sku); return successResponse(product); } catch (error) { return errorResponse(error.message || '获取数据失败'); } } @ApiOkResponse({ description: '成功返回产品列表', type: ProductListRes, }) @Get('/list') async getProductList( @Query() query: UnifiedSearchParamsDTO ): Promise { try { const data = await this.productService.getProductList(query); return successResponse(data); } catch (error) { this.logger.error('获取产品列表失败', error); return errorResponse(error?.message || error); } } @ApiOkResponse({ type: ProductRes }) @Post('/') async createProduct(@Body() productData: CreateProductDTO) { try { const data = await this.productService.createProduct(productData); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 导出所有产品 CSV @ApiOkResponse() @Get('/export') @ContentType('text/csv') async exportProductsCSV() { try { const csv = await this.productService.exportProductsCSV(); // 设置下载文件名(附件形式) const date = new Date(); const pad = (n: number) => String(n).padStart(2, '0'); const name = `products-${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}.csv`; this.ctx.set('Content-Disposition', `attachment; filename=${name}`); return csv; } catch (error) { return errorResponse(error?.message || error); } } // 导入产品(CSV 文件) @ApiOkResponse() @Post('/import') async importProductsCSV(@Files() files: any) { try { // 条件判断:确保存在文件 const file = files?.[0]; if (!file) return errorResponse('未接收到上传文件'); const result = await this.productService.importProductsCSV(file); return successResponse(result); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: ProductRes }) @Put('/:id') async updateProduct(@Param('id') id: number, @Body() productData: UpdateProductDTO) { try { const data = await this.productService.updateProduct(id, productData); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Put('/batch-update') async batchUpdateProduct(@Body() batchUpdateProductDTO: BatchUpdateProductDTO) { try { await this.productService.batchUpdateProduct(batchUpdateProductDTO); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Post('/batch-delete') async batchDeleteProduct(@Body() body: BatchDeleteProductDTO) { try { const result = await this.productService.batchDeleteProduct(body.ids); if (result.failed > 0) { return errorResponse(`成功删除 ${result.success} 个,失败 ${result.failed} 个。首个错误: ${result.errors[0]}`); } return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: ProductRes }) @Put('updateNameCn/:id/:nameCn') async updatenameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) { try { const data = await this.productService.updatenameCn(id, nameCn); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 根据站点SKU查询产品 @ApiOkResponse({ type: ProductRes }) @Get('/site-sku/:siteSku') async getProductBySiteSku(@Param('siteSku') siteSku: string) { try { const product = await this.productService.findProductBySiteSku(siteSku); return successResponse(product); } catch (error) { return errorResponse(error.message || '获取数据失败'); } } // 获取产品的站点SKU绑定 @ApiOkResponse() @Get('/:id/site-skus') async getProductSiteSkus(@Param('id') id: number) { try { const data = await this.productService.getProductSiteSkus(id); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 覆盖式绑定产品的站点SKU列表 @ApiOkResponse() @Post('/:id/site-skus') async bindProductSiteSkus(@Param('id') id: number, @Body() body: { codes: string[] }) { try { const data = await this.productService.bindSiteSkus(id, body?.codes || []); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: ProductRes }) @Get('/:id') async getProductById(@Param('id') id: number) { try { const product = await this.productService.getProductById(id); return successResponse(product); } catch (error) { return errorResponse(error.message || '获取数据失败'); } } @ApiOkResponse({ type: BooleanRes }) @Del('/:id') async deleteProduct(@Param('id') id: number) { try { const data = await this.productService.deleteProduct(id); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 获取产品的库存组成 @ApiOkResponse() @Get('/:id/components') async getProductComponents(@Param('id') id: number) { try { const data = await this.productService.getProductComponents(id); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 根据 SKU 自动绑定组成(匹配所有相同 SKU 的库存) @ApiOkResponse() @Post('/:id/components/auto') async autoBindComponents(@Param('id') id: number) { try { const data = await this.productService.autoBindComponentsBySku(id); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 通用属性接口:分页列表 @ApiOkResponse() @Get('/attribute') async getAttributeList( @Query('dictName') dictName: string, @Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string ) { try { const data = await this.productService.getAttributeList( dictName, { current, pageSize }, name ); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 通用属性接口:全部列表 @ApiOkResponse() @Get('/attributeAll') async getAttributeAll(@Query('dictName') dictName: string) { try { const data = await this.productService.getAttributeAll(dictName); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 通用属性接口:创建 @ApiOkResponse() @Post('/attribute') async createAttribute( @Query('dictName') dictName: string, @Body() body: { title: string; name: string } ) { try { // 调用 getOrCreateAttribute 方法,如果不存在则创建,如果存在则返回 const data = await this.productService.getOrCreateAttribute(dictName, body.title, body.name); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 通用属性接口:更新 @ApiOkResponse() @Put('/attribute/:id') async updateAttribute( @Param('id') id: number, @Query('dictName') dictName: string, @Body() body: { title?: string; name?: string } ) { try { if (body?.name) { const hasItem = await this.productService.hasAttribute( dictName, body.name, id ); if (hasItem) return errorResponse('字典项已存在'); } const data = await this.productService.updateAttribute(id, body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 通用属性接口:删除 @ApiOkResponse({ type: BooleanRes }) @Del('/attribute/:id') async deleteAttribute(@Param('id') id: number) { try { await this.productService.deleteAttribute(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 兼容旧接口:品牌 @ApiOkResponse() @Get('/brandAll') async compatBrandAll() { try { const data = await this.productService.getAttributeAll('brand'); // 返回所有品牌字典项 return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Get('/brands') async compatBrands(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) { try { const data = await this.productService.getAttributeList('brand', { current, pageSize }, name); // 分页品牌列表 return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Post('/brand') async compatCreateBrand(@Body() body: { title: string; name: string; image?: string; shortName?: string }) { try { const has = await this.productService.hasAttribute('brand', body.name); // 唯一性校验 if (has) return errorResponse('品牌已存在'); const data = await this.productService.createAttribute('brand', body); // 创建品牌字典项 return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Put('/brand/:id') async compatUpdateBrand(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) { try { if (body?.name) { const has = await this.productService.hasAttribute('brand', body.name, id); // 唯一性校验(排除自身) if (has) return errorResponse('品牌已存在'); } const data = await this.productService.updateAttribute(id, body); // 更新品牌字典项 return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Del('/brand/:id') async compatDeleteBrand(@Param('id') id: number) { try { await this.productService.deleteAttribute(id); // 删除品牌字典项 return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 兼容旧接口:口味 @ApiOkResponse() @Get('/flavorsAll') async compatFlavorsAll() { try { const data = await this.productService.getAttributeAll('flavor'); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Get('/flavors') async compatFlavors(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) { try { const data = await this.productService.getAttributeList('flavor', { current, pageSize }, name); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Post('/flavors') async compatCreateFlavors(@Body() body: { title: string; name: string; image?: string; shortName?: string }) { try { const has = await this.productService.hasAttribute('flavor', body.name); if (has) return errorResponse('口味已存在'); const data = await this.productService.createAttribute('flavor', body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Put('/flavors/:id') async compatUpdateFlavors(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) { try { if (body?.name) { const has = await this.productService.hasAttribute('flavor', body.name, id); if (has) return errorResponse('口味已存在'); } const data = await this.productService.updateAttribute(id, body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Del('/flavors/:id') async compatDeleteFlavors(@Param('id') id: number) { try { await this.productService.deleteAttribute(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 兼容旧接口:规格 @ApiOkResponse() @Get('/strengthAll') async compatStrengthAll() { try { const data = await this.productService.getAttributeAll('strength'); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Get('/strength') async compatStrength(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) { try { const data = await this.productService.getAttributeList('strength', { current, pageSize }, name); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Post('/strength') async compatCreateStrength(@Body() body: { title: string; name: string; image?: string; shortName?: string }) { try { const has = await this.productService.hasAttribute('strength', body.name); if (has) return errorResponse('规格已存在'); const data = await this.productService.createAttribute('strength', body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Put('/strength/:id') async compatUpdateStrength(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) { try { if (body?.name) { const has = await this.productService.hasAttribute('strength', body.name, id); if (has) return errorResponse('规格已存在'); } const data = await this.productService.updateAttribute(id, body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Del('/strength/:id') async compatDeleteStrength(@Param('id') id: number) { try { await this.productService.deleteAttribute(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 兼容旧接口:尺寸 @ApiOkResponse() @Get('/sizeAll') async compatSizeAll() { try { const data = await this.productService.getAttributeAll('size'); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Get('/size') async compatSize(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) { try { const data = await this.productService.getAttributeList('size', { current, pageSize }, name); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Post('/size') async compatCreateSize(@Body() body: { title: string; name: string; image?: string; shortName?: string }) { try { const has = await this.productService.hasAttribute('size', body.name); if (has) return errorResponse('尺寸已存在'); const data = await this.productService.createAttribute('size', body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse() @Put('/size/:id') async compatUpdateSize(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) { try { if (body?.name) { const has = await this.productService.hasAttribute('size', body.name, id); if (has) return errorResponse('尺寸已存在'); } const data = await this.productService.updateAttribute(id, body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } @ApiOkResponse({ type: BooleanRes }) @Del('/size/:id') async compatDeleteSize(@Param('id') id: number) { try { await this.productService.deleteAttribute(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 获取所有分类 @ApiOkResponse({ description: '获取所有分类' }) @Get('/categories/all') async getCategoriesAll() { try { const data = await this.productService.getCategoriesAll(); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 获取分类下的属性配置 @ApiOkResponse({ description: '获取分类下的属性配置' }) @Get('/category/:id/attributes') async getCategoryAttributes(@Param('id') id: number) { try { const data = await this.productService.getCategoryAttributes(id); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 创建分类 @ApiOkResponse({ description: '创建分类' }) @Post('/category') async createCategory(@Body() body: CreateCategoryDTO) { try { const data = await this.productService.createCategory(body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 更新分类 @ApiOkResponse({ description: '更新分类' }) @Put('/category/:id') async updateCategory(@Param('id') id: number, @Body() body: UpdateCategoryDTO) { try { const data = await this.productService.updateCategory(id, body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 删除分类 @ApiOkResponse({ description: '删除分类' }) @Del('/category/:id') async deleteCategory(@Param('id') id: number) { try { await this.productService.deleteCategory(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 创建分类属性 @ApiOkResponse({ description: '创建分类属性' }) @Post('/category/attribute') async createCategoryAttribute(@Body() body: { categoryId: number; dictId: number }) { try { const data = await this.productService.createCategoryAttribute(body); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 删除分类属性 @ApiOkResponse({ description: '删除分类属性' }) @Del('/category/attribute/:id') async deleteCategoryAttribute(@Param('id') id: number) { try { await this.productService.deleteCategoryAttribute(id); return successResponse(true); } catch (error) { return errorResponse(error?.message || error); } } // 同步库存 SKU 到产品单品 @ApiOkResponse({ description: '同步库存 SKU 到产品单品' }) @Post('/sync-stock') async syncStockToProduct() { try { const data = await this.productService.syncStockToProduct(); return successResponse(data); } catch (error) { return errorResponse(error?.message || error); } } // 同步单个产品到站点 @ApiOkResponse({ description: '同步单个产品到站点', type: SyncProductToSiteResultDTO }) @Post('/sync-to-site') async syncToSite(@Body() body: SyncProductToSiteDTO) { try { const result = await this.productService.syncToSite(body); return successResponse(result); } catch (error) { return errorResponse(error?.message || error); } } // 从站点同步产品到本地 @ApiOkResponse({ description: '从站点同步产品到本地', type: ProductRes }) @Post('/sync-from-site') async syncProductFromSite(@Body() body: { siteId: number; siteProductId: string | number ,sku: string}) { try { const { siteId, siteProductId, sku } = body; const product = await this.productService.syncProductFromSite(siteId, siteProductId, sku); return successResponse(product); } catch (error) { return errorResponse(error?.message || error); } } // 批量从站点同步产品到本地 @ApiOkResponse({ description: '批量从站点同步产品到本地', type: SyncOperationResultDTO }) @Post('/batch-sync-from-site') async batchSyncFromSite(@Body() body: { siteId: number; siteProductIds: (string | number)[] }) { try { throw new Error('批量同步产品到本地暂未实现'); // const { siteId, siteProductIds } = body; // const result = await this.productService.batchSyncFromSite(siteId, siteProductIds.map((id) => ({ siteProductId: id, sku: '' }))); // // 将服务层返回的结果转换为统一格式 // const errors = result.errors.map((error: string) => { // // 提取产品ID部分作为标识符 // const match = error.match(/站点产品ID (\d+) /); // const identifier = match ? match[1] : 'unknown'; // return { // identifier: identifier, // error: error // }; // }); // return successResponse({ // total: siteProductIds.length, // processed: result.synced + errors.length, // synced: result.synced, // errors: errors // }); } catch (error) { return errorResponse(error?.message || error); } } // 批量同步产品到站点 @ApiOkResponse({ description: '批量同步产品到站点', type: SyncOperationResultDTO }) @Post('/batch-sync-to-site') async batchSyncToSite(@Body() body: BatchSyncProductToSiteDTO) { try { const { siteId, data } = body; const result = await this.productService.batchSyncToSite(siteId, data); return successResponse(result); } catch (error) { return errorResponse(error?.message || error); } } }