642 lines
21 KiB
TypeScript
642 lines
21 KiB
TypeScript
import { Inject, Provide, sleep } from '@midwayjs/core';
|
|
import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm';
|
|
import { Service } from '../entity/service.entity';
|
|
import { In, IsNull, Like, Repository } from 'typeorm';
|
|
import { ShippingAddress } from '../entity/shipping_address.entity';
|
|
|
|
import { Order } from '../entity/order.entity';
|
|
import { Shipment } from '../entity/shipment.entity';
|
|
import { ShipmentItem } from '../entity/shipment_item.entity';
|
|
import { OrderShipment } from '../entity/order_shipment.entity';
|
|
import { QueryServiceDTO, ShipmentBookDTO, ShipmentFeeBookDTO } from '../dto/logistics.dto';
|
|
import {
|
|
ErpOrderStatus,
|
|
OrderStatus,
|
|
ShipmentType,
|
|
StockRecordOperationType,
|
|
} from '../enums/base.enum';
|
|
// import { generateUniqueId } from '../utils/helper.util';
|
|
import { FreightcomService } from './freightcom.service';
|
|
import { StockRecord } from '../entity/stock_record.entity';
|
|
import { Stock } from '../entity/stock.entity';
|
|
import { plainToClass } from 'class-transformer';
|
|
import { WPService } from './wp.service';
|
|
// import { Product } from '../entity/product.entty';
|
|
import { ShippingDetailsDTO } from '../dto/freightcom.dto';
|
|
import { CanadaPostService } from './canadaPost.service';
|
|
import { OrderItem } from '../entity/order_item.entity';
|
|
import { OrderSale } from '../entity/order_sale.entity';
|
|
import { UniExpressService } from './uni_express.service';
|
|
import { StockPoint } from '../entity/stock_point.entity';
|
|
import { OrderService } from './order.service';
|
|
import { convertKeysFromCamelToSnake } from '../utils/object-transform.util';
|
|
import { SiteService } from './site.service';
|
|
|
|
@Provide()
|
|
export class LogisticsService {
|
|
@InjectEntityModel(Service)
|
|
serviceModel: Repository<Service>;
|
|
|
|
@InjectEntityModel(ShippingAddress)
|
|
shippingAddressModel: Repository<ShippingAddress>;
|
|
|
|
@InjectEntityModel(Stock)
|
|
stockModel: Repository<Stock>;
|
|
|
|
@InjectEntityModel(Order)
|
|
orderModel: Repository<Order>;
|
|
|
|
@InjectEntityModel(StockPoint)
|
|
stockPointModel: Repository<StockPoint>
|
|
|
|
@InjectEntityModel(OrderSale)
|
|
orderSaleModel: Repository<OrderSale>;
|
|
|
|
@InjectEntityModel(Shipment)
|
|
shipmentModel: Repository<Shipment>;
|
|
|
|
@InjectEntityModel(ShipmentItem)
|
|
shipmentItemModel: Repository<ShipmentItem>;
|
|
|
|
@InjectEntityModel(OrderShipment)
|
|
orderShipmentModel: Repository<OrderShipment>;
|
|
|
|
@InjectEntityModel(OrderItem)
|
|
orderItem: Repository<OrderItem>;
|
|
|
|
@Inject()
|
|
freightcomService: FreightcomService;
|
|
|
|
@Inject()
|
|
canadaPostService: CanadaPostService;
|
|
|
|
@Inject()
|
|
uniExpressService: UniExpressService;
|
|
|
|
@Inject()
|
|
wpService: WPService;
|
|
|
|
@Inject()
|
|
orderService: OrderService;
|
|
|
|
@Inject()
|
|
dataSourceManager: TypeORMDataSourceManager;
|
|
|
|
@Inject()
|
|
private readonly siteService: SiteService;
|
|
|
|
async getServiceList(param: QueryServiceDTO) {
|
|
const { pageSize, current, carrier_name, isActive } = param;
|
|
const where: Record<string, any> = {};
|
|
if (carrier_name) where.carrier_name = Like(`%${carrier_name}%`);
|
|
if (isActive !== undefined) where.isActive = isActive;
|
|
const [items, total] = await this.serviceModel.findAndCount({
|
|
where,
|
|
skip: (current - 1) * pageSize,
|
|
take: pageSize,
|
|
});
|
|
return { items, total, current, pageSize };
|
|
}
|
|
|
|
async toggleServiceActive(id: string, isActive: boolean) {
|
|
const service = await this.serviceModel.findOne({ where: { id } });
|
|
if (!service) {
|
|
throw new Error('服务商不存在');
|
|
}
|
|
service.isActive = isActive;
|
|
return this.serviceModel.save(service);
|
|
}
|
|
|
|
async getActiveServices() {
|
|
const services = await this.serviceModel.find({
|
|
where: { isActive: true },
|
|
});
|
|
if (!services) {
|
|
return [];
|
|
}
|
|
return services.map(service => service.id);
|
|
}
|
|
|
|
async createShippingAddress(shippingAddress: ShippingAddress) {
|
|
return await this.shippingAddressModel.save(shippingAddress);
|
|
}
|
|
|
|
async updateShipmentState(shipment: Shipment) {
|
|
try {
|
|
const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number);
|
|
console.log('updateShipmentState data:', data);
|
|
shipment.state = data.data[0].state;
|
|
if (shipment.state in [203, 215, 216, 230]) { // todo,写常数
|
|
shipment.finished = true;
|
|
}
|
|
this.shipmentModel.save(shipment);
|
|
return shipment.state;
|
|
} catch (error) {
|
|
throw error;
|
|
// throw new Error(`更新运单状态失败 ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async updateShipmentStateById(id: number) {
|
|
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: id });
|
|
return this.updateShipmentState(shipment);
|
|
}
|
|
|
|
async updateShippingAddress(id: number, shippingAddress: ShippingAddress) {
|
|
const address = await this.shippingAddressModel.findOneBy({ id });
|
|
if (!address) {
|
|
throw new Error(`发货地址 ID ${id} 不存在`);
|
|
}
|
|
await this.shippingAddressModel.update(id, shippingAddress);
|
|
return await this.shippingAddressModel.findOneBy({ id });
|
|
}
|
|
|
|
async getShippingAddressList() {
|
|
return await this.shippingAddressModel.find();
|
|
}
|
|
|
|
async delShippingAddress(id: number) {
|
|
const address = await this.shippingAddressModel.findOneBy({ id });
|
|
if (!address) {
|
|
throw new Error(`发货地址 ID ${id} 不存在`);
|
|
}
|
|
const result = await this.shippingAddressModel.delete(id);
|
|
return result.affected > 0;
|
|
}
|
|
|
|
// async saveTracking(
|
|
// orderId: number,
|
|
// shipment: Record<string, any>,
|
|
// data: ShipmentBookDTO
|
|
// ) {
|
|
// const order = await this.orderModel.findOneBy({ id: orderId });
|
|
// const orderTracking = this.orderTrackingModel.save({
|
|
// orderId: String(orderId),
|
|
// siteId: order.siteId,
|
|
// externalOrderId: order.externalOrderId,
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
async getRateList(details: ShippingDetailsDTO) {
|
|
details.destination.address.country = 'CA';
|
|
details.origin.address.country = 'CA';
|
|
const { request_id } = await this.freightcomService.getRateEstimate(
|
|
details
|
|
);
|
|
const rateRequest = {
|
|
'customer-number': this.canadaPostService.customerNumber,
|
|
'parcel-characteristics': {
|
|
weight: details?.packaging_properties?.packages?.reduce(
|
|
(cur, next) => cur + (next?.measurements?.weight?.value || 0),
|
|
0
|
|
),
|
|
},
|
|
'origin-postal-code': details.origin.address.postal_code.replace(
|
|
/\s/g,
|
|
''
|
|
),
|
|
destination: {
|
|
domestic: {
|
|
'postal-code': details.destination.address.postal_code.replace(
|
|
/\s/g,
|
|
''
|
|
),
|
|
},
|
|
},
|
|
};
|
|
const canadaPostRates = await this.canadaPostService.getRates(rateRequest);
|
|
|
|
await sleep(3000);
|
|
const rates = await this.freightcomService.getRates(request_id);
|
|
return [...rates, ...canadaPostRates];
|
|
}
|
|
|
|
async getShipmentLabel(shipmentId) {
|
|
try {
|
|
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: shipmentId });
|
|
if (!shipment) {
|
|
throw new Error('运单不存在');
|
|
}
|
|
return await this.uniExpressService.getLabel(shipment.return_tracking_number);
|
|
} catch (e) {
|
|
throw new Error('获取运单失败');
|
|
}
|
|
}
|
|
|
|
async removeShipment(shipmentId: number) {
|
|
try {
|
|
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: shipmentId });
|
|
if (shipment.state !== '190') { // todo,写常数
|
|
throw new Error('订单当前状态无法删除');
|
|
}
|
|
const order: Order = await this.orderModel.findOneBy({ id: shipment.order_id });
|
|
const dataSource = this.dataSourceManager.getDataSource('default');
|
|
let transactionError = undefined;
|
|
await dataSource.transaction(async manager => {
|
|
const orderRepo = manager.getRepository(Order);
|
|
const shipmentRepo = manager.getRepository(Shipment);
|
|
|
|
order.shipmentId = null;
|
|
orderRepo.save(order);
|
|
|
|
shipmentRepo.remove(shipment);
|
|
|
|
const res = await this.uniExpressService.deleteShipment(shipment.return_tracking_number);
|
|
console.log('res', res.data); // todo
|
|
|
|
await orderRepo.save(order);
|
|
|
|
}).catch(error => {
|
|
transactionError = error;
|
|
});
|
|
|
|
if (transactionError !== undefined) {
|
|
throw new Error(`数据库同步错误: ${transactionError.message}`);
|
|
}
|
|
|
|
try {
|
|
// 同步订单状态到woocommerce
|
|
const site = await this.siteService.get(Number(order.siteId), true);
|
|
if (order.status === OrderStatus.COMPLETED) {
|
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
|
status: OrderStatus.PROCESSING,
|
|
});
|
|
order.status = OrderStatus.PROCESSING;
|
|
}
|
|
order.orderStatus = ErpOrderStatus.PROCESSING;
|
|
this.orderModel.save(order);
|
|
|
|
// todo 同步到wooccommerce删除运单信息
|
|
await this.wpService.deleteFulfillment(site, order.externalOrderId, shipment.tracking_id);
|
|
} catch (error) {
|
|
console.log('同步到woocommerce失败', error);
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
} catch {
|
|
throw new Error('删除运单失败');
|
|
}
|
|
}
|
|
|
|
async getShipmentFee(data: ShipmentFeeBookDTO) {
|
|
try {
|
|
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
|
|
const reqBody = {
|
|
...convertKeysFromCamelToSnake(data),
|
|
pickup_warehouse: stock_point.upStreamStockPointId,
|
|
currency: 'CAD',
|
|
// item_description: data.sales, // todo: 货品信息
|
|
}
|
|
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
|
|
if (resShipmentFee.status !== 'SUCCESS') {
|
|
throw new Error(resShipmentFee.ret_msg);
|
|
}
|
|
return resShipmentFee.data.totalAfterTax * 100;
|
|
} catch (e) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
async createShipment(orderId: number, data: ShipmentBookDTO, userId: number) {
|
|
const order = await this.orderModel.findOneBy({ id: orderId });
|
|
const { sales } = data;
|
|
if (!order) {
|
|
throw new Error('订单不存在');
|
|
}
|
|
if (
|
|
order.orderStatus !== ErpOrderStatus.PROCESSING &&
|
|
order.orderStatus !== ErpOrderStatus.PENDING_RESHIPMENT
|
|
) {
|
|
throw new Error('订单状态不正确 ');
|
|
}
|
|
|
|
let resShipmentOrder;
|
|
try {
|
|
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
|
|
const reqBody = {
|
|
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,
|
|
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: 货品信息
|
|
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',
|
|
custom_field: {
|
|
'order_id': order.externalOrderId
|
|
}
|
|
}
|
|
|
|
// 添加运单
|
|
resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
|
|
|
|
// 记录物流信息,并将订单状态转到完成
|
|
if (resShipmentOrder.status === 'SUCCESS') {
|
|
order.orderStatus = ErpOrderStatus.COMPLETED;
|
|
} else {
|
|
throw new Error('运单生成失败');
|
|
}
|
|
const dataSource = this.dataSourceManager.getDataSource('default');
|
|
let transactionError = undefined;
|
|
let shipmentId = undefined;
|
|
await dataSource.transaction(async manager => {
|
|
const orderRepo = manager.getRepository(Order);
|
|
const shipmentRepo = manager.getRepository(Shipment);
|
|
const tracking_provider = 'UniUni'; // todo: id未确定,后写进常数
|
|
|
|
// 同步物流信息到woocommerce
|
|
const site = await this.siteService.get(Number(order.siteId), true);
|
|
const res = await this.wpService.createFulfillment(site, order.externalOrderId, {
|
|
tracking_number: resShipmentOrder.data.tno,
|
|
tracking_provider: tracking_provider,
|
|
});
|
|
|
|
if (order.orderStatus === ErpOrderStatus.COMPLETED) {
|
|
const shipment = await shipmentRepo.save({
|
|
tracking_provider: tracking_provider,
|
|
tracking_id: res.data.tracking_id,
|
|
unique_id: resShipmentOrder.data.uni_order_sn,
|
|
stockPointId: String(data.stockPointId), // todo
|
|
state: resShipmentOrder.data.uni_status_code,
|
|
return_tracking_number: resShipmentOrder.data.tno,
|
|
fee: data.details.shipmentFee,
|
|
order: order
|
|
});
|
|
order.shipmentId = shipment.id;
|
|
shipmentId = shipment.id;
|
|
}
|
|
|
|
// 同步订单状态到woocommerce
|
|
if (order.status !== OrderStatus.COMPLETED) {
|
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
|
status: OrderStatus.COMPLETED,
|
|
});
|
|
order.status = OrderStatus.COMPLETED;
|
|
}
|
|
order.orderStatus = ErpOrderStatus.COMPLETED;
|
|
|
|
await orderRepo.save(order);
|
|
}).catch(error => {
|
|
transactionError = error
|
|
});
|
|
|
|
if (transactionError !== undefined) {
|
|
console.log('err', transactionError);
|
|
throw transactionError;
|
|
}
|
|
|
|
// 更新产品发货信息
|
|
this.orderService.updateOrderSales(order.id, sales);
|
|
|
|
return {
|
|
data: {
|
|
shipmentId
|
|
}
|
|
};
|
|
} catch (error) {
|
|
if (resShipmentOrder.status === 'SUCCESS') {
|
|
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
|
|
}
|
|
throw new Error(`上游请求错误:${error}`);
|
|
}
|
|
}
|
|
|
|
async syncShipment() {
|
|
try {
|
|
const shipments = await this.shipmentModel.find({
|
|
where: {
|
|
primary_tracking_number: IsNull(),
|
|
},
|
|
});
|
|
if (!shipments) return;
|
|
for (const shipment of shipments) {
|
|
await this.getShipment(shipment.id);
|
|
}
|
|
} catch (error) {
|
|
console.log('syncShipment', error);
|
|
}
|
|
}
|
|
|
|
async syncShipmentStatus() {
|
|
const shipments = await this.shipmentModel.find({
|
|
where: {
|
|
state: In([
|
|
'draft',
|
|
'waiting-for-transit',
|
|
'waiting-for-scheduling',
|
|
'in-transit',
|
|
]),
|
|
},
|
|
});
|
|
if (!shipments) return;
|
|
for (const item of shipments) {
|
|
try {
|
|
let res;
|
|
if (item.type === ShipmentType.FREIGHTCOM) {
|
|
res = await this.freightcomService.getShipment(item.id);
|
|
} else if (item.type === ShipmentType.CANADAPOST) {
|
|
res = await this.canadaPostService.getShipment(
|
|
item.primary_tracking_number
|
|
);
|
|
res.shipment.id = item.id;
|
|
}
|
|
if (!res) return;
|
|
const shipment = plainToClass(Shipment, res.shipment);
|
|
this.shipmentModel.save(shipment);
|
|
} catch (error) {
|
|
console.log('syncShipmentStatus error');
|
|
}
|
|
}
|
|
}
|
|
|
|
async getShipment(id: number) {
|
|
const orderShipments: OrderShipment[] = await this.orderShipmentModel.find({
|
|
where: { shipment_id: id },
|
|
});
|
|
if (!orderShipments || orderShipments.length === 0) return;
|
|
const oldShipment = await this.shipmentModel.findOneBy({ id });
|
|
let res;
|
|
if (oldShipment.type === ShipmentType.FREIGHTCOM) {
|
|
res = await this.freightcomService.getShipment(id);
|
|
} else if (oldShipment.type === ShipmentType.CANADAPOST) {
|
|
res = await this.canadaPostService.getShipment(
|
|
oldShipment.primary_tracking_number
|
|
);
|
|
res.shipment.id = oldShipment.id;
|
|
}
|
|
if (!res) return;
|
|
const shipment = plainToClass(Shipment, res.shipment);
|
|
await this.shipmentModel.save(shipment);
|
|
for (const orderShipment of orderShipments) {
|
|
const order = await this.orderModel.findOneBy({
|
|
id: orderShipment.order_id,
|
|
});
|
|
const site = await this.siteService.get(Number(order.siteId), true);
|
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
|
status: OrderStatus.COMPLETED,
|
|
});
|
|
await this.wpService.createFulfillment(site, order.externalOrderId, {
|
|
tracking_number: shipment.primary_tracking_number,
|
|
tracking_provider: shipment?.rate?.carrier_name,
|
|
});
|
|
}
|
|
}
|
|
|
|
async delShipment(id: number, userId: number) {
|
|
|
|
const shipment = await this.shipmentModel.findOneBy({ id });
|
|
if (!shipment) throw new Error('物流不存在');
|
|
if (shipment.type === ShipmentType.FREIGHTCOM) {
|
|
await this.freightcomService.cancelShipment(shipment.id);
|
|
} else if (shipment.type === ShipmentType.CANADAPOST) {
|
|
await this.canadaPostService.cancelShipment(shipment.id);
|
|
}
|
|
const dataSource = this.dataSourceManager.getDataSource('default');
|
|
return dataSource.transaction(async manager => {
|
|
const shipmentRepo = manager.getRepository(Shipment);
|
|
const shipmentItemRepo = manager.getRepository(ShipmentItem);
|
|
const orderShipmentRepo = manager.getRepository(OrderShipment);
|
|
const stockRecordRepo = manager.getRepository(StockRecord);
|
|
const stockRepo = manager.getRepository(Stock);
|
|
const orderRepo = manager.getRepository(Order);
|
|
const orderShipments = await orderShipmentRepo.findBy({
|
|
shipment_id: id,
|
|
});
|
|
const shipmentItems = await shipmentItemRepo.findBy({ shipment_id: id });
|
|
await shipmentRepo.delete({ id });
|
|
await shipmentItemRepo.delete({ shipment_id: id });
|
|
await orderShipmentRepo.delete({ shipment_id: id });
|
|
for (const item of shipmentItems) {
|
|
const stock = await stockRepo.findOne({
|
|
where: {
|
|
stockPointId: orderShipments[0].stockPointId,
|
|
sku: item.sku,
|
|
},
|
|
});
|
|
stock.quantity += item.quantity;
|
|
await stockRepo.save(stock);
|
|
await stockRecordRepo.save({
|
|
stockPointId: orderShipments[0].stockPointId,
|
|
sku: item.sku,
|
|
operationType: StockRecordOperationType.IN,
|
|
quantityChange: item.quantity,
|
|
operatorId: userId,
|
|
note: `订单${orderShipments.map(v => v.order_id).join(',')} 取消发货`,
|
|
});
|
|
}
|
|
|
|
await orderRepo.update(
|
|
{ id: In(orderShipments.map(v => v.order_id)) },
|
|
{
|
|
orderStatus: ErpOrderStatus.PENDING_RESHIPMENT,
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
async getOrderList(number: string) {
|
|
const orders = await this.orderModel.find({
|
|
where: {
|
|
externalOrderId: Like(`%${number}%`),
|
|
},
|
|
});
|
|
|
|
// 从数据库批量获取站点信息,构建映射以避免 N+1 查询
|
|
const siteIds = Array.from(new Set(orders.map(o => o.siteId).filter(Boolean)));
|
|
const { items: sites } = await this.siteService.list({ current: 1, pageSize: 1000, ids: siteIds.join(',') }, false);
|
|
const siteMap = new Map(sites.map((s: any) => [s.id, s.name]));
|
|
|
|
return orders.map(order => ({
|
|
...order,
|
|
name: siteMap.get(order.siteId) || '',
|
|
}));
|
|
}
|
|
|
|
async getListByOrderId(id: number) {
|
|
const item = await this.orderItem.find({ where: { orderId: id } });
|
|
const saleItem = await this.orderSaleModel.find({ where: { orderId: id } });
|
|
|
|
return {
|
|
item,
|
|
saleItem
|
|
};
|
|
}
|
|
|
|
async getList(param: Record<string, any>) {
|
|
const {
|
|
pageSize = 10,
|
|
current = 1,
|
|
return_tracking_number,
|
|
stockPointId,
|
|
externalOrderId,
|
|
} = param;
|
|
|
|
const offset = pageSize * (current - 1);
|
|
const values: any[] = [];
|
|
let whereClause = 'WHERE 1=1';
|
|
|
|
if (return_tracking_number) {
|
|
whereClause += ' AND s.return_tracking_number LIKE ?';
|
|
values.push(`%${return_tracking_number}%`);
|
|
}
|
|
|
|
if (stockPointId) {
|
|
whereClause += ' AND sp.id = ?';
|
|
values.push(stockPointId);
|
|
}
|
|
|
|
// todo,增加订单号搜索
|
|
if (externalOrderId) {
|
|
whereClause += ' AND o.externalOrderId = ?';
|
|
values.push(externalOrderId);
|
|
}
|
|
|
|
const sql = `
|
|
SELECT s.*, sp.name, o.externalOrderId, o.siteId
|
|
FROM shipment s
|
|
LEFT JOIN \`order\` o ON s.order_id = o.id
|
|
LEFT JOIN stock_point sp ON s.stock_point_id = sp.id
|
|
${whereClause}
|
|
ORDER BY s.createdAt DESC
|
|
LIMIT ?, ?
|
|
`;
|
|
|
|
values.push(offset, Number(pageSize));
|
|
|
|
const items = await this.serviceModel.query(sql, values);
|
|
// 单独计算总数
|
|
const countSql = `
|
|
SELECT COUNT(DISTINCT s.id) as total
|
|
FROM shipment s
|
|
LEFT JOIN \`order\` o ON s.order_id = o.id
|
|
LEFT JOIN stock_point sp ON s.stock_point_id = sp.id
|
|
${whereClause}
|
|
`;
|
|
|
|
const countResult = await this.serviceModel.query(
|
|
countSql,
|
|
values.slice(0, values.length - 2)
|
|
);
|
|
const total = countResult[0]?.total || 0;
|
|
|
|
return { items, total, current, pageSize };
|
|
}
|
|
}
|