feat(分类): 添加分类管理相关功能
refactor(商品类型): 将商品类型从 simple 改为 single fix(数据库配置): 更新数据库连接配置 style(分类): 修正分类中文标题 chore: 移除无用的脚本文件
This commit is contained in:
parent
0180360519
commit
b8aee530e8
|
|
@ -1 +0,0 @@
|
|||
export {};
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const mysql = require("mysql2/promise");
|
||||
async function run() {
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
socketPath: '/Users/zksu/Library/Application Support/Local/run/oLbUT7qMU/mysql/mysqld.sock',
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
});
|
||||
console.log('Connected to database server.');
|
||||
await connection.query('CREATE DATABASE IF NOT EXISTS inventory');
|
||||
console.log('Database "inventory" created or already exists.');
|
||||
await connection.end();
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Error:', e);
|
||||
}
|
||||
}
|
||||
run();
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlX2RiLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY3JlYXRlX2RiLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQ0Esd0NBQXdDO0FBRXhDLEtBQUssVUFBVSxHQUFHO0lBQ2hCLElBQUksQ0FBQztRQUNILE1BQU0sVUFBVSxHQUFHLE1BQU0sS0FBSyxDQUFDLGdCQUFnQixDQUFDO1lBQzlDLFVBQVUsRUFBRSwrRUFBK0U7WUFDM0YsSUFBSSxFQUFFLE1BQU07WUFDWixRQUFRLEVBQUUsTUFBTTtTQUNqQixDQUFDLENBQUM7UUFFSCxPQUFPLENBQUMsR0FBRyxDQUFDLCtCQUErQixDQUFDLENBQUM7UUFFN0MsTUFBTSxVQUFVLENBQUMsS0FBSyxDQUFDLHlDQUF5QyxDQUFDLENBQUM7UUFDbEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1FBRS9ELE1BQU0sVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ1gsT0FBTyxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDN0IsQ0FBQztBQUNILENBQUM7QUFFRCxHQUFHLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIlxuaW1wb3J0ICogYXMgbXlzcWwgZnJvbSAnbXlzcWwyL3Byb21pc2UnO1xuXG5hc3luYyBmdW5jdGlvbiBydW4oKSB7XG4gIHRyeSB7XG4gICAgY29uc3QgY29ubmVjdGlvbiA9IGF3YWl0IG15c3FsLmNyZWF0ZUNvbm5lY3Rpb24oe1xuICAgICAgc29ja2V0UGF0aDogJy9Vc2Vycy96a3N1L0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9Mb2NhbC9ydW4vb0xiVVQ3cU1VL215c3FsL215c3FsZC5zb2NrJyxcbiAgICAgIHVzZXI6ICdyb290JyxcbiAgICAgIHBhc3N3b3JkOiAncm9vdCcsXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZygnQ29ubmVjdGVkIHRvIGRhdGFiYXNlIHNlcnZlci4nKTtcblxuICAgIGF3YWl0IGNvbm5lY3Rpb24ucXVlcnkoJ0NSRUFURSBEQVRBQkFTRSBJRiBOT1QgRVhJU1RTIGludmVudG9yeScpO1xuICAgIGNvbnNvbGUubG9nKCdEYXRhYmFzZSBcImludmVudG9yeVwiIGNyZWF0ZWQgb3IgYWxyZWFkeSBleGlzdHMuJyk7XG4gICAgXG4gICAgYXdhaXQgY29ubmVjdGlvbi5lbmQoKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yOicsIGUpO1xuICB9XG59XG5cbnJ1bigpO1xuIl19
|
||||
|
|
@ -1 +0,0 @@
|
|||
export {};
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const mysql = require("mysql2/promise");
|
||||
async function run() {
|
||||
try {
|
||||
const connection = await mysql.createConnection({
|
||||
host: '127.0.0.1',
|
||||
port: 10014,
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
database: 'inventory'
|
||||
});
|
||||
console.log('Connected to database.');
|
||||
const tables = ['product', 'category_attribute', 'category'];
|
||||
for (const table of tables) {
|
||||
console.log(`\nConstraints for table: ${table}`);
|
||||
const [rows] = await connection.execute(`
|
||||
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE
|
||||
FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = 'inventory' AND TABLE_NAME = '${table}'
|
||||
`);
|
||||
console.table(rows);
|
||||
}
|
||||
await connection.end();
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Error:', e);
|
||||
}
|
||||
}
|
||||
run();
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlhZ25vc2VfZGIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJkaWFnbm9zZV9kYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUNBLHdDQUF3QztBQUV4QyxLQUFLLFVBQVUsR0FBRztJQUNoQixJQUFJLENBQUM7UUFDSCxNQUFNLFVBQVUsR0FBRyxNQUFNLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQztZQUM5QyxJQUFJLEVBQUUsV0FBVztZQUNqQixJQUFJLEVBQUUsS0FBSztZQUNYLElBQUksRUFBRSxNQUFNO1lBQ1osUUFBUSxFQUFFLE1BQU07WUFDaEIsUUFBUSxFQUFFLFdBQVc7U0FDdEIsQ0FBQyxDQUFDO1FBRUgsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBRXRDLE1BQU0sTUFBTSxHQUFHLENBQUMsU0FBUyxFQUFFLG9CQUFvQixFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBRTdELEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7WUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUNqRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxVQUFVLENBQUMsT0FBTyxDQUFDOzs7NkRBR2UsS0FBSztPQUMzRCxDQUFDLENBQUM7WUFDSCxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3RCLENBQUM7UUFFRCxNQUFNLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNYLE9BQU8sQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdCLENBQUM7QUFDSCxDQUFDO0FBRUQsR0FBRyxFQUFFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJcbmltcG9ydCAqIGFzIG15c3FsIGZyb20gJ215c3FsMi9wcm9taXNlJztcblxuYXN5bmMgZnVuY3Rpb24gcnVuKCkge1xuICB0cnkge1xuICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBhd2FpdCBteXNxbC5jcmVhdGVDb25uZWN0aW9uKHtcbiAgICAgIGhvc3Q6ICcxMjcuMC4wLjEnLFxuICAgICAgcG9ydDogMTAwMTQsXG4gICAgICB1c2VyOiAncm9vdCcsXG4gICAgICBwYXNzd29yZDogJ3Jvb3QnLFxuICAgICAgZGF0YWJhc2U6ICdpbnZlbnRvcnknXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZygnQ29ubmVjdGVkIHRvIGRhdGFiYXNlLicpO1xuXG4gICAgY29uc3QgdGFibGVzID0gWydwcm9kdWN0JywgJ2NhdGVnb3J5X2F0dHJpYnV0ZScsICdjYXRlZ29yeSddO1xuXG4gICAgZm9yIChjb25zdCB0YWJsZSBvZiB0YWJsZXMpIHtcbiAgICAgIGNvbnNvbGUubG9nKGBcXG5Db25zdHJhaW50cyBmb3IgdGFibGU6ICR7dGFibGV9YCk7XG4gICAgICBjb25zdCBbcm93c10gPSBhd2FpdCBjb25uZWN0aW9uLmV4ZWN1dGUoYFxuICAgICAgICBTRUxFQ1QgQ09OU1RSQUlOVF9OQU1FLCBDT05TVFJBSU5UX1RZUEUgXG4gICAgICAgIEZST00gaW5mb3JtYXRpb25fc2NoZW1hLlRBQkxFX0NPTlNUUkFJTlRTIFxuICAgICAgICBXSEVSRSBUQUJMRV9TQ0hFTUEgPSAnaW52ZW50b3J5JyBBTkQgVEFCTEVfTkFNRSA9ICcke3RhYmxlfSdcbiAgICAgIGApO1xuICAgICAgY29uc29sZS50YWJsZShyb3dzKTtcbiAgICB9XG4gICAgXG4gICAgYXdhaXQgY29ubmVjdGlvbi5lbmQoKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yOicsIGUpO1xuICB9XG59XG5cbnJ1bigpO1xuIl19
|
||||
|
|
@ -536,4 +536,88 @@ export class ProductController {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@ import { SeederOptions } from 'typeorm-extension';
|
|||
|
||||
const options: DataSourceOptions & SeederOptions = {
|
||||
type: 'mysql',
|
||||
// host: 'localhost',
|
||||
// port: 10014,
|
||||
socketPath: '/Users/zksu/Library/Application Support/Local/run/oLbUT7qMU/mysql/mysqld.sock',
|
||||
host: 'localhost',
|
||||
port: 23306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
password: '12345678',
|
||||
database: 'inventory',
|
||||
synchronize: true,
|
||||
logging: true,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default class CategorySeeder implements Seeder {
|
|||
{
|
||||
name: 'pouches-can',
|
||||
title: 'Pouches Can',
|
||||
titleCN: ' nicotine 袋',
|
||||
titleCN: '口含烟盒',
|
||||
sort: 3
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ export class CreateProductDTO {
|
|||
@Rule(RuleType.number())
|
||||
promotionPrice?: number;
|
||||
|
||||
// 中文注释:商品类型(默认 simple;bundle 需手动设置组成)
|
||||
@ApiProperty({ description: '商品类型', enum: ['simple', 'bundle'], default: 'simple', required: false })
|
||||
@Rule(RuleType.string().valid('simple', 'bundle').default('simple'))
|
||||
// 中文注释:商品类型(默认 single;bundle 需手动设置组成)
|
||||
@ApiProperty({ description: '商品类型', enum: ['single', 'bundle'], default: 'single', required: false })
|
||||
@Rule(RuleType.string().valid('single', 'bundle').default('single'))
|
||||
type?: string;
|
||||
|
||||
// 中文注释:仅当 type 为 'bundle' 时,才需要提供 components
|
||||
|
|
@ -123,9 +123,9 @@ export class UpdateProductDTO {
|
|||
@Rule(RuleType.array())
|
||||
attributes?: AttributeInputDTO[];
|
||||
|
||||
// 中文注释:商品类型更新(simple 或 bundle)
|
||||
@ApiProperty({ description: '商品类型', enum: ['simple', 'bundle'], required: false })
|
||||
@Rule(RuleType.string().valid('simple', 'bundle'))
|
||||
// 中文注释:商品类型(single 或 bundle)
|
||||
@ApiProperty({ description: '商品类型', enum: ['single', 'bundle'], required: false })
|
||||
@Rule(RuleType.string().valid('single', 'bundle'))
|
||||
type?: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { Stock } from '../entity/stock.entity';
|
|||
import { StockPoint } from '../entity/stock_point.entity';
|
||||
import { ProductStockComponent } from '../entity/product_stock_component.entity';
|
||||
import { Category } from '../entity/category.entity';
|
||||
import { CategoryAttribute } from '../entity/category_attribute.entity';
|
||||
|
||||
@Provide()
|
||||
export class ProductService {
|
||||
|
|
@ -70,7 +71,96 @@ export class ProductService {
|
|||
async getWpProducts() {
|
||||
return this.wpProductModel.find();
|
||||
}
|
||||
// 获取所有分类
|
||||
async getCategoriesAll(): Promise<Category[]> {
|
||||
return this.categoryModel.find({
|
||||
order: {
|
||||
sort: 'ASC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 获取分类下的属性配置
|
||||
async getCategoryAttributes(categoryId: number): Promise<any[]> {
|
||||
const category = await this.categoryModel.findOne({
|
||||
where: { id: categoryId },
|
||||
relations: ['attributes', 'attributes.attributeDict', 'attributes.attributeDict.items'],
|
||||
});
|
||||
|
||||
if (!category) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 格式化返回,方便前端使用
|
||||
return category.attributes.map(attr => ({
|
||||
id: attr.id,
|
||||
dictId: attr.attributeDict.id,
|
||||
name: attr.attributeDict.name,
|
||||
title: attr.attributeDict.title,
|
||||
items: attr.attributeDict.items, // 如果需要返回具体的选项
|
||||
}));
|
||||
}
|
||||
|
||||
// 创建分类
|
||||
async createCategory(payload: Partial<Category>): Promise<Category> {
|
||||
const exists = await this.categoryModel.findOne({ where: { name: payload.name } });
|
||||
if (exists) {
|
||||
throw new Error('分类已存在');
|
||||
}
|
||||
return this.categoryModel.save(payload);
|
||||
}
|
||||
|
||||
// 更新分类
|
||||
async updateCategory(id: number, payload: Partial<Category>): Promise<Category> {
|
||||
const category = await this.categoryModel.findOne({ where: { id } });
|
||||
if (!category) {
|
||||
throw new Error('分类不存在');
|
||||
}
|
||||
await this.categoryModel.update(id, payload);
|
||||
return this.categoryModel.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
// 删除分类
|
||||
async deleteCategory(id: number): Promise<boolean> {
|
||||
const result = await this.categoryModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
// 创建分类属性关联
|
||||
async createCategoryAttribute(payload: { categoryId: number; dictId: number }): Promise<any> {
|
||||
const category = await this.categoryModel.findOne({ where: { id: payload.categoryId } });
|
||||
if (!category) {
|
||||
throw new Error('分类不存在');
|
||||
}
|
||||
|
||||
const dict = await this.dictModel.findOne({ where: { id: payload.dictId } });
|
||||
if (!dict) {
|
||||
throw new Error('字典不存在');
|
||||
}
|
||||
|
||||
const existing = await this.categoryModel.manager.findOne(CategoryAttribute, {
|
||||
where: {
|
||||
category: { id: payload.categoryId },
|
||||
attributeDict: { id: payload.dictId },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new Error('该属性已关联到此分类');
|
||||
}
|
||||
|
||||
const attr = this.categoryModel.manager.create(CategoryAttribute, {
|
||||
category,
|
||||
attributeDict: dict
|
||||
});
|
||||
return this.categoryModel.manager.save(attr);
|
||||
}
|
||||
|
||||
// 删除分类属性关联
|
||||
async deleteCategoryAttribute(id: number): Promise<boolean> {
|
||||
const result = await this.categoryModel.manager.delete(CategoryAttribute, id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
|
||||
// async findProductsByName(name: string): Promise<Product[]> {
|
||||
|
|
@ -309,7 +399,7 @@ export class ProductService {
|
|||
if (categoryItem) {
|
||||
product.category = categoryItem;
|
||||
}
|
||||
// 条件判断(中文注释:设置商品类型,默认 simple)
|
||||
// 条件判断(中文注释:设置商品类型,默认 single)
|
||||
product.type = (createProductDTO.type as any) || 'single';
|
||||
|
||||
// 生成或设置 SKU(中文注释:基于属性字典项的 name 生成)
|
||||
|
|
|
|||
Loading…
Reference in New Issue