527 lines
16 KiB
TypeScript
527 lines
16 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, SetProductComponentsDTO } 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;
|
||
ProductRes;
|
||
|
||
@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 } = query;
|
||
try {
|
||
const data = await this.productService.getProductList(
|
||
{ current, pageSize },
|
||
name,
|
||
brandId
|
||
);
|
||
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 = 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?.data) return errorResponse('未接收到上传文件');
|
||
const result = await this.productService.importProductsCSV(file.data);
|
||
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 = this.productService.updateProduct(id, productData);
|
||
return successResponse(data);
|
||
} 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 = this.productService.updatenameCn(id, nameCn);
|
||
return successResponse(data);
|
||
} catch (error) {
|
||
return errorResponse(error?.message || error);
|
||
}
|
||
}
|
||
|
||
@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);
|
||
}
|
||
}
|
||
|
||
// 中文注释:设置产品的库存组成(覆盖式)
|
||
@ApiOkResponse()
|
||
@Post('/:id/components')
|
||
async setProductComponents(@Param('id') id: number, @Body() body: SetProductComponentsDTO) {
|
||
try {
|
||
const data = await this.productService.setProductComponents(id, body?.items || []);
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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 }) {
|
||
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);
|
||
}
|
||
}
|
||
}
|