forked from yoone/API
1
0
Fork 0

refactor(站点管理): 移除配置中的站点数组,统一通过数据库获取站点信息

重构多个控制器和服务,将硬编码的站点配置替换为通过 SiteService 从数据库获取
使用批量查询优化站点名称映射,避免 N+1 查询问题
兼容新旧站点数据结构,确保平滑过渡
This commit is contained in:
tikkhun 2025-11-22 11:41:49 +08:00
parent 1d62730ca0
commit c7480ccc8a
6 changed files with 112 additions and 105 deletions

View File

@ -1,4 +1,4 @@
import { Config, HttpStatus, Inject } from '@midwayjs/core'; import { HttpStatus, Inject } from '@midwayjs/core';
import { import {
Controller, Controller,
Post, Post,
@ -11,8 +11,8 @@ import { Context } from '@midwayjs/koa';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import { WpProductService } from '../service/wp_product.service'; import { WpProductService } from '../service/wp_product.service';
import { WPService } from '../service/wp.service'; import { WPService } from '../service/wp.service';
import { SiteService } from '../service/site.service';
import { OrderService } from '../service/order.service'; import { OrderService } from '../service/order.service';
import { WpSite } from '../interface';
@Controller('/webhook') @Controller('/webhook')
export class WebhookController { export class WebhookController {
@ -30,8 +30,10 @@ export class WebhookController {
@Inject() @Inject()
ctx: Context; ctx: Context;
@Config('wpSite') @Inject()
sites: WpSite[]; private readonly siteService: SiteService;
// 中文注释:移除配置中的站点数组,来源统一改为数据库
@Get('/') @Get('/')
async test() { async test() {
@ -47,7 +49,8 @@ export class WebhookController {
const signature = header['x-wc-webhook-signature']; const signature = header['x-wc-webhook-signature'];
const topic = header['x-wc-webhook-topic']; const topic = header['x-wc-webhook-topic'];
const source = header['x-wc-webhook-source']; const source = header['x-wc-webhook-source'];
let site = this.sites.find(item => item.id === siteId); // 中文注释:从数据库获取站点配置
const site = await this.siteService.get(Number(siteId), true);
if (!site || !source.includes(site.wpApiUrl)) { if (!site || !source.includes(site.wpApiUrl)) {
console.log('domain not match'); console.log('domain not match');

View File

@ -7,7 +7,6 @@ import {
Query, Query,
Put, Put,
Body, Body,
Config,
} from '@midwayjs/core'; } from '@midwayjs/core';
import { WpProductService } from '../service/wp_product.service'; import { WpProductService } from '../service/wp_product.service';
import { errorResponse, successResponse } from '../utils/response.util'; import { errorResponse, successResponse } from '../utils/response.util';
@ -20,22 +19,13 @@ import {
UpdateWpProductDTO, UpdateWpProductDTO,
} from '../dto/wp_product.dto'; } from '../dto/wp_product.dto';
import { WPService } from '../service/wp.service'; import { WPService } from '../service/wp.service';
import { WpSite } from '../interface'; import { SiteService } from '../service/site.service';
import { import {
ProductsRes, ProductsRes,
} from '../dto/reponse.dto'; } from '../dto/reponse.dto';
@Controller('/wp_product') @Controller('/wp_product')
export class WpProductController { export class WpProductController {
@Inject() // 中文注释:移除控制器内的配置站点引用,统一由服务层处理站点数据
wpService: WPService;
@Config('wpSite')
sites: WpSite[];
getSite(id: string): WpSite {
let idx = this.sites.findIndex(item => item.id === id);
return this.sites[idx];
}
@Inject() @Inject()
private readonly wpProductService: WpProductService; private readonly wpProductService: WpProductService;
@ -43,6 +33,9 @@ export class WpProductController {
@Inject() @Inject()
private readonly wpApiService: WPService; private readonly wpApiService: WPService;
@Inject()
private readonly siteService: SiteService;
@ApiOkResponse({ @ApiOkResponse({
type: BooleanRes, type: BooleanRes,
}) })
@ -127,7 +120,7 @@ export class WpProductController {
if (isDuplicate) { if (isDuplicate) {
return errorResponse('SKU已存在'); return errorResponse('SKU已存在');
} }
const site = await this.wpProductService.getSite(siteId); const site = await this.siteService.get(Number(siteId), true);
const result = await this.wpApiService.updateProduct( const result = await this.wpApiService.updateProduct(
site, site,
productId, productId,
@ -167,7 +160,7 @@ export class WpProductController {
if (isDuplicate) { if (isDuplicate) {
return errorResponse('SKU已存在'); return errorResponse('SKU已存在');
} }
const site = await this.wpProductService.getSite(siteId); const site = await this.siteService.get(Number(siteId), true);
const result = await this.wpApiService.updateVariation( const result = await this.wpApiService.updateVariation(
site, site,
productId, productId,

View File

@ -1,4 +1,4 @@
import { Config, Inject, Provide, sleep } from '@midwayjs/core'; import { Inject, Provide, sleep } from '@midwayjs/core';
import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm'; import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm';
import { Service } from '../entity/service.entity'; import { Service } from '../entity/service.entity';
import { In, IsNull, Like, Repository } from 'typeorm'; import { In, IsNull, Like, Repository } from 'typeorm';
@ -21,7 +21,6 @@ import { StockRecord } from '../entity/stock_record.entity';
import { Stock } from '../entity/stock.entity'; import { Stock } from '../entity/stock.entity';
import { plainToClass } from 'class-transformer'; import { plainToClass } from 'class-transformer';
import { WPService } from './wp.service'; import { WPService } from './wp.service';
import { WpSite } from '../interface';
// import { Product } from '../entity/product.entty'; // import { Product } from '../entity/product.entty';
import { ShippingDetailsDTO } from '../dto/freightcom.dto'; import { ShippingDetailsDTO } from '../dto/freightcom.dto';
import { CanadaPostService } from './canadaPost.service'; import { CanadaPostService } from './canadaPost.service';
@ -31,6 +30,7 @@ import { UniExpressService } from './uni_express.service';
import { StockPoint } from '../entity/stock_point.entity'; import { StockPoint } from '../entity/stock_point.entity';
import { OrderService } from './order.service'; import { OrderService } from './order.service';
import { convertKeysFromCamelToSnake } from '../utils/object-transform.util'; import { convertKeysFromCamelToSnake } from '../utils/object-transform.util';
import { SiteService } from './site.service';
@Provide() @Provide()
export class LogisticsService { export class LogisticsService {
@ -82,13 +82,8 @@ export class LogisticsService {
@Inject() @Inject()
dataSourceManager: TypeORMDataSourceManager; dataSourceManager: TypeORMDataSourceManager;
@Config('wpSite') @Inject()
sites: WpSite[]; private readonly siteService: SiteService;
getSite(id: string): WpSite {
let idx = this.sites.findIndex(item => item.id === id);
return this.sites[idx];
}
async getServiceList(param: QueryServiceDTO) { async getServiceList(param: QueryServiceDTO) {
const { pageSize, current, carrier_name, isActive } = param; const { pageSize, current, carrier_name, isActive } = param;
@ -263,7 +258,7 @@ export class LogisticsService {
try { try {
// 同步订单状态到woocommerce // 同步订单状态到woocommerce
const site = await this.getSite(order.siteId); const site = await this.siteService.get(Number(order.siteId), true);
if (order.status === OrderStatus.COMPLETED) { if (order.status === OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, { await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.PROCESSING, status: OrderStatus.PROCESSING,
@ -367,7 +362,7 @@ export class LogisticsService {
const tracking_provider = 'UniUni'; // todo: id未确定后写进常数 const tracking_provider = 'UniUni'; // todo: id未确定后写进常数
// 同步物流信息到woocommerce // 同步物流信息到woocommerce
const site = await this.getSite(order.siteId); const site = await this.siteService.get(Number(order.siteId), true);
const res = await this.wpService.createShipment(site, order.externalOrderId, { const res = await this.wpService.createShipment(site, order.externalOrderId, {
tracking_number: resShipmentOrder.data.tno, tracking_number: resShipmentOrder.data.tno,
tracking_provider: tracking_provider, tracking_provider: tracking_provider,
@ -493,7 +488,7 @@ export class LogisticsService {
const order = await this.orderModel.findOneBy({ const order = await this.orderModel.findOneBy({
id: orderShipment.order_id, id: orderShipment.order_id,
}); });
const site = this.getSite(order.siteId); const site = await this.siteService.get(Number(order.siteId), true);
await this.wpService.updateOrder(site, order.externalOrderId, { await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.COMPLETED, status: OrderStatus.COMPLETED,
}); });
@ -563,7 +558,10 @@ export class LogisticsService {
}, },
}); });
const siteMap = new Map(this.sites.map(site => [site.id, site.siteName])); // 中文注释:从数据库批量获取站点信息,构建映射以避免 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.siteName]));
return orders.map(order => ({ return orders.map(order => ({
...order, ...order,

View File

@ -1,5 +1,6 @@
import { Config, Inject, Provide } from '@midwayjs/core'; import { Inject, Provide } from '@midwayjs/core';
import { WPService } from './wp.service'; import { WPService } from './wp.service';
import { WpSite } from '../interface';
import { Order } from '../entity/order.entity'; import { Order } from '../entity/order.entity';
import { In, Like, Repository } from 'typeorm'; import { In, Like, Repository } from 'typeorm';
import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm'; import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm';
@ -27,7 +28,7 @@ import dayjs = require('dayjs');
import { OrderDetailRes } from '../dto/reponse.dto'; import { OrderDetailRes } from '../dto/reponse.dto';
import { OrderNote } from '../entity/order_note.entity'; import { OrderNote } from '../entity/order_note.entity';
import { User } from '../entity/user.entity'; import { User } from '../entity/user.entity';
import { WpSite } from '../interface'; import { SiteService } from './site.service';
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';
@ -35,8 +36,6 @@ import { OrderSaleOriginal } from '../entity/order_item_original.entity';
@Provide() @Provide()
export class OrderService { export class OrderService {
@Config('wpSite')
sites: WpSite[];
@Inject() @Inject()
wpService: WPService; wpService: WPService;
@ -101,6 +100,9 @@ export class OrderService {
@InjectEntityModel(Customer) @InjectEntityModel(Customer)
customerModel: Repository<Customer>; customerModel: Repository<Customer>;
@Inject()
siteService: SiteService;
async syncOrders(siteId: string) { async syncOrders(siteId: string) {
const orders = await this.wpService.getOrders(siteId); // 调用 WooCommerce API 获取订单 const orders = await this.wpService.getOrders(siteId); // 调用 WooCommerce API 获取订单
for (const order of orders) { for (const order of orders) {
@ -127,12 +129,9 @@ export class OrderService {
return return
} }
try { try {
const site = this.sites.find(v => v.id === siteId); const site = await this.siteService.get(siteId);
if (!site) { // 中文注释:将订单状态同步到 WooCommerce然后切换至下一状态
throw new Error(`更新订单信息,但失败,原因为 ${siteId} 的站点信息不存在`) await this.wpService.updateOrder(site, String(order.id), { status: order.status });
}
// 同步更新回 wordpress 的 order 状态
await this.wpService.updateOrder(site, order.id, { status: order.status });
order.status = this.orderAutoNextStatusMap[originStatus]; order.status = this.orderAutoNextStatusMap[originStatus];
} catch (error) { } catch (error) {
console.error('更新订单状态失败,原因为:', error) console.error('更新订单状态失败,原因为:', error)
@ -1262,7 +1261,7 @@ export class OrderService {
} }
async getOrderDetail(id: number): Promise<OrderDetailRes> { async getOrderDetail(id: number): Promise<OrderDetailRes> {
const order = await this.orderModel.findOne({ where: { id } }); const order = await this.orderModel.findOne({ where: { id } });
const site = this.sites.find(site => site.id === order.siteId); const site = await this.siteService.get(Number(order.siteId), true);
const items = await this.orderItemModel.find({ where: { orderId: id } }); const items = await this.orderItemModel.find({ where: { orderId: id } });
const sales = await this.orderSaleModel.find({ where: { orderId: id } }); const sales = await this.orderSaleModel.find({ where: { orderId: id } });
const refunds = await this.orderRefundModel.find({ const refunds = await this.orderRefundModel.find({
@ -1415,20 +1414,22 @@ export class OrderService {
]), ]),
}, },
}); });
return orders.map(order => { // 中文注释:批量获取订单涉及的站点名称,避免使用配置文件
return { 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.siteName]));
return orders.map(order => ({
externalOrderId: order.externalOrderId, externalOrderId: order.externalOrderId,
id: order.id, id: order.id,
siteName: siteName: siteMap.get(order.siteId) || '',
this.sites.find(site => site.id === order.siteId)?.siteName || '', }));
};
});
} }
async cancelOrder(id: number) { async cancelOrder(id: number) {
const order = await this.orderModel.findOne({ where: { id } }); const order = await this.orderModel.findOne({ where: { id } });
if (!order) throw new Error(`订单 ${id}不存在`); if (!order) throw new Error(`订单 ${id}不存在`);
const site = this.wpService.geSite(order.siteId); const s: any = await this.siteService.get(Number(order.siteId), true);
const site = { id: String(s.id), wpApiUrl: s.apiUrl, consumerKey: s.consumerKey, consumerSecret: s.consumerSecret, siteName: s.siteName, email: '', emailPswd: '' } as WpSite;
if (order.status !== OrderStatus.CANCEL) { if (order.status !== OrderStatus.CANCEL) {
await this.wpService.updateOrder(site, order.externalOrderId, { await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.CANCEL, status: OrderStatus.CANCEL,
@ -1442,7 +1443,7 @@ export class OrderService {
async refundOrder(id: number) { async refundOrder(id: number) {
const order = await this.orderModel.findOne({ where: { id } }); const order = await this.orderModel.findOne({ where: { id } });
if (!order) throw new Error(`订单 ${id}不存在`); if (!order) throw new Error(`订单 ${id}不存在`);
const site = this.wpService.geSite(order.siteId); const site = await this.siteService.get(Number(order.siteId), true);
if (order.status !== OrderStatus.REFUNDED) { if (order.status !== OrderStatus.REFUNDED) {
await this.wpService.updateOrder(site, order.externalOrderId, { await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.REFUNDED, status: OrderStatus.REFUNDED,
@ -1456,7 +1457,7 @@ export class OrderService {
async completedOrder(id: number) { async completedOrder(id: number) {
const order = await this.orderModel.findOne({ where: { id } }); const order = await this.orderModel.findOne({ where: { id } });
if (!order) throw new Error(`订单 ${id}不存在`); if (!order) throw new Error(`订单 ${id}不存在`);
const site = this.wpService.geSite(order.siteId); const site = await this.siteService.get(order.siteId);
if (order.status !== OrderStatus.COMPLETED) { if (order.status !== OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, { await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.COMPLETED, status: OrderStatus.COMPLETED,

View File

@ -1,16 +1,16 @@
import { Config, Provide } from '@midwayjs/core'; import { Inject, Provide } from '@midwayjs/core';
import axios, { AxiosRequestConfig } from 'axios'; import axios, { AxiosRequestConfig } from 'axios';
import WooCommerceRestApi, { WooCommerceRestApiVersion } from '@woocommerce/woocommerce-rest-api'; import WooCommerceRestApi, { WooCommerceRestApiVersion } from '@woocommerce/woocommerce-rest-api';
import { WpSite } from '../interface';
import { WpProduct } from '../entity/wp_product.entity'; import { WpProduct } from '../entity/wp_product.entity';
import { Variation } from '../entity/variation.entity'; import { Variation } from '../entity/variation.entity';
import { UpdateVariationDTO, UpdateWpProductDTO } from '../dto/wp_product.dto'; import { UpdateVariationDTO, UpdateWpProductDTO } from '../dto/wp_product.dto';
import { ProductStatus, ProductStockStatus } from '../enums/base.enum'; import { ProductStatus, ProductStockStatus } from '../enums/base.enum';
import { SiteService } from './site.service';
@Provide() @Provide()
export class WPService { export class WPService {
@Config('wpSite') @Inject()
sites: WpSite[]; private readonly siteService: SiteService;
/** /**
* URL / / * URL / /
@ -35,12 +35,12 @@ export class WPService {
* @param site * @param site
* @param namespace API wc/v3 wcs/v1 * @param namespace API wc/v3 wcs/v1
*/ */
private createApi(site: WpSite, namespace: WooCommerceRestApiVersion = 'wc/v3') { private createApi(site: any, namespace: WooCommerceRestApiVersion = 'wc/v3') {
const url = site.wpApiUrl ?? site.apiUrl; // 中文注释:兼容数据库 Site 与 WpSite 结构
return new WooCommerceRestApi({ return new WooCommerceRestApi({
url: site.wpApiUrl, url,
consumerKey: site.consumerKey, consumerKey: site.consumerKey,
consumerSecret: site.consumerSecret, consumerSecret: site.consumerSecret,
// SDK 的版本字段有联合类型限制,这里兼容插件命名空间(例如 wcs/v1
version: namespace, version: namespace,
}); });
} }
@ -78,20 +78,18 @@ export class WPService {
* @param consumerSecret WooCommerce * @param consumerSecret WooCommerce
*/ */
geSite(id: string): WpSite {
let idx = this.sites.findIndex(item => item.id === id);
return this.sites[idx];
}
async fetchData<T>( async fetchData<T>(
endpoint: string, endpoint: string,
site: WpSite, site: any,
param: Record<string, any> = {} param: Record<string, any> = {}
): Promise<T> { ): Promise<T> {
try { try {
const { wpApiUrl, consumerKey, consumerSecret } = site; const apiUrl = site.wpApiUrl ?? site.apiUrl;
const { consumerKey, consumerSecret } = site;
// 构建 URL规避多/或少/问题 // 构建 URL规避多/或少/问题
const url = this.buildURL(wpApiUrl, '/wp-json', endpoint); const url = this.buildURL(apiUrl, '/wp-json', endpoint);
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
'base64' 'base64'
); );
@ -111,22 +109,22 @@ export class WPService {
async fetchPagedData<T>( async fetchPagedData<T>(
endpoint: string, endpoint: string,
site: WpSite, site: any,
page: number = 1, page: number = 1,
perPage: number = 100 perPage: number = 100
): Promise<T[]> { ): Promise<T[]> {
const allData: T[] = []; const allData: T[] = [];
const { wpApiUrl, consumerKey, consumerSecret } = site; const { apiUrl, consumerKey, consumerSecret } = site;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
'base64' 'base64'
); );
console.log(`!!!wpApiUrl, consumerKey, consumerSecret, auth`,wpApiUrl, consumerKey, consumerSecret, auth) console.log(`!!!wpApiUrl, consumerKey, consumerSecret, auth`,site.apiUrl, consumerKey, consumerSecret, auth)
let hasMore = true; let hasMore = true;
while (hasMore) { while (hasMore) {
const config: AxiosRequestConfig = { const config: AxiosRequestConfig = {
method: 'GET', method: 'GET',
// 构建 URL规避多/或少/问题 // 构建 URL规避多/或少/问题
url: this.buildURL(wpApiUrl, '/wp-json', endpoint), url: this.buildURL(apiUrl, '/wp-json', endpoint),
headers: { headers: {
Authorization: `Basic ${auth}`, Authorization: `Basic ${auth}`,
}, },
@ -156,18 +154,18 @@ export class WPService {
return allData; return allData;
} }
async getProducts(site: WpSite): Promise<WpProduct[]> { async getProducts(site: any): Promise<WpProduct[]> {
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<WpProduct>(api, 'products'); return await this.sdkGetAll<WpProduct>(api, 'products');
} }
async getVariations(site: WpSite, productId: number): Promise<Variation[]> { async getVariations(site: any, productId: number): Promise<Variation[]> {
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<Variation>(api, `products/${productId}/variations`); return await this.sdkGetAll<Variation>(api, `products/${productId}/variations`);
} }
async getVariation( async getVariation(
site: WpSite, site: any,
productId: number, productId: number,
variationId: number variationId: number
): Promise<Variation> { ): Promise<Variation> {
@ -180,13 +178,13 @@ export class WPService {
siteId: string, siteId: string,
orderId: string orderId: string
): Promise<Record<string, any>> { ): Promise<Record<string, any>> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
const res = await api.get(`orders/${orderId}`); const res = await api.get(`orders/${orderId}`);
return res.data as Record<string, any>; return res.data as Record<string, any>;
} }
async getOrders(siteId: string): Promise<Record<string, any>[]> { async getOrders(siteId: string): Promise<Record<string, any>[]> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<Record<string, any>>(api, 'orders'); return await this.sdkGetAll<Record<string, any>>(api, 'orders');
} }
@ -197,7 +195,7 @@ export class WPService {
* *
*/ */
async getSubscriptions(siteId: string): Promise<Record<string, any>[]> { async getSubscriptions(siteId: string): Promise<Record<string, any>[]> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
// 优先使用 Subscriptions 命名空间 wcs/v1失败回退 wc/v3 // 优先使用 Subscriptions 命名空间 wcs/v1失败回退 wc/v3
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<Record<string, any>>(api, 'subscriptions'); return await this.sdkGetAll<Record<string, any>>(api, 'subscriptions');
@ -209,7 +207,7 @@ export class WPService {
orderId: string, orderId: string,
refundId: number refundId: number
): Promise<Record<string, any>> { ): Promise<Record<string, any>> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
const res = await api.get(`orders/${orderId}/refunds/${refundId}`); const res = await api.get(`orders/${orderId}/refunds/${refundId}`);
return res.data as Record<string, any>; return res.data as Record<string, any>;
@ -219,7 +217,7 @@ export class WPService {
siteId: string, siteId: string,
orderId: number orderId: number
): Promise<Record<string, any>[]> { ): Promise<Record<string, any>[]> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/refunds`); return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/refunds`);
} }
@ -229,7 +227,7 @@ export class WPService {
orderId: number, orderId: number,
noteId: number noteId: number
): Promise<Record<string, any>> { ): Promise<Record<string, any>> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
const res = await api.get(`orders/${orderId}/notes/${noteId}`); const res = await api.get(`orders/${orderId}/notes/${noteId}`);
return res.data as Record<string, any>; return res.data as Record<string, any>;
@ -239,24 +237,25 @@ export class WPService {
siteId: string, siteId: string,
orderId: number orderId: number
): Promise<Record<string, any>[]> { ): Promise<Record<string, any>[]> {
const site = this.geSite(siteId); const site = await this.siteService.get(siteId);
const api = this.createApi(site, 'wc/v3'); const api = this.createApi(site, 'wc/v3');
return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/notes`); return await this.sdkGetAll<Record<string, any>>(api, `orders/${orderId}/notes`);
} }
async updateData<T>( async updateData<T>(
endpoint: string, endpoint: string,
site: WpSite, site: any,
data: Record<string, any> data: Record<string, any>
): Promise<Boolean> { ): Promise<Boolean> {
const { wpApiUrl, consumerKey, consumerSecret } = site; const apiUrl = site.wpApiUrl ?? site.apiUrl;
const { consumerKey, consumerSecret } = site;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
'base64' 'base64'
); );
const config: AxiosRequestConfig = { const config: AxiosRequestConfig = {
method: 'PUT', method: 'PUT',
// 构建 URL规避多/或少/问题 // 构建 URL规避多/或少/问题
url: this.buildURL(wpApiUrl, '/wp-json', endpoint), url: this.buildURL(apiUrl, '/wp-json', endpoint),
headers: { headers: {
Authorization: `Basic ${auth}`, Authorization: `Basic ${auth}`,
}, },
@ -276,7 +275,7 @@ export class WPService {
* @param data * @param data
*/ */
async updateProduct( async updateProduct(
site: WpSite, site: any,
productId: string, productId: string,
data: UpdateWpProductDTO data: UpdateWpProductDTO
): Promise<Boolean> { ): Promise<Boolean> {
@ -295,7 +294,7 @@ export class WPService {
* @param stock_status * @param stock_status
*/ */
async updateProductStatus( async updateProductStatus(
site: WpSite, site: any,
productId: string, productId: string,
status: ProductStatus, status: ProductStatus,
stock_status: ProductStockStatus stock_status: ProductStockStatus
@ -315,7 +314,7 @@ export class WPService {
* @param data * @param data
*/ */
async updateVariation( async updateVariation(
site: WpSite, site: any,
productId: string, productId: string,
variationId: string, variationId: string,
data: UpdateVariationDTO data: UpdateVariationDTO
@ -336,7 +335,7 @@ export class WPService {
* Order * Order
*/ */
async updateOrder( async updateOrder(
site: WpSite, site: any,
orderId: string, orderId: string,
data: Record<string, any> data: Record<string, any>
): Promise<Boolean> { ): Promise<Boolean> {
@ -344,11 +343,12 @@ export class WPService {
} }
async createShipment( async createShipment(
site: WpSite, site: any,
orderId: string, orderId: string,
data: Record<string, any> data: Record<string, any>
) { ) {
const { wpApiUrl, consumerKey, consumerSecret } = site; const apiUrl = site.wpApiUrl ?? site.apiUrl;
const { consumerKey, consumerSecret } = site;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
'base64' 'base64'
); );
@ -356,7 +356,7 @@ export class WPService {
method: 'POST', method: 'POST',
// 构建 URL规避多/或少/问题 // 构建 URL规避多/或少/问题
url: this.buildURL( url: this.buildURL(
wpApiUrl, apiUrl,
'/wp-json', '/wp-json',
'wc-ast/v3/orders', 'wc-ast/v3/orders',
orderId, orderId,
@ -371,11 +371,12 @@ export class WPService {
} }
async deleteShipment( async deleteShipment(
site: WpSite, site: any,
orderId: string, orderId: string,
trackingId: string, trackingId: string,
): Promise<Boolean> { ): Promise<Boolean> {
const { wpApiUrl, consumerKey, consumerSecret } = site; const apiUrl = site.wpApiUrl ?? site.apiUrl;
const { consumerKey, consumerSecret } = site;
const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString(
'base64' 'base64'
); );
@ -386,7 +387,7 @@ export class WPService {
method: 'DELETE', method: 'DELETE',
// 构建 URL规避多/或少/问题 // 构建 URL规避多/或少/问题
url: this.buildURL( url: this.buildURL(
wpApiUrl, apiUrl,
'/wp-json', '/wp-json',
'wc-ast/v3/orders', 'wc-ast/v3/orders',
orderId, orderId,

View File

@ -1,5 +1,5 @@
import { Product } from '../entity/product.entity'; import { Product } from '../entity/product.entity';
import { Config, Inject, Provide } from '@midwayjs/core'; import { Inject, Provide } from '@midwayjs/core';
import { WPService } from './wp.service'; import { WPService } from './wp.service';
import { WpSite } from '../interface'; import { WpSite } from '../interface';
import { WpProduct } from '../entity/wp_product.entity'; import { WpProduct } from '../entity/wp_product.entity';
@ -12,28 +12,38 @@ import {
UpdateWpProductDTO, UpdateWpProductDTO,
} from '../dto/wp_product.dto'; } from '../dto/wp_product.dto';
import { ProductStatus, ProductStockStatus } from '../enums/base.enum'; import { ProductStatus, ProductStockStatus } from '../enums/base.enum';
import { SiteService } from './site.service';
@Provide() @Provide()
export class WpProductService { export class WpProductService {
@Config('wpSite') // 中文注释:移除配置中的站点数组,统一从数据库获取站点信息
sites: WpSite[];
@Inject() @Inject()
private readonly wpApiService: WPService; private readonly wpApiService: WPService;
@Inject()
private readonly siteService: SiteService;
@InjectEntityModel(WpProduct) @InjectEntityModel(WpProduct)
wpProductModel: Repository<WpProduct>; wpProductModel: Repository<WpProduct>;
@InjectEntityModel(Variation) @InjectEntityModel(Variation)
variationModel: Repository<Variation>; variationModel: Repository<Variation>;
getSite(id: string): WpSite {
let idx = this.sites.findIndex(item => item.id === id);
return this.sites[idx];
}
async syncAllSites() { async syncAllSites() {
for (const site of this.sites) { // 中文注释:从数据库获取所有启用的站点,并逐站点同步产品与变体
const { items } = await this.siteService.list({ current: 1, pageSize: 1000, isDisabled: false }, true);
for (const s of items as any[]) {
const site: WpSite = {
id: String(s.id),
wpApiUrl: s.apiUrl,
consumerKey: s.consumerKey,
consumerSecret: s.consumerSecret,
siteName: s.siteName,
email: '',
emailPswd: '',
};
const products = await this.wpApiService.getProducts(site); const products = await this.wpApiService.getProducts(site);
for (const product of products) { for (const product of products) {
const variations = const variations =
@ -46,7 +56,8 @@ export class WpProductService {
} }
async syncSite(siteId: string) { async syncSite(siteId: string) {
const site = this.getSite(siteId); // 中文注释:通过数据库获取站点并转换为 WpSite用于后续 WooCommerce 同步
const site = await this.siteService.get(Number(siteId), true);
const externalProductIds = this.wpProductModel.createQueryBuilder('wp_product') const externalProductIds = this.wpProductModel.createQueryBuilder('wp_product')
.select([ .select([
'wp_product.id ', 'wp_product.id ',
@ -91,7 +102,7 @@ const excludeValues = [];
// 控制产品上下架 // 控制产品上下架
async updateProductStatus(id: number, status: ProductStatus, stock_status: ProductStockStatus) { async updateProductStatus(id: number, status: ProductStatus, stock_status: ProductStockStatus) {
const wpProduct = await this.wpProductModel.findOneBy({ id }); const wpProduct = await this.wpProductModel.findOneBy({ id });
const site = await this.getSite(wpProduct.siteId); const site = await this.siteService.get(Number(wpProduct.siteId), true);
wpProduct.status = status; wpProduct.status = status;
wpProduct.stockStatus = stock_status; wpProduct.stockStatus = stock_status;
const res = await this.wpApiService.updateProductStatus(site, wpProduct.externalProductId, status, stock_status); const res = await this.wpApiService.updateProductStatus(site, wpProduct.externalProductId, status, stock_status);