Compare commits
9 Commits
8d0eec06fd
...
5d7e0090aa
| Author | SHA1 | Date |
|---|---|---|
|
|
5d7e0090aa | |
|
|
ecdedcc041 | |
|
|
b2ee61e47d | |
|
|
64c1d1afe5 | |
|
|
4eb45af452 | |
|
|
a8d12a695e | |
|
|
a00a95c9a3 | |
|
|
82c8640f0c | |
|
|
cb00076bd3 |
|
|
@ -367,7 +367,6 @@ export class ShopyyAdapter implements ISiteAdapter {
|
|||
date_paid: typeof item.pay_at === 'number'
|
||||
? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString()
|
||||
: null,
|
||||
|
||||
refunds: [],
|
||||
currency_symbol: (currencySymbols[item.currency] || '$') || '',
|
||||
date_created:
|
||||
|
|
@ -387,6 +386,7 @@ export class ShopyyAdapter implements ISiteAdapter {
|
|||
tracking_number: f.tracking_number || '',
|
||||
shipping_provider: f.tracking_company || '',
|
||||
shipping_method: f.tracking_company || '',
|
||||
|
||||
date_created: typeof f.created_at === 'number'
|
||||
? new Date(f.created_at * 1000).toISOString()
|
||||
: f.created_at || '',
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ export class StatisticsController {
|
|||
|
||||
@ApiOkResponse()
|
||||
@Get('/orderSource')
|
||||
async getOrderSorce(@Query() params) {
|
||||
async getOrderSource(@Query() params) {
|
||||
try {
|
||||
return successResponse(await this.statisticsService.getOrderSorce(params));
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import { SiteService } from '../service/site.service';
|
|||
import { OrderService } from '../service/order.service';
|
||||
import { SiteApiService } from '../service/site-api.service';
|
||||
|
||||
|
||||
|
||||
@Controller('/webhook')
|
||||
export class WebhookController {
|
||||
private secret = 'YOONE24kd$kjcdjflddd';
|
||||
|
|
@ -177,20 +179,15 @@ export class WebhookController {
|
|||
console.log('Unhandled event:', topic);
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: 'Webhook processed successfully',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: 'Webhook verification failed',
|
||||
};
|
||||
return {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: 'Webhook processed successfully',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ export interface ShopyyOrder {
|
|||
financial_status?: number;
|
||||
fulfillment_status?: number;
|
||||
// 创建与更新时间可能为时间戳
|
||||
date_paid?: number | string;
|
||||
created_at?: number | string;
|
||||
date_added?: string;
|
||||
updated_at?: number | string;
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export class UpdateSiteDTO {
|
|||
skuPrefix?: string;
|
||||
|
||||
// 区域
|
||||
@ApiProperty({ description: '区域' })
|
||||
@ApiProperty({ description: '区域', required: false })
|
||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
||||
areas?: string[];
|
||||
|
||||
|
|
@ -133,6 +133,10 @@ export class UpdateSiteDTO {
|
|||
@ApiProperty({ description: '站点网站URL', required: false })
|
||||
@Rule(RuleType.string().optional())
|
||||
websiteUrl?: string;
|
||||
|
||||
@ApiProperty({ description: 'Webhook URL', required: false })
|
||||
@Rule(RuleType.string().optional())
|
||||
webhookUrl?: string;
|
||||
}
|
||||
|
||||
export class QuerySiteDTO {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ export class OrderStatisticsParams {
|
|||
@Rule(RuleType.number().allow(null))
|
||||
siteId?: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.array().allow(null))
|
||||
country?: any[];
|
||||
|
||||
@ApiProperty({
|
||||
enum: ['all', 'first_purchase', 'repeat_purchase'],
|
||||
default: 'all',
|
||||
|
|
|
|||
|
|
@ -722,14 +722,14 @@ export class OrderService {
|
|||
return {
|
||||
product: await this.productModel.findOne({
|
||||
where: { sku: comp.sku },
|
||||
relations: ['components','attributes'],
|
||||
relations: ['components', 'attributes'],
|
||||
}),
|
||||
quantity: comp.quantity * orderItem.quantity,
|
||||
}
|
||||
})) : [{ product, quantity: orderItem.quantity }]
|
||||
|
||||
const orderSales: OrderSale[] = componentDetails.map(componentDetail => {
|
||||
if(!componentDetail.product) return null
|
||||
if (!componentDetail.product) return null
|
||||
const attrsObj = this.productService.getAttributesObject(product.attributes)
|
||||
const orderSale = plainToClass(OrderSale, {
|
||||
orderId: orderItem.orderId,
|
||||
|
|
@ -2639,7 +2639,4 @@ export class OrderService {
|
|||
throw new Error(`导出CSV文件失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,19 @@ export class StatisticsService {
|
|||
orderItemRepository: Repository<OrderItem>;
|
||||
|
||||
async getOrderStatistics(params: OrderStatisticsParams) {
|
||||
const { startDate, endDate, grouping, siteId } = params;
|
||||
const { startDate, endDate, grouping, siteId, country } = params;
|
||||
// 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 end = dayjs(endDate).add(1, 'd').format('YYYY-MM-DD');
|
||||
let sql
|
||||
|
|
@ -54,6 +65,8 @@ export class StatisticsService {
|
|||
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
|
||||
),
|
||||
|
|
@ -247,7 +260,10 @@ export class StatisticsService {
|
|||
LEFT JOIN order_item oi ON o.id = oi.orderId
|
||||
WHERE o.date_paid IS NOT NULL
|
||||
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
|
||||
),
|
||||
order_sales_summary AS (
|
||||
|
|
@ -440,6 +456,10 @@ export class StatisticsService {
|
|||
LEFT JOIN order_item oi ON o.id = oi.orderId
|
||||
WHERE o.date_paid IS NOT NULL
|
||||
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')
|
||||
GROUP BY o.id, o.date_paid, o.customer_email, o.total, o.source_type, o.siteId, o.utm_source
|
||||
),
|
||||
|
|
@ -1314,7 +1334,14 @@ export class StatisticsService {
|
|||
}
|
||||
|
||||
async getOrderSorce(params) {
|
||||
const sql = `
|
||||
const { country } = params;
|
||||
|
||||
let siteIds = []
|
||||
if (country) {
|
||||
siteIds = await this.getSiteIds(country)
|
||||
}
|
||||
|
||||
let sql = `
|
||||
WITH cutoff_months AS (
|
||||
SELECT
|
||||
DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 7 MONTH), '%Y-%m') AS start_month,
|
||||
|
|
@ -1326,7 +1353,10 @@ export class StatisticsService {
|
|||
DATE_FORMAT(MIN(date_paid), '%Y-%m') AS first_order_month,
|
||||
SUM(total) AS first_order_total
|
||||
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
|
||||
),
|
||||
order_months AS (
|
||||
|
|
@ -1334,7 +1364,10 @@ export class StatisticsService {
|
|||
customer_email,
|
||||
DATE_FORMAT(date_paid, '%Y-%m') AS order_month
|
||||
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 (
|
||||
SELECT o.customer_email, o.order_month, u.first_order_month,u.first_order_total, c.start_month
|
||||
|
|
@ -1366,7 +1399,7 @@ export class StatisticsService {
|
|||
ORDER BY order_month DESC, first_order_month_group
|
||||
`
|
||||
|
||||
const inactiveSql = `
|
||||
let inactiveSql = `
|
||||
WITH
|
||||
cutoff_months AS (
|
||||
SELECT
|
||||
|
|
@ -1381,7 +1414,10 @@ export class StatisticsService {
|
|||
date_paid,
|
||||
total
|
||||
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 (
|
||||
|
|
@ -1524,4 +1560,13 @@ 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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue