diff --git a/src/controller/webhook.controller.ts b/src/controller/webhook.controller.ts index f73c61e..02bdb38 100644 --- a/src/controller/webhook.controller.ts +++ b/src/controller/webhook.controller.ts @@ -52,7 +52,7 @@ export class WebhookController { // 中文注释:从数据库获取站点配置 const site = await this.siteService.get(Number(siteId), true); - if (!site || !source.includes(site.wpApiUrl)) { + if (!site || !source.includes(site.apiUrl)) { console.log('domain not match'); return { code: HttpStatus.BAD_REQUEST, @@ -97,13 +97,13 @@ export class WebhookController { ? await this.wpApiService.getVariations(site, body.id) : []; await this.wpProductService.syncProductAndVariations( - site.id, + String(site.id), body, variations ); break; case 'product.deleted': - await this.wpProductService.delWpProduct(site.id, body.id); + await this.wpProductService.delWpProduct(String(site.id), body.id); break; case 'order.created': case 'order.updated': diff --git a/src/service/order.service.ts b/src/service/order.service.ts index a5c906c..91848c6 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -1351,7 +1351,8 @@ export class OrderService { return { ...order, siteName: site?.siteName, - email: site?.email, + // 中文注释:Site 实体无邮箱字段,这里返回空字符串保持兼容 + email: '', items, sales, refundItems, diff --git a/src/service/site.service.ts b/src/service/site.service.ts index 157ddf1..c5f176b 100644 --- a/src/service/site.service.ts +++ b/src/service/site.service.ts @@ -12,32 +12,38 @@ export class SiteService { siteModel: Repository; async syncFromConfig(sites: WpSite[] = []) { - for (const s of sites) { - const exist = await this.siteModel.findOne({ where: { siteName: s.siteName } }); + // 将配置中的 WpSite 同步到数据库 Site 表(用于一次性导入或初始化) + for (const siteConfig of sites) { + // 按站点名称查询是否已存在记录 + const exist = await this.siteModel.findOne({ where: { siteName: siteConfig.siteName } }); + // 将 WpSite 字段映射为 Site 实体字段 const payload: Partial = { - siteName: s.siteName, - apiUrl: (s as any).wpApiUrl, - consumerKey: (s as any).consumerKey, - consumerSecret: (s as any).consumerSecret, + siteName: siteConfig.siteName, + apiUrl: (siteConfig as any).wpApiUrl, + consumerKey: (siteConfig as any).consumerKey, + consumerSecret: (siteConfig as any).consumerSecret, type: 'woocommerce', }; + // 存在则更新,不存在则插入新记录 if (exist) await this.siteModel.update({ id: exist.id }, payload); else await this.siteModel.insert(payload as Site); } } async create(data: Partial) { + // 创建新的站点记录 await this.siteModel.insert(data as Site); return true; } async update(id: string | number, data: UpdateSiteDTO) { + // 更新指定站点记录,将布尔 isDisabled 转换为数值 0/1 const payload: Partial = { ...data, isDisabled: - data.isDisabled === undefined + data.isDisabled === undefined // 未传入则不更新该字段 ? undefined - : data.isDisabled + : data.isDisabled // true -> 1, false -> 0 ? 1 : 0, } as any; @@ -46,19 +52,25 @@ export class SiteService { } async get(id: string | number, includeSecret = false) { - const s = await this.siteModel.findOne({ where: { id: Number(id) } }); - if (!s) return null; - if (includeSecret) return s; - const { consumerKey, consumerSecret, emailPswd, ...rest } = s as any; + // 根据主键获取站点;includeSecret 为 true 时返回密钥字段 + const site = await this.siteModel.findOne({ where: { id: Number(id) } }); + if (!site) return null; + if (includeSecret) return site; + // 默认不返回密钥,进行字段脱敏 + const { consumerKey, consumerSecret, ...rest } = site; return rest; } async list(param: { current?: number; pageSize?: number; keyword?: string; isDisabled?: boolean; ids?: string }, includeSecret = false) { + // 分页查询站点列表,支持关键字、禁用状态与 ID 列表过滤 const { current = 1, pageSize = 10, keyword, isDisabled, ids } = (param || {}) as any; const where: any = {}; + // 按名称模糊查询 if (keyword) where.siteName = Like(`%${keyword}%`); + // 按禁用状态过滤(布尔转数值) if (typeof isDisabled === 'boolean') where.isDisabled = isDisabled ? 1 : 0; if (ids) { + // 解析逗号分隔的 ID 字符串为数字数组,并过滤非法值 const numIds = String(ids) .split(',') .filter(Boolean) @@ -66,15 +78,18 @@ export class SiteService { .filter((v) => !Number.isNaN(v)); if (numIds.length > 0) where.id = In(numIds); } + // 进行分页查询(skip/take)并返回总条数 const [items, total] = await this.siteModel.findAndCount({ where, skip: (current - 1) * pageSize, take: pageSize }); - const data = includeSecret ? items : items.map((s: any) => { - const { consumerKey, consumerSecret, ...rest } = s; + // 根据 includeSecret 决定是否脱敏返回密钥字段 + const data = includeSecret ? items : items.map((item: any) => { + const { consumerKey, consumerSecret, ...rest } = item; return rest; }); return { items: data, total, current, pageSize }; } async disable(id: string | number, disabled: boolean) { + // 设置站点禁用状态(true -> 1, false -> 0) await this.siteModel.update({ id: Number(id) }, { isDisabled: disabled ? 1 : 0 }); return true; } diff --git a/src/service/wp.service.ts b/src/service/wp.service.ts index 110ff84..443018f 100644 --- a/src/service/wp.service.ts +++ b/src/service/wp.service.ts @@ -51,15 +51,19 @@ export class WPService { const page = params.page ?? 1; const per_page = params.per_page ?? 100; const res = await api.get(resource.replace(/^\/+/, ''), { ...params, page, per_page }); + if (res?.headers?.['content-type']?.includes('text/html')) { + throw new Error('接口返回了 text/html,可能为 WordPress 登录页或错误页,请检查站点配置或权限'); + } const data = res.data as T[]; const totalPages = Number(res.headers?.['x-wp-totalpages'] ?? 1); - return { items: data, totalPages, page, per_page }; + const total = Number(res.headers?.['x-wp-total']?? 1) + return { items: data, total, totalPages, page, per_page }; } /** * 通过 SDK 聚合分页数据,返回全部数据 */ - private async sdkGetAll(api: any, resource: string, params: Record = {}, maxPages: number = 50): Promise { + private async sdkGetAll(api: WooCommerceRestApi, resource: string, params: Record = {}, maxPages: number = 50): Promise { const result: T[] = []; for (let page = 1; page <= maxPages; page++) { const { items, totalPages } = await this.sdkGetPage(api, resource, { ...params, page }); diff --git a/src/service/wp_product.service.ts b/src/service/wp_product.service.ts index ddc214a..0e641e2 100644 --- a/src/service/wp_product.service.ts +++ b/src/service/wp_product.service.ts @@ -1,7 +1,6 @@ import { Product } from '../entity/product.entity'; import { Inject, Provide } from '@midwayjs/core'; import { WPService } from './wp.service'; -import { WpSite } from '../interface'; import { WpProduct } from '../entity/wp_product.entity'; import { InjectEntityModel } from '@midwayjs/typeorm'; import { And, Like, Not, Repository } from 'typeorm'; @@ -33,24 +32,15 @@ export class WpProductService { async syncAllSites() { // 中文注释:从数据库获取所有启用的站点,并逐站点同步产品与变体 - const { items } = await this.siteService.list({ current: 1, pageSize: Infinity, 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 { items: sites } = await this.siteService.list({ current: 1, pageSize: Infinity, isDisabled: false }, true); + for (const site of sites) { const products = await this.wpApiService.getProducts(site); for (const product of products) { const variations = product.type === 'variable' ? await this.wpApiService.getVariations(site, product.id) : []; - await this.syncProductAndVariations(site.id, product, variations); + await this.syncProductAndVariations(String(site.id), product, variations); } } } @@ -80,7 +70,7 @@ export class WpProductService { ? await this.wpApiService.getVariations(site, product.id) : []; - await this.syncProductAndVariations(site.id, product, variations); + await this.syncProductAndVariations(String(site.id), product, variations); } const filteredIds = externalIds.filter(id => !excludeValues.includes(id));