Compare commits

..

6 Commits

Author SHA1 Message Date
tikkhun 8d0eec06fd fix: 修复订单服务中产品属性和组件处理的问题
处理产品属性为空的情况,避免空指针异常
为产品组件查询添加关联关系
在订单销售记录创建时增加对空产品的过滤
添加新的品牌判断逻辑
2026-01-10 15:06:35 +08:00
tikkhun 78d82ffbac refactor(service): 移除订单服务中未使用的orderItems和orderSales字段(暂时有问题) 2026-01-10 12:01:26 +08:00
tikkhun f1c809bfd2 refactor: 移除未使用的导入和注释掉的生命周期钩子 2026-01-10 11:52:04 +08:00
tikkhun ba1c6aafd6 refactor(订单服务): 移除冗余的订单可编辑性检查注释
注释说明检查应在 save 方法中进行
2026-01-10 11:16:35 +08:00
tikkhun 17435b1381 feat(订单): 增强订单相关功能及数据模型
- 在订单实体中添加orderItems和orderSales字段
- 优化QueryOrderSalesDTO,增加排序字段和更多描述信息
- 重构saveOrderSale方法,使用产品属性自动设置品牌和强度
- 在订单查询中返回关联的orderItems和orderSales数据
- 添加getAttributesObject方法处理产品属性
2026-01-10 11:15:24 +08:00
tikkhun c9f9310a29 fix(product.service): 支持英文分号和逗号分隔siteSkus字段
修改siteSkus字段的分隔符处理逻辑,使其同时支持英文分号和逗号作为分隔符,提高数据兼容性
2026-01-09 16:54:52 +08:00
8 changed files with 27 additions and 75 deletions

View File

