API/src/controller/product.controller.ts

685 lines
21 KiB
TypeScript

import {
Inject,
Post,
Put,
Get,
Body,
Param,
Del,
Query,
Controller,
} from '@midwayjs/core';
import { ProductService } from '../service/product.service';
import { errorResponse, successResponse } from '../utils/response.util';
import { CreateProductDTO, QueryProductDTO, UpdateProductDTO, BatchUpdateProductDTO, BatchDeleteProductDTO } from '../dto/product.dto';
import { ApiOkResponse } from '@midwayjs/swagger';
import { BooleanRes, ProductListRes, ProductRes, ProductsRes } from '../dto/reponse.dto';
import { ContentType, Files } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
@Controller('/product')
export class ProductController {
@Inject()
productService: ProductService;
@Inject()
ctx: Context;
@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: QueryProductDTO
): Promise<ProductListRes> {
const { current = 1, pageSize = 10, name, brandId, sortField, sortOrder } = query;
try {
const data = await this.productService.getProductList(
{ current, pageSize },
name,
brandId,
sortField,
sortOrder
);
return successResponse(data);
} catch (error) {
console.log(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: any) {
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: any) {
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);
}
}
}