refactor(订单模块): 将siteId字段类型从string改为number并添加nullable
- 统一所有实体类中siteId字段类型为number - 为siteId字段添加nullable: true配置 - 更新相关服务、控制器和DTO中的类型定义 - 修复订单同步和产品同步中的类型转换问题 - 优化数据库查询条件避免类型不一致
This commit is contained in:
parent
a5996363c8
commit
8b31da07a0
|
|
@ -27,7 +27,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"mysql2": "^3.11.5",
|
||||
"mysql2": "^3.15.3",
|
||||
"nodemailer": "^7.0.5",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
"swagger-ui-dist": "^5.18.2",
|
||||
|
|
@ -3388,9 +3388,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.15.0.tgz",
|
||||
"integrity": "sha512-tT6pomf5Z/I7Jzxu8sScgrYBMK9bUFWd7Kbo6Fs1L0M13OOIJ/ZobGKS3Z7tQ8Re4lj+LnLXIQVZZxa3fhYKzA==",
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz",
|
||||
"integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.1",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"mysql2": "^3.11.5",
|
||||
"mysql2": "^3.15.3",
|
||||
"nodemailer": "^7.0.5",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
"swagger-ui-dist": "^5.18.2",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { OrderFee } from '../entity/order_fee.entity';
|
|||
import { OrderRefund } from '../entity/order_refund.entity';
|
||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
||||
import { OrderSale } from '../entity/order_sale.entity';
|
||||
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderItemOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
import { Service } from '../entity/service.entity';
|
||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||
|
|
@ -61,7 +61,7 @@ export default {
|
|||
OrderRefund,
|
||||
OrderRefundItem,
|
||||
OrderSale,
|
||||
OrderSaleOriginal,
|
||||
OrderItemOriginal,
|
||||
OrderShipment,
|
||||
ShipmentItem,
|
||||
Shipment,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export class OrderController {
|
|||
type: BooleanRes,
|
||||
})
|
||||
@Post('/syncOrder/:siteId')
|
||||
async syncOrder(@Param('siteId') siteId: string) {
|
||||
async syncOrder(@Param('siteId') siteId: number) {
|
||||
try {
|
||||
await this.orderService.syncOrders(siteId);
|
||||
return successResponse(true);
|
||||
|
|
@ -51,7 +51,7 @@ export class OrderController {
|
|||
})
|
||||
@Post('/syncOrder/:siteId/order/:orderId')
|
||||
async syncOrderById(
|
||||
@Param('siteId') siteId: string,
|
||||
@Param('siteId') siteId: number,
|
||||
@Param('orderId') orderId: string
|
||||
) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class SubscriptionController {
|
|||
// 同步订阅:根据站点 ID 拉取并更新本地订阅数据
|
||||
@ApiOkResponse({ type: BooleanRes })
|
||||
@Post('/sync/:siteId')
|
||||
async sync(@Param('siteId') siteId: string) {
|
||||
async sync(@Param('siteId') siteId: number) {
|
||||
try {
|
||||
await this.subscriptionService.syncSubscriptions(siteId);
|
||||
return successResponse(true);
|
||||
|
|
|
|||
|
|
@ -43,14 +43,15 @@ export class WebhookController {
|
|||
@Post('/woocommerce')
|
||||
async handleWooWebhook(
|
||||
@Body() body: any,
|
||||
@Query('siteId') siteId: string,
|
||||
@Query('siteId') siteIdStr: string,
|
||||
@Headers() header: any
|
||||
) {
|
||||
const signature = header['x-wc-webhook-signature'];
|
||||
const topic = header['x-wc-webhook-topic'];
|
||||
const source = header['x-wc-webhook-source'];
|
||||
const siteId = Number(siteIdStr);
|
||||
// 从数据库获取站点配置
|
||||
const site = await this.siteService.get(Number(siteId), true);
|
||||
const site = await this.siteService.get(siteId, true);
|
||||
|
||||
if (!site || !source.includes(site.apiUrl)) {
|
||||
console.log('domain not match');
|
||||
|
|
@ -97,13 +98,13 @@ export class WebhookController {
|
|||
? await this.wpApiService.getVariations(site, body.id)
|
||||
: [];
|
||||
await this.wpProductService.syncProductAndVariations(
|
||||
String(site.id),
|
||||
site.id,
|
||||
body,
|
||||
variations
|
||||
);
|
||||
break;
|
||||
case 'product.deleted':
|
||||
await this.wpProductService.delWpProduct(String(site.id), body.id);
|
||||
await this.wpProductService.delWpProduct(site.id, body.id);
|
||||
break;
|
||||
case 'order.created':
|
||||
case 'order.updated':
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export class WpProductController {
|
|||
type: BooleanRes,
|
||||
})
|
||||
@Post('/sync/:siteId')
|
||||
async syncProducts(@Param('siteId') siteId: string) {
|
||||
async syncProducts(@Param('siteId') siteId: number) {
|
||||
try {
|
||||
await this.wpProductService.syncSite(siteId);
|
||||
return successResponse(true);
|
||||
|
|
@ -107,7 +107,7 @@ export class WpProductController {
|
|||
})
|
||||
@Put('/siteId/:siteId/products/:productId')
|
||||
async updateProduct(
|
||||
@Param('siteId') siteId: string,
|
||||
@Param('siteId') siteId: number,
|
||||
@Param('productId') productId: string,
|
||||
@Body() body: UpdateWpProductDTO
|
||||
) {
|
||||
|
|
@ -120,7 +120,8 @@ export class WpProductController {
|
|||
if (isDuplicate) {
|
||||
return errorResponse('SKU已存在');
|
||||
}
|
||||
const site = await this.siteService.get(Number(siteId), true);
|
||||
|
||||
const site = await this.siteService.get(siteId, true);
|
||||
const result = await this.wpApiService.updateProduct(
|
||||
site,
|
||||
productId,
|
||||
|
|
@ -145,7 +146,7 @@ export class WpProductController {
|
|||
*/
|
||||
@Put('/siteId/:siteId/products/:productId/variations/:variationId')
|
||||
async updateVariation(
|
||||
@Param('siteId') siteId: string,
|
||||
@Param('siteId') siteId: number,
|
||||
@Param('productId') productId: string,
|
||||
@Param('variationId') variationId: string,
|
||||
@Body() body: UpdateVariationDTO
|
||||
|
|
@ -160,7 +161,7 @@ export class WpProductController {
|
|||
if (isDuplicate) {
|
||||
return errorResponse('SKU已存在');
|
||||
}
|
||||
const site = await this.siteService.get(Number(siteId), true);
|
||||
const site = await this.siteService.get(siteId, true);
|
||||
const result = await this.wpApiService.updateVariation(
|
||||
site,
|
||||
productId,
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ export class QueryOrderDTO {
|
|||
externalOrderId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
siteId: string;
|
||||
@Rule(RuleType.number())
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string().allow(''))
|
||||
|
|
@ -115,8 +115,8 @@ export class QueryOrderSalesDTO {
|
|||
pageSize: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
siteId: string;
|
||||
@Rule(RuleType.number())
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
|
|
@ -156,8 +156,8 @@ export class QueryOrderItemDTO {
|
|||
pageSize: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string().allow(''))
|
||||
siteId: string;
|
||||
@Rule(RuleType.number().allow(''))
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string().allow(''))
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ export class Order {
|
|||
id: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 来源站点唯一标识
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderCoupon {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column( )
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderFee {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string;
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderItem {
|
|||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 来源站点唯一标识
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import {
|
|||
} from 'typeorm';
|
||||
import { Order } from './order.entity';
|
||||
|
||||
@Entity('order_sale_original')
|
||||
@Entity('order_item_original')
|
||||
@Exclude()
|
||||
export class OrderSaleOriginal {
|
||||
export class OrderItemOriginal {
|
||||
@ApiProperty()
|
||||
@PrimaryGeneratedColumn()
|
||||
@Expose()
|
||||
|
|
@ -27,9 +27,9 @@ export class OrderSaleOriginal {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column({ nullable: true })
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderRefund {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderRefundItem {
|
|||
refundId: number; // 订单 refund ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ export class OrderSale {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
siteId: number; // 来源站点唯一标识
|
||||
|
||||
@ApiProperty()
|
||||
@Column({ nullable: true })
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export class OrderShipping {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string;
|
||||
siteId: number; // 站点ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ export class Subscription {
|
|||
|
||||
// 站点唯一标识,用于区分不同来源站点
|
||||
@ApiProperty({ description: '来源站点唯一标识' })
|
||||
@Column()
|
||||
@Column({ nullable: true })
|
||||
@Expose()
|
||||
siteId: string;
|
||||
siteId: number;
|
||||
|
||||
// WooCommerce 订阅的原始 ID(字符串化),用于幂等更新
|
||||
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@ export class Variation {
|
|||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '1',
|
||||
description: 'wp网站ID',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: '站点 id',
|
||||
example: 1,
|
||||
required: false
|
||||
})
|
||||
@Column()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
@Column({ nullable: true })
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '1',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { Site } from './site.entity';
|
||||
import {
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
|
|
@ -5,6 +6,8 @@ import {
|
|||
UpdateDateColumn,
|
||||
Unique,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { ProductStatus, ProductStockStatus, ProductType } from '../enums/base.enum';
|
||||
|
|
@ -22,13 +25,17 @@ export class WpProduct {
|
|||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '1',
|
||||
example: 1,
|
||||
description: 'wp网站ID',
|
||||
type: 'string',
|
||||
type: 'number',
|
||||
required: true,
|
||||
})
|
||||
@Column()
|
||||
siteId: string;
|
||||
@Column({ type: 'int', nullable: true })
|
||||
siteId: number;
|
||||
|
||||
@ManyToOne(() => Site)
|
||||
@JoinColumn({ name: 'siteId', referencedColumnName: 'id' })
|
||||
site: Site;
|
||||
|
||||
@ApiProperty({
|
||||
example: '1',
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ export class LogisticsService {
|
|||
// 从数据库批量获取站点信息,构建映射以避免 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) => [String(s.id), s.name]));
|
||||
const siteMap = new Map(sites.map((s: any) => [s.id, s.name]));
|
||||
|
||||
return orders.map(order => ({
|
||||
...order,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { In, Like, Repository } from 'typeorm';
|
|||
import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { OrderItem } from '../entity/order_item.entity';
|
||||
import { OrderItemOriginal } from '../entity/order_items_original.entity';
|
||||
|
||||
import { OrderSale } from '../entity/order_sale.entity';
|
||||
import { WpProduct } from '../entity/wp_product.entity';
|
||||
import { Product } from '../entity/product.entity';
|
||||
|
|
@ -32,7 +32,7 @@ import { SiteService } from './site.service';
|
|||
import { ShipmentItem } from '../entity/shipment_item.entity';
|
||||
import { UpdateStockDTO } from '../dto/stock.dto';
|
||||
import { StockService } from './stock.service';
|
||||
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderItemOriginal } from '../entity/order_item_original.entity';
|
||||
|
||||
@Provide()
|
||||
export class OrderService {
|
||||
|
|
@ -58,8 +58,6 @@ export class OrderService {
|
|||
@InjectEntityModel(OrderSale)
|
||||
orderSaleModel: Repository<OrderSale>;
|
||||
|
||||
@InjectEntityModel(OrderSaleOriginal)
|
||||
orderSaleOriginalModel: Repository<OrderSaleOriginal>;
|
||||
|
||||
@InjectEntityModel(WpProduct)
|
||||
wpProductModel: Repository<WpProduct>;
|
||||
|
|
@ -103,15 +101,17 @@ export class OrderService {
|
|||
@Inject()
|
||||
siteService: SiteService;
|
||||
|
||||
async syncOrders(siteId: string) {
|
||||
const orders = await this.wpService.getOrders(siteId); // 调用 WooCommerce API 获取订单
|
||||
async syncOrders(siteId: number) {
|
||||
// 调用 WooCommerce API 获取订单
|
||||
const orders = await this.wpService.getOrders(siteId);
|
||||
for (const order of orders) {
|
||||
await this.syncSingleOrder(siteId, order);
|
||||
}
|
||||
}
|
||||
|
||||
async syncOrderById(siteId: string, orderId: string) {
|
||||
const order = await this.wpService.getOrder(siteId, orderId);
|
||||
async syncOrderById(siteId: number, orderId: string) {
|
||||
// 调用 WooCommerce API 获取订单
|
||||
const order = await this.wpService.getOrder(String(siteId), orderId);
|
||||
await this.syncSingleOrder(siteId, order, true);
|
||||
}
|
||||
// 订单状态切换表
|
||||
|
|
@ -120,7 +120,7 @@ export class OrderService {
|
|||
[OrderStatus.RETURN_CANCELLED]: OrderStatus.REFUNDED // 已取消退款转为 refunded
|
||||
}
|
||||
// 由于 wordpress 订单状态和 我们的订单状态 不一致,需要做转换
|
||||
async autoUpdateOrderStatus(siteId: string, order: any) {
|
||||
async autoUpdateOrderStatus(siteId: number, order: any) {
|
||||
console.log('更新订单状态', order)
|
||||
// 其他状态保持不变
|
||||
const originStatus = order.status;
|
||||
|
|
@ -138,7 +138,7 @@ export class OrderService {
|
|||
}
|
||||
}
|
||||
// wordpress 发来,
|
||||
async syncSingleOrder(siteId: string, order: any, forceUpdate = false) {
|
||||
async syncSingleOrder(siteId: number, order: any, forceUpdate = false) {
|
||||
let {
|
||||
line_items,
|
||||
shipping_lines,
|
||||
|
|
@ -226,7 +226,7 @@ export class OrderService {
|
|||
}
|
||||
}
|
||||
|
||||
async saveOrder(siteId: string, order: Order): Promise<Order> {
|
||||
async saveOrder(siteId: number, order: Order): Promise<Order> {
|
||||
order.externalOrderId = String(order.id);
|
||||
order.siteId = siteId;
|
||||
delete order.id;
|
||||
|
|
@ -305,7 +305,7 @@ export class OrderService {
|
|||
}
|
||||
|
||||
async saveOrderItems(params: {
|
||||
siteId: string;
|
||||
siteId: number;
|
||||
orderId: number;
|
||||
externalOrderId: string;
|
||||
orderItems: Record<string, any>[];
|
||||
|
|
@ -438,14 +438,14 @@ export class OrderService {
|
|||
externalOrderId,
|
||||
refunds,
|
||||
}: {
|
||||
siteId: string;
|
||||
siteId: number;
|
||||
orderId: number;
|
||||
externalOrderId: string;
|
||||
refunds: Record<string, any>[];
|
||||
}) {
|
||||
for (const item of refunds) {
|
||||
const refund = await this.wpService.getOrderRefund(
|
||||
siteId,
|
||||
String(siteId),
|
||||
externalOrderId,
|
||||
item.id
|
||||
);
|
||||
|
|
@ -1302,28 +1302,7 @@ 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);
|
||||
});
|
||||
}
|
||||
|
||||
// update order_item_origin if not exist
|
||||
const order_item_origin_count = await this.orderItemOriginalModel.countBy({ orderId: id });
|
||||
if (order_item_origin_count === 0 && items.length != 0) {
|
||||
items.forEach(async sale => {
|
||||
const { id: saleId, ...saleData } = sale;
|
||||
await this.orderItemOriginalModel.save(saleData);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('create order sale origin error: ', error.message);
|
||||
}
|
||||
|
||||
// 关联数据:订阅与相关订单(用于前端关联展示)
|
||||
let relatedList: any[] = [];
|
||||
|
|
@ -1422,7 +1401,7 @@ export class OrderService {
|
|||
return orders.map(order => ({
|
||||
externalOrderId: order.externalOrderId,
|
||||
id: order.id,
|
||||
name: siteMap.get(order.siteId) || '',
|
||||
name: siteMap.get(String(order.siteId)) || '',
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -1478,16 +1457,23 @@ export class OrderService {
|
|||
|
||||
|
||||
async createOrder(data: Record<string, any>) {
|
||||
const { sales, total, billing, customer_email, billing_phone } = data;
|
||||
// 从数据中解构出需要用的属性
|
||||
const { siteId, sales, total, billing, customer_email, billing_phone } = data;
|
||||
// 如果没有 siteId,则抛出错误
|
||||
if (!siteId) {
|
||||
throw new Error('siteId is required');
|
||||
}
|
||||
// 获取默认数据源
|
||||
const dataSource = this.dataSourceManager.getDataSource('default');
|
||||
const now = new Date();
|
||||
// 在事务中处理订单创建
|
||||
return dataSource.transaction(async manager => {
|
||||
const orderRepo = manager.getRepository(Order);
|
||||
const orderSaleRepo = manager.getRepository(OrderSale);
|
||||
const OrderSaleOriginalRepo = manager.getRepository(OrderSaleOriginal);
|
||||
const productRepo = manager.getRepository(Product);
|
||||
// 保存订单信息
|
||||
const order = await orderRepo.save({
|
||||
siteId: '-1',
|
||||
siteId,
|
||||
externalOrderId: '-1',
|
||||
status: OrderStatus.PROCESSING,
|
||||
orderStatus: ErpOrderStatus.PROCESSING,
|
||||
|
|
@ -1501,11 +1487,12 @@ export class OrderService {
|
|||
billing,
|
||||
shipping: billing,
|
||||
});
|
||||
// 遍历销售项目并保存
|
||||
for (const sale of sales) {
|
||||
const product = await productRepo.findOne({ where: { sku: sale.sku } });
|
||||
const saleItem = {
|
||||
orderId: order.id,
|
||||
siteId: '-1',
|
||||
siteId: order.siteId,
|
||||
externalOrderItemId: '-1',
|
||||
productId: product.id,
|
||||
name: product.name,
|
||||
|
|
@ -1513,7 +1500,6 @@ export class OrderService {
|
|||
quantity: sale.quantity,
|
||||
};
|
||||
await orderSaleRepo.save(saleItem);
|
||||
await OrderSaleOriginalRepo.save(saleItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1606,7 +1592,7 @@ export class OrderService {
|
|||
const orderRepo = manager.getRepository(Order);
|
||||
const orderSaleRepo = manager.getRepository(OrderSale);
|
||||
const orderItemRepo = manager.getRepository(OrderItem);
|
||||
const OrderItemOriginalRepo = manager.getRepository(OrderItemOriginal);
|
||||
|
||||
|
||||
const productRepo = manager.getRepository(Product);
|
||||
const WpProductRepo = manager.getRepository(WpProduct);
|
||||
|
|
@ -1614,7 +1600,7 @@ export class OrderService {
|
|||
const order = await orderRepo.findOneBy({ id: orderId });
|
||||
let product: Product;
|
||||
let wpProduct: WpProduct;
|
||||
let wpOrderItemOriginal: OrderItemOriginal;
|
||||
|
||||
await orderSaleRepo.delete({ orderId });
|
||||
await orderItemRepo.delete({ orderId });
|
||||
for (const sale of data['sales']) {
|
||||
|
|
@ -1631,13 +1617,8 @@ export class OrderService {
|
|||
|
||||
for (const item of data['items']) {
|
||||
wpProduct = await WpProductRepo.findOneBy({ sku: item['sku'] });
|
||||
wpOrderItemOriginal = await OrderItemOriginalRepo.findOneBy({ sku: item['sku'] });
|
||||
let externalVariationId = wpOrderItemOriginal?.externalVariationId;
|
||||
let price = wpOrderItemOriginal?.price;
|
||||
if (wpOrderItemOriginal == null) {
|
||||
externalVariationId = '0';
|
||||
price = 0;
|
||||
}
|
||||
|
||||
|
||||
await orderItemRepo.save({
|
||||
orderId,
|
||||
siteId: order.siteId,
|
||||
|
|
@ -1645,8 +1626,7 @@ export class OrderService {
|
|||
name: wpProduct.name,
|
||||
externalOrderId: order.externalOrderId,
|
||||
externalProductId: wpProduct.externalProductId,
|
||||
externalVariationId: externalVariationId,
|
||||
price: price,
|
||||
|
||||
sku: item['sku'],
|
||||
quantity: item['quantity'],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export class SubscriptionService {
|
|||
* 同步指定站点的订阅列表
|
||||
* - 从 WooCommerce 拉取订阅并逐条入库/更新
|
||||
*/
|
||||
async syncSubscriptions(siteId: string) {
|
||||
async syncSubscriptions(siteId: number) {
|
||||
const subs = await this.wpService.getSubscriptions(siteId);
|
||||
for (const sub of subs) {
|
||||
await this.syncSingleSubscription(siteId, sub);
|
||||
|
|
@ -31,7 +31,7 @@ export class SubscriptionService {
|
|||
* - 规范化字段、设置幂等键 externalSubscriptionId
|
||||
* - 已存在则更新,不存在则新增
|
||||
*/
|
||||
async syncSingleSubscription(siteId: string, sub: any) {
|
||||
async syncSingleSubscription(siteId: number, sub: any) {
|
||||
const { line_items, ...raw } = sub;
|
||||
const entity: Partial<Subscription> = {
|
||||
...raw,
|
||||
|
|
@ -43,7 +43,7 @@ export class SubscriptionService {
|
|||
};
|
||||
delete (entity as any).id;
|
||||
const existing = await this.subscriptionModel.findOne({
|
||||
where: { externalSubscriptionId: String(sub.id), siteId },
|
||||
where: { externalSubscriptionId: String(sub.id), siteId: siteId },
|
||||
});
|
||||
const saveEntity = plainToClass(Subscription, entity);
|
||||
if (existing) {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ export class WPService {
|
|||
const res = await api.get(`orders/${orderId}`);
|
||||
return res.data as Record<string, any>;
|
||||
}
|
||||
async getOrders(siteId: string): Promise<Record<string, any>[]> {
|
||||
async getOrders(siteId: number): Promise<Record<string, any>[]> {
|
||||
const site = await this.siteService.get(siteId);
|
||||
const api = this.createApi(site, 'wc/v3');
|
||||
return await this.sdkGetAll<Record<string, any>>(api, 'orders');
|
||||
|
|
@ -197,7 +197,7 @@ export class WPService {
|
|||
* 优先尝试 wc/v1/subscriptions(Subscriptions 插件提供),失败时回退 wc/v3/subscriptions。
|
||||
* 返回所有分页合并后的订阅数组。
|
||||
*/
|
||||
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
|
||||
async getSubscriptions(siteId: number): Promise<Record<string, any>[]> {
|
||||
const site = await this.siteService.get(siteId);
|
||||
// 优先使用 Subscriptions 命名空间 wcs/v1,失败回退 wc/v3
|
||||
const api = this.createApi(site, 'wc/v3');
|
||||
|
|
|
|||
|
|
@ -40,21 +40,21 @@ export class WpProductService {
|
|||
product.type === 'variable'
|
||||
? await this.wpApiService.getVariations(site, product.id)
|
||||
: [];
|
||||
await this.syncProductAndVariations(String(site.id), product, variations);
|
||||
await this.syncProductAndVariations(site.id, product, variations);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 同步一个网站
|
||||
async syncSite(siteId: string) {
|
||||
async syncSite(siteId: number) {
|
||||
// 通过数据库获取站点并转换为 WpSite,用于后续 WooCommerce 同步
|
||||
const site = await this.siteService.get(Number(siteId), true);
|
||||
const site = await this.siteService.get(siteId, true);
|
||||
const externalProductIds = this.wpProductModel.createQueryBuilder('wp_product')
|
||||
.select([
|
||||
'wp_product.id ',
|
||||
'wp_product.externalProductId ',
|
||||
])
|
||||
.where('wp_product.siteId = :siteIds ', {
|
||||
siteIds: siteId,
|
||||
.where('wp_product.siteId = :siteId', {
|
||||
siteId,
|
||||
})
|
||||
const rawResult = await externalProductIds.getRawMany();
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ export class WpProductService {
|
|||
? await this.wpApiService.getVariations(site, product.id)
|
||||
: [];
|
||||
|
||||
await this.syncProductAndVariations(String(site.id), product, variations);
|
||||
await this.syncProductAndVariations(site.id, product, variations);
|
||||
}
|
||||
|
||||
const filteredIds = externalIds.filter(id => !excludeValues.includes(id));
|
||||
|
|
@ -78,13 +78,13 @@ export class WpProductService {
|
|||
await this.variationModel.createQueryBuilder('variation')
|
||||
.update()
|
||||
.set({ on_delete: true })
|
||||
.where(" variation.externalProductId in (:...filteredId) ", { filteredId: filteredIds })
|
||||
.where('variation.siteId = :siteId AND variation.externalProductId IN (:...filteredId)', { siteId, filteredId: filteredIds })
|
||||
.execute();
|
||||
|
||||
this.wpProductModel.createQueryBuilder('wp_product')
|
||||
.update()
|
||||
.set({ on_delete: true })
|
||||
.where(" wp_product.externalProductId in (:...filteredId) ", { filteredId: filteredIds })
|
||||
.where('wp_product.siteId = :siteId AND wp_product.externalProductId IN (:...filteredId)', { siteId, filteredId: filteredIds })
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ export class WpProductService {
|
|||
// 控制产品上下架
|
||||
async updateProductStatus(id: number, status: ProductStatus, stock_status: ProductStockStatus) {
|
||||
const wpProduct = await this.wpProductModel.findOneBy({ id });
|
||||
const site = await this.siteService.get(Number(wpProduct.siteId), true);
|
||||
const site = await this.siteService.get(wpProduct.siteId, true);
|
||||
wpProduct.status = status;
|
||||
wpProduct.stockStatus = stock_status;
|
||||
const res = await this.wpApiService.updateProductStatus(site, wpProduct.externalProductId, status, stock_status);
|
||||
|
|
@ -105,7 +105,7 @@ export class WpProductService {
|
|||
}
|
||||
|
||||
async findProduct(
|
||||
siteId: string,
|
||||
siteId: number,
|
||||
externalProductId: string
|
||||
): Promise<WpProduct | null> {
|
||||
return await this.wpProductModel.findOne({
|
||||
|
|
@ -114,17 +114,17 @@ export class WpProductService {
|
|||
}
|
||||
|
||||
async findVariation(
|
||||
siteId: string,
|
||||
siteId: number,
|
||||
externalProductId: string,
|
||||
externalVariationId: string
|
||||
): Promise<Variation | null> {
|
||||
return await this.variationModel.findOne({
|
||||
where: { siteId, externalProductId, externalVariationId },
|
||||
where: { siteId, externalProductId, externalVariationId, on_delete: false },
|
||||
});
|
||||
}
|
||||
|
||||
async updateWpProduct(
|
||||
siteId: string,
|
||||
siteId: number,
|
||||
productId: string,
|
||||
product: UpdateWpProductDTO
|
||||
) {
|
||||
|
|
@ -140,7 +140,7 @@ export class WpProductService {
|
|||
}
|
||||
|
||||
async updateWpProductVaritation(
|
||||
siteId: string,
|
||||
siteId: number,
|
||||
productId: string,
|
||||
variationId: string,
|
||||
variation: UpdateVariationDTO
|
||||
|
|
@ -163,7 +163,7 @@ export class WpProductService {
|
|||
}
|
||||
|
||||
async syncProductAndVariations(
|
||||
siteId: string,
|
||||
siteId: number,
|
||||
product: WpProduct,
|
||||
variations: Variation[]
|
||||
) {
|
||||
|
|
@ -180,6 +180,8 @@ export class WpProductService {
|
|||
product.sale_price && (existingProduct.sale_price = product.sale_price);
|
||||
existingProduct.on_sale = product.on_sale;
|
||||
existingProduct.metadata = product.metadata;
|
||||
existingProduct.tags = product.tags;
|
||||
existingProduct.categories = product.categories;
|
||||
await this.wpProductModel.save(existingProduct);
|
||||
} else {
|
||||
existingProduct = this.wpProductModel.create({
|
||||
|
|
@ -195,6 +197,8 @@ export class WpProductService {
|
|||
...(product.sale_price ? { sale_price: product.sale_price } : {}),
|
||||
on_sale: product.on_sale,
|
||||
metadata: product.metadata,
|
||||
tags: product.tags,
|
||||
categories: product.categories,
|
||||
});
|
||||
await this.wpProductModel.save(existingProduct);
|
||||
}
|
||||
|
|
@ -202,7 +206,7 @@ export class WpProductService {
|
|||
// 2. 处理变体同步
|
||||
if (product.type === 'variable') {
|
||||
const currentVariations = await this.variationModel.find({
|
||||
where: { siteId, externalProductId: String(product.id) },
|
||||
where: { siteId, externalProductId: String(product.id), on_delete: false },
|
||||
});
|
||||
const syncedVariationIds = new Set(variations.map(v => String(v.id)));
|
||||
const variationsToDelete = currentVariations.filter(
|
||||
|
|
@ -252,21 +256,23 @@ export class WpProductService {
|
|||
}
|
||||
} else {
|
||||
// 清理之前的变体
|
||||
await this.variationModel.delete({
|
||||
siteId,
|
||||
externalProductId: String(product.id),
|
||||
});
|
||||
await this.variationModel.update(
|
||||
{ siteId, externalProductId: String(product.id) },
|
||||
{ on_delete: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async syncVariation(siteId: string, productId: string, variation: Variation) {
|
||||
async syncVariation(siteId: number, productId: string, variation: Variation) {
|
||||
let existingProduct = await this.findProduct(siteId, String(productId));
|
||||
if (!existingProduct) return;
|
||||
const existingVariation = await this.findVariation(
|
||||
const existingVariation = await this.variationModel.findOne({
|
||||
where: {
|
||||
siteId,
|
||||
String(productId),
|
||||
String(variation.id)
|
||||
);
|
||||
externalProductId: String(productId),
|
||||
externalVariationId: String(variation.id),
|
||||
},
|
||||
});
|
||||
|
||||
if (existingVariation) {
|
||||
existingVariation.name = variation.name;
|
||||
|
|
@ -314,6 +320,7 @@ export class WpProductService {
|
|||
where.on_delete = false;
|
||||
|
||||
const products = await this.wpProductModel.find({
|
||||
relations: ['site'],
|
||||
where,
|
||||
skip: (current - 1) * pageSize,
|
||||
take: pageSize,
|
||||
|
|
@ -428,7 +435,7 @@ export class WpProductService {
|
|||
*/
|
||||
async isSkuDuplicate(
|
||||
sku: string,
|
||||
excludeSiteId?: string,
|
||||
excludeSiteId?: number,
|
||||
excludeProductId?: string,
|
||||
excludeVariationId?: string
|
||||
): Promise<boolean> {
|
||||
|
|
@ -439,8 +446,8 @@ export class WpProductService {
|
|||
varWhere.siteId = Not(excludeSiteId);
|
||||
varWhere.externalProductId = Not(excludeProductId);
|
||||
varWhere.externalVariationId = Not(excludeVariationId);
|
||||
} else {
|
||||
where.externalProductId = Not(excludeProductId);
|
||||
} else if (excludeProductId) {
|
||||
where.siteId = Not(excludeSiteId);
|
||||
where.externalProductId = Not(excludeProductId);
|
||||
}
|
||||
|
||||
|
|
@ -486,7 +493,7 @@ export class WpProductService {
|
|||
}
|
||||
}
|
||||
|
||||
async delWpProduct(siteId: string, productId: string) {
|
||||
async delWpProduct(siteId: number, productId: string) {
|
||||
const product = await this.wpProductModel.findOne({
|
||||
where: { siteId, externalProductId: productId },
|
||||
});
|
||||
|
|
@ -495,13 +502,13 @@ export class WpProductService {
|
|||
await this.variationModel.createQueryBuilder('variation')
|
||||
.update()
|
||||
.set({ on_delete: true })
|
||||
.where(" variation.externalProductId = :externalProductId ", { externalProductId: productId })
|
||||
.where('variation.siteId = :siteId AND variation.externalProductId = :externalProductId', { siteId, externalProductId: productId })
|
||||
.execute();
|
||||
|
||||
const sums = await this.wpProductModel.createQueryBuilder('wp_product')
|
||||
.update()
|
||||
.set({ on_delete: true })
|
||||
.where(" wp_product.externalProductId = :externalProductId ", { externalProductId: productId })
|
||||
.where('wp_product.siteId = :siteId AND wp_product.externalProductId = :externalProductId', { siteId, externalProductId: productId })
|
||||
.execute();
|
||||
|
||||
console.log(sums);
|
||||
|
|
|
|||
Loading…
Reference in New Issue