From 0dac006116a9b26112b9e9afe6bf69abf8bf0123 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Tue, 27 Jan 2026 10:29:50 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(=E4=BA=A7=E5=93=81=E6=9C=8D=E5=8A=A1):?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E4=BA=A7=E5=93=81=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=B9=B6=E6=B7=BB=E5=8A=A0=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构 getProductBySiteSku 方法以支持更灵活的查询条件 在 site-product 实体中添加 price 字段 新增 site-product 控制器和服务用于管理站点商品 修改订单服务以支持站点参数传递 --- src/service/order.service.ts | 21 +++++++++++++------- src/service/product.service.ts | 35 +++++++++++++--------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/service/order.service.ts b/src/service/order.service.ts index fb3baf1..94afb97 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -733,14 +733,21 @@ export class OrderService { if (!orderItem.sku) return; // 从数据库查询产品,关联查询组件 - const componentDetails = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name }, site); - if(!componentDetails?.length){ - return - } + const productDetail = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name },site); - const orderSales: OrderSale[] = componentDetails.map(({product, parentProduct, quantity}) => { - if (!product) return null - console.log('product',product) + if (!productDetail || !productDetail.quantity) return; + const { product, quantity } = productDetail + const componentDetails: { product: Product, quantity: number }[] = product.components?.length > 0 ? await Promise.all(product.components.map(async comp => { + return { + product: await this.productModel.findOne({ + where: { id: comp.productId }, + }), + quantity: comp.quantity * orderItem.quantity, + } + })) : [{ product, quantity }] + + const orderSales: OrderSale[] = componentDetails.map(componentDetail => { + if (!componentDetail.product) return null const attrsObj = this.productService.getAttributesObject(product.attributes) const orderSale = plainToClass(OrderSale, { orderId: orderItem.orderId, diff --git a/src/service/product.service.ts b/src/service/product.service.ts index 7c14d37..830c7c2 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -1785,33 +1785,24 @@ export class ProductService { attributes: attributes.length > 0 ? attributes : undefined, } } - // 获取库存单品列表 - async getComponentDetailFromSiteSku(siteProduct: { sku: string, name?: string }, site: Site): Promise<{ product: Product,parentProduct?: Product, quantity: number }[]> { + isMixedSku(sku: string) { + const splitSKu = sku.split('-') + const last = splitSKu[splitSKu.length - 1] + const second = splitSKu[splitSKu.length - 2] + // 这里判断 second 是否是数字 + return sku.includes('-MX-') || sku.includes('-Mixed-') || /^\d+$/.test(second) && /^\d+$/.test(last) + } + async getComponentDetailFromSiteSku(siteProduct: { sku: string, name: string }, site: Site) { if (!siteProduct.sku) { throw new Error('siteSku 不能为空') } - const product = await this.getProductBySiteSku(siteProduct.sku, site) + let product = await this.getProductBySiteSku(siteProduct.sku, site) - if (!product) return - - if(!product?.components?.length){ - return [{ - product, - quantity:1 - }] + return { + product, + quantity: 1, } - - return await Promise.all(product.components.map(async comp => { - return { - product: await this.productModel.findOne({ - where: { sku: comp.sku }, - relations: ['category', 'attributes', 'attributes.dict', 'components'] - }), - parentProduct: product, // 这里得记录一下他的爸爸用来记录 - quantity: comp.quantity, - } - })) } // 准备创建产品的 DTO, 处理类型转换和默认值 @@ -2150,7 +2141,7 @@ export class ProductService { .leftJoinAndSelect('product.components', 'components') .leftJoinAndSelect('product.siteSkus', 'siteSku') .where('siteSku.sku LIKE :siteSku', { siteSku: `%${siteSku}%` }) - .orWhere('product.sku = :siteSku', { siteSku }) + .orWhere('product.sku = :siteSku', { siteSku }); if (site) { queryBuilder.orWhere('product.sku = :processedSku', { -- 2.40.1 From 12ebad65701d01b63ee905c00ecd32997700105f Mon Sep 17 00:00:00 2001 From: tikkhun Date: Tue, 27 Jan 2026 18:42:00 +0800 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E4=B8=AD=E4=BA=A7=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加对productDetail.product的检查,避免在product为undefined时访问components属性 --- src/service/order.service.ts | 3 ++- src/service/product.service.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 94afb97..d78121d 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -735,8 +735,9 @@ export class OrderService { // 从数据库查询产品,关联查询组件 const productDetail = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name },site); - if (!productDetail || !productDetail.quantity) return; + if (!productDetail || !productDetail.product || !productDetail.quantity) return; const { product, quantity } = productDetail + const componentDetails: { product: Product, quantity: number }[] = product.components?.length > 0 ? await Promise.all(product.components.map(async comp => { return { product: await this.productModel.findOne({ diff --git a/src/service/product.service.ts b/src/service/product.service.ts index 830c7c2..f670e5d 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -1798,7 +1798,7 @@ export class ProductService { } let product = await this.getProductBySiteSku(siteProduct.sku, site) - + return { product, quantity: 1, -- 2.40.1 From fe30fabf085056b6d7306f854032b21724b35271 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Tue, 27 Jan 2026 18:53:54 +0800 Subject: [PATCH 3/8] =?UTF-8?q?refactor(product):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BB=84=E4=BB=B6=E8=AF=A6=E6=83=85=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=B9=B6=E6=94=AF=E6=8C=81=E6=95=B0=E9=87=8F=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将获取组件详情的逻辑从order.service.ts移到product.service.ts中统一处理 新增quantity参数支持组件数量计算 返回结果中增加parentProduct信息用于追踪父产品 --- src/service/order.service.ts | 21 ++++++--------------- src/service/product.service.ts | 26 ++++++++++++++++++++------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/service/order.service.ts b/src/service/order.service.ts index d78121d..8a92c56 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -733,22 +733,13 @@ export class OrderService { if (!orderItem.sku) return; // 从数据库查询产品,关联查询组件 - const productDetail = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name },site); + const componentDetails = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name },orderItem.quantity,site); + if(!componentDetails?.length){ + return + } - if (!productDetail || !productDetail.product || !productDetail.quantity) return; - const { product, quantity } = productDetail - - const componentDetails: { product: Product, quantity: number }[] = product.components?.length > 0 ? await Promise.all(product.components.map(async comp => { - return { - product: await this.productModel.findOne({ - where: { id: comp.productId }, - }), - quantity: comp.quantity * orderItem.quantity, - } - })) : [{ product, quantity }] - - const orderSales: OrderSale[] = componentDetails.map(componentDetail => { - if (!componentDetail.product) return null + const orderSales: OrderSale[] = componentDetails.map(({product, parentProduct, quantity}) => { + if (!product) return null const attrsObj = this.productService.getAttributesObject(product.attributes) const orderSale = plainToClass(OrderSale, { orderId: orderItem.orderId, diff --git a/src/service/product.service.ts b/src/service/product.service.ts index f670e5d..581485b 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -1792,17 +1792,31 @@ export class ProductService { // 这里判断 second 是否是数字 return sku.includes('-MX-') || sku.includes('-Mixed-') || /^\d+$/.test(second) && /^\d+$/.test(last) } - async getComponentDetailFromSiteSku(siteProduct: { sku: string, name: string }, site: Site) { + async getComponentDetailFromSiteSku(siteProduct: { sku: string, name: string }, quantity: number = 1, site: Site): Promise<{ product: Product,parentProduct?: Product, quantity: number }[]> { if (!siteProduct.sku) { throw new Error('siteSku 不能为空') } - let product = await this.getProductBySiteSku(siteProduct.sku, site) - - return { - product, - quantity: 1, + const product = await this.getProductBySiteSku(siteProduct.sku, site) + + if (!product) return + + if(!product?.components?.length){ + return [{ + product, + quantity + }] } + + return await Promise.all(product.components.map(async comp => { + return { + product: await this.productModel.findOne({ + where: { id: comp.productId }, + }), + parentProduct: product, // 这里得记录一下他的爸爸用来记录 + quantity: comp.quantity * quantity, + } + })) } // 准备创建产品的 DTO, 处理类型转换和默认值 -- 2.40.1 From 5fa5ed21b0e3d44a6f419a19d3ad5bf6531fb090 Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Wed, 28 Jan 2026 17:37:39 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat(service):=20=E6=96=B0=E5=A2=9EWintopay?= =?UTF-8?q?=E7=89=A9=E6=B5=81=E6=9C=8D=E5=8A=A1=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=AF=BC=E5=87=BA=E5=92=8C=E7=89=A9=E6=B5=81?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增Wintopay物流服务接口,支持物流信息更新功能 优化订单导出功能,增加动态商品列显示 简化物流服务状态判断逻辑,修复运单号生成问题 --- src/service/Wintopay.service.ts | 96 ++++++++++++++++++++++++++++++++ src/service/logistics.service.ts | 33 ++++++----- src/service/order.service.ts | 34 ++++++++--- 3 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 src/service/Wintopay.service.ts diff --git a/src/service/Wintopay.service.ts b/src/service/Wintopay.service.ts new file mode 100644 index 0000000..eaef0c7 --- /dev/null +++ b/src/service/Wintopay.service.ts @@ -0,0 +1,96 @@ +import { Inject, Provide } from '@midwayjs/core'; +import axios from 'axios'; +import dayjs = require('dayjs'); +import utc = require('dayjs/plugin/utc'); +import timezone = require('dayjs/plugin/timezone'); + +// 扩展dayjs功能 +dayjs.extend(utc); +dayjs.extend(timezone); + + +// Wintopay 物流更新请求接口 +interface LogisticsUpdateRequest { + trade_id: string; // 订单的流水号 + track_number: string; // 物流单号 + track_brand: string; // 物流公司编号 +} + +// Wintopay 物流更新响应接口 +interface LogisticsUpdateResponse { + code: string; + message: string; + data: { + trade_id: string; + track_brand: string; + track_number: string; + time: number; + }; + error: any; + request_id: string; +} + +@Provide() +export class WintopayService { + @Inject() logger; + + // 默认配置 + private config = { + //测试环境配置,在生产环境记得换掉 + apiBaseUrl: 'https://stage-merchant-api.wintopay.com', + Authorization: 'Bearer kV8w1er8dFw9p9g2kb0mer398hD8hfWk', + }; + + // 发送请求 + private async sendRequest(url: string, data: any): Promise { + try { + const headers = { + 'Content-Type': 'application/json', + 'Authorization': this.config.Authorization, + }; + + // 发送请求 - 临时禁用SSL证书验证以解决UNABLE_TO_VERIFY_LEAF_SIGNATURE错误 + const response = await axios.post( + `${this.config.apiBaseUrl}${url}`, + data, + { + headers, + httpsAgent: new (require('https').Agent)({ + rejectUnauthorized: false + }) + } + ); + + return response.data; + } catch (error) { + this.logger.error('Wintopay API请求失败:', error); + throw error; + } + } + + + /** + * 更新订单物流信息 + * @param params 物流更新参数 + * @returns 物流更新响应 + */ + async logisticsUpdate(params: LogisticsUpdateRequest): Promise { + try { + this.logger.info('开始更新物流信息:', params); + + const response = await this.sendRequest('/v1/logistics/update', params); + + this.logger.info('物流更新成功:', response); + return response; + } catch (error: any) { + this.logger.error('物流更新失败:', error); + + // 处理API返回的错误 + if (error.response?.data) { + throw new Error(`物流更新失败: ${error.response.data.message || '未知错误'}`); + } + + throw new Error(`物流更新请求失败: ${error.message || '网络错误'}`); + } + } +} \ No newline at end of file diff --git a/src/service/logistics.service.ts b/src/service/logistics.service.ts index 2a58e59..b43f3d0 100644 --- a/src/service/logistics.service.ts +++ b/src/service/logistics.service.ts @@ -327,17 +327,20 @@ export class LogisticsService { let resShipmentFee: any; if (data.shipmentPlatform === 'uniuni') { resShipmentFee = await this.uniExpressService.getRates(reqBody); + if (resShipmentFee.status !== 'SUCCESS') { + throw new Error(resShipmentFee.ret_msg); + } + return resShipmentFee.data.totalAfterTax * 100; } else if (data.shipmentPlatform === 'freightwaves') { const fre_reqBody = await this.convertToFreightwavesRateTry(data); resShipmentFee = await this.freightwavesService.rateTry(fre_reqBody); + return resShipmentFee.totalAmount * 100; } else { throw new Error('不支持的运单平台'); } - if (resShipmentFee.status !== 'SUCCESS') { - throw new Error(resShipmentFee.ret_msg); - } - return resShipmentFee.data.totalAfterTax * 100; + + } catch (e) { throw e; } @@ -360,12 +363,7 @@ export class LogisticsService { try { resShipmentOrder = await this.mepShipment(data, order); - // 记录物流信息,并将订单状态转到完成,uniuni状态为SUCCESS,tms.freightwaves状态为00000200 - if (resShipmentOrder.status === 'SUCCESS' || resShipmentOrder.code === '00000200') { - order.orderStatus = ErpOrderStatus.COMPLETED; - } else { - throw new Error('运单生成失败'); - } + order.orderStatus = ErpOrderStatus.COMPLETED; const dataSource = this.dataSourceManager.getDataSource('default'); let transactionError = undefined; let shipmentId = undefined; @@ -384,8 +382,8 @@ export class LogisticsService { unique_id = resShipmentOrder.data.uni_order_sn; state = resShipmentOrder.data.uni_status_code; } else { - co = resShipmentOrder.data?.shipOrderId; - unique_id = resShipmentOrder.data?.shipOrderId; + co = resShipmentOrder.shipOrderId; + unique_id = resShipmentOrder.shipOrderId; state = ErpOrderStatus.COMPLETED; } @@ -728,6 +726,11 @@ export class LogisticsService { }; // 添加运单 resShipmentOrder = await this.uniExpressService.createShipment(reqBody); + + // 记录物流信息,并将订单状态转到完成,uniuni状态为SUCCESS,tms.freightwaves状态为00000200 + if (resShipmentOrder.status !== 'SUCCESS') { + throw new Error('运单生成失败'); + } } if (data.shipmentPlatform === 'freightwaves') { @@ -735,7 +738,7 @@ export class LogisticsService { const reqBody: any = { // shipCompany: 'UPSYYZ7000NEW', shipCompany: data.courierCompany || "", - partnerOrderNumber: order.siteId + '-1-' + order.externalOrderId, + partnerOrderNumber: order.siteId + '-' + order.externalOrderId, warehouseId: '25072621030107400060', shipper: { name: data.details.origin.contact_name, // 姓名 @@ -811,8 +814,8 @@ export class LogisticsService { return resShipmentOrder; } catch (error) { // 处理错误,例如记录日志或抛出异常 - throw new Error(`物流订单处理失败: ${error}`); - + throw new Error(`物流订单处理失败: ${error}`); + } } diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 8a92c56..cf09271 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -2454,18 +2454,18 @@ export class OrderService { */ // TODO async exportOrder(ids: number[]) { - // 日期 订单号 姓名地址 邮箱 号码 订单内容 盒数 换盒数 换货内容 快递号 + // 日期 订单号 姓名地址 邮箱 号码 盒数 换盒数 换货内容 快递号 商品1 数量1 商品2 数量2... interface ExportData { '日期': string; '订单号': string; '姓名地址': string; '邮箱': string; '号码': string; - '订单内容': string; '盒数': number; '换盒数': number; '换货内容': string; '快递号': string; + [key: string]: any; // 支持动态添加的商品和数量列 } try { @@ -2512,6 +2512,15 @@ export class OrderService { return acc; }, {} as Record); + // 计算最大商品数量 + let maxItemsCount = 0; + orders.forEach(order => { + const items = orderItemsByOrderId[order.id] || []; + if (items.length > maxItemsCount) { + maxItemsCount = items.length; + } + }); + // 构建导出数据 const exportDataList: ExportData[] = orders.map(order => { // 获取订单的订单项 @@ -2520,9 +2529,6 @@ export class OrderService { // 计算总盒数 const boxCount = items.reduce((total, item) => total + item.quantity, 0); - // 构建订单内容 - const orderContent = items.map(item => `${item.name} x ${item.quantity}`).join('; '); - // 构建姓名地址 const shipping = order.shipping; const billing = order.billing; @@ -2547,18 +2553,32 @@ export class OrderService { const exchangeBoxCount = 0; const exchangeContent = ''; - return { + // 构建基础数据对象 + const baseData: ExportData = { '日期': order.date_created?.toISOString().split('T')[0] || '', '订单号': order.externalOrderId || '', '姓名地址': nameAddress, '邮箱': order.customer_email || '', '号码': phone, - '订单内容': orderContent, '盒数': boxCount, '换盒数': exchangeBoxCount, '换货内容': exchangeContent, '快递号': trackingNumber }; + + // 添加商品和数量列 + items.forEach((item, index) => { + baseData[`商品${index + 1}`] = item.name; + baseData[`数量${index + 1}`] = item.quantity; + }); + + // 填充空值,确保所有行的列数一致 + for (let i = items.length; i < maxItemsCount; i++) { + baseData[`商品${i + 1}`] = ''; + baseData[`数量${i + 1}`] = ''; + } + + return baseData; }); // 返回CSV字符串内容给前端 -- 2.40.1 From 5488e1b7c6651586b9c6170a8dc41c3bbdd048bd Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Wed, 28 Jan 2026 19:36:51 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix(logistics):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=B4=A7=E8=BF=90=E5=B9=B3=E5=8F=B0courierCompany=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当courierCompany为"最优物流"时设置为空字符串,否则使用原值 --- src/service/logistics.service.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/service/logistics.service.ts b/src/service/logistics.service.ts index b43f3d0..a0c158a 100644 --- a/src/service/logistics.service.ts +++ b/src/service/logistics.service.ts @@ -734,10 +734,15 @@ export class LogisticsService { } if (data.shipmentPlatform === 'freightwaves') { + + let courierCompany: string = ""; + if (data.courierCompany != "最优物流") { + courierCompany = data.courierCompany; + } // 根据TMS系统对接说明文档格式化参数 const reqBody: any = { // shipCompany: 'UPSYYZ7000NEW', - shipCompany: data.courierCompany || "", + shipCompany: courierCompany, partnerOrderNumber: order.siteId + '-' + order.externalOrderId, warehouseId: '25072621030107400060', shipper: { @@ -831,12 +836,15 @@ export class LogisticsService { id: data.address_id, }, }) - const address = shipments?.address; + let courierCompany: string = ""; + if (data.courierCompany != "最优物流") { + courierCompany = data.courierCompany; + } // 转换为RateTryRequest格式 const r = { //shipCompany: 'UPSYYZ7000NEW', // 必填,但ShipmentFeeBookDTO中缺少 - shipCompany: data.courierCompany || "", + shipCompany: courierCompany, partnerOrderNumber: `order-${Date.now()}`, // 必填,使用时间戳生成 warehouseId: '25072621030107400060', // 可选,使用stockPointId转换 shipper: { -- 2.40.1 From 4bde69862533eabd8ab58cd78b80b05370794ccb Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Fri, 30 Jan 2026 14:24:33 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat(=E7=89=A9=E6=B5=81):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=89=A9=E6=B5=81=E5=88=AB=E5=90=8D=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E7=89=A9=E6=B5=81?= =?UTF-8?q?=E5=85=AC=E5=8F=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 logistics_alias 实体用于存储物流公司别名映射 - 修改 ShipmentBookDTO 中 courierCompany 的校验规则为 any 类型 - 在订单服务中实现物流别名查询和映射功能 - 移除物流服务中 courierCompany 的特殊处理逻辑 --- src/config/config.default.ts | 2 + src/dto/logistics.dto.ts | 2 +- src/entity/logistics_alias.emtity.ts | 34 ++++ src/service/logistics.service.ts | 13 +- src/service/order.service.ts | 225 ++++++++++++++------------- 5 files changed, 159 insertions(+), 117 deletions(-) create mode 100644 src/entity/logistics_alias.emtity.ts diff --git a/src/config/config.default.ts b/src/config/config.default.ts index f826242..ac5f89a 100644 --- a/src/config/config.default.ts +++ b/src/config/config.default.ts @@ -42,6 +42,7 @@ import DictSeeder from '../db/seeds/dict.seeder'; import CategorySeeder from '../db/seeds/category.seeder'; import CategoryAttributeSeeder from '../db/seeds/category_attribute.seeder'; import { SiteSku } from '../entity/site-sku.entity'; +import { logisticsAlias } from '../entity/logistics_alias.emtity'; export default { // use for cookie sign key, should change to your own and keep security @@ -88,6 +89,7 @@ export default { Area, CategoryAttribute, Category, + logisticsAlias, ], synchronize: true, logging: false, diff --git a/src/dto/logistics.dto.ts b/src/dto/logistics.dto.ts index 48841db..f0bd6ba 100644 --- a/src/dto/logistics.dto.ts +++ b/src/dto/logistics.dto.ts @@ -25,7 +25,7 @@ export class ShipmentBookDTO { shipmentPlatform: string; @ApiProperty() - @Rule(RuleType.string()) + @Rule(RuleType.any()) courierCompany: string; } diff --git a/src/entity/logistics_alias.emtity.ts b/src/entity/logistics_alias.emtity.ts new file mode 100644 index 0000000..3a217d6 --- /dev/null +++ b/src/entity/logistics_alias.emtity.ts @@ -0,0 +1,34 @@ + +import { ApiProperty } from '@midwayjs/swagger'; +import { Entity,CreateDateColumn,UpdateDateColumn, PrimaryGeneratedColumn, Column } from 'typeorm'; + +@Entity('logistics_alias') +export class logisticsAlias { + @PrimaryGeneratedColumn() + id: number; + + @ApiProperty({ type: 'string' }) + @Column() + logistics_company:string + + @ApiProperty({ type: 'string' }) + @Column() + logistics_alias:string + + @ApiProperty({ type: 'string' }) + @Column() + platform:string + + + // 是否可删除 + @Column({ default: true, comment: '是否可删除' }) + deletable: boolean; + // 创建时间 + @CreateDateColumn() + createdAt: Date; + + // 更新时间 + @UpdateDateColumn() + updatedAt: Date; + +} \ No newline at end of file diff --git a/src/service/logistics.service.ts b/src/service/logistics.service.ts index a0c158a..d27fe94 100644 --- a/src/service/logistics.service.ts +++ b/src/service/logistics.service.ts @@ -735,14 +735,10 @@ export class LogisticsService { if (data.shipmentPlatform === 'freightwaves') { - let courierCompany: string = ""; - if (data.courierCompany != "最优物流") { - courierCompany = data.courierCompany; - } // 根据TMS系统对接说明文档格式化参数 const reqBody: any = { // shipCompany: 'UPSYYZ7000NEW', - shipCompany: courierCompany, + shipCompany: data.courierCompany, partnerOrderNumber: order.siteId + '-' + order.externalOrderId, warehouseId: '25072621030107400060', shipper: { @@ -836,15 +832,12 @@ export class LogisticsService { id: data.address_id, }, }) + const address = shipments?.address; - let courierCompany: string = ""; - if (data.courierCompany != "最优物流") { - courierCompany = data.courierCompany; - } // 转换为RateTryRequest格式 const r = { //shipCompany: 'UPSYYZ7000NEW', // 必填,但ShipmentFeeBookDTO中缺少 - shipCompany: courierCompany, + shipCompany: data.courierCompany, partnerOrderNumber: `order-${Date.now()}`, // 必填,使用时间戳生成 warehouseId: '25072621030107400060', // 可选,使用stockPointId转换 shipper: { diff --git a/src/service/order.service.ts b/src/service/order.service.ts index cf09271..752c1c5 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -42,6 +42,7 @@ import { UnifiedOrderDTO } from '../dto/site-api.dto'; import { CustomerService } from './customer.service'; import { ProductService } from './product.service'; import { Site } from '../entity/site.entity'; +import { logisticsAlias } from '../entity/logistics_alias.emtity'; @Provide() export class OrderService { @@ -54,6 +55,9 @@ export class OrderService { @InjectEntityModel(Order) orderModel: Repository; + @InjectEntityModel(logisticsAlias) + logisticsAliasModel: Repository; + @InjectEntityModel(User) userModel: Repository; @@ -151,7 +155,7 @@ export class OrderService { where: { externalOrderId: String(order.id), siteId: siteId }, }); if (!existingOrder) { - this.logger.debug("数据库中不存在", order.id, '订单状态:', order.status) + this.logger.debug("数据库中不存在", order.id, '订单状态:', order.status) } // 同步单个订单 await this.syncSingleOrder(siteId, order); @@ -630,7 +634,7 @@ export class OrderService { await this.saveOrderItem(entity); // 为每个订单项创建对应的销售项(OrderSale) const site = await this.siteService.get(siteId); - await this.saveOrderSale(entity,site); + await this.saveOrderSale(entity, site); } } @@ -720,7 +724,7 @@ export class OrderService { */ // TODO 这里存的是库存商品实际 // 所以叫做 orderInventoryItems 可能更合适 - async saveOrderSale(orderItem: OrderItem,site:Site) { + async saveOrderSale(orderItem: OrderItem, site: Site) { const currentOrderSale = await this.orderSaleModel.find({ where: { siteId: orderItem.siteId, @@ -733,12 +737,12 @@ export class OrderService { if (!orderItem.sku) return; // 从数据库查询产品,关联查询组件 - const componentDetails = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name },orderItem.quantity,site); - if(!componentDetails?.length){ - return + const componentDetails = await this.productService.getComponentDetailFromSiteSku({ sku: orderItem.sku, name: orderItem.name }, orderItem.quantity, site); + if (!componentDetails?.length) { + return } - const orderSales: OrderSale[] = componentDetails.map(({product, parentProduct, quantity}) => { + const orderSales: OrderSale[] = componentDetails.map(({ product, parentProduct, quantity }) => { if (!product) return null const attrsObj = this.productService.getAttributesObject(product.attributes) const orderSale = plainToClass(OrderSale, { @@ -2821,107 +2825,116 @@ export class OrderService { return result; } - // 从 CSV 导入产品;存在则更新,不存在则创建 - /** - * 导入 Wintopay 表格并回填物流信息 - * @param file 上传的文件 - * @returns 处理后的数据(包含更新的物流信息) - */ + // 从 CSV 导入产品;存在则更新,不存在则创建 + /** + * 导入 Wintopay 表格并回填物流信息 + * @param file 上传的文件 + * @returns 处理后的数据(包含更新的物流信息) + */ async importWintopayTable(file: any): Promise { - let updated = 0; - const errors: BatchErrorItem[] = []; - - // 解析文件获取工作表 - let buffer: Buffer; - if (Buffer.isBuffer(file)) { - buffer = file; - } else if (file?.data) { - if (typeof file.data === 'string') { - buffer = fs.readFileSync(file.data); - } else { - buffer = file.data; - } - } else { - throw new Error('无效的文件输入'); - } - - const workbook = xlsx.read(buffer, { type: 'buffer', codepage: 65001 }); - const worksheet = workbook.Sheets[workbook.SheetNames[0]]; - - // 获取表头和数据 - const jsonData = xlsx.utils.sheet_to_json(worksheet, { header: 1 }); - const headers = jsonData[0] as string[]; - const dataRows = jsonData.slice(1) as any[][]; - - // 查找各列的索引 - const columnIndices = { - orderNumber: headers.indexOf('订单号'), - logisticsCompany: headers.indexOf('物流公司'), - trackingNumber: headers.indexOf('单号-单元格文本格式'), - orderCreateTime: headers.indexOf('订单创建时间'), - orderEmail: headers.indexOf('订单邮箱'), - orderSite: headers.indexOf('订单网站'), - name: headers.indexOf('姓名'), - refund: headers.indexOf('退款'), - chargeback: headers.indexOf('拒付') - }; - - // 遍历数据行 - for (let i = 0; i < dataRows.length; i++) { - const row = dataRows[i]; - const orderNumber = row[columnIndices.orderNumber]; - - if (!orderNumber) { - errors.push({ identifier: `行 ${i + 2}`, error: '订单号为空' }); - continue; - } - - try { - let orderNumbers=""; - if (orderNumber.includes('_')&&orderNumber.includes('-')) { - orderNumbers = orderNumber.split('_')[0].toString(); - orderNumbers = orderNumbers.split('-')[1]; - } - // 通过订单号查询订单 - const order = await this.orderModel.findOne({ where: { externalOrderId: orderNumbers } }); - if (order) { - // 通过orderId查询fulfillments - const fulfillments = await this.orderFulfillmentModel.find({ where: { order_id: order.id } }); - - if (fulfillments && fulfillments.length > 0) { - const fulfillment = fulfillments[0]; // 假设每个订单只有一个物流信息 - // 回填物流信息 - if (columnIndices.logisticsCompany !== -1) { - row[columnIndices.logisticsCompany] = fulfillment.shipping_provider || ''; - } - if (columnIndices.trackingNumber !== -1) { - row[columnIndices.trackingNumber] = fulfillment.tracking_number || ''; - } - - updated++; - } - } - } catch (error) { - errors.push({ identifier: `行 ${i + 2}`, error: `处理失败: ${error.message}` }); - } - } - - // 将数据转换为对象数组,与 exportOrder 方法返回格式一致 - const resultData = dataRows.map((row, index) => { - const rowData: any = {}; - headers.forEach((header, colIndex) => { - rowData[header] = row[colIndex] || ''; - }); - // 添加行号信息 - rowData['行号'] = index + 2; - return rowData; - }); - - - // 返回XLSX buffer内容给前端 - // const xlsxBuffer = await this.exportToXlsx(resultData, { type: 'buffer' }); - return resultData; + let updated = 0; + const errors: BatchErrorItem[] = []; + // 解析文件获取工作表 + let buffer: Buffer; + if (Buffer.isBuffer(file)) { + buffer = file; + } else if (file?.data) { + if (typeof file.data === 'string') { + buffer = fs.readFileSync(file.data); + } else { + buffer = file.data; + } + } else { + throw new Error('无效的文件输入'); } + const workbook = xlsx.read(buffer, { type: 'buffer', codepage: 65001 }); + const worksheet = workbook.Sheets[workbook.SheetNames[0]]; + + // 获取表头和数据 + const jsonData = xlsx.utils.sheet_to_json(worksheet, { header: 1 }); + const headers = jsonData[0] as string[]; + const dataRows = jsonData.slice(1) as any[][]; + + // 查找各列的索引 + const columnIndices = { + orderNumber: headers.indexOf('订单号'), + logisticsCompany: headers.indexOf('物流公司'), + trackingNumber: headers.indexOf('单号-单元格文本格式'), + orderCreateTime: headers.indexOf('订单创建时间'), + orderEmail: headers.indexOf('订单邮箱'), + orderSite: headers.indexOf('订单网站'), + name: headers.indexOf('姓名'), + refund: headers.indexOf('退款'), + chargeback: headers.indexOf('拒付') + }; + + const logisticsAliases = await this.logisticsAliasModel.find(); + + // 构建物流公司别名映射 + const logisticsAliasMap = new Map(logisticsAliases.map(alias => [ alias.logistics_alias,alias.logistics_company])); + + // 遍历数据行 + for (let i = 0; i < dataRows.length; i++) { + const row = dataRows[i]; + const orderNumber = row[columnIndices.orderNumber]; + + if (!orderNumber) { + errors.push({ identifier: `行 ${i + 2}`, error: '订单号为空' }); + continue; + } + + try { + let orderNumbers = orderNumber; + // 确保 orderNumber 是字符串类型 + const orderNumberStr = String(orderNumber); + if (orderNumberStr.includes('_') && orderNumberStr.includes('-')) { + orderNumbers = orderNumberStr.split('_')[0].toString(); + orderNumbers = orderNumbers.split('-')[1]; + } + // 通过订单号查询订单 + const order = await this.orderModel.findOne({ where: { externalOrderId: orderNumbers } }); + if (order) { + // 通过orderId查询fulfillments + const fulfillments = await this.orderFulfillmentModel.find({ where: { order_id: order.id } }); + + if (fulfillments && fulfillments.length > 0) { + const fulfillment = fulfillments[0]; // 假设每个订单只有一个物流信息 + + const shipping_provider = logisticsAliasMap.get(fulfillment.shipping_provider); + // 回填物流信息 + if (columnIndices.logisticsCompany !== -1) { + row[columnIndices.logisticsCompany] = shipping_provider || ''; + } + if (columnIndices.trackingNumber !== -1) { + row[columnIndices.trackingNumber] = fulfillment.tracking_number || ''; + } + + updated++; + } + } + } catch (error) { + errors.push({ identifier: `行 ${i + 2}`, error: `处理失败: ${error.message}` }); + } + } + + // 将数据转换为对象数组,与 exportOrder 方法返回格式一致 + const resultData = dataRows.map((row, index) => { + const rowData: any = {}; + headers.forEach((header, colIndex) => { + rowData[header] = row[colIndex] || ''; + }); + // 添加行号信息 + rowData['行号'] = index + 2; + return rowData; + }); + + + // 返回XLSX buffer内容给前端 + // const xlsxBuffer = await this.exportToXlsx(resultData, { type: 'buffer' }); + return resultData; + + } + } -- 2.40.1 From e30cce45b751fd50f441d6ea5d0bfb93d15b36b9 Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Fri, 30 Jan 2026 15:02:35 +0800 Subject: [PATCH 7/8] =?UTF-8?q?style:=20=E4=BF=AE=E5=A4=8D=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F=E9=97=AE=E9=A2=98=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=E7=A9=BA=E6=A0=BC=E5=92=8C=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 调整多个服务文件中的代码格式,统一空格和缩进规范 --- src/service/Wintopay.service.ts | 24 ++++----- src/service/order.service.ts | 2 +- src/service/product.service.ts | 8 +-- src/service/wp.service.ts | 90 +++++++++++++++++---------------- 4 files changed, 63 insertions(+), 61 deletions(-) diff --git a/src/service/Wintopay.service.ts b/src/service/Wintopay.service.ts index eaef0c7..cf2491a 100644 --- a/src/service/Wintopay.service.ts +++ b/src/service/Wintopay.service.ts @@ -33,15 +33,15 @@ interface LogisticsUpdateResponse { @Provide() export class WintopayService { @Inject() logger; - - // 默认配置 - private config = { - //测试环境配置,在生产环境记得换掉 - apiBaseUrl: 'https://stage-merchant-api.wintopay.com', - Authorization: 'Bearer kV8w1er8dFw9p9g2kb0mer398hD8hfWk', - }; - // 发送请求 + // 默认配置 + private config = { + //测试环境配置,在生产环境记得换掉 + apiBaseUrl: 'https://stage-merchant-api.wintopay.com', + Authorization: 'Bearer kV8w1er8dFw9p9g2kb0mer398hD8hfWk', + }; + + // 发送请求 private async sendRequest(url: string, data: any): Promise { try { const headers = { @@ -77,19 +77,19 @@ export class WintopayService { async logisticsUpdate(params: LogisticsUpdateRequest): Promise { try { this.logger.info('开始更新物流信息:', params); - + const response = await this.sendRequest('/v1/logistics/update', params); - + this.logger.info('物流更新成功:', response); return response; } catch (error: any) { this.logger.error('物流更新失败:', error); - + // 处理API返回的错误 if (error.response?.data) { throw new Error(`物流更新失败: ${error.response.data.message || '未知错误'}`); } - + throw new Error(`物流更新请求失败: ${error.message || '网络错误'}`); } } diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 752c1c5..2e5efe3 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -2873,7 +2873,7 @@ export class OrderService { const logisticsAliases = await this.logisticsAliasModel.find(); // 构建物流公司别名映射 - const logisticsAliasMap = new Map(logisticsAliases.map(alias => [ alias.logistics_alias,alias.logistics_company])); + const logisticsAliasMap = new Map(logisticsAliases.map(alias => [alias.logistics_alias, alias.logistics_company])); // 遍历数据行 for (let i = 0; i < dataRows.length; i++) { diff --git a/src/service/product.service.ts b/src/service/product.service.ts index 581485b..a32f487 100644 --- a/src/service/product.service.ts +++ b/src/service/product.service.ts @@ -1792,16 +1792,16 @@ export class ProductService { // 这里判断 second 是否是数字 return sku.includes('-MX-') || sku.includes('-Mixed-') || /^\d+$/.test(second) && /^\d+$/.test(last) } - async getComponentDetailFromSiteSku(siteProduct: { sku: string, name: string }, quantity: number = 1, site: Site): Promise<{ product: Product,parentProduct?: Product, quantity: number }[]> { + async getComponentDetailFromSiteSku(siteProduct: { sku: string, name: string }, quantity: number = 1, site: Site): Promise<{ product: Product, parentProduct?: Product, quantity: number }[]> { if (!siteProduct.sku) { throw new Error('siteSku 不能为空') } - const product = await this.getProductBySiteSku(siteProduct.sku, site) + const product = await this.getProductBySiteSku(siteProduct.sku, site) if (!product) return - if(!product?.components?.length){ + if (!product?.components?.length) { return [{ product, quantity @@ -1816,7 +1816,7 @@ export class ProductService { parentProduct: product, // 这里得记录一下他的爸爸用来记录 quantity: comp.quantity * quantity, } - })) + })) } // 准备创建产品的 DTO, 处理类型转换和默认值 diff --git a/src/service/wp.service.ts b/src/service/wp.service.ts index 3500bcb..8da7d40 100644 --- a/src/service/wp.service.ts +++ b/src/service/wp.service.ts @@ -16,7 +16,7 @@ import { WooProduct, WooVariation, WpMediaGetListParams } from '../dto/woocommer const MAX_PAGE_SIZE = 100; @Provide() export class WPService implements IPlatformService { - + @Inject() private readonly siteService: SiteService; @@ -75,7 +75,7 @@ export class WPService implements IPlatformService { } const data = res.data as T[]; const totalPages = Number(res.headers?.['x-wp-totalpages'] ?? 1); - const total = Number(res.headers?.['x-wp-total']?? 1) + const total = Number(res.headers?.['x-wp-total'] ?? 1) return { items: data, total, totalPages, page, per_page, page_size: per_page }; } @@ -94,9 +94,9 @@ export class WPService implements IPlatformService { * 默认按 date_created 倒序排列,确保获取最新的数据 */ private async sdkGetAllConcurrent( - api: WooCommerceRestApi, - resource: string, - params: Record = {}, + api: WooCommerceRestApi, + resource: string, + params: Record = {}, maxPages: number = MAX_PAGE_SIZE, concurrencyLimit: number = 5 ): Promise { @@ -118,7 +118,7 @@ export class WPService implements IPlatformService { // 限制最大页数,避免过多的并发请求 const actualMaxPages = Math.min(totalPages, maxPages); - + // 收集所有页面数据,从第二页开始 const allItems = [...firstPageItems]; let currentPage = 2; @@ -127,7 +127,7 @@ export class WPService implements IPlatformService { while (currentPage <= actualMaxPages) { const batchPromises: Promise[] = []; const batchSize = Math.min(concurrencyLimit, actualMaxPages - currentPage + 1); - + // 创建当前批次的并发请求 for (let i = 0; i < batchSize; i++) { const page = currentPage + i; @@ -137,18 +137,18 @@ export class WPService implements IPlatformService { console.error(`获取第 ${page} 页数据失败:`, error); return []; // 如果某页获取失败,返回空数组,不影响整体结果 }); - + batchPromises.push(pagePromise); } // 等待当前批次完成 const batchResults = await Promise.all(batchPromises); - + // 合并当前批次的数据 for (const pageItems of batchResults) { allItems.push(...pageItems); } - + // 移动到下一批次 currentPage += batchSize; } @@ -206,7 +206,7 @@ export class WPService implements IPlatformService { const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString( 'base64' ); - console.log(`!!!wpApiUrl, consumerKey, consumerSecret, auth`,site.apiUrl, consumerKey, consumerSecret, auth) + console.log(`!!!wpApiUrl, consumerKey, consumerSecret, auth`, site.apiUrl, consumerKey, consumerSecret, auth) let hasMore = true; while (hasMore) { const config: AxiosRequestConfig = { @@ -259,8 +259,8 @@ export class WPService implements IPlatformService { // 导出 WooCommerce 产品为特殊CSV(平台特性) async exportProductsCsvSpecial(site: any, page: number = 1, pageSize: number = 100): Promise { const list = await this.getProducts(site, { page, per_page: pageSize }); - const header = ['id','name','type','status','sku','regular_price','sale_price','stock_status','stock_quantity']; - const rows = (list.items || []).map((p: any) => [p.id,p.name,p.type,p.status,p.sku,p.regular_price,p.sale_price,p.stock_status,p.stock_quantity]); + const header = ['id', 'name', 'type', 'status', 'sku', 'regular_price', 'sale_price', 'stock_status', 'stock_quantity']; + const rows = (list.items || []).map((p: any) => [p.id, p.name, p.type, p.status, p.sku, p.regular_price, p.sale_price, p.stock_status, p.stock_quantity]); const csv = [header.join(','), ...rows.map(r => r.map(v => String(v ?? '')).join(','))].join('\n'); return csv; } @@ -289,7 +289,7 @@ export class WPService implements IPlatformService { const res = await api.get(`orders/${orderId}`); return res.data as Record; } - async getOrders(siteId: number,params: Record = {}): Promise[]> { + async getOrders(siteId: number, params: Record = {}): Promise[]> { const site = await this.siteService.get(siteId); const api = this.createApi(site, 'wc/v3'); return await this.sdkGetAll>(api, 'orders', params); @@ -367,10 +367,10 @@ export class WPService implements IPlatformService { const api = this.createApi(site, 'wc/v3'); // 确保价格为字符串 if (data.regular_price !== undefined && data.regular_price !== null) { - data.regular_price = String(data.regular_price); + data.regular_price = String(data.regular_price); } if (data.sale_price !== undefined && data.sale_price !== null) { - data.sale_price = String(data.sale_price); + data.sale_price = String(data.sale_price); } // 处理标签字段,如果为字符串数组则转换为 WooCommerce 所需的对象数组 if (Array.isArray((data as any).tags)) { @@ -696,7 +696,7 @@ export class WPService implements IPlatformService { Authorization: `Basic ${auth}`, }, }; - + try { const response = await axios.request(config); return response.data || []; @@ -740,19 +740,19 @@ export class WPService implements IPlatformService { ); const fulfillmentData: any = {}; - + if (data.shipping_provider !== undefined) { fulfillmentData.shipping_provider = data.shipping_provider; } - + if (data.tracking_number !== undefined) { fulfillmentData.tracking_number = data.tracking_number; } - + if (data.shipping_method !== undefined) { fulfillmentData.shipping_method = data.shipping_method; } - + if (data.status !== undefined) { fulfillmentData.status = data.status; } @@ -780,7 +780,7 @@ export class WPService implements IPlatformService { }, data: fulfillmentData, }; - + try { const response = await axios.request(config); return response.data; @@ -803,10 +803,10 @@ export class WPService implements IPlatformService { try { const response = await api.post('products/batch', data); const result = response.data; - + // 转换 WooCommerce 批量操作结果为统一格式 - const errors: Array<{identifier: string, error: string}> = []; - + const errors: Array<{ identifier: string, error: string }> = []; + // WooCommerce 返回格式: { create: [...], update: [...], delete: [...] } // 错误信息可能在每个项目的 error 字段中 const checkForErrors = (items: any[]) => { @@ -819,12 +819,12 @@ export class WPService implements IPlatformService { } }); }; - + // 检查每个操作类型的结果中的错误 if (result.create) checkForErrors(result.create); if (result.update) checkForErrors(result.update); if (result.delete) checkForErrors(result.delete); - + return { total: (data.create?.length || 0) + (data.update?.length || 0) + (data.delete?.length || 0), processed: (result.create?.length || 0) + (result.update?.length || 0) + (result.delete?.length || 0), @@ -1022,7 +1022,7 @@ export class WPService implements IPlatformService { async getMedia(siteId: number, page: number = 1, perPage: number = 20): Promise<{ items: any[], total: number, totalPages: number }> { const site = await this.siteService.get(siteId, true); if (!site) { - throw new Error('站点不存在'); + throw new Error('站点不存在'); } const endpoint = 'wp/v2/media'; const apiUrl = site.apiUrl; @@ -1030,15 +1030,15 @@ export class WPService implements IPlatformService { // 构建 URL,规避多/或少/问题 const url = this.buildURL(apiUrl, '/wp-json', endpoint); const auth = Buffer.from(`${consumerKey}:${consumerSecret}`).toString('base64'); - + const response = await axios.get(url, { headers: { Authorization: `Basic ${auth}` }, params: { page, per_page: perPage } }); - + const total = Number(response.headers['x-wp-total'] || 0); const totalPages = Number(response.headers['x-wp-totalpages'] || 0); - + return { items: response.data, total, @@ -1046,7 +1046,7 @@ export class WPService implements IPlatformService { }; } -public async fetchMediaPaged(site: any, params: Partial = {}) { + public async fetchMediaPaged(site: any, params: Partial = {}) { const apiUrl = site.apiUrl; const { consumerKey, consumerSecret } = site as any; const endpoint = 'wp/v2/media'; @@ -1061,15 +1061,15 @@ public async fetchMediaPaged(site: any, params: Partial = } }); // 检查是否有错误信息 - if(response?.data?.message){ + if (response?.data?.message) { throw new Error(`获取${apiUrl}条媒体文件失败,原因为${response.data.message}`) } - if(!Array.isArray(response.data)) { + if (!Array.isArray(response.data)) { throw new Error(`获取${apiUrl}条媒体文件失败,原因为返回数据不是数组`); } const total = Number(response.headers['x-wp-total'] || 0); const totalPages = Number(response.headers['x-wp-totalpages'] || 0); - return { items: response.data, total, totalPages, page:params.page ?? 1, per_page: params.per_page ?? 20, page_size: params.per_page ?? 20 }; + return { items: response.data, total, totalPages, page: params.page ?? 1, per_page: params.per_page ?? 20, page_size: params.per_page ?? 20 }; } /** * 上传媒体文件 @@ -1091,15 +1091,15 @@ public async fetchMediaPaged(site: any, params: Partial = // 假设 file 是 MidwayJS 的 file 对象 // MidwayJS 上传文件通常在 tmp 目录,需要读取流 formData.append('file', fs.createReadStream(file.data), { - filename: file.filename, - contentType: file.mimeType, + filename: file.filename, + contentType: file.mimeType, }); // Axios headers for multipart const headers = { - Authorization: `Basic ${auth}`, - 'Content-Disposition': `attachment; filename=${file.filename}`, - ...formData.getHeaders(), + Authorization: `Basic ${auth}`, + 'Content-Disposition': `attachment; filename=${file.filename}`, + ...formData.getHeaders(), }; try { @@ -1205,10 +1205,12 @@ public async fetchMediaPaged(site: any, params: Partial = throw new Error('source_url 不存在'); } // 下载源文件为 Buffer - const resp = await axios.get(srcUrl, { responseType: 'arraybuffer', timeout: 30000, - headers: { - 'User-Agent': 'Mozilla/5.0 (compatible; Node.js Axios)', - } }); + const resp = await axios.get(srcUrl, { + responseType: 'arraybuffer', timeout: 30000, + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Node.js Axios)', + } + }); const inputBuffer = Buffer.from(resp.data); // 条件判断 如果下载的 Buffer 为空则抛出错误 if (!inputBuffer || inputBuffer.length === 0) { -- 2.40.1 From 9558761d178cdf05e3b62f3a91b4791e68e3b3c4 Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Fri, 30 Jan 2026 15:06:44 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E7=89=A9=E6=B5=81?= =?UTF-8?q?=E5=88=AB=E5=90=8D=E5=AE=9E=E4=BD=93=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF=E5=B9=B6=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将文件名从 logistics_alias.emtity.ts 修正为 logistics_alias.entity.ts 更新所有引用该文件的导入路径 --- src/config/config.default.ts | 2 +- src/entity/logistics_alias.emtity.ts | 34 ---------------------------- src/entity/logistics_alias.entity.ts | 34 ++++++++++++++++++++++++++++ src/service/order.service.ts | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 src/entity/logistics_alias.emtity.ts create mode 100644 src/entity/logistics_alias.entity.ts diff --git a/src/config/config.default.ts b/src/config/config.default.ts index ac5f89a..2a37fd0 100644 --- a/src/config/config.default.ts +++ b/src/config/config.default.ts @@ -42,7 +42,7 @@ import DictSeeder from '../db/seeds/dict.seeder'; import CategorySeeder from '../db/seeds/category.seeder'; import CategoryAttributeSeeder from '../db/seeds/category_attribute.seeder'; import { SiteSku } from '../entity/site-sku.entity'; -import { logisticsAlias } from '../entity/logistics_alias.emtity'; +import { logisticsAlias } from '../entity/logistics_alias.entity'; export default { // use for cookie sign key, should change to your own and keep security diff --git a/src/entity/logistics_alias.emtity.ts b/src/entity/logistics_alias.emtity.ts deleted file mode 100644 index 3a217d6..0000000 --- a/src/entity/logistics_alias.emtity.ts +++ /dev/null @@ -1,34 +0,0 @@ - -import { ApiProperty } from '@midwayjs/swagger'; -import { Entity,CreateDateColumn,UpdateDateColumn, PrimaryGeneratedColumn, Column } from 'typeorm'; - -@Entity('logistics_alias') -export class logisticsAlias { - @PrimaryGeneratedColumn() - id: number; - - @ApiProperty({ type: 'string' }) - @Column() - logistics_company:string - - @ApiProperty({ type: 'string' }) - @Column() - logistics_alias:string - - @ApiProperty({ type: 'string' }) - @Column() - platform:string - - - // 是否可删除 - @Column({ default: true, comment: '是否可删除' }) - deletable: boolean; - // 创建时间 - @CreateDateColumn() - createdAt: Date; - - // 更新时间 - @UpdateDateColumn() - updatedAt: Date; - -} \ No newline at end of file diff --git a/src/entity/logistics_alias.entity.ts b/src/entity/logistics_alias.entity.ts new file mode 100644 index 0000000..bf2c475 --- /dev/null +++ b/src/entity/logistics_alias.entity.ts @@ -0,0 +1,34 @@ + +import { ApiProperty } from '@midwayjs/swagger'; +import { Entity, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn, Column } from 'typeorm'; + +@Entity('logistics_alias') +export class logisticsAlias { + @PrimaryGeneratedColumn() + id: number; + + @ApiProperty({ type: 'string' }) + @Column() + logistics_company: string + + @ApiProperty({ type: 'string' }) + @Column() + logistics_alias: string + + @ApiProperty({ type: 'string' }) + @Column() + platform: string + + + // 是否可删除 + @Column({ default: true, comment: '是否可删除' }) + deletable: boolean; + // 创建时间 + @CreateDateColumn() + createdAt: Date; + + // 更新时间 + @UpdateDateColumn() + updatedAt: Date; + +} \ No newline at end of file diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 2e5efe3..4196090 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -42,7 +42,7 @@ import { UnifiedOrderDTO } from '../dto/site-api.dto'; import { CustomerService } from './customer.service'; import { ProductService } from './product.service'; import { Site } from '../entity/site.entity'; -import { logisticsAlias } from '../entity/logistics_alias.emtity'; +import { logisticsAlias } from '../entity/logistics_alias.entity'; @Provide() export class OrderService { -- 2.40.1