@ -367,6 +367,7 @@ export class ShopyyAdapter implements ISiteAdapter {
date_paid: typeof item.pay_at === 'number' date_paid: typeof item.pay_at === 'number'
? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString() ? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString()
: null, : null,
refunds: [], refunds: [],
currency_symbol: (currencySymbols[item.currency] || '$') || '', currency_symbol: (currencySymbols[item.currency] || '$') || '',
date_created: date_created:
@ -386,7 +387,6 @@ export class ShopyyAdapter implements ISiteAdapter {
tracking_number: f.tracking_number || '', tracking_number: f.tracking_number || '',
shipping_provider: f.tracking_company || '', shipping_provider: f.tracking_company || '',
shipping_method: f.tracking_company || '', shipping_method: f.tracking_company || '',
date_created: typeof f.created_at === 'number' date_created: typeof f.created_at === 'number'
? new Date(f.created_at * 1000).toISOString() ? new Date(f.created_at * 1000).toISOString()
: f.created_at || '', : f.created_at || '',

View File

@ -79,7 +79,7 @@ export class StatisticsController {
@ApiOkResponse() @ApiOkResponse()
@Get('/orderSource') @Get('/orderSource')
async getOrderSource(@Query() params) { async getOrderSorce(@Query() params) {
try { try {
return successResponse(await this.statisticsService.getOrderSorce(params)); return successResponse(await this.statisticsService.getOrderSorce(params));
} catch (error) { } catch (error) {

View File

@ -14,8 +14,6 @@ import { SiteService } from '../service/site.service';
import { OrderService } from '../service/order.service'; import { OrderService } from '../service/order.service';
import { SiteApiService } from '../service/site-api.service'; import { SiteApiService } from '../service/site-api.service';
@Controller('/webhook') @Controller('/webhook')
export class WebhookController { export class WebhookController {
private secret = 'YOONE24kd$kjcdjflddd'; private secret = 'YOONE24kd$kjcdjflddd';
@ -184,10 +182,15 @@ export class WebhookController {
success: true, success: true,
message: 'Webhook processed successfully', message: 'Webhook processed successfully',
}; };
} else {
return {
code: 403,
success: false,
message: 'Webhook verification failed',
};
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
} }
} }

View File

@ -346,7 +346,6 @@ export interface ShopyyOrder {
financial_status?: number; financial_status?: number;
fulfillment_status?: number; fulfillment_status?: number;
// 创建与更新时间可能为时间戳 // 创建与更新时间可能为时间戳
date_paid?: number | string;
created_at?: number | string; created_at?: number | string;
date_added?: string; date_added?: string;
updated_at?: number | string; updated_at?: number | string;

View File

@ -121,7 +121,7 @@ export class UpdateSiteDTO {
skuPrefix?: string; skuPrefix?: string;
// 区域 // 区域
@ApiProperty({ description: '区域', required: false }) @ApiProperty({ description: '区域' })
@Rule(RuleType.array().items(RuleType.string()).optional()) @Rule(RuleType.array().items(RuleType.string()).optional())
areas?: string[]; areas?: string[];
@ -133,10 +133,6 @@ export class UpdateSiteDTO {
@ApiProperty({ description: '站点网站URL', required: false }) @ApiProperty({ description: '站点网站URL', required: false })
@Rule(RuleType.string().optional()) @Rule(RuleType.string().optional())
websiteUrl?: string; websiteUrl?: string;
@ApiProperty({ description: 'Webhook URL', required: false })
@Rule(RuleType.string().optional())
webhookUrl?: string;
} }
export class QuerySiteDTO { export class QuerySiteDTO {

View File

@ -19,10 +19,6 @@ export class OrderStatisticsParams {
@Rule(RuleType.number().allow(null)) @Rule(RuleType.number().allow(null))
siteId?: number; siteId?: number;
@ApiProperty()
@Rule(RuleType.array().allow(null))
country?: any[];
@ApiProperty({ @ApiProperty({
enum: ['all', 'first_purchase', 'repeat_purchase'], enum: ['all', 'first_purchase', 'repeat_purchase'],
default: 'all', default: 'all',

View File

@ -2639,4 +2639,7 @@ export class OrderService {
throw new Error(`导出CSV文件失败: ${error.message}`); throw new Error(`导出CSV文件失败: ${error.message}`);
} }
} }
} }

View File

@ -15,19 +15,8 @@ export class StatisticsService {
orderItemRepository: Repository<OrderItem>; orderItemRepository: Repository<OrderItem>;
async getOrderStatistics(params: OrderStatisticsParams) { async getOrderStatistics(params: OrderStatisticsParams) {
const { startDate, endDate, grouping, siteId, country } = params; const { startDate, endDate, grouping, siteId } = params;
// const keywords = keyword ? keyword.split(' ').filter(Boolean) : []; // const keywords = keyword ? keyword.split(' ').filter(Boolean) : [];
let siteIds = []
if (country) {
siteIds = await this.getSiteIds(country)
}
if (siteId) {
siteIds.push(siteId)
}
const start = dayjs(startDate).format('YYYY-MM-DD'); const start = dayjs(startDate).format('YYYY-MM-DD');
const end = dayjs(endDate).add(1, 'd').format('YYYY-MM-DD'); const end = dayjs(endDate).add(1, 'd').format('YYYY-MM-DD');
let sql let sql
@ -65,8 +54,6 @@ export class StatisticsService {
AND o.status IN('processing','completed') AND o.status IN('processing','completed')
`; `;
if (siteId) sql += ` AND o.siteId=${siteId}`; if (siteId) sql += ` AND o.siteId=${siteId}`;
if (siteIds.length) sql += ` AND o.siteId IN (${siteIds.join(',')})`;
sql += ` sql += `
GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source
), ),
@ -260,10 +247,7 @@ export class StatisticsService {
LEFT JOIN order_item oi ON o.id = oi.orderId LEFT JOIN order_item oi ON o.id = oi.orderId
WHERE o.date_paid IS NOT NULL WHERE o.date_paid IS NOT NULL
AND o.date_paid >= '${start}' AND o.date_paid < '${end}' AND o.date_paid >= '${start}' AND o.date_paid < '${end}'
AND o.status IN ('processing','completed')`; AND o.status IN ('processing','completed')
if (siteId) sql += ` AND o.siteId=${siteId}`;
if (siteIds.length) sql += ` AND o.siteId IN (${siteIds.join(',')})`;
sql +=`
GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source
), ),
order_sales_summary AS ( order_sales_summary AS (
@ -456,10 +440,6 @@ export class StatisticsService {
LEFT JOIN order_item oi ON o.id = oi.orderId LEFT JOIN order_item oi ON o.id = oi.orderId
WHERE o.date_paid IS NOT NULL WHERE o.date_paid IS NOT NULL
AND o.date_paid >= '${start}' AND o.date_paid < '${end}' AND o.date_paid >= '${start}' AND o.date_paid < '${end}'
`;
if (siteId) sql += ` AND o.siteId=${siteId}`;
if (siteIds.length) sql += ` AND o.siteId IN (${siteIds.join(',')})`;
sql +=`
AND o.status IN ('processing','completed') AND o.status IN ('processing','completed')
GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source
), ),
@ -1334,14 +1314,7 @@ export class StatisticsService {
} }
async getOrderSorce(params) { async getOrderSorce(params) {
const { country } = params; const sql = `
let siteIds = []
if (country) {
siteIds = await this.getSiteIds(country)
}
let sql = `
WITH cutoff_months AS ( WITH cutoff_months AS (
SELECT SELECT
DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 7 MONTH), '%Y-%m') AS start_month, DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 7 MONTH), '%Y-%m') AS start_month,
@ -1353,10 +1326,7 @@ export class StatisticsService {
DATE_FORMAT(MIN(date_paid), '%Y-%m') AS first_order_month, DATE_FORMAT(MIN(date_paid), '%Y-%m') AS first_order_month,
SUM(total) AS first_order_total SUM(total) AS first_order_total
FROM \`order\` FROM \`order\`
WHERE status IN ('processing', 'completed')`; WHERE status IN ('processing', 'completed')
if (siteIds.length!=0) sql += ` AND siteId IN ('${siteIds.join("','")}')`;
else sql += ` AND siteId IS NULL `;
sql += `
GROUP BY customer_email GROUP BY customer_email
), ),
order_months AS ( order_months AS (
@ -1364,10 +1334,7 @@ export class StatisticsService {
customer_email, customer_email,
DATE_FORMAT(date_paid, '%Y-%m') AS order_month DATE_FORMAT(date_paid, '%Y-%m') AS order_month
FROM \`order\` FROM \`order\`
WHERE status IN ('processing', 'completed')`; WHERE status IN ('processing', 'completed')
if (siteIds.length!=0) sql += ` AND siteId IN ('${siteIds.join("','")}')`;
else sql += ` AND siteId IS NULL `;
sql += `
), ),
filtered_orders AS ( filtered_orders AS (
SELECT o.customer_email, o.order_month, u.first_order_month,u.first_order_total, c.start_month SELECT o.customer_email, o.order_month, u.first_order_month,u.first_order_total, c.start_month
@ -1399,7 +1366,7 @@ export class StatisticsService {
ORDER BY order_month DESC, first_order_month_group ORDER BY order_month DESC, first_order_month_group
` `
let inactiveSql = ` const inactiveSql = `
WITH WITH
cutoff_months AS ( cutoff_months AS (
SELECT SELECT
@ -1414,10 +1381,7 @@ export class StatisticsService {
date_paid, date_paid,
total total
FROM \`order\` FROM \`order\`
WHERE status IN ('processing', 'completed')`; WHERE status IN ('processing', 'completed')
if (siteIds.length!=0) inactiveSql += ` AND siteId IN ('${siteIds.join("','")}')`;
else inactiveSql += ` AND siteId IS NULL `;
inactiveSql += `
), ),
filtered_users AS ( filtered_users AS (
@ -1560,13 +1524,4 @@ export class StatisticsService {
} }
async getSiteIds(country: any[]) {
const sql = `
SELECT DISTINCT sa.siteId as site_id FROM area a left join site_areas_area sa on a.id = sa.areaId WHERE a.code IN ('${country.join("','")}')
`
const res = await this.orderRepository.query(sql)
return res.map(item => item.site_id)
}
} }