Compare commits
No commits in common. "8a1b929692ed2fa7a7642d72499ff834dfd3d622" and "dd4ae5383fd4f132f75f060f760dbaf44df743a1" have entirely different histories.
8a1b929692
...
dd4ae5383f
|
|
@ -22,7 +22,7 @@ import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
import { LogisticsService } from '../service/logistics.service';
|
import { LogisticsService } from '../service/logistics.service';
|
||||||
import { ShippingDetailsDTO } from '../dto/freightcom.dto';
|
import { ShippingDetailsDTO } from '../dto/freightcom.dto';
|
||||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||||
import { QueryServiceDTO, ShipmentBookDTO, ShipmentFeeBookDTO } from '../dto/logistics.dto';
|
import { QueryServiceDTO, ShipmentBookDTO } from '../dto/logistics.dto';
|
||||||
import { User } from '../decorator/user.decorator';
|
import { User } from '../decorator/user.decorator';
|
||||||
|
|
||||||
@Controller('/logistics')
|
@Controller('/logistics')
|
||||||
|
|
@ -180,7 +180,7 @@ export class LogisticsController {
|
||||||
)
|
)
|
||||||
@Post('/getShipmentFee')
|
@Post('/getShipmentFee')
|
||||||
async getShipmentFee(
|
async getShipmentFee(
|
||||||
@Body() data: ShipmentFeeBookDTO
|
@Body() data: ShipmentBookDTO
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const fee = await this.logisticsService.getShipmentFee(data);
|
const fee = await this.logisticsService.getShipmentFee(data);
|
||||||
|
|
@ -222,7 +222,7 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/updateState/:shipmentId')
|
@Post('/updateState/:id')
|
||||||
async updateShipmentState(
|
async updateShipmentState(
|
||||||
@Param('shipmentId') shipmentId: number
|
@Param('shipmentId') shipmentId: number
|
||||||
) {
|
) {
|
||||||
|
|
@ -247,11 +247,11 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/getOrderList')
|
@Post('/getTrackingNumber')
|
||||||
async getOrderList(@Query('number') number: string) {
|
async getTrackingNumber(@Query('number') number: string) {
|
||||||
try {
|
try {
|
||||||
return successResponse(
|
return successResponse(
|
||||||
await this.logisticsService.getOrderList(number)
|
await this.logisticsService.getTrackingNumber(number)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
|
@ -259,11 +259,11 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/getListByOrderId')
|
@Post('/getListByTrackingId')
|
||||||
async getListByOrderId(@Query('id') orderId: number) {
|
async getListByTrackingId(@Query('shipment_id') shipment_id: number) {
|
||||||
try {
|
try {
|
||||||
return successResponse(
|
return successResponse(
|
||||||
await this.logisticsService.getListByOrderId(orderId)
|
await this.logisticsService.getListByTrackingId(shipment_id)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,11 @@ export class OrderController {
|
||||||
@Get('/getOrders')
|
@Get('/getOrders')
|
||||||
async getOrders(
|
async getOrders(
|
||||||
@Query()
|
@Query()
|
||||||
param: QueryOrderDTO,
|
param: QueryOrderDTO
|
||||||
@User() user
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const count = await this.orderService.getOrderStatus(param);
|
const count = await this.orderService.getOrderStatus(param);
|
||||||
const data = await this.orderService.getOrders(param, user.id);
|
const data = await this.orderService.getOrders(param);
|
||||||
return successResponse({
|
return successResponse({
|
||||||
...data,
|
...data,
|
||||||
count,
|
count,
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,11 @@ export class UserController {
|
||||||
@Inject()
|
@Inject()
|
||||||
userService: UserService;
|
userService: UserService;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
ctx;
|
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: LoginRes,
|
type: LoginRes,
|
||||||
})
|
})
|
||||||
@Post('/login')
|
@Post('/login')
|
||||||
async login(@Body() body) {
|
async login(@Body() body) {
|
||||||
this.ctx.logger.info('ip:', this.ctx.ip, '; path:', this.ctx.path, '; user:', body?.username);
|
|
||||||
try {
|
try {
|
||||||
const result = await this.userService.login(body);
|
const result = await this.userService.login(body);
|
||||||
return successResponse(result, '登录成功');
|
return successResponse(result, '登录成功');
|
||||||
|
|
|
||||||
|
|
@ -21,50 +21,6 @@ export class ShipmentBookDTO {
|
||||||
orderIds?: number[];
|
orderIds?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShipmentFeeBookDTO {
|
|
||||||
@ApiProperty()
|
|
||||||
stockPointId: number;
|
|
||||||
@ApiProperty()
|
|
||||||
sender: string;
|
|
||||||
@ApiProperty()
|
|
||||||
startPhone: string;
|
|
||||||
@ApiProperty()
|
|
||||||
startPostalCode: string;
|
|
||||||
@ApiProperty()
|
|
||||||
pickupAddress: string;
|
|
||||||
// pickupWarehouse: number; // 此处用 stockPointId 到后端解析
|
|
||||||
@ApiProperty()
|
|
||||||
shipperCountryCode: string;
|
|
||||||
@ApiProperty()
|
|
||||||
receiver: string;
|
|
||||||
@ApiProperty()
|
|
||||||
city: string;
|
|
||||||
@ApiProperty()
|
|
||||||
province: string;
|
|
||||||
@ApiProperty()
|
|
||||||
country: string;
|
|
||||||
@ApiProperty()
|
|
||||||
postalCode: string;
|
|
||||||
@ApiProperty()
|
|
||||||
deliveryAddress: string;
|
|
||||||
@ApiProperty()
|
|
||||||
receiverPhone: string;
|
|
||||||
@ApiProperty()
|
|
||||||
receiverEmail: string;
|
|
||||||
@ApiProperty()
|
|
||||||
length: number;
|
|
||||||
@ApiProperty()
|
|
||||||
width: number;
|
|
||||||
@ApiProperty()
|
|
||||||
height: number;
|
|
||||||
@ApiProperty()
|
|
||||||
dimensionUom: string;
|
|
||||||
@ApiProperty()
|
|
||||||
weight: number;
|
|
||||||
@ApiProperty()
|
|
||||||
weightUom: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PaymentMethodDTO {
|
export class PaymentMethodDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Exclude, Expose } from 'class-transformer';
|
import { Exclude, Expose } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
BeforeInsert,
|
|
||||||
BeforeUpdate,
|
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
|
|
@ -58,26 +56,6 @@ export class OrderSale {
|
||||||
@Expose()
|
@Expose()
|
||||||
isPackage: boolean;
|
isPackage: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ default: false })
|
|
||||||
@Expose()
|
|
||||||
isYoone: boolean;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ default: false })
|
|
||||||
@Expose()
|
|
||||||
isZex: boolean;
|
|
||||||
|
|
||||||
@ApiProperty({ nullable: true })
|
|
||||||
@Column({ type: 'int', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
size: number | null;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ default: false })
|
|
||||||
@Expose()
|
|
||||||
isYooneNew: boolean;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
description: '创建时间',
|
description: '创建时间',
|
||||||
|
|
@ -93,25 +71,4 @@ export class OrderSale {
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
|
|
||||||
// === 自动计算逻辑 ===
|
|
||||||
@BeforeInsert()
|
|
||||||
@BeforeUpdate()
|
|
||||||
setFlags() {
|
|
||||||
if (!this.name) return;
|
|
||||||
const lower = this.name.toLowerCase();
|
|
||||||
this.isYoone = lower.includes('yoone');
|
|
||||||
this.isZex = lower.includes('zex');
|
|
||||||
this.isYooneNew = this.isYoone && lower.includes('new');
|
|
||||||
let size: number | null = null;
|
|
||||||
const sizes = [3, 6, 9, 12, 15, 18];
|
|
||||||
for (const s of sizes) {
|
|
||||||
if (lower.includes(s.toString())) {
|
|
||||||
size = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { Order } from '../entity/order.entity';
|
||||||
import { Shipment } from '../entity/shipment.entity';
|
import { Shipment } from '../entity/shipment.entity';
|
||||||
import { ShipmentItem } from '../entity/shipment_item.entity';
|
import { ShipmentItem } from '../entity/shipment_item.entity';
|
||||||
import { OrderShipment } from '../entity/order_shipment.entity';
|
import { OrderShipment } from '../entity/order_shipment.entity';
|
||||||
import { QueryServiceDTO, ShipmentBookDTO, ShipmentFeeBookDTO } from '../dto/logistics.dto';
|
import { QueryServiceDTO, ShipmentBookDTO } from '../dto/logistics.dto';
|
||||||
import {
|
import {
|
||||||
ErpOrderStatus,
|
ErpOrderStatus,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
|
|
@ -30,7 +30,6 @@ import { OrderSale } from '../entity/order_sale.entity';
|
||||||
import { UniExpressService } from './uni_express.service';
|
import { UniExpressService } from './uni_express.service';
|
||||||
import { StockPoint } from '../entity/stock_point.entity';
|
import { StockPoint } from '../entity/stock_point.entity';
|
||||||
import { OrderService } from './order.service';
|
import { OrderService } from './order.service';
|
||||||
import { convertKeysFromCamelToSnake } from '../utils/object-transform.util';
|
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
export class LogisticsService {
|
export class LogisticsService {
|
||||||
|
|
@ -136,13 +135,12 @@ export class LogisticsService {
|
||||||
this.shipmentModel.save(shipment);
|
this.shipmentModel.save(shipment);
|
||||||
return shipment.state;
|
return shipment.state;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw new Error(`更新运单状态失败 ${error.message}`);
|
||||||
// throw new Error(`更新运单状态失败 ${error.message}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateShipmentStateById(id: number) {
|
async updateShipmentStateById(id: number) {
|
||||||
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: id });
|
const shipment:Shipment = await this.shipmentModel.findOneBy({ id : id });
|
||||||
return this.updateShipmentState(shipment);
|
return this.updateShipmentState(shipment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +217,7 @@ export class LogisticsService {
|
||||||
|
|
||||||
async getShipmentLabel(shipmentId) {
|
async getShipmentLabel(shipmentId) {
|
||||||
try {
|
try {
|
||||||
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: shipmentId });
|
const shipment:Shipment = await this.shipmentModel.findOneBy({id: shipmentId});
|
||||||
if (!shipment) {
|
if (!shipment) {
|
||||||
throw new Error('运单不存在');
|
throw new Error('运单不存在');
|
||||||
}
|
}
|
||||||
|
|
@ -231,11 +229,11 @@ export class LogisticsService {
|
||||||
|
|
||||||
async removeShipment(shipmentId: number) {
|
async removeShipment(shipmentId: number) {
|
||||||
try {
|
try {
|
||||||
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: shipmentId });
|
const shipment:Shipment = await this.shipmentModel.findOneBy({id: shipmentId});
|
||||||
if (shipment.state !== '190') { // todo,写常数
|
if (shipment.state !== '190') { // todo,写常数
|
||||||
throw new Error('订单当前状态无法删除');
|
throw new Error('订单当前状态无法删除');
|
||||||
}
|
}
|
||||||
const order: Order = await this.orderModel.findOneBy({ id: shipment.order_id });
|
const order:Order = await this.orderModel.findOneBy({id: shipment.order_id});
|
||||||
const dataSource = this.dataSourceManager.getDataSource('default');
|
const dataSource = this.dataSourceManager.getDataSource('default');
|
||||||
let transactionError = undefined;
|
let transactionError = undefined;
|
||||||
await dataSource.transaction(async manager => {
|
await dataSource.transaction(async manager => {
|
||||||
|
|
@ -271,7 +269,7 @@ export class LogisticsService {
|
||||||
}
|
}
|
||||||
order.orderStatus = ErpOrderStatus.PROCESSING;
|
order.orderStatus = ErpOrderStatus.PROCESSING;
|
||||||
this.orderModel.save(order);
|
this.orderModel.save(order);
|
||||||
|
|
||||||
// todo 同步到wooccommerce删除运单信息
|
// todo 同步到wooccommerce删除运单信息
|
||||||
await this.wpService.deleteShipment(site, order.externalOrderId, shipment.tracking_id);
|
await this.wpService.deleteShipment(site, order.externalOrderId, shipment.tracking_id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -285,19 +283,34 @@ export class LogisticsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getShipmentFee(data: ShipmentFeeBookDTO) {
|
async getShipmentFee(data: ShipmentBookDTO) {
|
||||||
try {
|
try {
|
||||||
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
|
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId});
|
||||||
const reqBody = {
|
const reqBody = {
|
||||||
...convertKeysFromCamelToSnake(data),
|
sender: data.details.origin.contact_name,
|
||||||
|
start_phone: data.details.origin.phone_number,
|
||||||
|
start_postal_code: data.details.origin.address.postal_code.replace(/\s/g, ''),
|
||||||
|
pickup_address: data.details.origin.address.address_line_1,
|
||||||
pickup_warehouse: stock_point.upStreamStockPointId,
|
pickup_warehouse: stock_point.upStreamStockPointId,
|
||||||
currency: 'CAD',
|
shipper_country_code: data.details.origin.address.country,
|
||||||
|
receiver: data.details.destination.contact_name,
|
||||||
|
city: data.details.destination.address.city,
|
||||||
|
province: data.details.destination.address.region,
|
||||||
|
country: data.details.destination.address.country,
|
||||||
|
postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''),
|
||||||
|
delivery_address: data.details.destination.address.address_line_1,
|
||||||
|
receiver_phone: data.details.destination.phone_number.number,
|
||||||
|
receiver_email: data.details.destination.email_addresses,
|
||||||
// item_description: data.sales, // todo: 货品信息
|
// item_description: data.sales, // todo: 货品信息
|
||||||
|
length: data.details.packaging_properties.packages[0].measurements.cuboid.l,
|
||||||
|
width: data.details.packaging_properties.packages[0].measurements.cuboid.w,
|
||||||
|
height: data.details.packaging_properties.packages[0].measurements.cuboid.h,
|
||||||
|
dimension_uom: data.details.packaging_properties.packages[0].measurements.cuboid.unit,
|
||||||
|
weight: data.details.packaging_properties.packages[0].measurements.weight.value,
|
||||||
|
weight_uom: data.details.packaging_properties.packages[0].measurements.weight.unit,
|
||||||
|
currency: 'CAD',
|
||||||
}
|
}
|
||||||
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
|
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
|
||||||
if (resShipmentFee.status !== 'SUCCESS') {
|
|
||||||
throw new Error(resShipmentFee.ret_msg);
|
|
||||||
}
|
|
||||||
return resShipmentFee.data.totalAfterTax * 100;
|
return resShipmentFee.data.totalAfterTax * 100;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
|
@ -319,7 +332,7 @@ export class LogisticsService {
|
||||||
|
|
||||||
let resShipmentOrder;
|
let resShipmentOrder;
|
||||||
try {
|
try {
|
||||||
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
|
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId});
|
||||||
const reqBody = {
|
const reqBody = {
|
||||||
sender: data.details.origin.contact_name,
|
sender: data.details.origin.contact_name,
|
||||||
start_phone: data.details.origin.phone_number,
|
start_phone: data.details.origin.phone_number,
|
||||||
|
|
@ -409,12 +422,10 @@ export class LogisticsService {
|
||||||
// 更新产品发货信息
|
// 更新产品发货信息
|
||||||
this.orderService.updateOrderSales(order.id, sales);
|
this.orderService.updateOrderSales(order.id, sales);
|
||||||
|
|
||||||
return {
|
return { data: {
|
||||||
data: {
|
shipmentId
|
||||||
shipmentId
|
} };
|
||||||
}
|
} catch(error) {
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
if (resShipmentOrder.status === 'SUCCESS') {
|
if (resShipmentOrder.status === 'SUCCESS') {
|
||||||
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
|
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
|
||||||
}
|
}
|
||||||
|
|
@ -471,7 +482,7 @@ export class LogisticsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getShipment(id: number) {
|
async getShipment(id: number) {
|
||||||
const orderShipments: OrderShipment[] = await this.orderShipmentModel.find({
|
const orderShipments:OrderShipment[] = await this.orderShipmentModel.find({
|
||||||
where: { shipment_id: id },
|
where: { shipment_id: id },
|
||||||
});
|
});
|
||||||
if (!orderShipments || orderShipments.length === 0) return;
|
if (!orderShipments || orderShipments.length === 0) return;
|
||||||
|
|
@ -555,7 +566,7 @@ export class LogisticsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderList(number: string) {
|
async getTrackingNumber(number: string) {
|
||||||
const orders = await this.orderModel.find({
|
const orders = await this.orderModel.find({
|
||||||
where: {
|
where: {
|
||||||
externalOrderId: Like(`%${number}%`),
|
externalOrderId: Like(`%${number}%`),
|
||||||
|
|
@ -570,14 +581,56 @@ export class LogisticsService {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getListByOrderId(id: number) {
|
async getListByTrackingId(id: number) {
|
||||||
const item = await this.orderItem.find({ where: { orderId: id } });
|
const qb = `
|
||||||
const saleItem = await this.orderSaleModel.find({ where: { orderId: id } });
|
SELECT
|
||||||
|
oi.name,
|
||||||
|
oi.quantity,
|
||||||
|
CASE
|
||||||
|
WHEN oi.externalVariationId != 0 THEN v.constitution
|
||||||
|
ELSE p.constitution
|
||||||
|
END AS constitution
|
||||||
|
FROM order_item oi
|
||||||
|
LEFT JOIN wp_product p ON oi.siteId=p.siteId AND oi.externalProductId=p.externalProductId
|
||||||
|
LEFT JOIN variation v ON oi.siteId=v.siteId AND oi.externalVariationId=v.externalVariationId
|
||||||
|
WHERE oi.orderId=?
|
||||||
|
`;
|
||||||
|
const saleItem = await this.orderSaleModel.query(qb, [id]);
|
||||||
|
const allSkus = new Set<string>();
|
||||||
|
for (const item of saleItem) {
|
||||||
|
if (!item.constitution) continue;
|
||||||
|
try {
|
||||||
|
item.constitution.forEach(c => allSkus.add(c.sku));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Invalid constitution JSON:', item.constitution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(allSkus);
|
||||||
|
const skuList = Array.from(allSkus);
|
||||||
|
let skuNameMap = new Map<string, string>();
|
||||||
|
|
||||||
return {
|
if (skuList.length > 0) {
|
||||||
item,
|
const placeholders = skuList.map(() => '?').join(', ');
|
||||||
saleItem
|
const productRows = await this.orderSaleModel.query(
|
||||||
};
|
`SELECT sku, name FROM product WHERE sku IN (${placeholders})`,
|
||||||
|
skuList
|
||||||
|
);
|
||||||
|
skuNameMap = new Map(productRows.map(p => [p.sku, p.name]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of saleItem) {
|
||||||
|
if (!item.constitution) continue;
|
||||||
|
try {
|
||||||
|
item.constitution = item.constitution.map(c => ({
|
||||||
|
...c,
|
||||||
|
name: skuNameMap.get(c.sku) || null,
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
item.constitution = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return saleItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getList(param: Record<string, any>) {
|
async getList(param: Record<string, any>) {
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,6 @@ export class OrderService {
|
||||||
@InjectEntityModel(Order)
|
@InjectEntityModel(Order)
|
||||||
orderModel: Repository<Order>;
|
orderModel: Repository<Order>;
|
||||||
|
|
||||||
@InjectEntityModel(User)
|
|
||||||
userModel: Repository<User>;
|
|
||||||
|
|
||||||
@InjectEntityModel(OrderItem)
|
@InjectEntityModel(OrderItem)
|
||||||
orderItemModel: Repository<OrderItem>;
|
orderItemModel: Repository<OrderItem>;
|
||||||
|
|
||||||
|
|
@ -549,7 +546,7 @@ export class OrderService {
|
||||||
current,
|
current,
|
||||||
pageSize,
|
pageSize,
|
||||||
customer_email,
|
customer_email,
|
||||||
}, userId = undefined) {
|
}) {
|
||||||
const parameters: any[] = [];
|
const parameters: any[] = [];
|
||||||
|
|
||||||
// 基础查询
|
// 基础查询
|
||||||
|
|
@ -632,14 +629,6 @@ export class OrderService {
|
||||||
totalQuery += ` AND o.date_created <= ?`;
|
totalQuery += ` AND o.date_created <= ?`;
|
||||||
parameters.push(endDate);
|
parameters.push(endDate);
|
||||||
}
|
}
|
||||||
const user = await this.userModel.findOneBy({id: userId});
|
|
||||||
if (user?.permissions?.includes('order-10-days')) {
|
|
||||||
sqlQuery += ` AND o.date_created >= ?`;
|
|
||||||
totalQuery += ` AND o.date_created >= ?`;
|
|
||||||
const tenDaysAgo = new Date();
|
|
||||||
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
|
|
||||||
parameters.push(tenDaysAgo.toISOString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理 status 参数
|
// 处理 status 参数
|
||||||
if (status) {
|
if (status) {
|
||||||
|
|
@ -751,177 +740,296 @@ export class OrderService {
|
||||||
|
|
||||||
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name }: QueryOrderSalesDTO) {
|
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name }: QueryOrderSalesDTO) {
|
||||||
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
||||||
const offset = (current - 1) * pageSize;
|
|
||||||
|
|
||||||
// -------------------------
|
const parameters: any[] = [startDate, endDate];
|
||||||
// 1. 查询总条数
|
|
||||||
// -------------------------
|
// 主查询:带分页
|
||||||
|
let sqlQuery = `
|
||||||
|
WITH product_purchase_counts AS (
|
||||||
|
SELECT
|
||||||
|
o.customer_email,
|
||||||
|
os.productId,
|
||||||
|
COUNT(DISTINCT o.id) AS order_count
|
||||||
|
FROM \`order\` o
|
||||||
|
JOIN order_sale os ON o.id = os.orderId
|
||||||
|
WHERE o.status IN ('completed', 'processing')
|
||||||
|
GROUP BY o.customer_email, os.productId
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
os.productId AS productId,
|
||||||
|
os.name AS name,
|
||||||
|
SUM(os.quantity) AS totalQuantity,
|
||||||
|
COUNT(DISTINCT os.orderId) AS totalOrders,
|
||||||
|
c.name AS categoryName,
|
||||||
|
COUNT(DISTINCT CASE WHEN pc.order_count = 1 THEN o.id END) AS firstOrderCount,
|
||||||
|
SUM(CASE WHEN pc.order_count = 1 THEN os.quantity ELSE 0 END) AS firstOrderYOONEBoxCount,
|
||||||
|
COUNT(DISTINCT CASE WHEN pc.order_count = 2 THEN o.id END) AS secondOrderCount,
|
||||||
|
SUM(CASE WHEN pc.order_count = 2 THEN os.quantity ELSE 0 END) AS secondOrderYOONEBoxCount,
|
||||||
|
COUNT(DISTINCT CASE WHEN pc.order_count = 3 THEN o.id END) AS thirdOrderCount,
|
||||||
|
SUM(CASE WHEN pc.order_count = 3 THEN os.quantity ELSE 0 END) AS thirdOrderYOONEBoxCount,
|
||||||
|
COUNT(DISTINCT CASE WHEN pc.order_count > 3 THEN o.id END) AS moreThirdOrderCount,
|
||||||
|
SUM(CASE WHEN pc.order_count > 3 THEN os.quantity ELSE 0 END) AS moreThirdOrderYOONEBoxCount
|
||||||
|
FROM order_sale os
|
||||||
|
INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
|
INNER JOIN product p ON os.productId = p.id
|
||||||
|
INNER JOIN category c ON p.categoryId = c.id
|
||||||
|
INNER JOIN product_purchase_counts pc ON pc.customer_email = o.customer_email AND pc.productId = os.productId
|
||||||
|
WHERE o.date_paid BETWEEN ? AND ?
|
||||||
|
AND o.status IN ('completed', 'processing')
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (siteId) {
|
||||||
|
sqlQuery += ' AND os.siteId = ?';
|
||||||
|
parameters.push(siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nameKeywords.length > 0) {
|
||||||
|
sqlQuery += ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' OR ') + ')';
|
||||||
|
parameters.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlQuery += `
|
||||||
|
GROUP BY os.productId, os.name, c.name
|
||||||
|
ORDER BY totalQuantity DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
`;
|
||||||
|
|
||||||
|
parameters.push(pageSize, (current - 1) * pageSize);
|
||||||
|
const items = await this.orderSaleModel.query(sqlQuery, parameters);
|
||||||
|
|
||||||
|
// 总条数
|
||||||
const countParams: any[] = [startDate, endDate];
|
const countParams: any[] = [startDate, endDate];
|
||||||
let countSql = `
|
let totalCountQuery = `
|
||||||
SELECT COUNT(DISTINCT os.productId) AS totalCount
|
SELECT COUNT(DISTINCT os.productId) AS totalCount
|
||||||
FROM order_sale os
|
FROM order_sale os
|
||||||
INNER JOIN \`order\` o ON o.id = os.orderId
|
INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
WHERE o.date_paid BETWEEN ? AND ?
|
WHERE o.date_paid BETWEEN ? AND ?
|
||||||
AND o.status IN ('completed','processing')
|
AND o.status IN ('completed', 'processing')
|
||||||
`;
|
`;
|
||||||
if (siteId) {
|
if (siteId) {
|
||||||
countSql += ' AND os.siteId = ?';
|
totalCountQuery += ' AND os.siteId = ?';
|
||||||
countParams.push(siteId);
|
countParams.push(siteId);
|
||||||
}
|
}
|
||||||
if (nameKeywords.length > 0) {
|
if (nameKeywords.length > 0) {
|
||||||
countSql += ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' AND ') + ')';
|
totalCountQuery += ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' OR ') + ')';
|
||||||
countParams.push(...nameKeywords.map(w => `%${w}%`));
|
countParams.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
}
|
|
||||||
const [countResult] = await this.orderSaleModel.query(countSql, countParams);
|
|
||||||
const totalCount = Number(countResult?.totalCount || 0);
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// 2. 分页查询 product 基础信息
|
|
||||||
// -------------------------
|
|
||||||
const itemParams: any[] = [startDate, endDate];
|
|
||||||
let nameCondition = '';
|
|
||||||
if (nameKeywords.length > 0) {
|
|
||||||
nameCondition = ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' AND ') + ')';
|
|
||||||
itemParams.push(...nameKeywords.map(w => `%${w}%`));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemSql = `
|
const totalCountResult = await this.orderSaleModel.query(totalCountQuery, countParams);
|
||||||
SELECT os.productId, os.name, SUM(os.quantity) AS totalQuantity, COUNT(DISTINCT os.orderId) AS totalOrders
|
|
||||||
|
// 一次查询获取所有 yoone box 数量
|
||||||
|
const totalQuantityParams: any[] = [startDate, endDate];
|
||||||
|
let totalQuantityQuery = `
|
||||||
|
SELECT
|
||||||
|
SUM(os.quantity) AS totalQuantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%3%' THEN os.quantity ELSE 0 END) AS yoone3Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%6%' THEN os.quantity ELSE 0 END) AS yoone6Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%9%' THEN os.quantity ELSE 0 END) AS yoone9Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%12%' THEN os.quantity ELSE 0 END) AS yoone12Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%12%' AND os.name LIKE '%NEW%' THEN os.quantity ELSE 0 END) AS yoone12QuantityNew,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%15%' THEN os.quantity ELSE 0 END) AS yoone15Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%yoone%' AND os.name LIKE '%18%' THEN os.quantity ELSE 0 END) AS yoone18Quantity,
|
||||||
|
SUM(CASE WHEN os.name LIKE '%zex%' THEN os.quantity ELSE 0 END) AS zexQuantity
|
||||||
FROM order_sale os
|
FROM order_sale os
|
||||||
INNER JOIN \`order\` o ON o.id = os.orderId
|
INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
WHERE o.date_paid BETWEEN ? AND ?
|
WHERE o.date_paid BETWEEN ? AND ?
|
||||||
AND o.status IN ('completed','processing')
|
AND o.status IN ('completed', 'processing')
|
||||||
`;
|
`;
|
||||||
if (siteId) {
|
if (siteId) {
|
||||||
itemSql += ' AND os.siteId = ?';
|
totalQuantityQuery += ' AND os.siteId = ?';
|
||||||
itemParams.push(siteId);
|
totalQuantityParams.push(siteId);
|
||||||
}
|
}
|
||||||
itemSql += nameCondition;
|
|
||||||
itemSql += `
|
|
||||||
GROUP BY os.productId, os.name
|
|
||||||
ORDER BY totalQuantity DESC
|
|
||||||
LIMIT ? OFFSET ?
|
|
||||||
`;
|
|
||||||
itemParams.push(pageSize, offset);
|
|
||||||
const items = await this.orderSaleModel.query(itemSql, itemParams);
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// 3. 批量统计当前页 product 历史复购
|
|
||||||
// -------------------------
|
|
||||||
if (items.length > 0) {
|
|
||||||
const productIds = items.map(i => i.productId);
|
|
||||||
const pcParams: any[] = [...productIds, startDate, endDate];
|
|
||||||
if (siteId) pcParams.push(siteId);
|
|
||||||
|
|
||||||
const pcSql = `
|
|
||||||
SELECT
|
|
||||||
os.productId,
|
|
||||||
SUM(CASE WHEN t.purchaseIndex = 1 THEN os.quantity ELSE 0 END) AS firstOrderYOONEBoxCount,
|
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 1 THEN 1 END) AS firstOrderCount,
|
|
||||||
SUM(CASE WHEN t.purchaseIndex = 2 THEN os.quantity ELSE 0 END) AS secondOrderYOONEBoxCount,
|
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 2 THEN 1 END) AS secondOrderCount,
|
|
||||||
SUM(CASE WHEN t.purchaseIndex = 3 THEN os.quantity ELSE 0 END) AS thirdOrderYOONEBoxCount,
|
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 3 THEN 1 END) AS thirdOrderCount,
|
|
||||||
SUM(CASE WHEN t.purchaseIndex > 3 THEN os.quantity ELSE 0 END) AS moreThirdOrderYOONEBoxCount,
|
|
||||||
COUNT(CASE WHEN t.purchaseIndex > 3 THEN 1 END) AS moreThirdOrderCount
|
|
||||||
FROM order_sale os
|
|
||||||
INNER JOIN (
|
|
||||||
SELECT o2.id AS orderId,
|
|
||||||
@idx := IF(@prev_email = o2.customer_email, @idx + 1, 1) AS purchaseIndex,
|
|
||||||
@prev_email := o2.customer_email
|
|
||||||
FROM \`order\` o2
|
|
||||||
CROSS JOIN (SELECT @idx := 0, @prev_email := '') vars
|
|
||||||
WHERE o2.status IN ('completed','processing')
|
|
||||||
ORDER BY o2.customer_email, o2.date_paid
|
|
||||||
) t ON t.orderId = os.orderId
|
|
||||||
WHERE os.productId IN (${productIds.map(() => '?').join(',')})
|
|
||||||
AND os.orderId IN (
|
|
||||||
SELECT id FROM \`order\`
|
|
||||||
WHERE date_paid BETWEEN ? AND ?
|
|
||||||
${siteId ? 'AND siteId = ?' : ''}
|
|
||||||
)
|
|
||||||
GROUP BY os.productId
|
|
||||||
`;
|
|
||||||
|
|
||||||
const pcResults = await this.orderSaleModel.query(pcSql, pcParams);
|
|
||||||
|
|
||||||
const pcMap = new Map<number, any>();
|
|
||||||
pcResults.forEach(r => pcMap.set(r.productId, r));
|
|
||||||
items.forEach(i => {
|
|
||||||
const r = pcMap.get(i.productId) || {};
|
|
||||||
i.firstOrderYOONEBoxCount = Number(r.firstOrderYOONEBoxCount || 0);
|
|
||||||
i.firstOrderCount = Number(r.firstOrderCount || 0);
|
|
||||||
i.secondOrderYOONEBoxCount = Number(r.secondOrderYOONEBoxCount || 0);
|
|
||||||
i.secondOrderCount = Number(r.secondOrderCount || 0);
|
|
||||||
i.thirdOrderYOONEBoxCount = Number(r.thirdOrderYOONEBoxCount || 0);
|
|
||||||
i.thirdOrderCount = Number(r.thirdOrderCount || 0);
|
|
||||||
i.moreThirdOrderYOONEBoxCount = Number(r.moreThirdOrderYOONEBoxCount || 0);
|
|
||||||
i.moreThirdOrderCount = Number(r.moreThirdOrderCount || 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------
|
|
||||||
// 4. 总量统计(时间段 + siteId)
|
|
||||||
// -------------------------
|
|
||||||
const totalParams: any[] = [startDate, endDate];
|
|
||||||
const yooneParams: any[] = [startDate, endDate];
|
|
||||||
let totalSql = `
|
|
||||||
SELECT
|
|
||||||
SUM(os.quantity) AS totalQuantity
|
|
||||||
FROM order_sale os
|
|
||||||
INNER JOIN \`order\` o ON o.id = os.orderId
|
|
||||||
WHERE o.date_paid BETWEEN ? AND ?
|
|
||||||
AND o.status IN ('completed','processing')
|
|
||||||
`;
|
|
||||||
let yooneSql = `
|
|
||||||
SELECT
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 3 THEN os.quantity ELSE 0 END) AS yoone3Quantity,
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 6 THEN os.quantity ELSE 0 END) AS yoone6Quantity,
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 9 THEN os.quantity ELSE 0 END) AS yoone9Quantity,
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 12 THEN os.quantity ELSE 0 END) AS yoone12Quantity,
|
|
||||||
SUM(CASE WHEN os.isYooneNew = 1 AND os.size = 12 THEN os.quantity ELSE 0 END) AS yoone12QuantityNew,
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 15 THEN os.quantity ELSE 0 END) AS yoone15Quantity,
|
|
||||||
SUM(CASE WHEN os.isYoone = 1 AND os.size = 18 THEN os.quantity ELSE 0 END) AS yoone18Quantity,
|
|
||||||
SUM(CASE WHEN os.isZex = 1 THEN os.quantity ELSE 0 END) AS zexQuantity
|
|
||||||
FROM order_sale os
|
|
||||||
INNER JOIN \`order\` o ON o.id = os.orderId
|
|
||||||
WHERE o.date_paid BETWEEN ? AND ?
|
|
||||||
AND o.status IN ('completed','processing')
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (siteId) {
|
|
||||||
totalSql += ' AND os.siteId = ?';
|
|
||||||
totalParams.push(siteId);
|
|
||||||
yooneSql += ' AND os.siteId = ?';
|
|
||||||
yooneParams.push(siteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nameKeywords.length > 0) {
|
if (nameKeywords.length > 0) {
|
||||||
totalSql += ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' AND ') + ')';
|
totalQuantityQuery += ' AND (' + nameKeywords.map(() => 'os.name LIKE ?').join(' OR ') + ')';
|
||||||
totalParams.push(...nameKeywords.map(w => `%${w}%`));
|
totalQuantityParams.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
}
|
}
|
||||||
|
|
||||||
const [totalResult] = await this.orderSaleModel.query(totalSql, totalParams);
|
const [totalQuantityResult] = await this.orderSaleModel.query(totalQuantityQuery, totalQuantityParams);
|
||||||
const [yooneResult] = await this.orderSaleModel.query(yooneSql, yooneParams);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items,
|
items,
|
||||||
total: totalCount, // ✅ 总条数
|
total: totalCountResult[0]?.totalCount || 0,
|
||||||
totalQuantity: Number(totalResult.totalQuantity || 0),
|
totalQuantity: Number(totalQuantityResult.totalQuantity || 0),
|
||||||
yoone3Quantity: Number(yooneResult.yoone3Quantity || 0),
|
yoone3Quantity: Number(totalQuantityResult.yoone3Quantity || 0),
|
||||||
yoone6Quantity: Number(yooneResult.yoone6Quantity || 0),
|
yoone6Quantity: Number(totalQuantityResult.yoone6Quantity || 0),
|
||||||
yoone9Quantity: Number(yooneResult.yoone9Quantity || 0),
|
yoone9Quantity: Number(totalQuantityResult.yoone9Quantity || 0),
|
||||||
yoone12Quantity: Number(yooneResult.yoone12Quantity || 0),
|
yoone12Quantity: Number(totalQuantityResult.yoone12Quantity || 0),
|
||||||
yoone12QuantityNew: Number(yooneResult.yoone12QuantityNew || 0),
|
yoone12QuantityNew: Number(totalQuantityResult.yoone12QuantityNew || 0),
|
||||||
yoone15Quantity: Number(yooneResult.yoone15Quantity || 0),
|
yoone15Quantity: Number(totalQuantityResult.yoone15Quantity || 0),
|
||||||
yoone18Quantity: Number(yooneResult.yoone18Quantity || 0),
|
yoone18Quantity: Number(totalQuantityResult.yoone18Quantity || 0),
|
||||||
zexQuantity: Number(yooneResult.zexQuantity || 0),
|
zexQuantity: Number(totalQuantityResult.zexQuantity || 0),
|
||||||
current,
|
current,
|
||||||
pageSize,
|
pageSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// async getOrderSales({
|
||||||
|
// siteId,
|
||||||
|
// startDate,
|
||||||
|
// endDate,
|
||||||
|
// current,
|
||||||
|
// pageSize,
|
||||||
|
// name,
|
||||||
|
// }: QueryOrderSalesDTO) {
|
||||||
|
// const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
||||||
|
// // 分页查询
|
||||||
|
// let sqlQuery = `
|
||||||
|
// WITH product_purchase_counts AS (
|
||||||
|
// SELECT o.customer_email,os.productId, os.name, COUNT(DISTINCT o.id,os.productId) AS order_count
|
||||||
|
// FROM \`order\` o
|
||||||
|
// JOIN order_sale os ON o.id = os.orderId
|
||||||
|
// WHERE o.status IN ('completed', 'processing')
|
||||||
|
// GROUP BY o.customer_email, os.productId, os.name
|
||||||
|
// )
|
||||||
|
// SELECT
|
||||||
|
// os.productId AS productId,
|
||||||
|
// os.name AS name,
|
||||||
|
// SUM(os.quantity) AS totalQuantity,
|
||||||
|
// COUNT(distinct os.orderId) AS totalOrders,
|
||||||
|
// c.name AS categoryName,
|
||||||
|
// COUNT(DISTINCT CASE WHEN pc.order_count = 1 THEN o.id END) AS firstOrderCount,
|
||||||
|
// SUM(CASE WHEN pc.order_count = 1 THEN os.quantity ELSE 0 END) AS firstOrderYOONEBoxCount,
|
||||||
|
// COUNT(DISTINCT CASE WHEN pc.order_count = 2 THEN o.id END) AS secondOrderCount,
|
||||||
|
// SUM(CASE WHEN pc.order_count = 2 THEN os.quantity ELSE 0 END) AS secondOrderYOONEBoxCount,
|
||||||
|
// COUNT(DISTINCT CASE WHEN pc.order_count = 3 THEN o.id END) AS thirdOrderCount,
|
||||||
|
// SUM(CASE WHEN pc.order_count = 3 THEN os.quantity ELSE 0 END) AS thirdOrderYOONEBoxCount,
|
||||||
|
// COUNT(DISTINCT CASE WHEN pc.order_count > 3 THEN o.id END) AS moreThirdOrderCount,
|
||||||
|
// SUM(CASE WHEN pc.order_count > 3 THEN os.quantity ELSE 0 END) AS moreThirdOrderYOONEBoxCount
|
||||||
|
// FROM order_sale os
|
||||||
|
// INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
|
// INNER JOIN product p ON os.productId = p.id
|
||||||
|
// INNER JOIN category c ON p.categoryId = c.id
|
||||||
|
// INNER JOIN product_purchase_counts pc ON pc.customer_email = o.customer_email AND pc.productId = os.productId
|
||||||
|
// WHERE o.date_paid BETWEEN ? AND ?
|
||||||
|
// AND o.status IN ('processing', 'completed')
|
||||||
|
// `;
|
||||||
|
// const parameters: any[] = [startDate, endDate];
|
||||||
|
// if (siteId) {
|
||||||
|
// sqlQuery += ' AND os.siteId = ?';
|
||||||
|
// parameters.push(siteId);
|
||||||
|
// }
|
||||||
|
// if (nameKeywords.length > 0) {
|
||||||
|
// sqlQuery +=
|
||||||
|
// ' AND ' + nameKeywords.map(() => `os.name LIKE ?`).join(' AND ');
|
||||||
|
// parameters.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
|
// }
|
||||||
|
// sqlQuery += `
|
||||||
|
// GROUP BY os.productId, os.name, c.name
|
||||||
|
// ORDER BY totalQuantity DESC
|
||||||
|
// `;
|
||||||
|
// sqlQuery += ' LIMIT ? OFFSET ?';
|
||||||
|
// parameters.push(pageSize, (current - 1) * pageSize);
|
||||||
|
|
||||||
|
// // 执行查询并传递参数
|
||||||
|
// const items = await this.orderSaleModel.query(sqlQuery, parameters);
|
||||||
|
|
||||||
|
// let totalCountQuery = `
|
||||||
|
// SELECT COUNT(DISTINCT os.productId) AS totalCount
|
||||||
|
// FROM order_sale os
|
||||||
|
// INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
|
// INNER JOIN product p ON os.productId = p.id
|
||||||
|
// INNER JOIN category c ON p.categoryId = c.id
|
||||||
|
// WHERE o.date_created BETWEEN ? AND ?
|
||||||
|
// AND o.status IN ('processing', 'completed')
|
||||||
|
// `;
|
||||||
|
// const totalCountParameters: any[] = [startDate, endDate];
|
||||||
|
// if (siteId) {
|
||||||
|
// totalCountQuery += ' AND os.siteId = ?';
|
||||||
|
// totalCountParameters.push(siteId);
|
||||||
|
// }
|
||||||
|
// if (nameKeywords.length > 0) {
|
||||||
|
// totalCountQuery +=
|
||||||
|
// ' AND ' + nameKeywords.map(() => `os.name LIKE ?`).join(' AND ');
|
||||||
|
// totalCountParameters.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const totalCountResult = await this.orderSaleModel.query(
|
||||||
|
// totalCountQuery,
|
||||||
|
// totalCountParameters
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let totalQuantityQuery = `
|
||||||
|
// SELECT SUM(os.quantity) AS totalQuantity
|
||||||
|
// FROM order_sale os
|
||||||
|
// INNER JOIN \`order\` o ON o.id = os.orderId
|
||||||
|
// INNER JOIN product p ON os.productId = p.id
|
||||||
|
// INNER JOIN category c ON p.categoryId = c.id
|
||||||
|
// WHERE o.date_created BETWEEN ? AND ?
|
||||||
|
// AND o.status IN ('processing', 'completed')
|
||||||
|
// `;
|
||||||
|
|
||||||
|
// const totalQuantityParameters: any[] = [startDate, endDate];
|
||||||
|
// if (siteId) {
|
||||||
|
// totalQuantityQuery += ' AND os.siteId = ?';
|
||||||
|
// totalQuantityParameters.push(siteId);
|
||||||
|
// }
|
||||||
|
// const yoone3QuantityQuery =
|
||||||
|
// totalQuantityQuery + 'AND os.name LIKE "%yoone%" AND os.name LIKE "%3%"';
|
||||||
|
// const yoone6QuantityQuery =
|
||||||
|
// totalQuantityQuery + 'AND os.name LIKE "%yoone%" AND os.name LIKE "%6%"';
|
||||||
|
// const yoone9QuantityQuery =
|
||||||
|
// totalQuantityQuery + 'AND os.name LIKE "%yoone%" AND os.name LIKE "%9%"';
|
||||||
|
// const yoone12QuantityQuery =
|
||||||
|
// totalQuantityQuery + 'AND os.name LIKE "%yoone%" AND os.name LIKE "%12%"';
|
||||||
|
// const yoone15QuantityQuery =
|
||||||
|
// totalQuantityQuery + 'AND os.name LIKE "%yoone%" AND os.name LIKE "%15%"';
|
||||||
|
// const yooneParameters = [...totalQuantityParameters];
|
||||||
|
// if (nameKeywords.length > 0) {
|
||||||
|
// totalQuantityQuery +=
|
||||||
|
// ' AND ' + nameKeywords.map(() => `os.name LIKE ?`).join(' AND ');
|
||||||
|
// totalQuantityParameters.push(...nameKeywords.map(word => `%${word}%`));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const totalQuantityResult = await this.orderSaleModel.query(
|
||||||
|
// totalQuantityQuery,
|
||||||
|
// totalQuantityParameters
|
||||||
|
// );
|
||||||
|
// const yoone3QuantityResult = await this.orderSaleModel.query(
|
||||||
|
// yoone3QuantityQuery,
|
||||||
|
// yooneParameters
|
||||||
|
// );
|
||||||
|
// const yoone6QuantityResult = await this.orderSaleModel.query(
|
||||||
|
// yoone6QuantityQuery,
|
||||||
|
// yooneParameters
|
||||||
|
// );
|
||||||
|
// const yoone9QuantityResult = await this.orderSaleModel.query(
|
||||||
|
// yoone9QuantityQuery,
|
||||||
|
// yooneParameters
|
||||||
|
// );
|
||||||
|
// const yoone12QuantityResult = await this.orderSaleModel.query(
|
||||||
|
// yoone12QuantityQuery,
|
||||||
|
// yooneParameters
|
||||||
|
// );
|
||||||
|
// const yoone15QuantityResult = await this.orderSaleModel.query(
|
||||||
|
// yoone15QuantityQuery,
|
||||||
|
// yooneParameters
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// items,
|
||||||
|
// total: totalCountResult[0]?.totalCount,
|
||||||
|
// totalQuantity: Number(
|
||||||
|
// totalQuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// yoone3Quantity: Number(
|
||||||
|
// yoone3QuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// yoone6Quantity: Number(
|
||||||
|
// yoone6QuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// yoone9Quantity: Number(
|
||||||
|
// yoone9QuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// yoone12Quantity: Number(
|
||||||
|
// yoone12QuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// yoone15Quantity: Number(
|
||||||
|
// yoone15QuantityResult.reduce((sum, row) => sum + row.totalQuantity, 0)
|
||||||
|
// ),
|
||||||
|
// current,
|
||||||
|
// pageSize,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
async getOrderItems({
|
async getOrderItems({
|
||||||
siteId,
|
siteId,
|
||||||
startDate,
|
startDate,
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ export class UserService {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
deviceId,
|
deviceId,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
isSuper: user.isSuper,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
function camelToSnake(str: string): string {
|
|
||||||
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function snakeToCamel(str: string): string {
|
|
||||||
return str.replace(/(_[\w])/g, (match) =>
|
|
||||||
match[1].toUpperCase()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertKeysFromSnakeToCamel<T>(obj: T): T {
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map(convertKeysFromSnakeToCamel) as unknown as T;
|
|
||||||
} else if (obj !== null && typeof obj === 'object') {
|
|
||||||
return Object.keys(obj).reduce((acc, key) => {
|
|
||||||
const newKey = snakeToCamel(key);
|
|
||||||
acc[newKey] = convertKeysFromSnakeToCamel((obj as any)[key]);
|
|
||||||
return acc;
|
|
||||||
}, {} as any);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function convertKeysFromCamelToSnake<T>(obj: T): T {
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map(convertKeysFromCamelToSnake) as unknown as T;
|
|
||||||
} else if (obj !== null && typeof obj === 'object') {
|
|
||||||
return Object.keys(obj).reduce((acc, key) => {
|
|
||||||
const newKey = camelToSnake(key);
|
|
||||||
acc[newKey] = convertKeysFromCamelToSnake((obj as any)[key]);
|
|
||||||
return acc;
|
|
||||||
}, {} as any);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue