فهرست منبع

✨ feat(auth-module): 新增微信小店发货相关API接口

- 新增发货信息录入接口(upload-shipping-info.route.mt.ts)【后端】
- 新增获取快递公司列表接口(get-delivery-companies.route.mt.ts)【后端】
- 新增检查交易管理状态接口(get-is-trade-managed.route.mt.ts)【后端】
- 新增微信小店发货接口(send-wechat-shop-delivery.route.mt.ts)【后端】
- 在路由索引文件中注册新增的API路由【后端】

♻️ refactor(mini-auth-service): 重构微信发货功能并优化地址选择器逻辑

- 新增发货信息录入功能,支持物流快递、同城配送、虚拟发货和用户自提【后端】
- 新增获取快递公司列表功能【后端】
- 新增检查交易管理状态功能【后端】
- 新增微信小店发货功能【后端】
- 优化微信access_token缓存机制,支持租户隔离【后端】
- 新增根据订单号查询微信支付订单信息功能【后端】
- 新增根据订单ID查询用户openid功能【后端】
- 新增发货状态查询功能【后端】
- 新增发货错误信息处理功能【后端】

🐛 fix(city-selector): 修复地址选择器清除逻辑和表单验证

- 将清除下级选项的值从0改为undefined,避免表单验证错误【前端】
- 修复地址编辑页面的表单验证规则,将positive验证改为min(1)验证【前端】
- 修复地址编辑页面的默认值设置,从0改为undefined【前端】
- 修复地址编辑页面的数据回填逻辑,处理空值情况【前端】
- 修复地址编辑页面的提交逻辑,正确处理town字段【前端】

✨ feat(order-management): 新增微信小店发货功能集成

- 新增获取微信小店快递公司列表功能【前端】
- 新增调用微信小店发货API功能【前端】
- 新增调用微信小程序发货信息录入API功能【前端】
- 新增检查交易管理状态功能【前端】
- 优化发货表单,支持快递公司选择和手动输入【前端】
- 调整发货类型映射关系,3为虚拟发货,4为用户自提【前端】
- 默认发货类型设置为同城配送【前端】
- 发货成功后调用微信小程序发货信息录入API【前端】

♻️ refactor(redis-util): 重构微信access_token缓存支持租户隔离

- 修改setWechatAccessToken方法,支持租户ID参数【后端】
- 修改getWechatAccessToken方法,支持租户ID参数【后端】
- 修改deleteWechatAccessToken方法,支持租户ID参数【后端】
- 修改isWechatAccessTokenValid方法,支持租户ID参数【后端】
- 修改getWechatAccessTokenTTL方法,支持租户ID参数【后端】
- 修改clearAllWechatAccessTokens方法,支持租户ID参数【后端】
- 优化缓存键名生成逻辑,实现租户隔离【后端】
yourname 1 ماه پیش
والد
کامیت
7d29b84851

+ 6 - 6
mini/src/components/ui/city-selector.tsx

@@ -142,13 +142,13 @@ export const CitySelector: React.FC<CitySelectorProps> = ({
     // 当省份变化时,清除城市、区县、街道
     if (provinceValue !== undefined) {
       if (cityValue !== undefined) {
-        onCityChange?.(0) // 使用默认值而不是0
+        onCityChange?.(undefined) // 清除城市选择
       }
       if (districtValue !== undefined) {
-        onDistrictChange?.(0) // 使用默认值而不是0
+        onDistrictChange?.(undefined) // 清除区县选择
       }
       if (townValue !== undefined) {
-        onTownChange?.(0) // 使用默认值而不是0
+        onTownChange?.(undefined) // 清除街道选择
       }
     }
   }, [provinceValue])
@@ -157,10 +157,10 @@ export const CitySelector: React.FC<CitySelectorProps> = ({
     // 当城市变化时,清除区县、街道
     if (cityValue !== undefined) {
       if (districtValue !== undefined) {
-        onDistrictChange?.(0) // 使用默认值而不是0
+        onDistrictChange?.(undefined) // 清除区县选择
       }
       if (townValue !== undefined) {
-        onTownChange?.(0) // 使用默认值而不是0
+        onTownChange?.(undefined) // 清除街道选择
       }
     }
   }, [cityValue])
@@ -169,7 +169,7 @@ export const CitySelector: React.FC<CitySelectorProps> = ({
     // 当区县变化时,清除街道
     if (districtValue !== undefined) {
       if (townValue !== undefined) {
-        onTownChange?.(0) // 使用默认值而不是0
+        onTownChange?.(undefined) // 清除街道选择
       }
     }
   }, [districtValue])

+ 13 - 13
mini/src/pages/address-edit/index.tsx

@@ -20,10 +20,10 @@ import { CitySelector } from '@/components/ui/city-selector'
 const addressSchema = z.object({
   name: z.string().min(1, '请输入收货人姓名'),
   phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
-  province: z.number().positive('请选择省份'),
-  city: z.number().positive('请选择城市'),
-  district: z.number().positive('请选择区县'),
-  town: z.number().optional(),
+  province: z.number().min(1, '请选择省份'),
+  city: z.number().min(1, '请选择城市'),
+  district: z.number().min(1, '请选择区县'),
+  town: z.number().min(1).optional(),
   address: z.string().min(1, '请输入详细地址'),
   isDefault: z.boolean().optional()
 })
