Feature: 增加换货功能

This commit is contained in:
黄珑 2025-08-16 11:57:16 +08:00
parent cbd5ddc4d2
commit dd4ae5383f
13 changed files with 196 additions and 29 deletions

View File

@ -16,6 +16,7 @@ import { OrderFee } from '../entity/order_fee.entity';
import { OrderRefund } from '../entity/order_refund.entity'; import { OrderRefund } from '../entity/order_refund.entity';
import { OrderRefundItem } from '../entity/order_retund_item.entity'; import { OrderRefundItem } from '../entity/order_retund_item.entity';
import { OrderSale } from '../entity/order_sale.entity'; import { OrderSale } from '../entity/order_sale.entity';
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
import { OrderShipping } from '../entity/order_shipping.entity'; import { OrderShipping } from '../entity/order_shipping.entity';
import { Service } from '../entity/service.entity'; import { Service } from '../entity/service.entity';
import { ShippingAddress } from '../entity/shipping_address.entity'; import { ShippingAddress } from '../entity/shipping_address.entity';
@ -57,6 +58,7 @@ export default {
OrderRefund, OrderRefund,
OrderRefundItem, OrderRefundItem,
OrderSale, OrderSale,
OrderSaleOriginal,
OrderShipment, OrderShipment,
ShipmentItem, ShipmentItem,
Shipment, Shipment,

View File

@ -195,7 +195,7 @@ export class LogisticsController {
) )
@Post('/getShipmentLabel/:shipmentId') @Post('/getShipmentLabel/:shipmentId')
async getShipmentLabel( async getShipmentLabel(
@Param('shipmentId') shipmentId: string @Param('shipmentId') shipmentId: number
) { ) {
try { try {
const res = await this.logisticsService.getShipmentLabel(shipmentId); const res = await this.logisticsService.getShipmentLabel(shipmentId);
@ -224,7 +224,7 @@ export class LogisticsController {
@ApiOkResponse() @ApiOkResponse()
@Post('/updateState/:id') @Post('/updateState/:id')
async updateShipmentState( async updateShipmentState(
@Param('shipmentId') shipmentId: string @Param('shipmentId') shipmentId: number
) { ) {
try { try {
const data = await this.logisticsService.updateShipmentStateById(shipmentId); const data = await this.logisticsService.updateShipmentStateById(shipmentId);
@ -236,7 +236,7 @@ export class LogisticsController {
@ApiOkResponse() @ApiOkResponse()
@Del('/shipment/:id') @Del('/shipment/:id')
async delShipment(@Param('id') id: string, @User() user) { async delShipment(@Param('id') id: number, @User() user) {
try { try {
const data = await this.logisticsService.delShipment(id, user.id); const data = await this.logisticsService.delShipment(id, user.id);
return successResponse(data); return successResponse(data);
@ -260,7 +260,7 @@ export class LogisticsController {
@ApiOkResponse() @ApiOkResponse()
@Post('/getListByTrackingId') @Post('/getListByTrackingId')
async getListByTrackingId(@Query('shipment_id') shipment_id: string) { async getListByTrackingId(@Query('shipment_id') shipment_id: number) {
try { try {
return successResponse( return successResponse(
await this.logisticsService.getListByTrackingId(shipment_id) await this.logisticsService.getListByTrackingId(shipment_id)

View File

@ -108,6 +108,22 @@ export class OrderController {
} }
} }
@ApiOkResponse({
type: BooleanRes,
})
@Post('/updateOrderItems/:orderId')
async updateOrderItems(
@Param('orderId') orderId: number,
@Body() data: any
) {
try {
const res = await this.orderService.updateOrderSales(orderId, data);
return successResponse(res);
} catch (error) {
return errorResponse(error?.message || '更新失败');
}
}
@ApiOkResponse({ @ApiOkResponse({
type: BooleanRes, type: BooleanRes,
}) })

View File

@ -50,7 +50,7 @@ export class Order {
@ApiProperty() @ApiProperty()
@Column({ name: 'shipment_id', nullable: true }) @Column({ name: 'shipment_id', nullable: true })
@Expose() @Expose()
shipmentId: string; shipmentId: number;
@OneToOne(() => Shipment) @OneToOne(() => Shipment)
@JoinColumn({ name: 'shipment_id' }) @JoinColumn({ name: 'shipment_id' })

View File

@ -0,0 +1,78 @@
import { ApiProperty } from '@midwayjs/swagger';
import { Exclude, Expose } from 'class-transformer';
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Order } from './order.entity';
@Entity('order_sale_original')
@Exclude()
export class OrderSaleOriginal {
@ApiProperty()
@PrimaryGeneratedColumn()
@Expose()
id?: number;
@ApiProperty()
@ManyToOne(() => Order)
@JoinColumn({ name: 'order_id' })
@Expose()
orderId: number; // 订单 ID
@ApiProperty()
@Column()
@Expose()
siteId: string; // 来源站点唯一标识
@ApiProperty()
@Column()
@Expose()
externalOrderItemId: string; // WooCommerce 订单item ID
@ApiProperty()
@Column()
@Expose()
productId: number;
@ApiProperty()
@Column()
@Expose()
name: string;
@ApiProperty({ description: 'sku', type: 'string' })
@Expose()
@Column()
sku: string;
@ApiProperty()
@Column()
@Expose()
quantity: number;
@ApiProperty()
@Column({ default: false })
@Expose()
isPackage: boolean;
@ApiProperty({
example: '2022-12-12 11:11:11',
description: '创建时间',
})
@CreateDateColumn()
@Expose()
createdAt?: Date;
@ApiProperty({
example: '2022-12-12 11:11:11',
description: '更新时间',
})
@UpdateDateColumn()
@Expose()
updatedAt?: Date;
}

View File

@ -27,7 +27,7 @@ export class OrderSale {
siteId: string; // 来源站点唯一标识 siteId: string; // 来源站点唯一标识
@ApiProperty() @ApiProperty()
@Column() @Column({ nullable: true })
@Expose() @Expose()
externalOrderItemId: string; // WooCommerce 订单item ID externalOrderItemId: string; // WooCommerce 订单item ID

View File

@ -13,7 +13,7 @@ export class OrderShipment {
@ApiProperty() @ApiProperty()
@Column() @Column()
shipment_id: string; shipment_id: number;
@ApiProperty() @ApiProperty()
@Column() @Column()

View File

@ -19,7 +19,7 @@ export class Shipment {
@ApiProperty() @ApiProperty()
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
@Expose() @Expose()
id: string; id: number;
@ApiProperty() @ApiProperty()
@Column({ nullable: true }) @Column({ nullable: true })

View File

@ -4,9 +4,13 @@ import {
Column, Column,
CreateDateColumn, CreateDateColumn,
Entity, Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
UpdateDateColumn, UpdateDateColumn,
} from 'typeorm'; } from 'typeorm';
import { Shipment } from './shipment.entity';
import { Order } from './order.entity';
@Entity('shipment_item') @Entity('shipment_item')
@Exclude() @Exclude()
@ -17,17 +21,19 @@ export class ShipmentItem {
id: number; id: number;
@ApiProperty() @ApiProperty()
@Column()
@Expose() @Expose()
shipment_id: string; @ManyToOne(() => Shipment)
@JoinColumn({ name: 'shipment_id' })
shipment_id: number;
@ApiProperty() @ApiProperty()
@Column()
@Expose() @Expose()
productId: number; @ManyToOne(() => Order)
@JoinColumn({ name: 'order_id' })
order_id: number;
@ApiProperty() @ApiProperty()
@Column() @Column({ nullable: true })
@Expose() @Expose()
name: string; name: string;

View File

@ -157,7 +157,7 @@ export class CanadaPostService {
} }
/** 取消运单 */ /** 取消运单 */
async cancelShipment(shipmentId: string) { async cancelShipment(shipmentId: number) {
const url = `${this.url}/rs/${this.customerNumber}/${this.customerNumber}/shipment/${shipmentId}`; const url = `${this.url}/rs/${this.customerNumber}/${this.customerNumber}/shipment/${shipmentId}`;
const res = await axios.delete(url, { const res = await axios.delete(url, {

View File

@ -98,7 +98,7 @@ export class FreightcomService {
} }
// 查询运单详细信息 // 查询运单详细信息
async getShipment(shipment_id: string) { async getShipment(shipment_id: number) {
let { status, data } = await axios.request({ let { status, data } = await axios.request({
url: `${this.apiUrl}/shipment/${shipment_id}`, url: `${this.apiUrl}/shipment/${shipment_id}`,
method: 'GET', method: 'GET',
@ -117,7 +117,7 @@ export class FreightcomService {
} }
// 取消发货 // 取消发货
async cancelShipment(shipment_id: string) { async cancelShipment(shipment_id: number) {
const response = await axios.request({ const response = await axios.request({
url: `${this.apiUrl}/shipment/${shipment_id}`, url: `${this.apiUrl}/shipment/${shipment_id}`,
method: 'DELETE', method: 'DELETE',

View File

@ -29,6 +29,7 @@ import { OrderItem } from '../entity/order_item.entity';
import { OrderSale } from '../entity/order_sale.entity'; 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';
@Provide() @Provide()
export class LogisticsService { export class LogisticsService {
@ -74,6 +75,9 @@ export class LogisticsService {
@Inject() @Inject()
wpService: WPService; wpService: WPService;
@Inject()
orderService: OrderService;
@Inject() @Inject()
dataSourceManager: TypeORMDataSourceManager; dataSourceManager: TypeORMDataSourceManager;
@ -124,7 +128,6 @@ export class LogisticsService {
async updateShipmentState(shipment: Shipment) { async updateShipmentState(shipment: Shipment) {
try { try {
const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number); const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number);
// console.log('res', data, data.data[0].state);
shipment.state = data.data[0].state; shipment.state = data.data[0].state;
if (shipment.state in [203, 215, 216, 230]) { // todo,写常数 if (shipment.state in [203, 215, 216, 230]) { // todo,写常数
shipment.finished = true; shipment.finished = true;
@ -136,7 +139,7 @@ export class LogisticsService {
} }
} }
async updateShipmentStateById(id: string) { 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);
} }
@ -224,7 +227,7 @@ export class LogisticsService {
} }
} }
async removeShipment(shipmentId) { 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写常数
@ -292,7 +295,7 @@ export class LogisticsService {
shipper_country_code: data.details.origin.address.country, shipper_country_code: data.details.origin.address.country,
receiver: data.details.destination.contact_name, receiver: data.details.destination.contact_name,
city: data.details.destination.address.city, city: data.details.destination.address.city,
province: data.details.destination.address.region, // todo待确认 province: data.details.destination.address.region,
country: data.details.destination.address.country, country: data.details.destination.address.country,
postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''), postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''),
delivery_address: data.details.destination.address.address_line_1, delivery_address: data.details.destination.address.address_line_1,
@ -316,6 +319,7 @@ export class LogisticsService {
async createShipment(orderId: number, data: ShipmentBookDTO, userId: number) { async createShipment(orderId: number, data: ShipmentBookDTO, userId: number) {
const order = await this.orderModel.findOneBy({ id: orderId }); const order = await this.orderModel.findOneBy({ id: orderId });
const { sales } = data;
if (!order) { if (!order) {
throw new Error('订单不存在'); throw new Error('订单不存在');
} }
@ -338,7 +342,7 @@ export class LogisticsService {
shipper_country_code: data.details.origin.address.country, shipper_country_code: data.details.origin.address.country,
receiver: data.details.destination.contact_name, receiver: data.details.destination.contact_name,
city: data.details.destination.address.city, city: data.details.destination.address.city,
province: data.details.destination.address.region, // todo待确认 province: data.details.destination.address.region,
country: data.details.destination.address.country, country: data.details.destination.address.country,
postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''), postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''),
delivery_address: data.details.destination.address.address_line_1, delivery_address: data.details.destination.address.address_line_1,
@ -415,6 +419,9 @@ export class LogisticsService {
throw transactionError; throw transactionError;
} }
// 更新产品发货信息
this.orderService.updateOrderSales(order.id, sales);
return { data: { return { data: {
shipmentId shipmentId
} }; } };
@ -474,8 +481,8 @@ export class LogisticsService {
} }
} }
async getShipment(id: string) { async getShipment(id: number) {
const orderShipments = 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;
@ -507,7 +514,7 @@ export class LogisticsService {
} }
} }
async delShipment(id: string, userId: number) { async delShipment(id: number, userId: number) {
const shipment = await this.shipmentModel.findOneBy({ id }); const shipment = await this.shipmentModel.findOneBy({ id });
if (!shipment) throw new Error('物流不存在'); if (!shipment) throw new Error('物流不存在');
@ -574,7 +581,7 @@ export class LogisticsService {
})); }));
} }
async getListByTrackingId(id: string) { async getListByTrackingId(id: number) {
const qb = ` const qb = `
SELECT SELECT
oi.name, oi.name,

View File

@ -29,6 +29,7 @@ import { WpSite } from '../interface';
import { ShipmentItem } from '../entity/shipment_item.entity'; import { ShipmentItem } from '../entity/shipment_item.entity';
import { UpdateStockDTO } from '../dto/stock.dto'; import { UpdateStockDTO } from '../dto/stock.dto';
import { StockService } from './stock.service'; import { StockService } from './stock.service';
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
@Provide() @Provide()
export class OrderService { export class OrderService {
@ -50,6 +51,9 @@ export class OrderService {
@InjectEntityModel(OrderSale) @InjectEntityModel(OrderSale)
orderSaleModel: Repository<OrderSale>; orderSaleModel: Repository<OrderSale>;
@InjectEntityModel(OrderSaleOriginal)
orderSaleOriginalModel: Repository<OrderSaleOriginal>;
@InjectEntityModel(WpProduct) @InjectEntityModel(WpProduct)
wpProductModel: Repository<WpProduct>; wpProductModel: Repository<WpProduct>;
@ -214,12 +218,10 @@ export class OrderService {
entity.id = existingOrder.id; entity.id = existingOrder.id;
return entity; return entity;
} }
console.log('/////////////');
entity.orderStatus = this.mapOrderStatus(entity.status); entity.orderStatus = this.mapOrderStatus(entity.status);
const customer = await this.customerModel.findOne({ const customer = await this.customerModel.findOne({
where: { email: order.customer_email }, where: { email: order.customer_email },
}); });
console.log('error? ', this.customerModel);
if(!customer) { if(!customer) {
await this.customerModel.save({ await this.customerModel.save({
email: order.customer_email, email: order.customer_email,
@ -1182,6 +1184,21 @@ export class OrderService {
} }
} }
// update order_sale_origin if not exist
try {
const order_sale_origin_count = await this.orderSaleOriginalModel.countBy({ orderId: id });
if (order_sale_origin_count === 0) {
sales.forEach(async sale => {
const { id: saleId, ...saleData } = sale;
await this.orderSaleOriginalModel.save(saleData);
});
}
} catch (error) {
console.log('create order sale origin error: ', error.message);
}
return { return {
...order, ...order,
siteName: site.siteName, siteName: site.siteName,
@ -1297,6 +1314,7 @@ export class OrderService {
return dataSource.transaction(async manager => { return dataSource.transaction(async manager => {
const orderRepo = manager.getRepository(Order); const orderRepo = manager.getRepository(Order);
const orderSaleRepo = manager.getRepository(OrderSale); const orderSaleRepo = manager.getRepository(OrderSale);
const OrderSaleOriginalRepo = manager.getRepository(OrderSaleOriginal);
const productRepo = manager.getRepository(Product); const productRepo = manager.getRepository(Product);
const order = await orderRepo.save({ const order = await orderRepo.save({
siteId: '-1', siteId: '-1',
@ -1314,7 +1332,7 @@ export class OrderService {
}); });
for (const sale of sales) { for (const sale of sales) {
const product = await productRepo.findOne({ where: { sku: sale.sku } }); const product = await productRepo.findOne({ where: { sku: sale.sku } });
await orderSaleRepo.save({ const saleItem = {
orderId: order.id, orderId: order.id,
siteId: '-1', siteId: '-1',
externalOrderItemId: '-1', externalOrderItemId: '-1',
@ -1322,7 +1340,9 @@ export class OrderService {
name: product.name, name: product.name,
sku: sale.sku, sku: sale.sku,
quantity: sale.quantity, quantity: sale.quantity,
}); };
await orderSaleRepo.save(saleItem);
await OrderSaleOriginalRepo.save(saleItem);
} }
}); });
} }
@ -1363,4 +1383,42 @@ export class OrderService {
pageSize, pageSize,
}; };
} }
async updateOrderSales(orderId: number, sales: OrderSale[]) {
try {
const dataSource = this.dataSourceManager.getDataSource('default');
let transactionError = undefined;
await dataSource.transaction(async manager => {
const orderRepo = manager.getRepository(Order);
const orderSaleRepo = manager.getRepository(OrderSale);
const productRepo = manager.getRepository(Product);
const order = await orderRepo.findOneBy({ id: orderId });
let product:Product;
await orderSaleRepo.delete({ orderId });
for (const sale of sales) {
product = await productRepo.findOneBy({ sku: sale.sku });
await orderSaleRepo.save({
orderId,
siteId: order.siteId,
productId: product.id,
name: product.name,
sku: sale.sku,
quantity: sale.quantity
});
};
}).catch(error => {
transactionError = error;
});
if (transactionError !== undefined) {
throw new Error(`更新物流信息错误:${transactionError.message}`);
}
return true;
} catch (error) {
throw new Error(`更新发货产品失败:${error.message}`);
}
}
} }