docs: 修正中文标点符号和注释格式
统一将中文注释和文档中的全角括号和冒号改为半角格式 修正部分TODO注释的标点符号 统一接口文档中的描述符号格式
This commit is contained in:
parent
44a7578c50
commit
541276ec20
|
|
@ -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<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, // 用于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<string, any[]> = {};
|
|
||||||
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中添加空状态提示
|
|
||||||
<ProTable
|
|
||||||
// ... 其他属性
|
|
||||||
locale={{
|
|
||||||
emptyText: permutations.length === 0 && !loading ? '暂无数据,请检查分类属性配置' : '暂无数据'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 调试步骤
|
|
||||||
|
|
||||||
1. **检查网络请求**:
|
|
||||||
- 打开浏览器开发者工具
|
|
||||||
- 检查 `/product/categories/all` 请求是否成功
|
|
||||||
- 检查 `/product/category/:id/attributes` 请求返回的数据格式
|
|
||||||
- 检查 `/dict/items?dictId=:id` 请求是否成功
|
|
||||||
- 检查 `/product/list` 请求是否成功
|
|
||||||
|
|
||||||
2. **检查控制台日志**:
|
|
||||||
- 查看属性数据是否正确加载
|
|
||||||
- 查看属性值是否正确获取
|
|
||||||
- 查看排列组合是否正确生成
|
|
||||||
|
|
||||||
3. **检查数据结构**:
|
|
||||||
- 确认 `attributes` 数组是否正确
|
|
||||||
- 确认 `attributeValues` 对象是否正确填充
|
|
||||||
- 确认 `permutations` 数组是否正确生成
|
|
||||||
|
|
||||||
## 测试验证
|
|
||||||
|
|
||||||
1. 选择一个有属性配置的分类
|
|
||||||
2. 确认属性有对应的字典项
|
|
||||||
3. 检查排列组合是否正确显示
|
|
||||||
4. 验证现有产品匹配是否正确
|
|
||||||
|
|
@ -99,7 +99,7 @@ export class MainConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化数据库(如果不存在则创建)
|
* 初始化数据库(如果不存在则创建)
|
||||||
*/
|
*/
|
||||||
private async initializeDatabase(): Promise<void> {
|
private async initializeDatabase(): Promise<void> {
|
||||||
// 使用注入的数据库配置
|
// 使用注入的数据库配置
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { UnifiedSearchParamsDTO } from './api.dto';
|
import { UnifiedSearchParamsDTO } from './api.dto';
|
||||||
import { Customer } from '../entity/customer.entity';
|
import { Customer } from '../entity/customer.entity';
|
||||||
|
|
||||||
// 客户基本信息DTO(用于响应)
|
// 客户基本信息DTO(用于响应)
|
||||||
export class CustomerDTO extends Customer{
|
export class CustomerDTO extends Customer{
|
||||||
@ApiProperty({ description: '客户ID' })
|
@ApiProperty({ description: '客户ID' })
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -163,11 +163,11 @@ export class UpdateCustomerDTO {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询单个客户响应DTO(继承基本信息)
|
// 查询单个客户响应DTO(继承基本信息)
|
||||||
export class GetCustomerDTO extends CustomerDTO {
|
export class GetCustomerDTO extends CustomerDTO {
|
||||||
// 可以添加额外的详细信息字段
|
// 可以添加额外的详细信息字段
|
||||||
}
|
}
|
||||||
// 客户统计信息DTO(包含订单统计)
|
// 客户统计信息DTO(包含订单统计)
|
||||||
export class CustomerStatisticDTO extends CustomerDTO {
|
export class CustomerStatisticDTO extends CustomerDTO {
|
||||||
@ApiProperty({ description: '创建日期' })
|
@ApiProperty({ description: '创建日期' })
|
||||||
date_created: Date;
|
date_created: Date;
|
||||||
|
|
@ -209,7 +209,7 @@ export class CustomerStatisticWhereDTO {
|
||||||
customerId?: number;
|
customerId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 客户统计查询参数DTO(继承通用查询参数)
|
// 客户统计查询参数DTO(继承通用查询参数)
|
||||||
export type CustomerStatisticQueryParamsDTO = UnifiedSearchParamsDTO<CustomerStatisticWhereDTO>;
|
export type CustomerStatisticQueryParamsDTO = UnifiedSearchParamsDTO<CustomerStatisticWhereDTO>;
|
||||||
|
|
||||||
// 客户统计列表响应DTO
|
// 客户统计列表响应DTO
|
||||||
|
|
@ -259,7 +259,7 @@ export class BatchDeleteCustomerDTO {
|
||||||
|
|
||||||
// ====================== 查询操作 ======================
|
// ====================== 查询操作 ======================
|
||||||
|
|
||||||
// 客户查询条件DTO(用于UnifiedSearchParamsDTO的where参数)
|
// 客户查询条件DTO(用于UnifiedSearchParamsDTO的where参数)
|
||||||
export class CustomerWhereDTO {
|
export class CustomerWhereDTO {
|
||||||
@ApiProperty({ description: '邮箱筛选', required: false })
|
@ApiProperty({ description: '邮箱筛选', required: false })
|
||||||
email?: string;
|
email?: string;
|
||||||
|
|
@ -284,10 +284,10 @@ export class CustomerWhereDTO {
|
||||||
role?: string;
|
role?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 客户查询参数DTO(继承通用查询参数)
|
// 客户查询参数DTO(继承通用查询参数)
|
||||||
export type CustomerQueryParamsDTO = UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
export type CustomerQueryParamsDTO = UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
||||||
|
|
||||||
// 客户列表响应DTO(参考site-api.dto.ts中的分页格式)
|
// 客户列表响应DTO(参考site-api.dto.ts中的分页格式)
|
||||||
export class CustomerListResponseDTO {
|
export class CustomerListResponseDTO {
|
||||||
@ApiProperty({ description: '客户列表', type: [CustomerDTO] })
|
@ApiProperty({ description: '客户列表', type: [CustomerDTO] })
|
||||||
items: CustomerDTO[];
|
items: CustomerDTO[];
|
||||||
|
|
@ -359,6 +359,6 @@ export class SyncCustomersDTO {
|
||||||
@ApiProperty({ description: '站点ID' })
|
@ApiProperty({ description: '站点ID' })
|
||||||
siteId: number;
|
siteId: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '查询参数(支持where和orderBy)', type: UnifiedSearchParamsDTO, required: false })
|
@ApiProperty({ description: '查询参数(支持where和orderBy)', type: UnifiedSearchParamsDTO, required: false })
|
||||||
params?: UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
params?: UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ export class ShopyyAllProductQuery {
|
||||||
id?: string;
|
id?: string;
|
||||||
/** 商品标题,支持模糊查询 */
|
/** 商品标题,支持模糊查询 */
|
||||||
title?: string;
|
title?: string;
|
||||||
/** 商品状态,例如:上架、下架、删除等(具体值参考 Shopyy 接口文档) */
|
/** 商品状态,例如:上架、下架、删除等(具体值参考 Shopyy 接口文档) */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 商品SKU编码,库存保有单位,精确或模糊匹配 */
|
/** 商品SKU编码,库存保有单位,精确或模糊匹配 */
|
||||||
sku?: string;
|
sku?: string;
|
||||||
|
|
@ -34,21 +34,21 @@ export class ShopyyAllProductQuery {
|
||||||
variant_price_min?: string;
|
variant_price_min?: string;
|
||||||
/** 变体价格最大值,筛选变体价格小于等于该值的商品 */
|
/** 变体价格最大值,筛选变体价格小于等于该值的商品 */
|
||||||
variant_price_max?: string;
|
variant_price_max?: string;
|
||||||
/** 变体划线价(原价)最小值,筛选变体划线价大于等于该值的商品 */
|
/** 变体划线价(原价)最小值,筛选变体划线价大于等于该值的商品 */
|
||||||
variant_compare_at_price_min?: string;
|
variant_compare_at_price_min?: string;
|
||||||
/** 变体划线价(原价)最大值,筛选变体划线价小于等于该值的商品 */
|
/** 变体划线价(原价)最大值,筛选变体划线价小于等于该值的商品 */
|
||||||
variant_compare_at_price_max?: string;
|
variant_compare_at_price_max?: string;
|
||||||
/** 变体重量最小值,筛选变体重量大于等于该值的商品(单位参考接口文档) */
|
/** 变体重量最小值,筛选变体重量大于等于该值的商品(单位参考接口文档) */
|
||||||
variant_weight_min?: string;
|
variant_weight_min?: string;
|
||||||
/** 变体重量最大值,筛选变体重量小于等于该值的商品(单位参考接口文档) */
|
/** 变体重量最大值,筛选变体重量小于等于该值的商品(单位参考接口文档) */
|
||||||
variant_weight_max?: string;
|
variant_weight_max?: string;
|
||||||
/** 商品创建时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
/** 商品创建时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
||||||
created_at_min?: string;
|
created_at_min?: string;
|
||||||
/** 商品创建时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
/** 商品创建时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
||||||
created_at_max?: string;
|
created_at_max?: string;
|
||||||
/** 商品更新时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
/** 商品更新时间最小值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
||||||
updated_at_min?: string;
|
updated_at_min?: string;
|
||||||
/** 商品更新时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
/** 商品更新时间最大值,格式参考接口文档(如:YYYY-MM-DD HH:mm:ss) */
|
||||||
updated_at_max?: string;
|
updated_at_max?: string;
|
||||||
}
|
}
|
||||||
// 产品类型
|
// 产品类型
|
||||||
|
|
@ -133,9 +133,9 @@ export interface ShopyyVariant {
|
||||||
//
|
//
|
||||||
// 订单查询参数类型
|
// 订单查询参数类型
|
||||||
export interface ShopyyOrderQuery {
|
export interface ShopyyOrderQuery {
|
||||||
// 订单ID集合 多个ID用','联接 例:1,2,3
|
// 订单ID集合 多个ID用','联接 例:1,2,3
|
||||||
ids?: string;
|
ids?: string;
|
||||||
// 订单状态 100 未完成;110 待处理;180 已完成(确认收货); 190 取消;
|
// 订单状态 100 未完成;110 待处理;180 已完成(确认收货); 190 取消;
|
||||||
status?: string;
|
status?: string;
|
||||||
// 物流状态 300 未发货;310 部分发货;320 已发货;330(确认收货)
|
// 物流状态 300 未发货;310 部分发货;320 已发货;330(确认收货)
|
||||||
fulfillment_status?: string;
|
fulfillment_status?: string;
|
||||||
|
|
@ -159,9 +159,9 @@ export interface ShopyyOrderQuery {
|
||||||
page?: string;
|
page?: string;
|
||||||
// 每页条数
|
// 每页条数
|
||||||
limit?: string;
|
limit?: string;
|
||||||
// 排序字段(默认id) id=订单ID updated_at=最后更新时间 pay_at=支付时间
|
// 排序字段(默认id) id=订单ID updated_at=最后更新时间 pay_at=支付时间
|
||||||
order_field?: string;
|
order_field?: string;
|
||||||
// 排序方式(默认desc) desc=降序 asc=升序
|
// 排序方式(默认desc) desc=降序 asc=升序
|
||||||
order_by?: string;
|
order_by?: string;
|
||||||
// 订单列表类型
|
// 订单列表类型
|
||||||
group?: string;
|
group?: string;
|
||||||
|
|
@ -513,7 +513,7 @@ export class ShopyyFulfillmentDTO {
|
||||||
"tracking_number": string;
|
"tracking_number": string;
|
||||||
"courier_code": number;
|
"courier_code": number;
|
||||||
"note": string;
|
"note": string;
|
||||||
"mode": "replace" | 'cover' | null// 模式 replace(替换) cover (覆盖) 空(新增)
|
"mode": "replace" | 'cover' | null// 模式 replace(替换) cover (覆盖) 空(新增)
|
||||||
}
|
}
|
||||||
// https://www.apizza.net/project/e114fb8e628e0f604379f5b26f0d8330/browse
|
// https://www.apizza.net/project/e114fb8e628e0f604379f5b26f0d8330/browse
|
||||||
export class ShopyPartFulfillmentDTO {
|
export class ShopyPartFulfillmentDTO {
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ export class UnifiedProductAttributeDTO {
|
||||||
@ApiProperty({ description: '属性选项', type: [String] })
|
@ApiProperty({ description: '属性选项', type: [String] })
|
||||||
options: string[];
|
options: string[];
|
||||||
|
|
||||||
@ApiProperty({ description: '变体属性值(单个值)', required: false })
|
@ApiProperty({ description: '变体属性值(单个值)', required: false })
|
||||||
option?: string;
|
option?: string;
|
||||||
// 这个是属性的父级字典项
|
// 这个是属性的父级字典项
|
||||||
dict?: Dict;
|
dict?: Dict;
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ export class QuerySiteDTO {
|
||||||
@Rule(RuleType.boolean().optional())
|
@Rule(RuleType.boolean().optional())
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID列表(逗号分隔)', required: false })
|
@ApiProperty({ description: '站点ID列表(逗号分隔)', required: false })
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
ids?: string;
|
ids?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ export interface WooProduct {
|
||||||
id: number;
|
id: number;
|
||||||
// 创建时间
|
// 创建时间
|
||||||
date_created: string;
|
date_created: string;
|
||||||
// 创建时间(GMT)
|
// 创建时间(GMT)
|
||||||
date_created_gmt: string;
|
date_created_gmt: string;
|
||||||
// 更新时间
|
// 更新时间
|
||||||
date_modified: string;
|
date_modified: string;
|
||||||
// 更新时间(GMT)
|
// 更新时间(GMT)
|
||||||
date_modified_gmt: string;
|
date_modified_gmt: string;
|
||||||
// 产品类型 simple grouped external variable
|
// 产品类型 simple grouped external variable
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -20,7 +20,7 @@ export interface WooProduct {
|
||||||
status: string;
|
status: string;
|
||||||
// 是否为特色产品
|
// 是否为特色产品
|
||||||
featured: boolean;
|
featured: boolean;
|
||||||
// 目录可见性选项:visible, catalog, search and hidden. Default is visible.
|
// 目录可见性选项:visible, catalog, search and hidden. Default is visible.
|
||||||
catalog_visibility: string;
|
catalog_visibility: string;
|
||||||
|
|
||||||
// 常规价格
|
// 常规价格
|
||||||
|
|
@ -130,11 +130,11 @@ export interface WooVariation {
|
||||||
id: number;
|
id: number;
|
||||||
// 创建时间
|
// 创建时间
|
||||||
date_created: string;
|
date_created: string;
|
||||||
// 创建时间(GMT)
|
// 创建时间(GMT)
|
||||||
date_created_gmt: string;
|
date_created_gmt: string;
|
||||||
// 更新时间
|
// 更新时间
|
||||||
date_modified: string;
|
date_modified: string;
|
||||||
// 更新时间(GMT)
|
// 更新时间(GMT)
|
||||||
date_modified_gmt: string;
|
date_modified_gmt: string;
|
||||||
// 变体描述
|
// 变体描述
|
||||||
description: string;
|
description: string;
|
||||||
|
|
@ -150,11 +150,11 @@ export interface WooVariation {
|
||||||
price_html?: string;
|
price_html?: string;
|
||||||
// 促销开始日期
|
// 促销开始日期
|
||||||
date_on_sale_from?: string;
|
date_on_sale_from?: string;
|
||||||
// 促销开始日期(GMT)
|
// 促销开始日期(GMT)
|
||||||
date_on_sale_from_gmt?: string;
|
date_on_sale_from_gmt?: string;
|
||||||
// 促销结束日期
|
// 促销结束日期
|
||||||
date_on_sale_to?: string;
|
date_on_sale_to?: string;
|
||||||
// 促销结束日期(GMT)
|
// 促销结束日期(GMT)
|
||||||
date_on_sale_to_gmt?: string;
|
date_on_sale_to_gmt?: string;
|
||||||
// 是否在促销中
|
// 是否在促销中
|
||||||
on_sale: boolean;
|
on_sale: boolean;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export enum OrderStatus {
|
||||||
REFUNDED = 'refunded', // 已退款
|
REFUNDED = 'refunded', // 已退款
|
||||||
FAILED = 'failed', // 失败订单
|
FAILED = 'failed', // 失败订单
|
||||||
DRAFT = 'draft', // 草稿
|
DRAFT = 'draft', // 草稿
|
||||||
AUTO_DRAFT = 'auto-draft', // 自动草稿(TODO:不知道为什么出现)
|
AUTO_DRAFT = 'auto-draft', // 自动草稿(TODO:不知道为什么出现)
|
||||||
|
|
||||||
// TRASH = 'trash',
|
// TRASH = 'trash',
|
||||||
// refund 也就是退款相关的状态
|
// refund 也就是退款相关的状态
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export interface IPlatformService {
|
||||||
getOrder(siteId: number, orderId: string): Promise<any>;
|
getOrder(siteId: number, orderId: string): Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取订阅列表(如果平台支持)
|
* 获取订阅列表(如果平台支持)
|
||||||
* @param siteId 站点ID
|
* @param siteId 站点ID
|
||||||
* @returns 订阅列表数据
|
* @returns 订阅列表数据
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export class CustomerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof dateValue === 'number') {
|
if (typeof dateValue === 'number') {
|
||||||
// 处理Unix时间戳(秒或毫秒)
|
// 处理Unix时间戳(秒或毫秒)
|
||||||
return new Date(dateValue > 9999999999 ? dateValue : dateValue * 1000);
|
return new Date(dateValue > 9999999999 ? dateValue : dateValue * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ export class CustomerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建或更新客户(upsert)
|
* 创建或更新客户(upsert)
|
||||||
* 如果客户存在则更新,不存在则创建
|
* 如果客户存在则更新,不存在则创建
|
||||||
*/
|
*/
|
||||||
async upsertCustomer(
|
async upsertCustomer(
|
||||||
|
|
@ -157,24 +157,24 @@ export class CustomerService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从站点同步客户数据
|
* 从站点同步客户数据
|
||||||
* 第一步:调用adapter获取站点客户数据
|
* 第一步:调用adapter获取站点客户数据
|
||||||
* 第二步:通过upsertManyCustomers保存这些客户
|
* 第二步:通过upsertManyCustomers保存这些客户
|
||||||
*/
|
*/
|
||||||
async syncCustomersFromSite(
|
async syncCustomersFromSite(
|
||||||
siteId: number,
|
siteId: number,
|
||||||
params?: UnifiedSearchParamsDTO
|
params?: UnifiedSearchParamsDTO
|
||||||
): Promise<SyncOperationResult> {
|
): Promise<SyncOperationResult> {
|
||||||
try {
|
try {
|
||||||
// 第一步:获取适配器并从站点获取客户数据
|
// 第一步:获取适配器并从站点获取客户数据
|
||||||
const adapter = await this.siteApiService.getAdapter(siteId);
|
const adapter = await this.siteApiService.getAdapter(siteId);
|
||||||
const siteCustomers = await adapter.getAllCustomers(params || {});
|
const siteCustomers = await adapter.getAllCustomers(params || {});
|
||||||
|
|
||||||
// 第二步:将站点客户数据转换为客户实体数据
|
// 第二步:将站点客户数据转换为客户实体数据
|
||||||
const customersData = siteCustomers.map(siteCustomer => {
|
const customersData = siteCustomers.map(siteCustomer => {
|
||||||
return this.mapSiteCustomerToCustomer(siteCustomer, siteId);
|
return this.mapSiteCustomerToCustomer(siteCustomer, siteId);
|
||||||
})
|
})
|
||||||
|
|
||||||
// 第三步:批量upsert客户数据
|
// 第三步:批量upsert客户数据
|
||||||
const upsertResult = await this.upsertManyCustomers(customersData);
|
const upsertResult = await this.upsertManyCustomers(customersData);
|
||||||
return {
|
return {
|
||||||
total: siteCustomers.length,
|
total: siteCustomers.length,
|
||||||
|
|
@ -192,7 +192,7 @@ export class CustomerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取客户统计列表(包含订单统计信息)
|
* 获取客户统计列表(包含订单统计信息)
|
||||||
* 支持分页、搜索和排序功能
|
* 支持分页、搜索和排序功能
|
||||||
* 使用原生SQL查询实现复杂的统计逻辑
|
* 使用原生SQL查询实现复杂的统计逻辑
|
||||||
*/
|
*/
|
||||||
|
|
@ -363,7 +363,7 @@ export class CustomerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取纯粹的客户列表(不包含订单统计信息)
|
* 获取纯粹的客户列表(不包含订单统计信息)
|
||||||
* 支持基本的分页、搜索和排序功能
|
* 支持基本的分页、搜索和排序功能
|
||||||
* 使用TypeORM查询构建器实现
|
* 使用TypeORM查询构建器实现
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ export class DictService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新或创建字典项 (Upsert)
|
// 更新或创建字典项 (Upsert)
|
||||||
// 如果字典项已存在(根据 name 和 dictId 判断),则更新;否则创建新的
|
// 如果字典项已存在(根据 name 和 dictId 判断),则更新;否则创建新的
|
||||||
async upsertDictItem(dictId: number, itemData: {
|
async upsertDictItem(dictId: number, itemData: {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -252,7 +252,7 @@ export class DictService {
|
||||||
// 格式化 name
|
// 格式化 name
|
||||||
const formattedName = this.formatName(itemData.name);
|
const formattedName = this.formatName(itemData.name);
|
||||||
|
|
||||||
// 查找是否已存在该字典项(根据 name 和 dictId)
|
// 查找是否已存在该字典项(根据 name 和 dictId)
|
||||||
const existingItem = await this.dictItemModel.findOne({
|
const existingItem = await this.dictItemModel.findOne({
|
||||||
where: {
|
where: {
|
||||||
name: formattedName,
|
name: formattedName,
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@ export class OrderService {
|
||||||
// 如果不能更新 ERP 状态,则保留原有的 orderStatus
|
// 如果不能更新 ERP 状态,则保留原有的 orderStatus
|
||||||
entity.orderStatus = existingOrder.orderStatus;
|
entity.orderStatus = existingOrder.orderStatus;
|
||||||
}
|
}
|
||||||
// 更新订单数据(包括 shipping、billing 等字段)
|
// 更新订单数据(包括 shipping、billing 等字段)
|
||||||
await this.orderModel.update(existingOrder.id, entity);
|
await this.orderModel.update(existingOrder.id, entity);
|
||||||
entity.id = existingOrder.id;
|
entity.id = existingOrder.id;
|
||||||
return entity;
|
return entity;
|
||||||
|
|
@ -2567,8 +2567,8 @@ export class OrderService {
|
||||||
* 导出数据为CSV格式
|
* 导出数据为CSV格式
|
||||||
* @param {any[]} data 数据数组
|
* @param {any[]} data 数据数组
|
||||||
* @param {Object} options 配置选项
|
* @param {Object} options 配置选项
|
||||||
* @param {string} [options.type='string'] 输出类型:'string' | 'buffer'
|
* @param {string} [options.type='string'] 输出类型:'string' | 'buffer'
|
||||||
* @param {string} [options.fileName] 文件名(仅当需要写入文件时使用)
|
* @param {string} [options.fileName] 文件名(仅当需要写入文件时使用)
|
||||||
* @param {boolean} [options.writeFile=false] 是否写入文件
|
* @param {boolean} [options.writeFile=false] 是否写入文件
|
||||||
* @returns {string|Buffer} 根据type返回字符串或Buffer
|
* @returns {string|Buffer} 根据type返回字符串或Buffer
|
||||||
*/
|
*/
|
||||||
|
|
@ -2617,7 +2617,7 @@ async exportToCsv(data: any[], options: { type?: 'string' | 'buffer'; fileName?:
|
||||||
// 获取当前用户目录
|
// 获取当前用户目录
|
||||||
const userHomeDir = os.homedir();
|
const userHomeDir = os.homedir();
|
||||||
|
|
||||||
// 构建目标路径(下载目录)
|
// 构建目标路径(下载目录)
|
||||||
const downloadsDir = path.join(userHomeDir, 'Downloads');
|
const downloadsDir = path.join(userHomeDir, 'Downloads');
|
||||||
|
|
||||||
// 确保下载目录存在
|
// 确保下载目录存在
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ export class ShopyyService {
|
||||||
* @returns 完整URL
|
* @returns 完整URL
|
||||||
*/
|
*/
|
||||||
private buildURL(baseUrl: string, endpoint: string): string {
|
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 base = baseUrl.replace(/\/$/, '');
|
||||||
const end = endpoint.replace(/^\//, '');
|
const end = endpoint.replace(/^\//, '');
|
||||||
return `${base}/${end}`;
|
return `${base}/${end}`;
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ export class WPService implements IPlatformService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 导出 WooCommerce 产品为特殊CSV(平台特性)
|
// 导出 WooCommerce 产品为特殊CSV(平台特性)
|
||||||
async exportProductsCsvSpecial(site: any, page: number = 1, pageSize: number = 100): Promise<string> {
|
async exportProductsCsvSpecial(site: any, page: number = 1, pageSize: number = 100): Promise<string> {
|
||||||
const list = await this.getProducts(site, { page, per_page: pageSize });
|
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'];
|
const header = ['id','name','type','status','sku','regular_price','sale_price','stock_status','stock_quantity'];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue