|
|
|
|
@ -23,6 +23,7 @@ import {
|
|
|
|
|
} from '../enums/base.enum';
|
|
|
|
|
import { Variation } from '../entity/variation.entity';
|
|
|
|
|
import { CreateOrderNoteDTO, QueryOrderSalesDTO } from '../dto/order.dto';
|
|
|
|
|
import dayjs = require('dayjs');
|
|
|
|
|
import { OrderDetailRes } from '../dto/reponse.dto';
|
|
|
|
|
import { OrderNote } from '../entity/order_note.entity';
|
|
|
|
|
import { User } from '../entity/user.entity';
|
|
|
|
|
@ -310,6 +311,7 @@ export class OrderService {
|
|
|
|
|
externalOrderId: string;
|
|
|
|
|
orderItems: Record<string, any>[];
|
|
|
|
|
}) {
|
|
|
|
|
console.log('saveOrderItems params',params)
|
|
|
|
|
const { siteId, orderId, externalOrderId, orderItems } = params;
|
|
|
|
|
const currentOrderItems = await this.orderItemModel.find({
|
|
|
|
|
where: { siteId, externalOrderId: externalOrderId },
|
|
|
|
|
@ -614,6 +616,7 @@ export class OrderService {
|
|
|
|
|
customer_email,
|
|
|
|
|
payment_method,
|
|
|
|
|
billing_phone,
|
|
|
|
|
isSubscriptionOnly = false,
|
|
|
|
|
}, userId = undefined) {
|
|
|
|
|
const parameters: any[] = [];
|
|
|
|
|
|
|
|
|
|
@ -699,12 +702,14 @@ export class OrderService {
|
|
|
|
|
totalQuery += ` AND o.date_created <= ?`;
|
|
|
|
|
parameters.push(endDate);
|
|
|
|
|
}
|
|
|
|
|
// 支付方式筛选(使用参数化,避免SQL注入)
|
|
|
|
|
if (payment_method) {
|
|
|
|
|
sqlQuery += ` AND o.payment_method like "%${payment_method}%" `;
|
|
|
|
|
totalQuery += ` AND o.payment_method like "%${payment_method}%" `;
|
|
|
|
|
sqlQuery += ` AND o.payment_method LIKE ?`;
|
|
|
|
|
totalQuery += ` AND o.payment_method LIKE ?`;
|
|
|
|
|
parameters.push(`%${payment_method}%`);
|
|
|
|
|
}
|
|
|
|
|
const user = await this.userModel.findOneBy({ id: userId });
|
|
|
|
|
if (user?.permissions?.includes('order-10-days')) {
|
|
|
|
|
if (user?.permissions?.includes('order-10-days') && !startDate && !endDate) {
|
|
|
|
|
sqlQuery += ` AND o.date_created >= ?`;
|
|
|
|
|
totalQuery += ` AND o.date_created >= ?`;
|
|
|
|
|
const tenDaysAgo = new Date();
|
|
|
|
|
@ -729,6 +734,21 @@ export class OrderService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 仅订阅订单过滤:父订阅订单 或 行项目包含订阅相关元数据(兼容 JSON 与字符串存储)
|
|
|
|
|
if (isSubscriptionOnly) {
|
|
|
|
|
const subCond = `
|
|
|
|
|
AND (
|
|
|
|
|
EXISTS (
|
|
|
|
|
SELECT 1 FROM subscription s
|
|
|
|
|
WHERE s.siteId = o.siteId AND s.parent_id = o.externalOrderId
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
)
|
|
|
|
|
`;
|
|
|
|
|
sqlQuery += subCond;
|
|
|
|
|
totalQuery += subCond;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (customer_email) {
|
|
|
|
|
sqlQuery += ` AND o.customer_email LIKE ?`;
|
|
|
|
|
totalQuery += ` AND o.customer_email LIKE ?`;
|
|
|
|
|
@ -774,7 +794,6 @@ export class OrderService {
|
|
|
|
|
|
|
|
|
|
// 执行查询
|
|
|
|
|
const orders = await this.orderModel.query(sqlQuery, parameters);
|
|
|
|
|
|
|
|
|
|
return { items: orders, total, current, pageSize };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -786,7 +805,8 @@ export class OrderService {
|
|
|
|
|
keyword,
|
|
|
|
|
customer_email,
|
|
|
|
|
billing_phone,
|
|
|
|
|
}) {
|
|
|
|
|
isSubscriptionOnly = false,
|
|
|
|
|
}: any) {
|
|
|
|
|
const query = this.orderModel
|
|
|
|
|
.createQueryBuilder('order')
|
|
|
|
|
.select('order.orderStatus', 'status')
|
|
|
|
|
@ -824,11 +844,24 @@ export class OrderService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isSubscriptionOnly) {
|
|
|
|
|
query.andWhere(`(
|
|
|
|
|
EXISTS (
|
|
|
|
|
SELECT 1 FROM subscription s
|
|
|
|
|
WHERE s.siteId = order.siteId AND s.parent_id = order.externalOrderId
|
|
|
|
|
)
|
|
|
|
|
)`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await query.getRawMany();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name, exceptPackage }: QueryOrderSalesDTO) {
|
|
|
|
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
|
|
|
|
const defaultStart = dayjs().subtract(30, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
const defaultEnd = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
startDate = (startDate as any) || defaultStart as any;
|
|
|
|
|
endDate = (endDate as any) || defaultEnd as any;
|
|
|
|
|
const offset = (current - 1) * pageSize;
|
|
|
|
|
|
|
|
|
|
// -------------------------
|
|
|
|
|
@ -1032,6 +1065,10 @@ export class OrderService {
|
|
|
|
|
name,
|
|
|
|
|
}: QueryOrderSalesDTO) {
|
|
|
|
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
|
|
|
|
const defaultStart = dayjs().subtract(30, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
const defaultEnd = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
startDate = (startDate as any) || defaultStart as any;
|
|
|
|
|
endDate = (endDate as any) || defaultEnd as any;
|
|
|
|
|
// 分页查询
|
|
|
|
|
let sqlQuery = `
|
|
|
|
|
WITH product_purchase_counts AS (
|
|
|
|
|
@ -1134,6 +1171,64 @@ export class OrderService {
|
|
|
|
|
pageSize,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getOrderItemList({
|
|
|
|
|
siteId,
|
|
|
|
|
startDate,
|
|
|
|
|
endDate,
|
|
|
|
|
current,
|
|
|
|
|
pageSize,
|
|
|
|
|
name,
|
|
|
|
|
externalProductId,
|
|
|
|
|
externalVariationId,
|
|
|
|
|
}: any) {
|
|
|
|
|
const params: any[] = [];
|
|
|
|
|
let sql = `
|
|
|
|
|
SELECT
|
|
|
|
|
oi.*,
|
|
|
|
|
o.id AS orderId,
|
|
|
|
|
o.externalOrderId AS orderExternalOrderId,
|
|
|
|
|
o.date_created AS orderDateCreated,
|
|
|
|
|
o.customer_email AS orderCustomerEmail,
|
|
|
|
|
o.orderStatus AS orderStatus,
|
|
|
|
|
o.siteId AS orderSiteId,
|
|
|
|
|
CASE WHEN
|
|
|
|
|
JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"is_subscription"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcs_bought_as_subscription"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcsatt_scheme"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_subscription"')
|
|
|
|
|
THEN 1 ELSE 0 END AS isSubscriptionItem
|
|
|
|
|
FROM order_item oi
|
|
|
|
|
INNER JOIN \`order\` o ON o.id = oi.orderId
|
|
|
|
|
WHERE 1=1
|
|
|
|
|
`;
|
|
|
|
|
let countSql = `
|
|
|
|
|
SELECT COUNT(*) AS total
|
|
|
|
|
FROM order_item oi
|
|
|
|
|
INNER JOIN \`order\` o ON o.id = oi.orderId
|
|
|
|
|
WHERE 1=1
|
|
|
|
|
`;
|
|
|
|
|
const pushFilter = (cond: string, value: any) => {
|
|
|
|
|
sql += cond; countSql += cond; params.push(value);
|
|
|
|
|
};
|
|
|
|
|
if (startDate) pushFilter(' AND o.date_created >= ?', startDate);
|
|
|
|
|
if (endDate) pushFilter(' AND o.date_created <= ?', endDate);
|
|
|
|
|
if (siteId) pushFilter(' AND oi.siteId = ?', siteId);
|
|
|
|
|
if (name) {
|
|
|
|
|
pushFilter(' AND oi.name LIKE ?', `%${name}%`);
|
|
|
|
|
}
|
|
|
|
|
if (externalProductId) pushFilter(' AND oi.externalProductId = ?', externalProductId);
|
|
|
|
|
if (externalVariationId) pushFilter(' AND oi.externalVariationId = ?', externalVariationId);
|
|
|
|
|
|
|
|
|
|
sql += ' ORDER BY o.date_created DESC LIMIT ? OFFSET ?';
|
|
|
|
|
const listParams = [...params, pageSize, (current - 1) * pageSize];
|
|
|
|
|
|
|
|
|
|
const items = await this.orderItemModel.query(sql, listParams);
|
|
|
|
|
const [countRow] = await this.orderItemModel.query(countSql, params);
|
|
|
|
|
const total = Number(countRow?.total || 0);
|
|
|
|
|
|
|
|
|
|
return { items, total, current, pageSize };
|
|
|
|
|
}
|
|
|
|
|
async getOrderDetail(id: number): Promise<OrderDetailRes> {
|
|
|
|
|
const order = await this.orderModel.findOne({ where: { id } });
|
|
|
|
|
const site = this.sites.find(site => site.id === order.siteId);
|
|
|
|
|
@ -1212,6 +1307,75 @@ export class OrderService {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getRelatedByOrder(orderId: number) {
|
|
|
|
|
const order = await this.orderModel.findOne({ where: { id: orderId } });
|
|
|
|
|
if (!order) throw new Error('订单不存在');
|
|
|
|
|
const items = await this.orderItemModel.find({ where: { orderId } });
|
|
|
|
|
const siteId = order.siteId;
|
|
|
|
|
const productIds = items.map(i => i.externalProductId).filter(Boolean);
|
|
|
|
|
const variationIds = items.map(i => i.externalVariationId).filter(Boolean);
|
|
|
|
|
|
|
|
|
|
const subSql = `
|
|
|
|
|
SELECT * FROM subscription s
|
|
|
|
|
WHERE s.siteId = ? AND s.parent_id = ?
|
|
|
|
|
`;
|
|
|
|
|
const subscriptions = await this.orderModel.query(subSql, [siteId, order.externalOrderId]);
|
|
|
|
|
|
|
|
|
|
let conds: string[] = [];
|
|
|
|
|
let params: any[] = [siteId, orderId];
|
|
|
|
|
if (productIds.length > 0) {
|
|
|
|
|
conds.push(`oi.externalProductId IN (${productIds.map(() => '?').join(',')})`);
|
|
|
|
|
params = [...productIds, ...params];
|
|
|
|
|
}
|
|
|
|
|
if (variationIds.length > 0) {
|
|
|
|
|
conds.push(`oi.externalVariationId IN (${variationIds.map(() => '?').join(',')})`);
|
|
|
|
|
params = [...variationIds, ...params];
|
|
|
|
|
}
|
|
|
|
|
const whereCond = conds.length ? `AND (${conds.join(' OR ')})` : '';
|
|
|
|
|
const relatedItemOrdersSql = `
|
|
|
|
|
SELECT DISTINCT o.*
|
|
|
|
|
FROM order_item oi
|
|
|
|
|
INNER JOIN \`order\` o ON o.id = oi.orderId
|
|
|
|
|
WHERE oi.siteId = ?
|
|
|
|
|
${whereCond}
|
|
|
|
|
AND o.id <> ?
|
|
|
|
|
ORDER BY o.date_created DESC
|
|
|
|
|
LIMIT 100
|
|
|
|
|
`;
|
|
|
|
|
const relatedByItems = await this.orderItemModel.query(relatedItemOrdersSql, params);
|
|
|
|
|
|
|
|
|
|
const relatedBySubscriptionSql = `
|
|
|
|
|
SELECT DISTINCT o.*
|
|
|
|
|
FROM \`order\` o
|
|
|
|
|
WHERE o.siteId = ?
|
|
|
|
|
AND o.customer_email = ?
|
|
|
|
|
AND o.id <> ?
|
|
|
|
|
AND EXISTS (
|
|
|
|
|
SELECT 1 FROM order_item oi
|
|
|
|
|
WHERE oi.orderId = o.id
|
|
|
|
|
AND (
|
|
|
|
|
JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"is_subscription"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcs_bought_as_subscription"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_wcsatt_scheme"')
|
|
|
|
|
OR JSON_CONTAINS(JSON_EXTRACT(oi.meta_data, '$[*].key'), '"_subscription"')
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
ORDER BY o.date_created DESC
|
|
|
|
|
LIMIT 100
|
|
|
|
|
`;
|
|
|
|
|
const relatedBySubscription = await this.orderModel.query(relatedBySubscriptionSql, [siteId, order.customer_email, orderId]);
|
|
|
|
|
|
|
|
|
|
const allOrdersMap = new Map<number, any>();
|
|
|
|
|
relatedByItems.forEach(o => allOrdersMap.set(o.id, o));
|
|
|
|
|
relatedBySubscription.forEach(o => allOrdersMap.set(o.id, o));
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
order,
|
|
|
|
|
subscriptions,
|
|
|
|
|
orders: Array.from(allOrdersMap.values()),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async delOrder(id: number) {
|
|
|
|
|
const order = await this.orderModel.findOne({ where: { id } });
|
|
|
|
|
if (!order) throw new Error('订单不存在');
|
|
|
|
|
|