@@ -61,10 +61,10 @@ export default function AddressEditPage() {
     defaultValues: {
       name: '',
       phone: '',
-      province: 0, // 设为0,让CitySelector自动选择第一个省份
-      city: 0,     // 设为0,让CitySelector自动选择第一个城市
-      district: 0, // 设为0,让CitySelector自动选择第一个区县
-      town: 0,     // 设为0,让CitySelector自动选择第一个乡镇
+      province: undefined, // 设为undefined,让CitySelector自动选择第一个省份
+      city: undefined,     // 设为undefined,让CitySelector自动选择第一个城市
+      district: undefined, // 设为undefined,让CitySelector自动选择第一个区县
+      town: undefined,     // 设为undefined,让CitySelector自动选择第一个乡镇
       address: '',
       isDefault: false
     }
@@ -76,10 +76,10 @@ export default function AddressEditPage() {
       form.reset({
         name: address.name,
         phone: address.phone,
-        province: address.receiverProvince,
-        city: address.receiverCity,
-        district: address.receiverDistrict,
-        town: address.receiverTown || 0,
+        province: address.receiverProvince || undefined,
+        city: address.receiverCity || undefined,
+        district: address.receiverDistrict || undefined,
+        town: address.receiverTown || undefined,
         address: address.address,
         isDefault: address.isDefault === 1
       })
@@ -95,7 +95,7 @@ export default function AddressEditPage() {
         receiverProvince: data.province,
         receiverCity: data.city,
         receiverDistrict: data.district,
-        receiverTown: data.town,
+        receiverTown: data.town || 0,
         address: data.address,
         isDefault: data.isDefault ? 1 : 0
       }

+ 114 - 0
packages/core-module-mt/auth-module-mt/src/routes/get-delivery-companies.route.mt.ts

@@ -0,0 +1,114 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { MiniAuthService } from '../services/index.mt';
+import { AppDataSource } from '@d8d/shared-utils';
+import { ErrorSchema } from '@d8d/shared-utils';
+
+// 获取快递公司列表请求Schema
+const GetDeliveryCompaniesSchema = z.object({
+  tenantId: z.number().optional()
+});
+
+// 快递公司信息Schema
+const DeliveryCompanySchema = z.object({
+  delivery_id: z.string(),
+  delivery_name: z.string()
+});
+
+// 获取快递公司列表响应Schema
+const GetDeliveryCompaniesResponseSchema = z.object({
+  success: z.boolean(),
+  message: z.string(),
+  data: z.object({
+    company_list: z.array(DeliveryCompanySchema)
+  }).optional(),
+  error: z.any().optional()
+});
+
+const getDeliveryCompaniesRoute = createRoute({
+  method: 'post',
+  path: '/get-delivery-companies',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: GetDeliveryCompaniesSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '获取快递公司列表成功',
+      content: {
+        'application/json': {
+          schema: GetDeliveryCompaniesResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(getDeliveryCompaniesRoute, async (c) => {
+  try {
+    const miniAuthService = new MiniAuthService(AppDataSource);
+    const { tenantId } = c.req.valid('json');
+
+    // 调试输出
+    const timestamp = new Date().toISOString();
+
+    console.log('收到获取快递公司列表请求:', {
+      tenantId,
+      timestamp
+    });
+
+    // 调用服务获取快递公司列表
+    const result = await miniAuthService.getCompanyList(tenantId);
+
+    // 检查返回的数据结构
+    if (!result.company_list || !Array.isArray(result.company_list)) {
+      console.warn('微信API返回的快递公司列表格式不正确:', result);
+      return c.json({
+        code: 500,
+        message: '快递公司列表格式不正确'
+      }, 500);
+    }
+
+    return c.json({
+      success: true,
+      message: '获取快递公司列表成功',
+      data: {
+        company_list: result.company_list
+      }
+    }, 200);
+
+  } catch (error) {
+    console.error('获取快递公司列表失败:', error);
+
+    const errorMessage = error instanceof Error ? error.message : '获取快递公司列表失败';
+    const errorCode = (error as any)?.code || 500;
+
+    return c.json({
+      code: errorCode,
+      message: errorMessage
+    }, errorCode);
+  }
+});
+
+export default app;

+ 108 - 0
packages/core-module-mt/auth-module-mt/src/routes/get-is-trade-managed.route.mt.ts

@@ -0,0 +1,108 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { MiniAuthService } from '../services/index.mt';
+import { AppDataSource } from '@d8d/shared-utils';
+import { ErrorSchema } from '@d8d/shared-utils';
+
+// 获取交易管理状态请求Schema
+const GetIsTradeManagedSchema = z.object({
+  tenantId: z.number().optional()
+});
+
+// 交易管理状态响应Schema
+const IsTradeManagedResponseSchema = z.object({
+  success: z.boolean(),
+  message: z.string(),
+  data: z.object({
+    is_trade_managed: z.boolean()
+  }).optional(),
+  error: z.any().optional()
+});
+
+const getIsTradeManagedRoute = createRoute({
+  method: 'post',
+  path: '/get-is-trade-managed',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: GetIsTradeManagedSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '获取交易管理状态成功',
+      content: {
+        'application/json': {
+          schema: IsTradeManagedResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(getIsTradeManagedRoute, async (c) => {
+  try {
+    const miniAuthService = new MiniAuthService(AppDataSource);
+    const { tenantId } = c.req.valid('json');
+
+    // 调试输出
+    const timestamp = new Date().toISOString();
+
+    console.log('收到获取交易管理状态请求:', {
+      tenantId,
+      timestamp
+    });
+
+    // 调用服务获取交易管理状态
+    const result = await miniAuthService.getIsTradeManaged(tenantId);
+
+    // 检查返回的数据结构
+    if (typeof result.is_trade_managed !== 'boolean') {
+      console.warn('微信API返回的交易管理状态格式不正确:', result);
+      return c.json({
+        code: 500,
+        message: '交易管理状态格式不正确'
+      }, 500);
+    }
+
+    return c.json({
+      success: true,
+      message: '获取交易管理状态成功',
+      data: {
+        is_trade_managed: result.is_trade_managed
+      }
+    }, 200);
+
+  } catch (error) {
+    console.error('获取交易管理状态失败:', error);
+
+    const errorMessage = error instanceof Error ? error.message : '获取交易管理状态失败';
+    const errorCode = (error as any)?.code || 500;
+
+    return c.json({
+      code: errorCode,
+      message: errorMessage
+    }, errorCode);
+  }
+});
+
+export default app;

+ 9 - 1
packages/core-module-mt/auth-module-mt/src/routes/index.mt.ts

@@ -9,6 +9,10 @@ import logoutRoute from './logout.route.mt';
 import ssoVerifyRoute from './sso-verify.route.mt';
 import phoneDecryptRoute from './phone-decrypt.route.mt';
 import sendTemplateMessageRoute from './send-template-message.route.mt';
+import sendWechatShopDeliveryRoute from './send-wechat-shop-delivery.route.mt';
+import getDeliveryCompaniesRoute from './get-delivery-companies.route.mt';
+import getIsTradeManagedRoute from './get-is-trade-managed.route.mt';
+import uploadShippingInfoRoute from './upload-shipping-info.route.mt';
 
 // 创建统一的路由应用
 const authRoutes = new OpenAPIHono<AuthContext>()
@@ -20,7 +24,11 @@ const authRoutes = new OpenAPIHono<AuthContext>()
   .route('/', logoutRoute)
   .route('/', ssoVerifyRoute)
   .route('/', phoneDecryptRoute)
-  .route('/', sendTemplateMessageRoute);
+  .route('/', sendTemplateMessageRoute)
+  .route('/', sendWechatShopDeliveryRoute)
+  .route('/', getDeliveryCompaniesRoute)
+  .route('/', getIsTradeManagedRoute)
+  .route('/', uploadShippingInfoRoute);
 
 export { authRoutes };
 export default authRoutes;

+ 115 - 0
packages/core-module-mt/auth-module-mt/src/routes/send-wechat-shop-delivery.route.mt.ts

@@ -0,0 +1,115 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { MiniAuthService } from '../services/index.mt';
+import { AppDataSource } from '@d8d/shared-utils';
+import { ErrorSchema } from '@d8d/shared-utils';
+
+// 微信小店发货请求Schema
+const SendWechatShopDeliverySchema = z.object({
+  orderId: z.number().int().positive('订单ID必须为正整数'),
+  orderNo: z.string().min(1, '订单号不能为空'),
+  deliveryType: z.number().int().min(1, '发货方式最小为1').max(4, '发货方式最大为4'),
+  deliveryCompany: z.string().nullable().optional(),
+  deliveryNo: z.string().nullable().optional(),
+  tenantId: z.number().optional()
+});
+
+// 微信小店发货响应Schema
+const SendWechatShopDeliveryResponseSchema = z.object({
+  success: z.boolean(),
+  message: z.string(),
+  data: z.any().optional(),
+  error: z.any().optional()
+});
+
+const sendWechatShopDeliveryRoute = createRoute({
+  method: 'post',
+  path: '/send-wechat-shop-delivery',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: SendWechatShopDeliverySchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '微信小店发货成功',
+      content: {
+        'application/json': {
+          schema: SendWechatShopDeliveryResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(sendWechatShopDeliveryRoute, async (c) => {
+  try {
+    const miniAuthService = new MiniAuthService(AppDataSource);
+    const { orderId, orderNo, deliveryType, deliveryCompany, deliveryNo, tenantId } = c.req.valid('json');
+
+    // 调试输出
+    const timestamp = new Date().toISOString();
+
+    console.log('收到微信小店发货请求:', {
+      orderId,
+      orderNo,
+      deliveryType,
+      deliveryCompany,
+      deliveryNo,
+      tenantId,
+      timestamp
+    });
+
+    // 注意:这里需要根据实际情况获取微信小店订单ID
+    // 目前代码中假设orderNo就是微信小店订单ID,实际项目中可能需要从数据库查询映射关系
+    const wechatOrderId = orderNo; // 临时使用orderNo作为微信小店订单ID
+
+    // 调用服务发送微信小店发货
+    const result = await miniAuthService.sendWechatShopDelivery({
+      wechatOrderId,
+      deliveryCompany: deliveryCompany || undefined,
+      deliveryNo: deliveryNo || undefined,
+      tenantId
+    });
+
+    return c.json({
+      success: true,
+      message: '微信小店发货成功',
+      data: result
+    }, 200);
+
+  } catch (error) {
+    console.error('微信小店发货失败:', error);
+
+    const errorMessage = error instanceof Error ? error.message : '微信小店发货失败';
+    const errorCode = (error as any)?.code || 500;
+
+    return c.json({
+      success: false,
+      message: errorMessage,
+      error: error instanceof Error ? error.stack : error
+    }, errorCode);
+  }
+});
+
+export default app;

+ 119 - 0
packages/core-module-mt/auth-module-mt/src/routes/upload-shipping-info.route.mt.ts

@@ -0,0 +1,119 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { MiniAuthService } from '../services/index.mt';
+import { AppDataSource } from '@d8d/shared-utils';
+import { ErrorSchema } from '@d8d/shared-utils';
+
+// 发货信息录入请求Schema
+const UploadShippingInfoSchema = z.object({
+  orderId: z.string().min(1, '订单ID不能为空'),
+  deliveryType: z.number().int().min(1, '发货方式最小为1').max(4, '发货方式最大为4').describe('发货方式:1-物流快递, 2-同城配送, 3-虚拟发货, 4-用户自提'),
+  expressInfo: z.object({
+    deliveryId: z.string().min(1, '快递公司ID不能为空'),
+    waybillId: z.string().min(1, '快递单号不能为空')
+  }).optional(),
+  localDeliveryInfo: z.object({
+    deliveryName: z.string().min(1, '配送员姓名不能为空'),
+    deliveryPhone: z.string().min(1, '配送员手机号不能为空')
+  }).optional(),
+  isAllDelivered: z.boolean().default(true),
+  tenantId: z.number().optional()
+});
+
+// 发货信息录入响应Schema
+const UploadShippingInfoResponseSchema = z.object({
+  success: z.boolean(),
+  message: z.string(),
+  data: z.any().optional(),
+  error: z.any().optional()
+});
+
+const uploadShippingInfoRoute = createRoute({
+  method: 'post',
+  path: '/upload-shipping-info',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: UploadShippingInfoSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '发货信息录入成功',
+      content: {
+        'application/json': {
+          schema: UploadShippingInfoResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(uploadShippingInfoRoute, async (c) => {
+  try {
+    const miniAuthService = new MiniAuthService(AppDataSource);
+    const { orderId, deliveryType, expressInfo, localDeliveryInfo, isAllDelivered, tenantId } = c.req.valid('json');
+
+    // 调试输出
+    const timestamp = new Date().toISOString();
+
+    console.log('收到发货信息录入请求:', {
+      orderId,
+      deliveryType,
+      expressInfo,
+      localDeliveryInfo,
+      isAllDelivered,
+      tenantId,
+      timestamp
+    });
+
+    // 调用服务录入发货信息
+    const result = await miniAuthService.uploadShippingInfo({
+      orderId,
+      deliveryType,
+      expressInfo,
+      localDeliveryInfo,
+      isAllDelivered,
+      tenantId
+    });
+
+    return c.json({
+      success: true,
+      message: '发货信息录入成功',
+      data: result
+    }, 200);
+
+  } catch (error) {
+    console.error('发货信息录入失败:', error);
+
+    const errorMessage = error instanceof Error ? error.message : '发货信息录入失败';
+    const errorCode = (error as any)?.code || 500;
+
+    return c.json({
+      success: false,
+      message: errorMessage,
+      error: error instanceof Error ? error.stack : error
+    }, errorCode);
+  }
+});
+
+export default app;

+ 670 - 26
packages/core-module-mt/auth-module-mt/src/services/mini-auth.service.mt.ts

@@ -12,11 +12,13 @@ export class MiniAuthService {
   private userRepository: Repository<UserEntityMt>;
   private fileService: FileServiceMt;
   private systemConfigService: SystemConfigServiceMt;
+  private dataSource: DataSource;
 
   constructor(dataSource: DataSource) {
     this.userRepository = dataSource.getRepository(UserEntityMt);
     this.fileService = new FileServiceMt(dataSource);
     this.systemConfigService = new SystemConfigServiceMt(dataSource);
+    this.dataSource = dataSource;
   }
 
   async miniLogin(code: string, tenantId?: number): Promise<{ token: string; user: UserEntityMt; isNewUser: boolean }> {
@@ -221,8 +223,6 @@ export class MiniAuthService {
 
 
 
- 
-
   /**
    * 发送微信模板消息
    */
@@ -336,6 +336,627 @@ export class MiniAuthService {
     }
   }
 
+
+  
+  /**
+   * 根据订单号查询微信支付订单信息
+   */
+  private async getWechatOrderInfo(orderNo: string, tenantId?: number): Promise<{
+    outTradeNo: string;
+    wechatTransactionId?: string;
+  }> {
+    try {
+      // 1. 先通过订单号查询订单,获取订单ID
+      const orderRepository = this.dataSource.getRepository('OrderMt');
+      const order = await orderRepository.findOne({
+        where: {
+          orderNo: orderNo,
+          ...(tenantId !== undefined ? { tenantId } : {})
+        },
+        select: ['id'] // 只选择id字段
+      });
+
+      if (!order) {
+        throw new Error(`订单不存在: ${orderNo}`);
+      }
+
+      // 2. 通过订单ID查询支付记录
+      const paymentRepository = this.dataSource.getRepository('PaymentMtEntity');
+      const payment = await paymentRepository.findOne({
+        where: {
+          externalOrderId: order.id,
+          ...(tenantId !== undefined ? { tenantId } : {})
+        }
+      });
+
+      if (!payment) {
+        throw new Error(`支付记录不存在,订单号: ${orderNo}, 订单ID: ${order.id}`);
+      }
+
+      if (!payment.outTradeNo) {
+        throw new Error(`支付记录 ${orderNo} 没有微信支付商户订单号`);
+      }
+
+      return {
+        outTradeNo: payment.outTradeNo,
+        wechatTransactionId: payment.wechatTransactionId
+      };
+    } catch (error) {
+      console.error(`查询微信支付订单信息失败:`, {
+        orderNo,
+        tenantId,
+        error: error instanceof Error ? error.message : '未知错误'
+      });
+      throw error;
+    }
+  }
+
+  /**
+   * 根据订单ID查询用户openid
+   */
+  private async getOpenidByOrderId(orderId: string, tenantId?: number): Promise<string> {
+    try {
+      // 动态获取订单仓库,避免循环依赖
+      // 注意:这里使用 any 类型来避免类型导入问题
+      const orderRepository = this.dataSource.getRepository('OrderMt');
+
+      // 查询订单,包含用户关联关系
+      const order = await orderRepository.findOne({
+        where: { orderNo: orderId, ...(tenantId !== undefined ? { tenantId } : {}) },
+        relations: ['user']
+      });
+
+      if (!order) {
+        throw new Error(`订单不存在: ${orderId}`);
+      }
+
+      if (!order.user) {
+        throw new Error(`订单 ${orderId} 没有关联的用户信息`);
+      }
+
+      if (!order.user.openid) {
+        throw new Error(`订单 ${orderId} 关联的用户没有openid`);
+      }
+
+      return order.user.openid;
+    } catch (error) {
+      console.error(`查询订单openid失败:`, {
+        orderId,
+        tenantId,
+        error: error instanceof Error ? error.message : '未知错误'
+      });
+      throw error;
+    }
+  }
+
+  /**
+   * 发货信息录入
+   * 微信小程序发货信息录入API:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html
+   */
+  async uploadShippingInfo(params: {
+    orderId: string; // 小程序订单ID
+    deliveryType: number; // 发货方式:1-物流快递, 2-同城配送, 3-虚拟发货, 4-用户自提
+    expressInfo?: {
+      deliveryId: string; // 快递公司ID,通过getCompanyList获取
+      waybillId: string; // 快递单号
+    };
+    localDeliveryInfo?: {
+      deliveryName?: string; // 配送员姓名
+      deliveryPhone?: string; // 配送员手机号
+    };
+    isAllDelivered?: boolean; // 是否全部发货完成,默认true
+    tenantId?: number;
+  }): Promise<any> {
+    const { orderId, deliveryType, expressInfo, localDeliveryInfo, isAllDelivered = true, tenantId } = params;
+
+    // 参数验证
+    if (!orderId) {
+      throw new Error('订单ID不能为空');
+    }
+
+    const accessToken = await this.getWxAccessToken(tenantId);
+    //"98_eafrToobfnW67wu1fP8bmeqpL3gDnwIYE5QGkMRxyjArIF8c3w8K9bxWRq7oAy-miAJV033YRxJhEjHiNXghXL0Xv3AfTEqaJjqPUk5GVJpnnTkqDE5O7YdO2ZYJKCdABAWXP"; 
+
+    // 获取商户号(mchid)
+    let mchid: string | null = null;
+    if (tenantId !== undefined) {
+      const configKeys = ['wx.payment.merchant.id'];
+      const configs = await this.systemConfigService.getConfigsByKeys(configKeys, tenantId);
+      mchid = configs['wx.payment.merchant.id'];
+    }
+
+    // 如果系统配置中没有找到,回退到环境变量
+    if (!mchid) {
+      mchid = process.env.WECHAT_MERCHANT_ID || null;
+    }
+
+    if (!mchid) {
+      throw new Error('微信商户号配置缺失,无法使用小程序订单号发货');
+    }
+
+    // 查询支付记录,获取微信支付订单号
+    const wechatOrderInfo = await this.getWechatOrderInfo(orderId, tenantId);
+
+    // 查询订单对应的用户openid
+    const openid = await this.getOpenidByOrderId(orderId, tenantId);
+
+    const shippingData: any = {
+      order_key: {
+        order_number_type: 1, // 1-小程序订单号
+        out_trade_no: wechatOrderInfo.outTradeNo, // 使用微信支付商户订单号
+        mchid: mchid // 添加商户号字段
+      },
+      logistics_type: deliveryType,
+      delivery_mode: isAllDelivered ? 1 : 2,
+      shipping_list: [
+        {
+          item_desc: `订单${orderId}`, // 商品描述
+        }
+      ],
+      upload_time: new Date().toISOString().replace('Z', '+08:00'), // RFC 3339 格式,使用东八区时间
+      payer: {
+        openid: openid // 支付方openid,从订单获取用户openid
+      }
+    };
+
+    // 根据发货方式添加不同的信息
+    switch (deliveryType) {
+      case 1: // 物流快递
+        if (!expressInfo?.deliveryId || !expressInfo?.waybillId) {
+          throw new Error('快递发货需要提供快递公司ID和运单号');
+        }
+        shippingData.delivery_list = [
+          {
+            delivery_id: expressInfo.deliveryId,
+            waybill_id: expressInfo.waybillId
+          }
+        ];
+        break;
+
+      case 2: // 同城配送
+        // if (!localDeliveryInfo?.deliveryName || !localDeliveryInfo?.deliveryPhone) {
+        //   throw new Error('同城配送需要提供配送员姓名和手机号');
+        // }
+        // shippingData.delivery_list = [
+        //   {
+        //     delivery_name: localDeliveryInfo.deliveryName,
+        //     delivery_phone: localDeliveryInfo.deliveryPhone
+        //   }
+        // ];
+        // break;
+
+      case 3: // 虚拟发货
+      case 4: // 用户自提
+        // 无需物流,不需要额外信息
+        break;
+
+      default:
+        throw new Error(`不支持的发货方式: ${deliveryType}`);
+    }
+
+    const url = `https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=${accessToken}`;
+
+    try {
+      const response = await axios.post(url, shippingData, {
+        timeout: 10000,
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+
+      if (response.data.errcode && response.data.errcode !== 0) {
+        const errorMsg = this.getShippingErrorMsg(response.data.errcode, response.data.errmsg);
+        console.error(`微信发货失败: ${errorMsg}`, {
+          orderId,
+          deliveryType,
+          errcode: response.data.errcode,
+          errmsg: response.data.errmsg
+        });
+        throw new Error(errorMsg);
+      }
+
+      console.debug('微信发货成功:', {
+        orderId,
+        deliveryType,
+        response: response.data
+      });
+      return response.data;
+
+    } catch (error) {
+      console.error('发货信息录入异常:', {
+        orderId,
+        deliveryType,
+        error: error instanceof Error ? error.message : '未知错误',
+        stack: error instanceof Error ? error.stack : undefined
+      });
+
+      if (axios.isAxiosError(error)) {
+        if (error.code === 'ECONNABORTED') {
+          throw new Error('微信服务器连接超时,请稍后重试');
+        } else if (error.code === 'ENOTFOUND') {
+          throw new Error('无法连接到微信服务器,请检查网络');
+        } else {
+          throw new Error(`微信服务器连接失败: ${error.message}`);
+        }
+      } else if (error instanceof Error) {
+        // 如果是我们抛出的错误,直接抛出
+        throw error;
+      } else {
+        throw new Error('发货信息录入失败,请稍后重试');
+      }
+    }
+  }
+
+
+  async getCompanyList(tenantId?: number): Promise<any> {
+    
+    const accessToken = await this.getWxAccessToken(tenantId);
+ 
+    const url = `https://api.weixin.qq.com/product/delivery/get_company_list?access_token=${accessToken}`;
+
+    try {
+      const response = await axios.post(url, {}, {
+        timeout: 10000,
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+
+      if (response.data.errcode && response.data.errcode !== 0) {
+        // 处理特定的错误码
+        if (response.data.errcode === 48001) {
+          throw new Error('API功能未授权,请确认小程序已开通微信小店功能并申请相应权限');
+        }
+        throw new Error(`获取快递公司列表失败: ${response.data.errmsg} (errcode: ${response.data.errcode})`);
+      }
+
+      console.debug('获取快递公司列表成功:', response.data);
+      return response.data;
+
+    } catch (error) {
+      if (axios.isAxiosError(error)) {
+        throw new Error(`微信服务器连接失败: ${error.message}`);
+      }
+      throw error;
+    }
+  }
+
+  async getIsTradeManaged(tenantId?: number): Promise<any> {
+
+    const accessToken = await this.getWxAccessToken(tenantId);
+
+    const url = `https://api.weixin.qq.com/wxa/sec/order/is_trade_managed?access_token=${accessToken}`;
+
+
+    // 从系统配置获取appid
+    let appId: string | null = null;
+    const configKeys = ['wx.mini.app.id', 'wx.mini.app.secret'];
+
+    if (tenantId !== undefined) {
+      const configs = await this.systemConfigService.getConfigsByKeys(configKeys, tenantId);
+      appId = configs['wx.mini.app.id'];
+    }
+
+    // 如果系统配置中没有找到,回退到环境变量
+    if (!appId) {
+      appId = process.env.WX_MINI_APP_ID || null;
+    }
+
+    // 检查appid是否存在
+    if (!appId) {
+      throw new Error('微信小程序appid配置缺失');
+    }
+
+    try {
+      // 微信API要求POST方法,并且需要在请求体中传递appid参数
+      const requestBody = {
+        appid: appId
+      };
+
+      const response = await axios.post(url, requestBody, {
+        timeout: 10000,
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+
+      if (response.data.errcode && response.data.errcode !== 0) {
+        // 处理特定的错误码
+        if (response.data.errcode === 48001) {
+          throw new Error('API功能未授权,请确认小程序已开通微信小店功能并申请相应权限');
+        }
+        throw new Error(`检查交易管理状态失败: ${response.data.errmsg} (errcode: ${response.data.errcode})`);
+      }
+
+      console.debug('检查交易管理状态成功:', response.data);
+      return response.data;
+
+    } catch (error) {
+      if (axios.isAxiosError(error)) {
+        throw new Error(`微信服务器连接失败: ${error.message}`);
+      }
+      throw error;
+    }
+  }
+
+
+  // async getCompanyList(tenantId?: number): Promise<any> {
+  //   // 使用内置的快递公司列表,避免微信API权限问题
+  //   const builtInDeliveryCompanies = [
+  //     { delivery_id: 'SF', delivery_name: '顺丰速运' },
+  //     { delivery_id: 'STO', delivery_name: '申通快递' },
+  //     { delivery_id: 'YTO', delivery_name: '圆通速递' },
+  //     { delivery_id: 'YD', delivery_name: '韵达快递' },
+  //     { delivery_id: 'ZTO', delivery_name: '中通快递' },
+  //     { delivery_id: 'HTKY', delivery_name: '百世快递' },
+  //     { delivery_id: 'YZPY', delivery_name: '邮政快递' },
+  //     { delivery_id: 'JD', delivery_name: '京东物流' },
+  //     { delivery_id: 'EMS', delivery_name: 'EMS' },
+  //     { delivery_id: 'TTKDEX', delivery_name: '天天快递' },
+  //     { delivery_id: 'DBL', delivery_name: '德邦物流' },
+  //     { delivery_id: 'ZJS', delivery_name: '宅急送' }
+  //   ];
+  
+  //   console.debug('返回内置快递公司列表,共', builtInDeliveryCompanies.length, '家');
+    
+  //   return {
+  //     company_list: builtInDeliveryCompanies
+  //   };
+  // }
+
+
+
+  /**
+   * 查询发货状态
+   * 微信小程序发货状态查询API
+   */
+  async getShippingStatus(params: {
+    orderId: string; // 小程序订单ID
+    tenantId?: number;
+  }): Promise<{
+    orderId: string;
+    shippingStatus: number; // 发货状态:0-未发货,1-已发货,2-已签收,3-已取消
+    deliveryMode?: string; // 发货方式
+    expressInfo?: {
+      deliveryId: string;
+      deliveryName: string;
+      waybillId: string;
+    };
+    localDeliveryInfo?: {
+      deliveryName: string;
+      deliveryPhone: string;
+    };
+    shippingTime?: string; // 发货时间
+    estimatedDeliveryTime?: string; // 预计送达时间
+    actualDeliveryTime?: string; // 实际送达时间
+  }> {
+    const { orderId, tenantId } = params;
+
+    if (!orderId) {
+      throw new Error('订单ID不能为空');
+    }
+
+    const accessToken = await this.getWxAccessToken(tenantId);
+
+    const queryData = {
+      order_key: {
+        order_number_type: 1, // 1-小程序订单号
+        out_trade_no: orderId
+      }
+    };
+
+    const url = `https://api.weixin.qq.com/wxa/sec/order/get_shipping_info?access_token=${accessToken}`;
+
+    try {
+      const response = await axios.post(url, queryData, {
+        timeout: 10000,
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+
+      if (response.data.errcode && response.data.errcode !== 0) {
+        const errorMsg = this.getShippingErrorMsg(response.data.errcode, response.data.errmsg);
+        console.error(`查询发货状态失败: ${errorMsg}`, {
+          orderId,
+          errcode: response.data.errcode,
+          errmsg: response.data.errmsg
+        });
+        throw new Error(errorMsg);
+      }
+
+      const shippingInfo = response.data.shipping_info;
+      const result: {
+        orderId: string;
+        shippingStatus: number;
+        deliveryMode?: string;
+        expressInfo?: {
+          deliveryId: string;
+          deliveryName: string;
+          waybillId: string;
+        };
+        localDeliveryInfo?: {
+          deliveryName: string;
+          deliveryPhone: string;
+        };
+        shippingTime?: string;
+        estimatedDeliveryTime?: string;
+        actualDeliveryTime?: string;
+      } = {
+        orderId,
+        shippingStatus: shippingInfo?.logistics_status || 0,
+        deliveryMode: this.getDeliveryModeFromLogisticsType(shippingInfo?.logistics_type),
+        shippingTime: shippingInfo?.shipping_time,
+        estimatedDeliveryTime: shippingInfo?.estimated_delivery_time,
+        actualDeliveryTime: shippingInfo?.actual_delivery_time
+      };
+
+      // 处理快递信息
+      if (shippingInfo?.delivery_list?.length > 0) {
+        const delivery = shippingInfo.delivery_list[0];
+        if (delivery.delivery_id) {
+          result.expressInfo = {
+            deliveryId: delivery.delivery_id,
+            deliveryName: delivery.delivery_name || this.getDeliveryCompanyName(delivery.delivery_id),
+            waybillId: delivery.waybill_id
+          };
+        } else if (delivery.delivery_name) {
+          result.localDeliveryInfo = {
+            deliveryName: delivery.delivery_name,
+            deliveryPhone: delivery.delivery_phone
+          };
+        }
+      }
+
+      console.debug('发货状态查询成功:', {
+        orderId,
+        shippingStatus: result.shippingStatus,
+        deliveryMode: result.deliveryMode
+      });
+
+      return result;
+
+    } catch (error) {
+      console.error('发货状态查询异常:', {
+        orderId,
+        error: error instanceof Error ? error.message : '未知错误'
+      });
+
+      if (axios.isAxiosError(error)) {
+        throw new Error(`查询发货状态失败: ${error.message}`);
+      }
+      throw error;
+    }
+  }
+
+
+
+  /**
+   * 根据发货方式获取物流类型编码
+   * 微信API要求:0-无需物流,1-快递,2-同城配送
+   */
+  private getLogisticsType(deliveryMode: 'express' | 'no_logistics' | 'local_delivery'): number {
+    switch (deliveryMode) {
+      case 'no_logistics':
+        return 0;
+      case 'express':
+        return 1;
+      case 'local_delivery':
+        return 2;
+      default:
+        throw new Error(`不支持的发货方式: ${deliveryMode}`);
+    }
+  }
+
+  /**
+   * 获取发货错误信息
+   * 根据微信API错误码返回友好的错误信息
+   */
+  private getShippingErrorMsg(errcode: number, errmsg: string): string {
+    const errorMap: Record<number, string> = {
+      40001: 'access_token无效或已过期',
+      40003: '非法的openid',
+      40013: 'appid无效',
+      40029: 'code无效',
+      40050: '不支持的物流类型',
+      40051: '订单不存在',
+      40052: '订单状态不允许发货',
+      40053: '快递公司ID无效',
+      40054: '运单号格式错误',
+      40055: '同城配送信息不完整',
+      40056: '订单已发货,不能重复发货',
+      40057: '订单已取消,不能发货',
+      40058: '订单已退款,不能发货',
+      40059: '订单已关闭,不能发货',
+      41001: '缺少access_token参数',
+      41002: '缺少订单ID参数',
+      41003: '缺少物流类型参数',
+      41004: '缺少快递公司ID参数',
+      41005: '缺少运单号参数',
+      41006: '缺少同城配送员姓名',
+      41007: '缺少同城配送员手机号',
+      42001: 'access_token已过期',
+      43001: '需要GET请求',
+      43002: '需要POST请求',
+      43003: '需要HTTPS请求',
+      44001: '多媒体文件为空',
+      45001: '多媒体文件大小超过限制',
+      45002: '消息内容超过限制',
+      45003: '标题字段超过限制',
+      45004: '描述字段超过限制',
+      45005: '链接字段超过限制',
+      45006: '图片链接字段超过限制',
+      45007: '语音播放时间超过限制',
+      45008: '图文消息超过限制',
+      45009: '接口调用超过限制',
+      45010: '创建菜单个数超过限制',
+      45011: 'API调用太频繁,请稍候再试',
+      45015: '回复时间超过限制',
+      45016: '系统分组,不允许修改',
+      45017: '分组名字过长',
+      45018: '分组数量超过上限',
+      46001: '不存在媒体数据',
+      46002: '不存在的菜单版本',
+      46003: '不存在的菜单数据',
+      46004: '不存在的用户',
+      47001: '解析JSON/XML内容错误',
+      48001: 'api功能未授权',
+      50001: '用户未授权该api',
+      50002: '用户受限,可能是违规后接口被封禁'
+    };
+
+    const friendlyMsg = errorMap[errcode] || errmsg;
+    return `微信发货失败: ${friendlyMsg} (错误码: ${errcode})`;
+  }
+
+  /**
+   * 根据物流类型编码获取发货方式
+   */
+  private getDeliveryModeFromLogisticsType(logisticsType?: number): string {
+    switch (logisticsType) {
+      case 0:
+        return 'no_logistics';
+      case 1:
+        return 'express';
+      case 2:
+        return 'local_delivery';
+      default:
+        return 'unknown';
+    }
+  }
+
+  /**
+   * 根据快递公司ID获取公司名称
+   */
+  private getDeliveryCompanyName(deliveryId: string): string {
+    const deliveryMap: Record<string, string> = {
+      'SF': '顺丰速运',
+      'STO': '申通快递',
+      'YTO': '圆通速递',
+      'YD': '韵达快递',
+      'ZTO': '中通快递',
+      'HTKY': '百世快递',
+      'YZPY': '邮政快递',
+      'JD': '京东物流',
+      'YUNDA': '韵达快递',
+      'EMS': 'EMS',
+      'TTKDEX': '天天快递',
+      'DBL': '德邦物流',
+      'ZJS': '宅急送',
+      'QFKD': '全峰快递',
+      'UAPEX': '全一快递',
+      'CNEX': '佳吉快运',
+      'FAST': '快捷快递',
+      'POSTB': '邮政小包',
+      'GTO': '国通快递',
+      'ANE': '安能物流'
+    };
+
+    return deliveryMap[deliveryId] || deliveryId;
+  }
+
+
+
   /**
    * 快递公司名称到微信小店delivery_id的映射
    * 实际项目中需要从数据库或配置中获取
@@ -399,25 +1020,48 @@ export class MiniAuthService {
       throw new Error('微信小程序配置缺失');
     }
  
-    // 获取access_token
-    const accessToken = await this.getAccessToken(appId, appSecret);
+    // 获取access_token(支持租户隔离)
+    const accessToken = await this.getAccessToken(appId, appSecret, tenantId);
     return accessToken;
    }
 
   /**
-   * 获取微信access_token(带缓存机制)
+   * 获取微信access_token(带缓存机制,支持租户隔离
    */
-  private async getAccessToken(appId: string, appSecret: string): Promise<string> {
-    // 1. 首先尝试从Redis缓存获取
-    const cachedToken = await redisUtil.getWechatAccessToken(appId);
+  private async getAccessToken(appId: string, appSecret: string, tenantId?: number): Promise<string> {
+    // 1. 首先尝试从Redis缓存获取(支持租户隔离)
+    const cachedToken = await redisUtil.getWechatAccessToken(appId, tenantId);
 
     if (cachedToken) {
-      console.debug(`使用缓存的微信access_token,appId: ${appId}`);
-      return cachedToken;
+      // 检查缓存剩余时间,如果少于60秒则认为即将过期,重新获取
+      const ttl = await redisUtil.getWechatAccessTokenTTL(appId, tenantId);
+
+      // ttl返回值说明:
+      // - 正整数:剩余时间(秒)
+      // - -1:键存在但没有设置过期时间(不应该发生,因为我们设置了EX)
+      // - -2:键不存在(不应该发生,因为我们已经获取到了token)
+
+      if (ttl > 60) {
+        // 剩余时间大于60秒,使用缓存
+        console.debug(`使用缓存的微信access_token,appId: ${appId}, 租户ID: ${tenantId || '无'}, 剩余时间: ${ttl}秒`);
+        return cachedToken;
+      } else if (ttl === -1) {
+        // 键存在但没有设置过期时间,删除并重新获取
+        console.warn(`微信access_token缓存没有设置过期时间,删除并重新获取,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
+        await redisUtil.deleteWechatAccessToken(appId, tenantId);
+      } else if (ttl === -2) {
+        // 键不存在(虽然获取到了token,但可能被其他进程删除),重新获取
+        console.warn(`微信access_token缓存键不存在,重新获取,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
+      } else {
+        // ttl <= 60 且 > -2,剩余时间不足60秒,认为缓存即将过期,重新获取
+        console.debug(`微信access_token缓存即将过期,剩余时间: ${ttl}秒,重新获取,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
+        // 先删除即将过期的缓存
+        await redisUtil.deleteWechatAccessToken(appId, tenantId);
+      }
+    } else {
+      console.debug(`缓存中未找到微信access_token,从API获取,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
     }
 
-    console.debug(`缓存中未找到微信access_token,从API获取,appId: ${appId}`);
-
     // 2. 缓存中没有,调用微信API获取
     const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`;
 
@@ -431,12 +1075,12 @@ export class MiniAuthService {
       const accessToken = response.data.access_token;
       const expiresIn = response.data.expires_in || 7200; // 微信默认返回7200秒(2小时)
 
-      // 3. 将获取到的access_token存入Redis缓存
+      // 3. 将获取到的access_token存入Redis缓存(支持租户隔离)
       // 设置过期时间比微信返回的expires_in少100秒,确保安全
       const cacheExpiresIn = Math.max(expiresIn - 100, 600); // 最少缓存10分钟
-      await redisUtil.setWechatAccessToken(appId, accessToken, cacheExpiresIn);
+      await redisUtil.setWechatAccessToken(appId, accessToken, cacheExpiresIn, tenantId);
 
-      console.debug(`微信access_token获取成功并已缓存,appId: ${appId}, 过期时间: ${cacheExpiresIn}秒`);
+      console.debug(`微信access_token获取成功并已缓存,appId: ${appId}, 租户ID: ${tenantId || '无'}, 过期时间: ${cacheExpiresIn}秒`);
 
       return accessToken;
 
@@ -449,28 +1093,28 @@ export class MiniAuthService {
   }
 
   /**
-   * 强制刷新微信access_token(清除缓存并重新获取)
+   * 强制刷新微信access_token(清除缓存并重新获取,支持租户隔离
    */
-  private async refreshAccessToken(appId: string, appSecret: string): Promise<string> {
-    console.debug(`强制刷新微信access_token,appId: ${appId}`);
+  private async refreshAccessToken(appId: string, appSecret: string, tenantId?: number): Promise<string> {
+    console.debug(`强制刷新微信access_token,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
 
-    // 1. 清除缓存
-    await redisUtil.deleteWechatAccessToken(appId);
+    // 1. 清除缓存(支持租户隔离)
+    await redisUtil.deleteWechatAccessToken(appId, tenantId);
 
-    // 2. 重新获取
-    return await this.getAccessToken(appId, appSecret);
+    // 2. 重新获取(支持租户隔离)
+    return await this.getAccessToken(appId, appSecret, tenantId);
   }
 
   /**
-   * 检查微信access_token缓存状态
+   * 检查微信access_token缓存状态(支持租户隔离)
    */
-  private async checkAccessTokenCacheStatus(appId: string): Promise<{
+  private async checkAccessTokenCacheStatus(appId: string, tenantId?: number): Promise<{
     hasCache: boolean;
     ttl: number;
     isValid: boolean;
   }> {
-    const hasCache = await redisUtil.isWechatAccessTokenValid(appId);
-    const ttl = await redisUtil.getWechatAccessTokenTTL(appId);
+    const hasCache = await redisUtil.isWechatAccessTokenValid(appId, tenantId);
+    const ttl = await redisUtil.getWechatAccessTokenTTL(appId, tenantId);
 
     return {
       hasCache,

+ 455 - 35
packages/order-management-ui-mt/src/components/OrderManagement.tsx

@@ -1,4 +1,3 @@
-
 import { useState } from 'react';
 import { useQuery } from '@tanstack/react-query';
 import { useForm } from 'react-hook-form';
@@ -190,6 +189,222 @@ const formatWechatDate = (isoDateString: string | null | undefined): string => {
   }
 };
 
+// 获取微信小店快递公司列表
+const getWechatDeliveryCompanies = async (tenantId?: number): Promise<{ success: boolean; message: string; data?: any }> => {
+  try {
+    console.debug('准备获取微信小店快递公司列表:', { tenantId });
+
+    // 调用后端获取快递公司列表API
+    const response = await fetch('/api/v1/auth/get-delivery-companies', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        tenantId
+      }),
+    });
+
+    if (!response.ok) {
+      const errorText = await response.text();
+      console.error('获取快递公司列表API调用失败:', {
+        status: response.status,
+        statusText: response.statusText,
+        error: errorText
+      });
+
+      let errorMessage = `获取快递公司列表失败: ${response.status}`;
+      try {
+        const errorData = JSON.parse(errorText);
+        if (errorData.message) {
+          errorMessage = errorData.message;
+        }
+        if (errorData.error?.message) {
+          errorMessage = errorData.error.message;
+        }
+      } catch (e) {
+        // 如果无法解析为JSON,使用原始文本
+        if (errorText) {
+          errorMessage = errorText.substring(0, 200);
+        }
+      }
+
+      return {
+        success: false,
+        message: errorMessage,
+        error: errorText
+      };
+    }
+
+    const result = await response.json();
+    console.debug('获取快递公司列表成功:', result);
+
+    return {
+      success: true,
+      message: '获取快递公司列表成功',
+      data: result
+    };
+
+  } catch (error) {
+    console.error('调用获取快递公司列表API时出错:', error);
+    return {
+      success: false,
+      message: '获取快递公司列表失败',
+      error
+    };
+  }
+};
+
+// 调用微信小店发货API
+const sendWechatShopDelivery = async (order: OrderResponse, deliveryData: DeliveryRequest): Promise<{ success: boolean; message: string; data?: any }> => {
+  try {
+    console.debug('准备调用微信小店发货API:', {
+      orderId: order.id,
+      orderNo: order.orderNo,
+      deliveryType: deliveryData.deliveryType,
+      deliveryCompany: deliveryData.deliveryCompany,
+      deliveryNo: deliveryData.deliveryNo,
+      tenantId: order.tenantId
+    });
+
+    // 调用后端微信小店发货API
+    const response = await fetch('/api/v1/auth/send-wechat-shop-delivery', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        orderId: order.id,
+        orderNo: order.orderNo,
+        deliveryType: deliveryData.deliveryType,
+        deliveryCompany: deliveryData.deliveryCompany || null,
+        deliveryNo: deliveryData.deliveryNo || null,
+        tenantId: order.tenantId
+      }),
+    });
+
+    if (!response.ok) {
+      const errorText = await response.text();
+      console.error('微信小店发货API调用失败:', {
+        status: response.status,
+        statusText: response.statusText,
+        error: errorText
+      });
+      return {
+        success: false,
+        message: `微信小店发货失败: ${response.status}`,
+        error: errorText
+      };
+    }
+
+    const result = await response.json();
+    console.debug('微信小店发货成功:', result);
+
+    return {
+      success: true,
+      message: '微信小店发货成功',
+      data: result
+    };
+
+  } catch (error) {
+    console.error('调用微信小店发货API时出错:', error);
+    return {
+      success: false,
+      message: '微信小店发货失败',
+      error
+    };
+  }
+};
+
+// 调用微信小程序发货信息录入API
+const uploadShippingInfoToWechat = async (order: OrderResponse, deliveryData: DeliveryRequest, tenantId?: number): Promise<{ success: boolean; message: string; data?: any }> => {
+  try {
+    console.debug('准备调用微信小程序发货信息录入API:', {
+      orderId: order.id,
+      orderNo: order.orderNo,
+      deliveryType: deliveryData.deliveryType,
+      deliveryCompany: deliveryData.deliveryCompany,
+      deliveryNo: deliveryData.deliveryNo,
+      tenantId: tenantId || order.tenantId
+    });
+
+    // 根据发货类型准备参数
+    let expressInfo = undefined;
+    let localDeliveryInfo = undefined;
+
+    switch (deliveryData.deliveryType) {
+      case 1: // 物流快递
+        if (deliveryData.deliveryCompany && deliveryData.deliveryNo) {
+          // 注意:这里需要将快递公司名称转换为微信小店支持的delivery_id
+          // 实际项目中需要维护一个快递公司映射表,这里暂时使用快递公司名称
+          expressInfo = {
+            deliveryId: deliveryData.deliveryCompany, // 临时使用公司名称,实际应该用ID
+            waybillId: deliveryData.deliveryNo
+          };
+        }
+        break;
+      case 2: // 同城配送
+        // 同城配送需要配送员信息,这里使用默认值
+        // localDeliveryInfo = {
+        //   deliveryName: '配送员',
+        //   deliveryPhone: '13800138000'
+        // };
+        // break;
+      case 3: // 虚拟发货
+      case 4: // 用户自提
+        // 无需物流,不需要额外信息
+        break;
+    }
+
+    // 调用后端发货信息录入API
+    const response = await fetch('/api/v1/auth/upload-shipping-info', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        orderId: order.orderNo, // 使用订单号作为微信小程序订单ID
+        deliveryType: deliveryData.deliveryType, // 直接使用前端的deliveryType值
+        expressInfo,
+        localDeliveryInfo,
+        isAllDelivered: true,
+        tenantId: tenantId || order.tenantId
+      }),
+    });
+
+    if (!response.ok) {
+      const errorText = await response.text();
+      console.error('发货信息录入API调用失败:', {
+        status: response.status,
+        statusText: response.statusText,
+        error: errorText
+      });
+      return {
+        success: false,
+        message: `发货信息录入失败: ${response.status}`,
+        error: errorText
+      };
+    }
+
+    const result = await response.json();
+    console.debug('发货信息录入成功:', result);
+
+    return {
+      success: true,
+      message: '发货信息录入成功',
+      data: result
+    };
+
+  } catch (error) {
+    console.error('调用发货信息录入API时出错:', error);
+    return {
+      success: false,
+      message: '发货信息录入失败',
+      error
+    };
+  }
+};
+
 // 发货成功微信通知函数 - 使用新的配置化设计
 const sendDeliverySuccessNotification = async (order: OrderResponse, deliveryData: DeliveryRequest): Promise<WechatServiceMessageResult> => {
   // 检查是否有用户信息和openid
@@ -284,8 +499,8 @@ const deliveryTypeMap = {
   0: { label: '未发货', color: 'warning' },
   1: { label: '物流快递', color: 'info' },
   2: { label: '同城配送', color: 'info' },
-  3: { label: '用户自提', color: 'info' },
-  4: { label: '虚拟发货', color: 'info' },
+  3: { label: '虚拟发货', color: 'info' },
+  4: { label: '用户自提', color: 'info' },
 } as const;
 
 export const OrderManagement = () => {
@@ -302,6 +517,8 @@ export const OrderManagement = () => {
   const [selectedOrder, setSelectedOrder] = useState<OrderResponse | null>(null);
   const [deliveryModalOpen, setDeliveryModalOpen] = useState(false);
   const [deliveringOrder, setDeliveringOrder] = useState<OrderResponse | null>(null);
+  const [deliveryCompanies, setDeliveryCompanies] = useState<Array<{ delivery_id: string; delivery_name: string }>>([]);
+  const [loadingCompanies, setLoadingCompanies] = useState(false);
 
 
   // 表单实例
@@ -313,8 +530,8 @@ export const OrderManagement = () => {
   // 发货表单实例
   const deliveryForm = useForm<DeliveryRequest>({
     defaultValues: {
-      deliveryType: 1,
-      deliveryCompany: '',
+      deliveryType: 2, // 2表示同城配送
+      deliveryCompany: '__select__',
       deliveryNo: '',
       deliveryRemark: '',
     },
@@ -356,6 +573,67 @@ export const OrderManagement = () => {
     setSearchParams(prev => ({ ...prev, page: 1 }));
   };
 
+  // 检查交易管理状态
+  const handleCheckTradeManaged = async () => {
+    try {
+      console.debug('开始检查交易管理状态');
+
+      // 这里需要获取当前租户ID,可以从订单数据中获取第一个订单的tenantId
+      // 或者使用默认值,这里先使用默认值
+      const tenantId = data?.data?.[0]?.tenantId || undefined;
+
+      const response = await fetch('/api/v1/auth/get-is-trade-managed', {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          tenantId
+        }),
+      });
+
+      if (!response.ok) {
+        const errorText = await response.text();
+        console.error('检查交易管理状态失败:', {
+          status: response.status,
+          statusText: response.statusText,
+          error: errorText
+        });
+
+        let errorMessage = `检查交易管理状态失败: ${response.status}`;
+        try {
+          const errorData = JSON.parse(errorText);
+          if (errorData.message) {
+            errorMessage = errorData.message;
+          }
+        } catch (e) {
+          // 如果无法解析为JSON,使用原始文本
+          if (errorText) {
+            errorMessage = errorText.substring(0, 200);
+          }
+        }
+
+        toast.error(errorMessage);
+        return;
+      }
+
+      const result = await response.json();
+      console.debug('交易管理状态检查结果:', result);
+
+      if (result.success) {
+        const isTradeManaged = result.data?.is_trade_managed;
+        const statusText = isTradeManaged ? '已开启' : '未开启';
+        toast.success(`交易管理状态: ${statusText}`);
+      } else {
+        toast.error(result.message || '检查交易管理状态失败');
+      }
+
+    } catch (error) {
+      console.error('检查交易管理状态时出错:', error);
+      toast.error('检查交易管理状态失败,请重试');
+    }
+  };
+
   // 处理编辑订单
   const handleEditOrder = (order: OrderResponse) => {
     setEditingOrder(order);
@@ -374,14 +652,48 @@ export const OrderManagement = () => {
   };
 
   // 处理发货
-  const handleDeliveryOrder = (order: OrderResponse) => {
+  const handleDeliveryOrder = async (order: OrderResponse) => {
     setDeliveringOrder(order);
     deliveryForm.reset({
-      deliveryType: 1,
-      deliveryCompany: '',
+      deliveryType: 2, // 2表示同城配送
+      deliveryCompany: '__select__',
       deliveryNo: '',
       deliveryRemark: '',
     });
+
+    // // 获取快递公司列表
+    // setLoadingCompanies(true);
+    // try {
+    //   const result = await getWechatDeliveryCompanies(order.tenantId);
+    //   console.log("result:",result);
+    //   const resdata = result.data;
+    //   if (result.success && resdata.data?.company_list) {
+    //     const companyList = resdata.data.company_list;
+    //     setDeliveryCompanies(companyList);
+
+    //     // 如果有快递公司列表,自动选中第一个
+    //     if (companyList.length > 0) {
+    //       // 延迟设置,确保组件已经渲染
+    //       setTimeout(() => {
+    //         deliveryForm.setValue('deliveryCompany', companyList[0].delivery_name);
+    //       }, 0);
+    //     }
+    //   } else {
+    //     console.warn('获取快递公司列表失败或为空:', result);
+    //     setDeliveryCompanies([]);
+    //     // 如果获取失败,显示提示信息
+    //     if (resdata.message && !resdata.message.includes('获取快递公司列表成功')) {
+    //       toast.warning(`获取快递公司列表失败: ${resdata.message},请手动输入快递公司名称`);
+    //     }
+    //   }
+    // } catch (error) {
+    //   console.error('获取快递公司列表时出错:', error);
+    //   setDeliveryCompanies([]);
+    //   toast.warning('获取快递公司列表失败,请手动输入快递公司名称');
+    // } finally {
+    //   setLoadingCompanies(false);
+    // }
+
     setDeliveryModalOpen(true);
   };
 
@@ -418,6 +730,17 @@ export const OrderManagement = () => {
       return;
     }
 
+    // 处理快递公司选择值
+    if (data.deliveryCompany === '__select__' || data.deliveryCompany === '__manual__') {
+      data.deliveryCompany = null;
+    }
+
+    // 验证:如果选择物流快递方式,必须填写快递公司
+    if (data.deliveryType === 1 && !data.deliveryCompany) {
+      toast.error('请选择或输入快递公司');
+      return;
+    }
+
     try {
       // console.debug('发货请求数据:', {
       //   orderId: deliveringOrder.id,
@@ -480,30 +803,72 @@ export const OrderManagement = () => {
         toast.success(result.message || '发货成功');
         setDeliveryModalOpen(false);
         refetch();
-
-        // 发货成功后发送微信服务消息通知 - 使用新的配置化设计
+        // 发货成功后调用微信小程序发货信息录入API
         try {
-          const notificationResult = await sendDeliverySuccessNotification(deliveringOrder, data);
-
-          // 根据通知结果记录不同的日志
-          if (notificationResult.success) {
-            console.debug('微信发货通知发送成功:', notificationResult);
-          } else if (notificationResult.data?.skipped) {
-            // 用户未订阅,这是正常情况,记录为debug级别
-            console.debug('用户未订阅发货通知,跳过发送:', {
-              userId: deliveringOrder.user?.id,
-              username: deliveringOrder.user?.username,
-              reason: notificationResult.data.reason
-            });
+          const uploadResult = await uploadShippingInfoToWechat(deliveringOrder, data, deliveringOrder?.tenantId);
+
+          if (uploadResult.success) {
+            console.debug("微信小程序发货信息录入成功:", uploadResult);
+            // 可以在这里添加额外的成功处理,比如记录日志
           } else {
-            // 其他原因导致的失败,记录为warn级别
-            console.warn('微信发货通知发送失败,但发货成功:', notificationResult);
-            // 可以在这里添加额外的处理,比如记录到日志系统
+            // 微信小程序发货信息录入失败,记录警告但不影响主流程
+            console.warn("微信小程序发货信息录入失败,但系统发货成功:", uploadResult);
+            // 可以在这里添加额外的处理,比如记录到日志系统或发送告警
           }
-        } catch (notificationError) {
-          console.error('发送微信发货通知时发生异常:', notificationError);
+        } catch (uploadError) {
+          console.error("调用微信小程序发货信息录入API时发生异常:", uploadError);
           // 不阻止发货成功,只记录错误
         }
+
+
+        
+
+
+        // 发货成功后调用微信小店发货API
+        // try {
+        //   const wechatDeliveryResult = await sendWechatShopDelivery(deliveringOrder, data);
+
+        //   if (wechatDeliveryResult.success) {
+        //     console.debug('微信小店发货成功:', wechatDeliveryResult);
+        //     // 可以在这里添加额外的成功处理,比如记录日志
+        //   } else {
+        //     // 微信小店发货失败,记录警告但不影响主流程
+        //     console.warn('微信小店发货失败,但系统发货成功:', wechatDeliveryResult);
+        //     // 可以在这里添加额外的处理,比如记录到日志系统或发送告警
+        //   }
+        // } catch (wechatDeliveryError) {
+        //   console.error('调用微信小店发货API时发生异常:', wechatDeliveryError);
+        //   // 不阻止发货成功,只记录错误
+        // }
+
+
+
+
+
+        // 发货成功后发送微信服务消息通知 - 使用新的配置化设计
+        // try {
+        //   const notificationResult = await sendDeliverySuccessNotification(deliveringOrder, data);
+
+        //   // 根据通知结果记录不同的日志
+        //   if (notificationResult.success) {
+        //     console.debug('微信发货通知发送成功:', notificationResult);
+        //   } else if (notificationResult.data?.skipped) {
+        //     // 用户未订阅,这是正常情况,记录为debug级别
+        //     console.debug('用户未订阅发货通知,跳过发送:', {
+        //       userId: deliveringOrder.user?.id,
+        //       username: deliveringOrder.user?.username,
+        //       reason: notificationResult.data.reason
+        //     });
+        //   } else {
+        //     // 其他原因导致的失败,记录为warn级别
+        //     console.warn('微信发货通知发送失败,但发货成功:', notificationResult);
+        //     // 可以在这里添加额外的处理,比如记录到日志系统
+        //   }
+        // } catch (notificationError) {
+        //   console.error('发送微信发货通知时发生异常:', notificationError);
+        //   // 不阻止发货成功,只记录错误
+        // }
+
       } else {
         // 先尝试获取响应文本,避免JSON解析错误
         let errorText = '';
@@ -728,6 +1093,13 @@ export const OrderManagement = () => {
           <h1 className="text-2xl font-bold">订单管理</h1>
           <p className="text-muted-foreground">管理所有订单信息</p>
         </div>
+        {/* <Button
+          variant="outline"
+          onClick={() => handleCheckTradeManaged()}
+          data-testid="check-trade-managed-button"
+        >
+          检查交易管理状态
+        </Button> */}
       </div>
 
       {/* 搜索区域 */}
@@ -1180,7 +1552,7 @@ export const OrderManagement = () => {
                             </SelectTrigger>
                           </FormControl>
                           <SelectContent>
-                            <SelectItem value="1">物流快递</SelectItem>
+                            {/* <SelectItem value="1">物流快递</SelectItem> */}
                             <SelectItem value="2">同城配送</SelectItem>
                             <SelectItem value="3">用户自提</SelectItem>
                             <SelectItem value="4">虚拟发货</SelectItem>
@@ -1199,13 +1571,61 @@ export const OrderManagement = () => {
                         render={({ field }) => (
                           <FormItem>
                             <FormLabel>快递公司</FormLabel>
-                            <FormControl>
-                              <Input
-                                placeholder="输入快递公司名称"
-                                data-testid="delivery-company-input"
-                                {...field}
-                              />
-                            </FormControl>
+                            {deliveryForm.watch('deliveryCompany') === '__manual__' ? (
+                              <>
+                                <div className="flex gap-2 mb-2">
+                                  <Button
+                                    type="button"
+                                    variant="outline"
+                                    size="sm"
+                                    onClick={() => field.onChange('__select__')}
+                                  >
+                                    ← 返回选择列表
+                                  </Button>
+                                </div>
+                                <FormControl>
+                                  <Input
+                                    placeholder="请输入快递公司名称"
+                                    data-testid="delivery-company-manual-input"
+                                    value={field.value === '__manual__' ? '' : field.value}
+                                    onChange={(e) => field.onChange(e.target.value)}
+                                  />
+                                </FormControl>
+                              </>
+                            ) : (
+                              <Select
+                                onValueChange={field.onChange}
+                                value={field.value || ''}
+                                disabled={loadingCompanies}
+                              >
+                                <FormControl>
+                                  <SelectTrigger data-testid="delivery-company-select">
+                                    <SelectValue placeholder={
+                                      loadingCompanies ? "加载中..." : "选择快递公司"
+                                    } />
+                                  </SelectTrigger>
+                                </FormControl>
+                                <SelectContent>
+                                  <SelectItem value="__select__">请选择快递公司</SelectItem>
+                                  {deliveryCompanies.map((company) => (
+                                    <SelectItem
+                                      key={company.delivery_id}
+                                      value={company.delivery_name}
+                                    >
+                                      {company.delivery_name}
+                                    </SelectItem>
+                                  ))}
+                                  {deliveryCompanies.length === 0 && !loadingCompanies && (
+                                    <>
+                                      <SelectItem value="no_data" disabled>
+                                        暂无快递公司数据(API未授权)
+                                      </SelectItem>
+                                      <SelectItem value="__manual__">手动输入快递公司</SelectItem>
+                                    </>
+                                  )}
+                                </SelectContent>
+                              </Select>
+                            )}
                             <FormMessage />
                           </FormItem>
                         )}

+ 39 - 23
packages/shared-utils/src/utils/redis.util.ts

@@ -155,77 +155,93 @@ class RedisUtil {
   }
 
   /**
-   * 设置微信access_token缓存
+   * 设置微信access_token缓存(支持租户隔离)
    * @param appId 微信小程序appId
    * @param accessToken access_token值
    * @param expiresIn 过期时间(秒),微信返回的expires_in,默认7100秒(比微信的7200秒少100秒,确保安全)
+   * @param tenantId 租户ID,可选,用于多租户隔离
    */
-  async setWechatAccessToken(appId: string, accessToken: string, expiresIn: number = 7100): Promise<void> {
+  async setWechatAccessToken(appId: string, accessToken: string, expiresIn: number = 7100, tenantId?: number): Promise<void> {
     const client = await this.connect();
-    const key = `wechat_access_token:${appId}`;
+    const key = tenantId !== undefined
+      ? `wechat_access_token:${tenantId}:${appId}`
+      : `wechat_access_token:${appId}`;
     await client.set(key, accessToken, {
       EX: expiresIn
     });
-    console.debug(`微信access_token缓存设置成功,appId: ${appId}, 过期时间: ${expiresIn}秒`);
+    console.debug(`微信access_token缓存设置成功,appId: ${appId}, 租户ID: ${tenantId || '无'}, 过期时间: ${expiresIn}秒`);
   }
 
   /**
-   * 获取微信access_token缓存
+   * 获取微信access_token缓存(支持租户隔离)
    * @param appId 微信小程序appId
+   * @param tenantId 租户ID,可选,用于多租户隔离
    * @returns access_token值或null
    */
-  async getWechatAccessToken(appId: string): Promise<string | null> {
+  async getWechatAccessToken(appId: string, tenantId?: number): Promise<string | null> {
     const client = await this.connect();
-    const key = `wechat_access_token:${appId}`;
+    const key = tenantId !== undefined
+      ? `wechat_access_token:${tenantId}:${appId}`
+      : `wechat_access_token:${appId}`;
     const token = await client.get(key);
 
     if (token) {
-      console.debug(`从缓存获取微信access_token成功,appId: ${appId}`);
+      console.debug(`从缓存获取微信access_token成功,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
     } else {
-      console.debug(`缓存中未找到微信access_token,appId: ${appId}`);
+      console.debug(`缓存中未找到微信access_token,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
     }
 
     return token;
   }
 
   /**
-   * 删除微信access_token缓存
+   * 删除微信access_token缓存(支持租户隔离)
    * @param appId 微信小程序appId
+   * @param tenantId 租户ID,可选,用于多租户隔离
    */
-  async deleteWechatAccessToken(appId: string): Promise<void> {
+  async deleteWechatAccessToken(appId: string, tenantId?: number): Promise<void> {
     const client = await this.connect();
-    const key = `wechat_access_token:${appId}`;
+    const key = tenantId !== undefined
+      ? `wechat_access_token:${tenantId}:${appId}`
+      : `wechat_access_token:${appId}`;
     await client.del(key);
-    console.debug(`删除微信access_token缓存成功,appId: ${appId}`);
+    console.debug(`删除微信access_token缓存成功,appId: ${appId}, 租户ID: ${tenantId || '无'}`);
   }
 
   /**
-   * 检查微信access_token缓存是否有效
+   * 检查微信access_token缓存是否有效(支持租户隔离)
    * @param appId 微信小程序appId
+   * @param tenantId 租户ID,可选,用于多租户隔离
    * @returns 是否有效
    */
-  async isWechatAccessTokenValid(appId: string): Promise<boolean> {
-    const token = await this.getWechatAccessToken(appId);
+  async isWechatAccessTokenValid(appId: string, tenantId?: number): Promise<boolean> {
+    const token = await this.getWechatAccessToken(appId, tenantId);
     return !!token;
   }
 
   /**
-   * 获取微信access_token缓存的剩余生存时间
+   * 获取微信access_token缓存的剩余生存时间(支持租户隔离)
    * @param appId 微信小程序appId
+   * @param tenantId 租户ID,可选,用于多租户隔离
    * @returns 剩余时间(秒),-1表示永不过期,-2表示键不存在
    */
-  async getWechatAccessTokenTTL(appId: string): Promise<number> {
+  async getWechatAccessTokenTTL(appId: string, tenantId?: number): Promise<number> {
     const client = await this.connect();
-    const key = `wechat_access_token:${appId}`;
+    const key = tenantId !== undefined
+      ? `wechat_access_token:${tenantId}:${appId}`
+      : `wechat_access_token:${appId}`;
     return await client.ttl(key);
   }
 
   /**
    * 清除所有微信access_token缓存
+   * @param tenantId 租户ID,可选,如果提供则只清除该租户的缓存
    */
-  async clearAllWechatAccessTokens(): Promise<void> {
+  async clearAllWechatAccessTokens(tenantId?: number): Promise<void> {
     const client = await this.connect();
-    const pattern = `wechat_access_token:*`;
+    const pattern = tenantId !== undefined
+      ? `wechat_access_token:${tenantId}:*`
+      : `wechat_access_token:*`;
 
     // 使用SCAN命令遍历匹配的键并删除
     let cursor = 0;
@@ -240,11 +256,11 @@ class RedisUtil {
 
       if (keys.length > 0) {
         await client.del(keys);
-        console.debug(`批量删除微信access_token缓存,数量: ${keys.length}`);
+        console.debug(`批量删除微信access_token缓存,数量: ${keys.length}, 租户ID: ${tenantId || '所有'}`);
       }
     } while (cursor !== 0);
 
-    console.debug('所有微信access_token缓存已清除');
+    console.debug(`微信access_token缓存已清除,租户ID: ${tenantId || '所有'}`);
   }
 }