diff --git a/permutation_fix.md b/permutation_fix.md deleted file mode 100644 index de227d0..0000000 --- a/permutation_fix.md +++ /dev/null @@ -1,184 +0,0 @@ -# Permutation页面列表显示问题分析和修复方案 - -## 问题分析 - -经过代码分析,发现了以下几个可能导致列表不显示的问题: - -### 1. API路径不匹配 -前端代码中引用的API函数名与后端控制器中的路径不一致: -- 前端:`productcontrollerGetcategoriesall`、`productcontrollerGetcategoryattributes`、`productcontrollerGetproductlist` -- 后端实际的API路径:`/product/categories/all`、`/product/category/:id/attributes`、`/product/list` - -### 2. 数据格式问题 -- `getCategoryAttributes`返回的数据结构与前端期望的不匹配 -- 属性值获取逻辑可能存在问题 - -### 3. 组合生成逻辑问题 -- 在生成排列组合时,数据结构和键值对应可能不正确 - -## 修复方案 - -### 后端修复 - -1. **修改getCategoryAttributes方法** - 在`/Users/zksu/Developer/work/workcode/API/src/service/product.service.ts`中: - -```typescript -// 获取分类下的属性配置 -async getCategoryAttributes(categoryId: number): Promise { - 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, // 用于generateKeyFromPermutation - title: attr.attributeDict.title, // 用于列标题 - dict: { - id: attr.attributeDict.id, - name: attr.attributeDict.name, - title: attr.attributeDict.title, - items: attr.attributeDict.items || [] - } - })); -} -``` - -2. **确保dict/items接口可用** - 检查字典项获取接口: - -在`/Users/zksu/Developer/work/workcode/API/src/controller/dict.controller.ts`中添加或确认: - -```typescript -@Get('/items') -async getDictItems(@Query('dictId') dictId: number) { - try { - const dict = await this.dictModel.findOne({ - where: { id: dictId }, - relations: ['items'] - }); - - if (!dict) { - return []; - } - - return dict.items || []; - } catch (error) { - return errorResponse(error?.message || error); - } -} -``` - -### 前端修复建议 - -1. **添加错误处理和调试信息**: - -```typescript -// 在获取属性值的地方添加错误处理 -const fetchData = async () => { - setLoading(true); - try { - // 1. Fetch Attributes - const attrRes = await productcontrollerGetcategoryattributes({ - id: categoryId, - }); - console.log('Attributes response:', attrRes); // 调试用 - const attrs = Array.isArray(attrRes) ? attrRes : attrRes?.data || []; - setAttributes(attrs); - - // 2. Fetch Attribute Values (Dict Items) - const valuesMap: Record = {}; - for (const attr of attrs) { - const dictId = attr.dict?.id || attr.dictId; - if (dictId) { - try { - const itemsRes = await request('/dict/items', { - params: { dictId }, - }); - console.log(`Dict items for ${attr.name}:`, itemsRes); // 调试用 - valuesMap[attr.name] = itemsRes || []; - } catch (error) { - console.error(`Failed to fetch items for dict ${dictId}:`, error); - valuesMap[attr.name] = []; - } - } - } - setAttributeValues(valuesMap); - - // 3. Fetch Existing Products - await fetchProducts(categoryId); - } catch (error) { - console.error('Error in fetchData:', error); - message.error('获取数据失败'); - } finally { - setLoading(false); - } -}; -``` - -2. **修复组合生成逻辑**: - -```typescript -// 修改generateKeyFromPermutation函数 -const generateKeyFromPermutation = (perm: any) => { - const parts = Object.keys(perm).map((attrName) => { - const valItem = perm[attrName]; - const val = valItem.name || valItem.value; // 兼容不同的数据格式 - return `${attrName}:${val}`; - }); - return parts.sort().join('|'); -}; - -// 修改generateAttributeKey函数 -const generateAttributeKey = (attrs: any[]) => { - const parts = attrs.map((a) => { - const key = a.dict?.name || a.dictName || a.name; - const val = a.name || a.value; - return `${key}:${val}`; - }); - return parts.sort().join('|'); -}; -``` - -3. **添加空状态处理**: - -```typescript -// 在ProTable中添加空状态提示 - -``` - -## 调试步骤 - -1. **检查网络请求**: - - 打开浏览器开发者工具 - - 检查 `/product/categories/all` 请求是否成功 - - 检查 `/product/category/:id/attributes` 请求返回的数据格式 - - 检查 `/dict/items?dictId=:id` 请求是否成功 - - 检查 `/product/list` 请求是否成功 - -2. **检查控制台日志**: - - 查看属性数据是否正确加载 - - 查看属性值是否正确获取 - - 查看排列组合是否正确生成 - -3. **检查数据结构**: - - 确认 `attributes` 数组是否正确 - - 确认 `attributeValues` 对象是否正确填充 - - 确认 `permutations` 数组是否正确生成 - -## 测试验证 - -1. 选择一个有属性配置的分类 -2. 确认属性有对应的字典项 -3. 检查排列组合是否正确显示 -4. 验证现有产品匹配是否正确 \ No newline at end of file diff --git a/src/configuration.ts b/src/configuration.ts index a27dbb0..9711264 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -99,7 +99,7 @@ export class MainConfiguration { } /** - * 初始化数据库(如果不存在则创建) + * 初始化数据库(如果不存在则创建) */ private async initializeDatabase(): Promise { // 使用注入的数据库配置 diff --git a/src/dto/customer.dto.ts b/src/dto/customer.dto.ts index b93b2b3..74e78b8 100644 --- a/src/dto/customer.dto.ts +++ b/src/dto/customer.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty } from '@midwayjs/swagger'; import { UnifiedSearchParamsDTO } from './api.dto'; import { Customer } from '../entity/customer.entity'; -// 客户基本信息DTO(用于响应) +// 客户基本信息DTO(用于响应) export class CustomerDTO extends Customer{ @ApiProperty({ description: '客户ID' }) id: number; @@ -163,11 +163,11 @@ export class UpdateCustomerDTO { tags?: string[]; } -// 查询单个客户响应DTO(继承基本信息) +// 查询单个客户响应DTO(继承基本信息) export class GetCustomerDTO extends CustomerDTO { // 可以添加额外的详细信息字段 } -// 客户统计信息DTO(包含订单统计) +// 客户统计信息DTO(包含订单统计) export class CustomerStatisticDTO extends CustomerDTO { @ApiProperty({ description: '创建日期' }) date_created: Date; @@ -209,7 +209,7 @@ export class CustomerStatisticWhereDTO { customerId?: number; } -// 客户统计查询参数DTO(继承通用查询参数) +// 客户统计查询参数DTO(继承通用查询参数) export type CustomerStatisticQueryParamsDTO = UnifiedSearchParamsDTO; // 客户统计列表响应DTO @@ -259,7 +259,7 @@ export class BatchDeleteCustomerDTO { // ====================== 查询操作 ====================== -// 客户查询条件DTO(用于UnifiedSearchParamsDTO的where参数) +// 客户查询条件DTO(用于UnifiedSearchParamsDTO的where参数) export class CustomerWhereDTO { @ApiProperty({ description: '邮箱筛选', required: false }) email?: string; @@ -284,10 +284,10 @@ export class CustomerWhereDTO { role?: string; } -// 客户查询参数DTO(继承通用查询参数) +// 客户查询参数DTO(继承通用查询参数) export type CustomerQueryParamsDTO = UnifiedSearchParamsDTO; -// 客户列表响应DTO(参考site-api.dto.ts中的分页格式) +// 客户列表响应DTO(参考site-api.dto.ts中的分页格式) export class CustomerListResponseDTO { @ApiProperty({ description: '客户列表', type: [CustomerDTO] }) items: CustomerDTO[]; @@ -359,6 +359,6 @@ export class SyncCustomersDTO { @ApiProperty({ description: '站点ID' }) siteId: number; - @ApiProperty({ description: '查询参数(支持where和orderBy)', type: UnifiedSearchParamsDTO, required: false }) + @ApiProperty({ description: '查询参数(支持where和orderBy)', type: UnifiedSearchParamsDTO, required: false }) params?: UnifiedSearchParamsDTO; } \ No newline at end of file diff --git a/src/dto/shopyy.dto.ts b/src/dto/shopyy.dto.ts index 5aed39f..e775bdd 100644 --- a/src/dto/shopyy.dto.ts +++ b/src/dto/shopyy.dto.ts @@ -22,7 +22,7 @@ export class ShopyyAllProductQuery { id?: string; /** 商品标题,支持模糊查询 */ title?: string; - /** 商品状态,例如:上架、下架、删除等(具体值参考 Shopyy 接口文档) */ + /** 商品状态,例如:上架、下架、删除等(具体值参考 Shopyy 接口文档) */ status?: string; /** 商品SKU编码,库存保有单位,精确或模糊匹配 */ sku?: string; @@ -34,21 +34,21 @@ export class ShopyyAllProductQuery { variant_price_min?: string; /** 变体价格最大值,筛选变体价格小于等于该值的商品 */ variant_price_max?: string; - /** 变体划线价(原价)最小值,筛选变体划线价大于等于该值的商品 */ + /** 变体划线价(原价)最小值,筛选变体划线价大于等于该值的商品 */ variant_compare_at_price_min?: string; - /** 变体划线价(原价)最大值,筛选变体划线价小于等于该值的商品 */ + /** 变体划线价(原价)最大值,筛选变体划线价小于等于该值的商品 */ variant_compare_at_price_max?: string; - /** 变体重量最小值,筛选变体重量大于等于该值的商品(单位参考接口文档) */ + /** 变体重量最小值,筛选变体重量大于等于该值的商品(单位参考接口文档) */ variant_weight_min?: string; - /** 变体重量最大值,筛选变体重量小于等于该值的商品(单位参考接口文档) */ + /** 变体重量最大值,筛选变体重量小于等于该值的商品(单位参考接口文档) */ variant_weight_max?: string; - /** 商品创建时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ + /** 商品创建时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ created_at_min?: string; - /** 商品创建时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ + /** 商品创建时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ created_at_max?: string; - /** 商品更新时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ + /** 商品更新时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ updated_at_min?: string; - /** 商品更新时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ + /** 商品更新时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */ updated_at_max?: string; } // 产品类型 @@ -133,9 +133,9 @@ export interface ShopyyVariant { // // 订单查询参数类型 export interface ShopyyOrderQuery { - // 订单ID集合 多个ID用','联接 例:1,2,3 + // 订单ID集合 多个ID用','联接 例:1,2,3 ids?: string; - // 订单状态 100 未完成;110 待处理;180 已完成(确认收货); 190 取消; + // 订单状态 100 未完成;110 待处理;180 已完成(确认收货); 190 取消; status?: string; // 物流状态 300 未发货;310 部分发货;320 已发货;330(确认收货) fulfillment_status?: string; @@ -159,9 +159,9 @@ export interface ShopyyOrderQuery { page?: string; // 每页条数 limit?: string; - // 排序字段(默认id) id=订单ID updated_at=最后更新时间 pay_at=支付时间 + // 排序字段(默认id) id=订单ID updated_at=最后更新时间 pay_at=支付时间 order_field?: string; - // 排序方式(默认desc) desc=降序 asc=升序 + // 排序方式(默认desc) desc=降序 asc=升序 order_by?: string; // 订单列表类型 group?: string; @@ -513,7 +513,7 @@ export class ShopyyFulfillmentDTO { "tracking_number": string; "courier_code": number; "note": string; - "mode": "replace" | 'cover' | null// 模式 replace(替换) cover (覆盖) 空(新增) + "mode": "replace" | 'cover' | null// 模式 replace(替换) cover (覆盖) 空(新增) } // https://www.apizza.net/project/e114fb8e628e0f604379f5b26f0d8330/browse export class ShopyPartFulfillmentDTO { diff --git a/src/dto/site-api.dto.ts b/src/dto/site-api.dto.ts index ccf0a87..5c8e1d2 100644 --- a/src/dto/site-api.dto.ts +++ b/src/dto/site-api.dto.ts @@ -141,7 +141,7 @@ export class UnifiedProductAttributeDTO { @ApiProperty({ description: '属性选项', type: [String] }) options: string[]; - @ApiProperty({ description: '变体属性值(单个值)', required: false }) + @ApiProperty({ description: '变体属性值(单个值)', required: false }) option?: string; // 这个是属性的父级字典项 dict?: Dict; diff --git a/src/dto/site.dto.ts b/src/dto/site.dto.ts index 4ad43da..7c0267d 100644 --- a/src/dto/site.dto.ts +++ b/src/dto/site.dto.ts @@ -152,7 +152,7 @@ export class QuerySiteDTO { @Rule(RuleType.boolean().optional()) isDisabled?: boolean; - @ApiProperty({ description: '站点ID列表(逗号分隔)', required: false }) + @ApiProperty({ description: '站点ID列表(逗号分隔)', required: false }) @Rule(RuleType.string().optional()) ids?: string; } diff --git a/src/dto/woocommerce.dto.ts b/src/dto/woocommerce.dto.ts index cb09484..5077450 100644 --- a/src/dto/woocommerce.dto.ts +++ b/src/dto/woocommerce.dto.ts @@ -8,11 +8,11 @@ export interface WooProduct { id: number; // 创建时间 date_created: string; - // 创建时间(GMT) + // 创建时间(GMT) date_created_gmt: string; // 更新时间 date_modified: string; - // 更新时间(GMT) + // 更新时间(GMT) date_modified_gmt: string; // 产品类型 simple grouped external variable type: string; @@ -20,7 +20,7 @@ export interface WooProduct { status: string; // 是否为特色产品 featured: boolean; - // 目录可见性选项:visible, catalog, search and hidden. Default is visible. + // 目录可见性选项:visible, catalog, search and hidden. Default is visible. catalog_visibility: string; // 常规价格 @@ -130,11 +130,11 @@ export interface WooVariation { id: number; // 创建时间 date_created: string; - // 创建时间(GMT) + // 创建时间(GMT) date_created_gmt: string; // 更新时间 date_modified: string; - // 更新时间(GMT) + // 更新时间(GMT) date_modified_gmt: string; // 变体描述 description: string; @@ -150,11 +150,11 @@ export interface WooVariation { price_html?: string; // 促销开始日期 date_on_sale_from?: string; - // 促销开始日期(GMT) + // 促销开始日期(GMT) date_on_sale_from_gmt?: string; // 促销结束日期 date_on_sale_to?: string; - // 促销结束日期(GMT) + // 促销结束日期(GMT) date_on_sale_to_gmt?: string; // 是否在促销中 on_sale: boolean; diff --git a/src/enums/base.enum.ts b/src/enums/base.enum.ts index 68be364..c79c4c9 100644 --- a/src/enums/base.enum.ts +++ b/src/enums/base.enum.ts @@ -42,7 +42,7 @@ export enum OrderStatus { REFUNDED = 'refunded', // 已退款 FAILED = 'failed', // 失败订单 DRAFT = 'draft', // 草稿 - AUTO_DRAFT = 'auto-draft', // 自动草稿(TODO:不知道为什么出现) + AUTO_DRAFT = 'auto-draft', // 自动草稿(TODO:不知道为什么出现) // TRASH = 'trash', // refund 也就是退款相关的状态 diff --git a/src/interface/platform.interface.ts b/src/interface/platform.interface.ts index 231cd8d..1fd066e 100644 --- a/src/interface/platform.interface.ts +++ b/src/interface/platform.interface.ts @@ -53,7 +53,7 @@ export interface IPlatformService { getOrder(siteId: number, orderId: string): Promise; /** - * 获取订阅列表(如果平台支持) + * 获取订阅列表(如果平台支持) * @param siteId 站点ID * @returns 订阅列表数据 */ diff --git a/src/service/customer.service.ts b/src/service/customer.service.ts index 28e61b4..47f430f 100644 --- a/src/service/customer.service.ts +++ b/src/service/customer.service.ts @@ -66,7 +66,7 @@ export class CustomerService { } if (typeof dateValue === 'number') { - // 处理Unix时间戳(秒或毫秒) + // 处理Unix时间戳(秒或毫秒) return new Date(dateValue > 9999999999 ? dateValue : dateValue * 1000); } @@ -95,7 +95,7 @@ export class CustomerService { } /** - * 创建或更新客户(upsert) + * 创建或更新客户(upsert) * 如果客户存在则更新,不存在则创建 */ async upsertCustomer( @@ -157,24 +157,24 @@ export class CustomerService { /** * 从站点同步客户数据 - * 第一步:调用adapter获取站点客户数据 - * 第二步:通过upsertManyCustomers保存这些客户 + * 第一步:调用adapter获取站点客户数据 + * 第二步:通过upsertManyCustomers保存这些客户 */ async syncCustomersFromSite( siteId: number, params?: UnifiedSearchParamsDTO ): Promise { try { - // 第一步:获取适配器并从站点获取客户数据 + // 第一步:获取适配器并从站点获取客户数据 const adapter = await this.siteApiService.getAdapter(siteId); const siteCustomers = await adapter.getAllCustomers(params || {}); - // 第二步:将站点客户数据转换为客户实体数据 + // 第二步:将站点客户数据转换为客户实体数据 const customersData = siteCustomers.map(siteCustomer => { return this.mapSiteCustomerToCustomer(siteCustomer, siteId); }) - // 第三步:批量upsert客户数据 + // 第三步:批量upsert客户数据 const upsertResult = await this.upsertManyCustomers(customersData); return { total: siteCustomers.length, @@ -192,7 +192,7 @@ export class CustomerService { } /** - * 获取客户统计列表(包含订单统计信息) + * 获取客户统计列表(包含订单统计信息) * 支持分页、搜索和排序功能 * 使用原生SQL查询实现复杂的统计逻辑 */ @@ -363,7 +363,7 @@ export class CustomerService { } /** - * 获取纯粹的客户列表(不包含订单统计信息) + * 获取纯粹的客户列表(不包含订单统计信息) * 支持基本的分页、搜索和排序功能 * 使用TypeORM查询构建器实现 */ diff --git a/src/service/dict.service.ts b/src/service/dict.service.ts index 14a5a0b..6d683a6 100644 --- a/src/service/dict.service.ts +++ b/src/service/dict.service.ts @@ -239,7 +239,7 @@ export class DictService { } // 更新或创建字典项 (Upsert) - // 如果字典项已存在(根据 name 和 dictId 判断),则更新;否则创建新的 + // 如果字典项已存在(根据 name 和 dictId 判断),则更新;否则创建新的 async upsertDictItem(dictId: number, itemData: { name: string; title: string; @@ -252,7 +252,7 @@ export class DictService { // 格式化 name const formattedName = this.formatName(itemData.name); - // 查找是否已存在该字典项(根据 name 和 dictId) + // 查找是否已存在该字典项(根据 name 和 dictId) const existingItem = await this.dictItemModel.findOne({ where: { name: formattedName, diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 355790c..3dfd68b 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -479,7 +479,7 @@ export class OrderService { // 如果不能更新 ERP 状态,则保留原有的 orderStatus entity.orderStatus = existingOrder.orderStatus; } - // 更新订单数据(包括 shipping、billing 等字段) + // 更新订单数据(包括 shipping、billing 等字段) await this.orderModel.update(existingOrder.id, entity); entity.id = existingOrder.id; return entity; @@ -2567,8 +2567,8 @@ export class OrderService { * 导出数据为CSV格式 * @param {any[]} data 数据数组 * @param {Object} options 配置选项 - * @param {string} [options.type='string'] 输出类型:'string' | 'buffer' - * @param {string} [options.fileName] 文件名(仅当需要写入文件时使用) + * @param {string} [options.type='string'] 输出类型:'string' | 'buffer' + * @param {string} [options.fileName] 文件名(仅当需要写入文件时使用) * @param {boolean} [options.writeFile=false] 是否写入文件 * @returns {string|Buffer} 根据type返回字符串或Buffer */ @@ -2617,7 +2617,7 @@ async exportToCsv(data: any[], options: { type?: 'string' | 'buffer'; fileName?: // 获取当前用户目录 const userHomeDir = os.homedir(); - // 构建目标路径(下载目录) + // 构建目标路径(下载目录) const downloadsDir = path.join(userHomeDir, 'Downloads'); // 确保下载目录存在 diff --git a/src/service/shopyy.service.ts b/src/service/shopyy.service.ts index 0081f55..4a663cb 100644 --- a/src/service/shopyy.service.ts +++ b/src/service/shopyy.service.ts @@ -128,7 +128,7 @@ export class ShopyyService { * @returns 完整URL */ private buildURL(baseUrl: string, endpoint: string): string { - // ShopYY API URL格式:https://{shop}.shopyy.com/openapi/{version}/{endpoint} + // ShopYY API URL格式:https://{shop}.shopyy.com/openapi/{version}/{endpoint} const base = baseUrl.replace(/\/$/, ''); const end = endpoint.replace(/^\//, ''); return `${base}/${end}`; diff --git a/src/service/wp.service.ts b/src/service/wp.service.ts index 5dd355e..a4d66e5 100644 --- a/src/service/wp.service.ts +++ b/src/service/wp.service.ts @@ -254,7 +254,7 @@ export class WPService implements IPlatformService { } - // 导出 WooCommerce 产品为特殊CSV(平台特性) + // 导出 WooCommerce 产品为特殊CSV(平台特性) async exportProductsCsvSpecial(site: any, page: number = 1, pageSize: number = 100): Promise { const list = await this.getProducts(site, { page, per_page: pageSize }); const header = ['id','name','type','status','sku','regular_price','sale_price','stock_status','stock_quantity'];