diff --git a/package-lock.json b/package-lock.json index 371e5cf..58971a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index c40ff7a..8ce8aac 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/config/config.default.ts b/src/config/config.default.ts index 3538e60..3ff82a0 100644 --- a/src/config/config.default.ts +++ b/src/config/config.default.ts @@ -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, diff --git a/src/controller/order.controller.ts b/src/controller/order.controller.ts index cdeb3ad..40f5026 100644 --- a/src/controller/order.controller.ts +++ b/src/controller/order.controller.ts @@ -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 { diff --git a/src/controller/subscription.controller.ts b/src/controller/subscription.controller.ts index 5506063..080c799 100644 --- a/src/controller/subscription.controller.ts +++ b/src/controller/subscription.controller.ts @@ -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); diff --git a/src/controller/webhook.controller.ts b/src/controller/webhook.controller.ts index d15da14..bb254c7 100644 --- a/src/controller/webhook.controller.ts +++ b/src/controller/webhook.controller.ts @@ -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': diff --git a/src/controller/wp_product.controller.ts b/src/controller/wp_product.controller.ts index 8623b1d..02a644e 100644 --- a/src/controller/wp_product.controller.ts +++ b/src/controller/wp_product.controller.ts @@ -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, diff --git a/src/dto/order.dto.ts b/src/dto/order.dto.ts index 0f0b133..26a1b73 100644 --- a/src/dto/order.dto.ts +++ b/src/dto/order.dto.ts @@ -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('')) diff --git a/src/entity/order.entity.ts b/src/entity/order.entity.ts index fc51dd2..4c7d293 100644 --- a/src/entity/order.entity.ts +++ b/src/entity/order.entity.ts @@ -24,9 +24,9 @@ export class Order { id: number; @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 来源站点唯一标识 @ApiProperty() @Column() diff --git a/src/entity/order_coupon.entity.ts b/src/entity/order_coupon.entity.ts index f045620..0670870 100644 --- a/src/entity/order_coupon.entity.ts +++ b/src/entity/order_coupon.entity.ts @@ -22,9 +22,9 @@ export class OrderCoupon { orderId: number; // 订单 ID @ApiProperty() - @Column( ) + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 站点ID @ApiProperty() @Column() diff --git a/src/entity/order_fee.entity.ts b/src/entity/order_fee.entity.ts index 5b83850..b7d8888 100644 --- a/src/entity/order_fee.entity.ts +++ b/src/entity/order_fee.entity.ts @@ -22,9 +22,9 @@ export class OrderFee { orderId: number; // 订单 ID @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; + siteId: number; // 站点ID @ApiProperty() @Column() diff --git a/src/entity/order_item.entity.ts b/src/entity/order_item.entity.ts index 0a3fd5b..4a97b0a 100644 --- a/src/entity/order_item.entity.ts +++ b/src/entity/order_item.entity.ts @@ -22,9 +22,9 @@ export class OrderItem { name: string; @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 来源站点唯一标识 @ApiProperty() @Column() diff --git a/src/entity/order_item_original.entity.ts b/src/entity/order_item_original.entity.ts index 26c494a..d7ed055 100644 --- a/src/entity/order_item_original.entity.ts +++ b/src/entity/order_item_original.entity.ts @@ -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 }) diff --git a/src/entity/order_refund.entity.ts b/src/entity/order_refund.entity.ts index a9d5251..4b6a869 100644 --- a/src/entity/order_refund.entity.ts +++ b/src/entity/order_refund.entity.ts @@ -22,9 +22,9 @@ export class OrderRefund { orderId: number; // 订单 ID @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 站点ID @ApiProperty() @Column() diff --git a/src/entity/order_refund_item.entity.ts b/src/entity/order_refund_item.entity.ts index e148bbf..76cdebc 100644 --- a/src/entity/order_refund_item.entity.ts +++ b/src/entity/order_refund_item.entity.ts @@ -22,9 +22,9 @@ export class OrderRefundItem { refundId: number; // 订单 refund ID @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 站点ID @ApiProperty() @Column() diff --git a/src/entity/order_sale.entity.ts b/src/entity/order_sale.entity.ts index e7958ed..eec088f 100644 --- a/src/entity/order_sale.entity.ts +++ b/src/entity/order_sale.entity.ts @@ -24,9 +24,9 @@ export class OrderSale { orderId: number; // 订单 ID @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; // 来源站点唯一标识 + siteId: number; // 来源站点唯一标识 @ApiProperty() @Column({ nullable: true }) diff --git a/src/entity/order_shipping.entity.ts b/src/entity/order_shipping.entity.ts index f32e85e..8220992 100644 --- a/src/entity/order_shipping.entity.ts +++ b/src/entity/order_shipping.entity.ts @@ -22,9 +22,9 @@ export class OrderShipping { orderId: number; // 订单 ID @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; + siteId: number; // 站点ID @ApiProperty() @Column() diff --git a/src/entity/subscription.entity.ts b/src/entity/subscription.entity.ts index f4c76bb..10d205f 100644 --- a/src/entity/subscription.entity.ts +++ b/src/entity/subscription.entity.ts @@ -20,9 +20,9 @@ export class Subscription { // 站点唯一标识,用于区分不同来源站点 @ApiProperty({ description: '来源站点唯一标识' }) - @Column() + @Column({ nullable: true }) @Expose() - siteId: string; + siteId: number; // WooCommerce 订阅的原始 ID(字符串化),用于幂等更新 @ApiProperty({ description: 'WooCommerce 订阅 ID' }) diff --git a/src/entity/variation.entity.ts b/src/entity/variation.entity.ts index 7550bb5..9dcdea2 100644 --- a/src/entity/variation.entity.ts +++ b/src/entity/variation.entity.ts @@ -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', diff --git a/src/entity/wp_product.entity.ts b/src/entity/wp_product.entity.ts index c70a60f..939e13a 100644 --- a/src/entity/wp_product.entity.ts +++ b/src/entity/wp_product.entity.ts @@ -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', diff --git a/src/service/logistics.service.ts b/src/service/logistics.service.ts index 36d9232..0297fd3 100644 --- a/src/service/logistics.service.ts +++ b/src/service/logistics.service.ts @@ -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, diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 9fd08f1..6bf3aa8 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -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; - @InjectEntityModel(OrderSaleOriginal) - orderSaleOriginalModel: Repository; @InjectEntityModel(WpProduct) wpProductModel: Repository; @@ -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 { + async saveOrder(siteId: number, order: Order): Promise { 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[]; @@ -438,14 +438,14 @@ export class OrderService { externalOrderId, refunds, }: { - siteId: string; + siteId: number; orderId: number; externalOrderId: string; refunds: Record[]; }) { 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) { - 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,19 +1487,19 @@ 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', - externalOrderItemId: '-1', - productId: product.id, - name: product.name, - sku: sale.sku, - quantity: sale.quantity, - }; + orderId: order.id, + siteId: order.siteId, + externalOrderItemId: '-1', + productId: product.id, + name: product.name, + sku: sale.sku, + 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'], }); diff --git a/src/service/subscription.service.ts b/src/service/subscription.service.ts index 5ae83b0..90d344a 100644 --- a/src/service/subscription.service.ts +++ b/src/service/subscription.service.ts @@ -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 = { ...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) { diff --git a/src/service/wp.service.ts b/src/service/wp.service.ts index b3aafc4..714eff4 100644 --- a/src/service/wp.service.ts +++ b/src/service/wp.service.ts @@ -186,7 +186,7 @@ export class WPService { const res = await api.get(`orders/${orderId}`); return res.data as Record; } - async getOrders(siteId: string): Promise[]> { + async getOrders(siteId: number): Promise[]> { const site = await this.siteService.get(siteId); const api = this.createApi(site, 'wc/v3'); return await this.sdkGetAll>(api, 'orders'); @@ -197,7 +197,7 @@ export class WPService { * 优先尝试 wc/v1/subscriptions(Subscriptions 插件提供),失败时回退 wc/v3/subscriptions。 * 返回所有分页合并后的订阅数组。 */ - async getSubscriptions(siteId: string): Promise[]> { + async getSubscriptions(siteId: number): Promise[]> { const site = await this.siteService.get(siteId); // 优先使用 Subscriptions 命名空间 wcs/v1,失败回退 wc/v3 const api = this.createApi(site, 'wc/v3'); diff --git a/src/service/wp_product.service.ts b/src/service/wp_product.service.ts index 187b758..58cc3af 100644 --- a/src/service/wp_product.service.ts +++ b/src/service/wp_product.service.ts @@ -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 { return await this.wpProductModel.findOne({ @@ -114,17 +114,17 @@ export class WpProductService { } async findVariation( - siteId: string, + siteId: number, externalProductId: string, externalVariationId: string ): Promise { 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( - siteId, - String(productId), - String(variation.id) - ); + const existingVariation = await this.variationModel.findOne({ + where: { + siteId, + 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 { @@ -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);