From 0d90e20ec20f0aeee642756a7ebf9e8514174124 Mon Sep 17 00:00:00 2001
From: wangrong <wangrong@shsening.com>
Date: 星期一, 26 十月 2026 21:15:20 +0800
Subject: [PATCH] 微信小程序插件支持

---
 src/modules/base/db.json                    |   14 +
 src/modules/cs/socket/middleware/token.ts   |   13 +
 src/modules/user/controller/app/address.ts  |    8 
 typings/pay-wx.d.ts                         |   16 +
 src/modules/user/service/address.ts         |    4 
 src/modules/user/service/wx.ts              |  129 ++++++++++++
 src/modules/user/middleware/app.ts          |   27 ++
 typings/wxBaseMessage.d.ts                  |  153 +++++++++++++++
 src/modules/user/controller/app/login.ts    |   12 
 src/modules/app/controller/app/feedback.ts  |    6 
 src/modules/cs/controller/app/session.ts    |    4 
 src/modules/app/controller/app/complain.ts  |    6 
 src/modules/user/controller/app/info.ts     |   15 +
 src/modules/user/service/login.ts           |   84 +++++++
 src/modules/goods/controller/app/comment.ts |    4 
 src/modules/user/service/info.ts            |   29 ++
 typings/wx.d.ts                             |    7 
 src/modules/cs/controller/app/msg.ts        |    6 
 typings/plugin.d.ts                         |    2 
 src/modules/cs/service/session.ts           |    5 
 20 files changed, 493 insertions(+), 51 deletions(-)

diff --git a/src/modules/app/controller/app/complain.ts b/src/modules/app/controller/app/complain.ts
index aeaaa33..9bb9b9e 100644
--- a/src/modules/app/controller/app/complain.ts
+++ b/src/modules/app/controller/app/complain.ts
@@ -11,13 +11,13 @@
   entity: AppComplainEntity,
   insertParam: ctx => {
     return {
-      userId: ctx.user.id,
+      userId: ctx.user.userId,
     };
   },
   pageQueryOp: {
     fieldEq: ['a.type'],
     where: ctx => {
-      const userId = ctx.user.id;
+      const userId = ctx.user.userId;
       return [['a.userId = :userId', { userId }]];
     },
   },
@@ -31,7 +31,7 @@
 
   @Post('/submit', { summary: '鎻愪氦鎶曡瘔涓炬姤' })
   async submit(@Body() info) {
-    info.userId = this.ctx.user.id;
+    info.userId = this.ctx.user.userId;
     await this.appComplainService.submit(info);
     return this.ok();
   }
diff --git a/src/modules/app/controller/app/feedback.ts b/src/modules/app/controller/app/feedback.ts
index 17f4805..e4d2140 100644
--- a/src/modules/app/controller/app/feedback.ts
+++ b/src/modules/app/controller/app/feedback.ts
@@ -11,13 +11,13 @@
   entity: AppFeedbackEntity,
   insertParam: ctx => {
     return {
-      userId: ctx.user.id,
+      userId: ctx.user.userId,
     };
   },
   pageQueryOp: {
     fieldEq: ['a.type'],
     where: ctx => {
-      const userId = ctx.user.id;
+      const userId = ctx.user.userId;
       return [['a.userId = :userId', { userId }]];
     },
   },
@@ -31,7 +31,7 @@
 
   @Post('/submit', { summary: '鎻愪氦鎰忚鍙嶉' })
   async submit(@Body() info) {
-    info.userId = this.ctx.user.id;
+    info.userId = this.ctx.user.userId;
     await this.appFeedbackService.submit(info);
     return this.ok();
   }
diff --git a/src/modules/base/db.json b/src/modules/base/db.json
index dea4747..da8e5be 100644
--- a/src/modules/base/db.json
+++ b/src/modules/base/db.json
@@ -118,6 +118,20 @@
           "data": "400",
           "dataType": 0,
           "remark": "閫愬彴鏌ヨ璁惧鏃剁殑闂撮殧锛岄伩鍏嶈Е鍙� QPS 闄愭祦"
+      },
+      {
+          "keyName": "wx.mini.appId",
+          "name": "灏忕▼搴廇ppId",
+          "data": "wx88c344d1b0db243a",
+          "dataType": 0,
+          "remark": "寰俊灏忕▼搴� AppId锛岀敤浜庢墜鏈哄彿鐧诲綍"
+      },
+      {
+          "keyName": "wx.mini.appSecret",
+          "name": "灏忕▼搴廇ppSecret",
+          "data": "",
+          "dataType": 0,
+          "remark": "寰俊鍏紬骞冲彴-寮�鍙戠鐞�-寮�鍙戣缃腑鐨� AppSecret锛岀敤浜庣敤鎺堟潈鐮佹崲鍙栨墜鏈哄彿"
       }
   ],
   "base_sys_conf": [
diff --git a/src/modules/cs/controller/app/msg.ts b/src/modules/cs/controller/app/msg.ts
index 9aea144..453322c 100644
--- a/src/modules/cs/controller/app/msg.ts
+++ b/src/modules/cs/controller/app/msg.ts
@@ -38,7 +38,7 @@
       },
     ],
     where: ctx => {
-      return [['d.userId = :userId', { userId: ctx.user?.id }]];
+      return [['d.userId = :userId', { userId: ctx.user?.userId }]];
     },
   },
 })
@@ -51,7 +51,9 @@
 
   @Get('/unreadCount', { summary: '鏈娑堟伅鏁�' })
   async unreadCount() {
-    return this.ok(await this.csMsgService.unreadCount(this.ctx.user?.id, 1));
+    return this.ok(
+      await this.csMsgService.unreadCount(this.ctx.user?.userId, 1)
+    );
   }
 
   @Post('/read', { summary: '鏍囪宸茶' })
diff --git a/src/modules/cs/controller/app/session.ts b/src/modules/cs/controller/app/session.ts
index fe73446..4bb630b 100644
--- a/src/modules/cs/controller/app/session.ts
+++ b/src/modules/cs/controller/app/session.ts
@@ -15,11 +15,11 @@
 
   @Get('/detail', { summary: '浼氳瘽璇︽儏' })
   async detail() {
-    return this.ok(await this.csSessionService.detail(this.ctx.user?.id));
+    return this.ok(await this.csSessionService.detail(this.ctx.user?.userId));
   }
 
   @Post('/create', { summary: '鍒涘缓浼氳瘽' })
   async create() {
-    return this.ok(await this.csSessionService.create(this.ctx.user?.id));
+    return this.ok(await this.csSessionService.create(this.ctx.user?.userId));
   }
 }
diff --git a/src/modules/cs/service/session.ts b/src/modules/cs/service/session.ts
index 3994ad7..237025d 100644
--- a/src/modules/cs/service/session.ts
+++ b/src/modules/cs/service/session.ts
@@ -1,5 +1,5 @@
 import { Provide } from '@midwayjs/core';
-import { BaseService } from '@cool-midway/core';
+import { BaseService, CoolCommException } from '@cool-midway/core';
 import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 import { CsSessionEntity } from '../entity/session';
@@ -26,6 +26,9 @@
    * @returns
    */
   async create(userId: number) {
+    if (!userId) {
+      throw new CoolCommException('鐢ㄦ埛涓嶅瓨鍦�');
+    }
     const check = await this.detail(userId);
     if (check) {
       return check;
diff --git a/src/modules/cs/socket/middleware/token.ts b/src/modules/cs/socket/middleware/token.ts
index 42ac237..8e843f4 100644
--- a/src/modules/cs/socket/middleware/token.ts
+++ b/src/modules/cs/socket/middleware/token.ts
@@ -1,6 +1,7 @@
-import { Config, Middleware } from '@midwayjs/core';
+import { Config, Inject, Middleware } from '@midwayjs/core';
 import { NextFunction } from '@midwayjs/socketio';
 import * as jwt from 'jsonwebtoken';
+import { UserInfoService } from '../../../user/service/info';
 
 @Middleware()
 export class CsSocketTokenMiddleware {
@@ -9,6 +10,9 @@
 
   @Config('module.base.jwt')
   adminJwtConfig;
+
+  @Inject()
+  userInfoService: UserInfoService;
 
   resolve() {
     return async (ctx, next: NextFunction) => {
@@ -20,8 +24,13 @@
           token,
           isAdmin ? this.adminJwtConfig.secret : this.appJwtConfig.secret
         );
+        let userId = isAdmin ? data.userId : data.id;
+        if (!isAdmin) {
+          const info = await this.userInfoService.person(data.id);
+          userId = info?.id;
+        }
         ctx.connData = {
-          userId: isAdmin ? data.userId : data.id,
+          userId,
           isAdmin,
         };
       } catch (error) {
diff --git a/src/modules/goods/controller/app/comment.ts b/src/modules/goods/controller/app/comment.ts
index 521c871..8e9cc64 100644
--- a/src/modules/goods/controller/app/comment.ts
+++ b/src/modules/goods/controller/app/comment.ts
@@ -21,7 +21,7 @@
   entity: GoodsCommentEntity,
   insertParam: ctx => {
     return {
-      userId: ctx.user.id,
+      userId: ctx.user.userId,
     };
   },
   pageQueryOp: {
@@ -49,7 +49,7 @@
     @Body('orderId') orderId: number
   ) {
     return this.ok(
-      await this.goodsCommentService.submit(this.ctx.user.id, orderId, data)
+      await this.goodsCommentService.submit(this.ctx.user.userId, orderId, data)
     );
   }
 }
diff --git a/src/modules/user/controller/app/address.ts b/src/modules/user/controller/app/address.ts
index ae3eba5..bcf018d 100644
--- a/src/modules/user/controller/app/address.ts
+++ b/src/modules/user/controller/app/address.ts
@@ -13,12 +13,12 @@
   service: UserAddressService,
   insertParam: ctx => {
     return {
-      userId: ctx.user.id,
+      userId: ctx.user.userId,
     };
   },
   pageQueryOp: {
     where: async ctx => {
-      return [['userId =:userId', { userId: ctx.user.id }]];
+      return [['userId =:userId', { userId: ctx.user.userId }]];
     },
     addOrderBy: {
       isDefault: 'DESC',
@@ -34,6 +34,8 @@
 
   @Get('/default', { summary: '榛樿鍦板潃' })
   async default() {
-    return this.ok(await this.userAddressService.default(this.ctx.user.id));
+    return this.ok(
+      await this.userAddressService.default(this.ctx.user.userId)
+    );
   }
 }
diff --git a/src/modules/user/controller/app/info.ts b/src/modules/user/controller/app/info.ts
index d96e807..0bc8ab5 100644
--- a/src/modules/user/controller/app/info.ts
+++ b/src/modules/user/controller/app/info.ts
@@ -1,4 +1,10 @@
-import { CoolController, BaseController } from '@cool-midway/core';
+import {
+  CoolController,
+  BaseController,
+  CoolUrlTag,
+  CoolTag,
+  TagTypes,
+} from '@cool-midway/core';
 import { Body, Get, Inject, Post } from '@midwayjs/core';
 import { UserInfoService } from '../../service/info';
 import { UserInfoEntity } from '../../entity/info';
@@ -6,6 +12,7 @@
 /**
  * 鐢ㄦ埛淇℃伅
  */
+@CoolUrlTag()
 @CoolController({
   api: [],
   entity: UserInfoEntity,
@@ -63,10 +70,10 @@
     );
   }
 
-   @Post('/dashboard', { summary: '鑾峰彇鐢ㄦ埛纰崇Н鍒嗕俊鎭�' })
+  @CoolTag(TagTypes.IGNORE_TOKEN)
+  @Post('/dashboard', { summary: '鑾峰彇鐢ㄦ埛纰崇Н鍒嗕俊鎭�' })
   async dashboard(@Body() body) {
-    console.log(body)
-    const {unionid} = body;
+    const { unionid } = body;
     return this.ok(
       await this.userInfoService.dashboard(
         unionid
diff --git a/src/modules/user/controller/app/login.ts b/src/modules/user/controller/app/login.ts
index 3f76431..4b26801 100644
--- a/src/modules/user/controller/app/login.ts
+++ b/src/modules/user/controller/app/login.ts
@@ -59,15 +59,21 @@
   }
 
   @CoolTag(TagTypes.IGNORE_TOKEN)
-  @Post('/miniPhone', { summary: '缁戝畾灏忕▼搴忔墜鏈哄彿' })
+  @Post('/miniPhone', { summary: '灏忕▼搴忔墜鏈哄彿鎺堟潈鐧诲綍' })
   async miniPhone(@Body() body) {
-    const { code, encryptedData, iv } = body;
+    const { code, encryptedData, iv, loginCode } = body;
     return this.ok(
-      await this.userLoginService.miniPhone(code, encryptedData, iv)
+      await this.userLoginService.miniPhone(code, encryptedData, iv, loginCode)
     );
   }
 
   @CoolTag(TagTypes.IGNORE_TOKEN)
+  @Post('/miniAuto', { summary: '灏忕▼搴忛潤榛樼櫥褰�' })
+  async miniAuto(@Body('code') code: string) {
+    return this.ok(await this.userLoginService.miniAuto(code));
+  }
+
+  @CoolTag(TagTypes.IGNORE_TOKEN)
   @Get('/captcha', { summary: '鍥剧墖楠岃瘉鐮�' })
   async captcha(
     @Query('width') width: number,
diff --git a/src/modules/user/middleware/app.ts b/src/modules/user/middleware/app.ts
index 5988fa7..d97cb13 100644
--- a/src/modules/user/middleware/app.ts
+++ b/src/modules/user/middleware/app.ts
@@ -5,6 +5,7 @@
 import * as _ from 'lodash';
 import { CoolCommException, CoolUrlTagData, TagTypes } from '@cool-midway/core';
 import { Utils } from '../../../comm/utils';
+import { UserInfoService } from '../service/info';
 
 /**
  * 鐢ㄦ埛
@@ -38,14 +39,30 @@
       let { url } = ctx;
       url = url.replace(this.prefix, '').split('?')[0];
       if (_.startsWith(url, '/app/')) {
-        const token = ctx.get('Authorization');
+        let token = ctx.get('Authorization') || '';
+        token = token.replace(/^Bearer\s+/i, '').trim();
         try {
           ctx.user = jwt.verify(token, this.jwtConfig.secret);
-
-          if (ctx.user.isRefresh) {
-            throw new CoolCommException('鐧诲綍澶辨晥~');
+        } catch (error) {
+          ctx.user = undefined;
+          if (token) {
+            console.error('[user] token鏃犳晥:', (error as Error).message);
           }
-        } catch (error) {}
+        }
+        if (ctx.user?.isRefresh) {
+          ctx.user = undefined;
+        } else if (ctx.user?.id) {
+          try {
+            // 浠ょ墝閲岀殑 id 鏄� unionid锛堟墜鏈哄彿鐧诲綍鏃跺氨鏄墜鏈哄彿锛夛紝鏁村瀷 userId 鍒楄鐢ㄧ敤鎴蜂富閿�
+            const userInfoService = await ctx.requestContext.getAsync(
+              UserInfoService
+            );
+            const info = await userInfoService.person(ctx.user.id);
+            if (info?.id) {
+              ctx.user.userId = info.id;
+            }
+          } catch (error) {}
+        }
         // 浣跨敤matchUrl鏂规硶鏉ユ鏌RL鏄惁搴旇琚拷鐣�
         const isIgnored = this.ignoreUrls.some(pattern =>
           this.utils.matchUrl(pattern, url)
diff --git a/src/modules/user/service/address.ts b/src/modules/user/service/address.ts
index a478640..d676bab 100644
--- a/src/modules/user/service/address.ts
+++ b/src/modules/user/service/address.ts
@@ -27,7 +27,7 @@
   async list() {
     return this.userAddressEntity
       .createQueryBuilder()
-      .where('userId = :userId ', { userId: this.ctx.user.id })
+      .where('userId = :userId ', { userId: this.ctx.user.userId })
       .addOrderBy('isDefault', 'DESC')
       .getMany();
   }
@@ -44,7 +44,7 @@
           .createQueryBuilder()
           .update()
           .set({ isDefault: false })
-          .where('userId = :userId ', { userId: this.ctx.user.id })
+          .where('userId = :userId ', { userId: this.ctx.user.userId })
           .andWhere('id != :id', { id: data.id })
           .execute();
       }
diff --git a/src/modules/user/service/info.ts b/src/modules/user/service/info.ts
index c918e6f..73f26c9 100644
--- a/src/modules/user/service/info.ts
+++ b/src/modules/user/service/info.ts
@@ -50,8 +50,17 @@
    * @returns
    */
   async person(id) {
-    const info = await this.userInfoEntity.findOneBy({ id: Equal(id) });
-    //delete info.password;
+    const idText = id === undefined || id === null ? '' : String(id);
+    // 鎵嬫満鍙风櫥褰曠鍙戠殑鏄� unionid锛堟墜鏈哄彿锛夛紝涓嶈兘鎷垮幓鏌ユ暣鍨嬩富閿�
+    let info = null;
+    if (/^\d{1,10}$/.test(idText)) {
+      info = await this.userInfoEntity.findOneBy({ id: Equal(Number(idText)) });
+    }
+    if (!info && idText) {
+      info = await this.userInfoEntity.findOneBy({
+        unionid: Equal(idText),
+      });
+    }
     return info;
   }
 
@@ -60,8 +69,12 @@
    * @param userId
    */
   async logoff(userId: number) {
+    const info = await this.person(userId);
+    if (!info) {
+      throw new CoolCommException('鐢ㄦ埛涓嶅瓨鍦�');
+    }
     await this.userInfoEntity.update(
-      { id: userId },
+      { id: info.id },
       {
         status: 2,
         phone: "",
@@ -92,7 +105,7 @@
       }
     } catch (err) { }
     try {
-      return await this.userInfoEntity.update({ id }, param);
+      return await this.userInfoEntity.update({ id: info.id }, param);
     } catch (err) {
       throw new CoolCommException('鏇存柊澶辫触锛屽弬鏁伴敊璇垨鑰呮墜鏈哄彿宸插瓨鍦�');
     }
@@ -116,7 +129,7 @@
       throw new CoolCommException('楠岃瘉鐮侀敊璇�');
     }
 
-    await this.userInfoEntity.update(user.unionid, {
+    await this.userInfoEntity.update(user.id, {
       password: md5(password),
     });
   }
@@ -132,7 +145,11 @@
     if (!check) {
       throw new CoolCommException('楠岃瘉鐮侀敊璇�');
     }
-    await this.userInfoEntity.update({ id: userId }, { phone });
+    const info = await this.person(userId);
+    if (!info) {
+      throw new CoolCommException('鐢ㄦ埛涓嶅瓨鍦�');
+    }
+    await this.userInfoEntity.update({ id: info.id }, { phone });
   }
 
   /**
diff --git a/src/modules/user/service/login.ts b/src/modules/user/service/login.ts
index f1260bd..4964cfe 100644
--- a/src/modules/user/service/login.ts
+++ b/src/modules/user/service/login.ts
@@ -78,12 +78,85 @@
    * @param encryptedData
    * @param iv
    */
-  async miniPhone(code, encryptedData, iv) {
+  async miniPhone(code, encryptedData, iv, loginCode?) {
     const phone = await this.userWxService.miniPhone(code, encryptedData, iv);
-    if (phone) {
-      return await this.phone(phone);
-    } else {
+    if (!phone) {
       throw new CoolCommException('鑾峰緱鎵嬫満鍙峰け璐ワ紝璇锋鏌ラ厤缃�');
+    }
+    const token = await this.phone(phone);
+    await this.bindMiniOpenid(phone, loginCode);
+    return {
+      ...token,
+      phone,
+    };
+  }
+
+  /**
+   * 灏忕▼搴忓啀娆℃墦寮�鏃讹紝鐢� openid 闈欓粯鐧诲綍
+   */
+  async miniAuto(code: string) {
+    if (!code) {
+      return null;
+    }
+    const session = await this.userWxService.miniSession(code);
+    if (!session?.openid || session.errcode) {
+      return null;
+    }
+    const wx = await this.userWxEntity.findOneBy({
+      openid: Equal(session.openid),
+    });
+    if (!wx?.unionid) {
+      return null;
+    }
+    const user = await this.userInfoEntity.findOneBy({
+      unionid: Equal(wx.unionid),
+    });
+    if (!user?.phone || user.status === 2) {
+      return null;
+    }
+    const token = await this.token({ id: user.unionid });
+    return {
+      ...token,
+      phone: user.phone,
+      nickName: user.nickName,
+      avatarUrl: user.avatarUrl,
+      unionid: user.unionid,
+      id: user.id,
+    };
+  }
+
+  /**
+   * 鎶婂綋鍓嶅井淇� openid 璁板埌鎵嬫満鍙疯处鍙蜂笂锛屼緵涓嬫闈欓粯鐧诲綍
+   */
+  async bindMiniOpenid(phone: string, loginCode?: string) {
+    if (!loginCode) {
+      return;
+    }
+    try {
+      const session = await this.userWxService.miniSession(loginCode);
+      if (!session?.openid || session.errcode) {
+        return;
+      }
+      const user = await this.userInfoEntity.findOneBy({
+        phone: Equal(phone),
+      });
+      if (!user) {
+        return;
+      }
+      const unionid = user.unionid || session.unionid || phone;
+      const exist = await this.userWxEntity.findOneBy({
+        openid: Equal(session.openid),
+      });
+      await this.userWxEntity.save({
+        id: exist?.id,
+        openid: session.openid,
+        unionid,
+        nickName: user.nickName,
+        avatarUrl: user.avatarUrl,
+        type: 0,
+      });
+    } catch (err) {
+      // 缁戝畾 openid 澶辫触涓嶅奖鍝嶆湰娆℃墜鏈哄彿鐧诲綍
     }
   }
 
@@ -115,7 +188,8 @@
       user = {
         phone,
         unionid: phone,
-        loginType: 2,
+        loginType: 0,
+        departmentId: 1,
         nickName: phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2'),
       };
       await this.userInfoEntity.insert(user);
diff --git a/src/modules/user/service/wx.ts b/src/modules/user/service/wx.ts
index 9c1c97a..6c85ac7 100644
--- a/src/modules/user/service/wx.ts
+++ b/src/modules/user/service/wx.ts
@@ -6,6 +6,7 @@
 import * as moment from 'moment';
 import { Equal, Repository } from 'typeorm';
 import { v1 as uuid } from 'uuid';
+import { BaseSysParamEntity } from '../../base/entity/sys/param';
 import { PluginService } from '../../plugin/service/info';
 import { UserInfoEntity } from '../entity/info';
 import { UserWxEntity } from '../entity/wx';
@@ -18,14 +19,22 @@
   @Config('module.user')
   config;
 
+  @Config('wxMini')
+  wxMini: { appId?: string; appSecret?: string };
+
   @InjectEntityModel(UserInfoEntity)
   userInfoEntity: Repository<UserInfoEntity>;
 
   @InjectEntityModel(UserWxEntity)
   userWxEntity: Repository<UserWxEntity>;
 
+  @InjectEntityModel(BaseSysParamEntity)
+  baseSysParamEntity: Repository<BaseSysParamEntity>;
+
   @Inject()
   pluginService: PluginService;
+
+  private miniTokenCache: { appId: string; token: string; expireAt: number };
 
   /**
    * 鑾峰緱鎻掍欢瀹炰緥
@@ -49,6 +58,68 @@
   async getMiniApp() {
     const wxPlugin: any = await this.getPlugin();
     return wxPlugin.MiniApp();
+  }
+
+  async readParam(key: string) {
+    const row = await this.baseSysParamEntity.findOneBy({ keyName: key });
+    return (row?.data || '').trim();
+  }
+
+  /**
+   * 灏忕▼搴忚处鍙枫�備紭鍏堟彃浠讹紝鍏舵绯荤粺鍙傛暟 wx.mini.appId / wx.mini.appSecret
+   */
+  async getMiniCredential() {
+    try {
+      const app = await this.getMiniApp();
+      const account = app.getAccount();
+      const appId = account.getAppId?.() || account.appId;
+      const secret = account.getSecret?.() || account.appSecret || account.secret;
+      if (appId && secret) {
+        return { appId, secret };
+      }
+    } catch (error) {
+      // 鏈畨瑁呭井淇℃彃浠舵椂鏀圭敤绯荤粺鍙傛暟
+    }
+
+    const paramAppId = await this.readParam('wx.mini.appId');
+    const paramSecret = await this.readParam('wx.mini.appSecret');
+    const appId = paramAppId || this.wxMini?.appId || '';
+    const secret = paramSecret || this.wxMini?.appSecret || '';
+    if (!appId || !secret) {
+      throw new CoolCommException(
+        '鏈厤缃皬绋嬪簭瀵嗛挜銆傝鍦ㄧ郴缁熷弬鏁板~鍐� wx.mini.appSecret锛堝井淇″叕浼楀钩鍙�-寮�鍙戠鐞�-寮�鍙戣缃級'
+      );
+    }
+    return { appId, secret };
+  }
+
+  async getMiniAccessToken() {
+    const { appId, secret } = await this.getMiniCredential();
+    const now = Date.now();
+    if (
+      this.miniTokenCache &&
+      this.miniTokenCache.appId === appId &&
+      this.miniTokenCache.expireAt > now
+    ) {
+      return this.miniTokenCache.token;
+    }
+
+    const { data } = await axios.get('https://api.weixin.qq.com/cgi-bin/token', {
+      params: {
+        grant_type: 'client_credential',
+        appid: appId,
+        secret,
+      },
+    });
+    if (!data?.access_token) {
+      throw new CoolCommException(data?.errmsg || '鑾峰彇寰俊鍑瘉澶辫触锛岃妫�鏌� AppSecret');
+    }
+    this.miniTokenCache = {
+      appId,
+      token: data.access_token,
+      expireAt: now + Math.max(60, (data.expires_in || 7200) - 120) * 1000,
+    };
+    return data.access_token;
   }
 
   /**
@@ -215,10 +286,20 @@
    * @param conf 閰嶇疆
    */
   async miniSession(code) {
-    const app = await this.getMiniApp();
-    const utils = app.getUtils();
-    const result = await utils.codeToSession(code);
-    return result;
+    const { appId, secret } = await this.getMiniCredential();
+    const { data } = await axios.get(
+      'https://api.weixin.qq.com/sns/jscode2session',
+      {
+        params: {
+          appid: appId,
+          secret,
+          js_code: code,
+          grant_type: 'authorization_code',
+        },
+      }
+    );
+    console.log('[miniSession] appId=', appId, 'code鍓�6浣�=', String(code).slice(0,6), 'ret=', JSON.stringify(data));
+    return data;
   }
 
   /**
@@ -255,6 +336,11 @@
    * @param iv
    */
   async miniPhone(code, encryptedData, iv) {
+    // 鏂扮増鑾峰彇鎵嬫満鍙峰彧杩斿洖鍔ㄦ�佷护鐗岋紝鐢辨湇鍔$鍚戝井淇℃崲鍙栨墜鏈哄彿
+    if (!encryptedData || !iv) {
+      return await this.getPhoneNumber(code);
+    }
+
     const session = await this.miniSession(code);
     if (session.errcode) {
       throw new CoolCommException('鑾峰彇鎵嬫満鍙峰け璐ワ紝璇峰埛鏂伴噸璇�');
@@ -268,6 +354,41 @@
   }
 
   /**
+   * 鐢ㄦ墜鏈哄彿鍔ㄦ�佷护鐗屾崲鍙栨墜鏈哄彿
+   * @param code getPhoneNumber 杩斿洖鐨� code
+   */
+  async getPhoneNumber(code: string) {
+    if (!code) {
+      throw new CoolCommException('鏈幏鍙栧埌鎵嬫満鍙锋巿鏉�');
+    }
+
+    const accessToken = await this.getMiniAccessToken();
+
+    const { data } = await axios.post(
+      'https://api.weixin.qq.com/wxa/business/getuserphonenumber',
+      { code },
+      {
+        params: {
+          access_token: accessToken,
+        },
+      }
+    );
+
+    if (data?.errcode) {
+      throw new CoolCommException(data.errmsg || '鑾峰彇鎵嬫満鍙峰け璐�');
+    }
+
+    const phone =
+      data?.phone_info?.purePhoneNumber || data?.phone_info?.phoneNumber;
+
+    if (!phone) {
+      throw new CoolCommException('鑾峰緱鎵嬫満鍙峰け璐ワ紝璇锋鏌ラ厤缃�');
+    }
+
+    return phone;
+  }
+
+  /**
    * 灏忕▼搴忎俊鎭В瀵�
    * @param encryptedData
    * @param iv
diff --git a/typings/pay-wx.d.ts b/typings/pay-wx.d.ts
index 035a8fc..1ce2b6e 100644
--- a/typings/pay-wx.d.ts
+++ b/typings/pay-wx.d.ts
@@ -23,9 +23,16 @@
     mchid: string;
     key: string;
     notify_url: string;
-    publicKey: Buffer;
-    privateKey: Buffer;
+    publicKey: string;
+    privateKey: string;
+    verifyPublicKey?: string;
   }): Promise<any>;
+  /**
+   * 鑾峰緱Buffer
+   * @param path
+   * @returns
+   */
+  getBuffer(path: string): Promise<Buffer<any>>;
   /**
    * 鏍规嵁缁欏畾鐨� URL 涓嬭浇鏂囦欢骞惰繑鍥炰竴涓� Buffer銆�
    * @param {string} url 鏂囦欢鐨� URL
@@ -45,8 +52,9 @@
       mchid: string;
       key: string;
       notify_url: string;
-      publicKey: Buffer;
-      privateKey: Buffer;
+      publicKey: string;
+      privateKey: string;
+      verifyPublicKey?: string;
     },
   ): Promise<any>;
 }
diff --git a/typings/plugin.d.ts b/typings/plugin.d.ts
index 14b5d95..a5a69f7 100644
--- a/typings/plugin.d.ts
+++ b/typings/plugin.d.ts
@@ -1,3 +1,4 @@
+import * as wxBaseMessage from './wxBaseMessage';
 import * as wuliu from './wuliu';
 import * as pay_wx from './pay-wx';
 import * as wx from './wx';
@@ -15,4 +16,5 @@
   wx: wx.CoolPlugin;
   'pay-wx': pay_wx.CoolPlugin;
   wuliu: wuliu.CoolPlugin;
+  wxBaseMessage: wxBaseMessage.CoolPlugin;
 }
diff --git a/typings/wx.d.ts b/typings/wx.d.ts
index a1f8c73..efa34f3 100644
--- a/typings/wx.d.ts
+++ b/typings/wx.d.ts
@@ -7,6 +7,7 @@
   Work,
   OpenWork,
 } from 'node-easywechat';
+import { IncomingMessage } from 'http';
 /**
  * 寰俊
  */
@@ -18,6 +19,12 @@
    */
   OfficialAccount(config?: any): Promise<any>;
   /**
+   * 鍒涘缓璇锋眰
+   * @param req
+   * @returns
+   */
+  createRequest(req: IncomingMessage): Promise<any>;
+  /**
    * 灏忕▼搴�
    * @param config
    * @returns
diff --git a/typings/wxBaseMessage.d.ts b/typings/wxBaseMessage.d.ts
new file mode 100644
index 0000000..a4ca04b
--- /dev/null
+++ b/typings/wxBaseMessage.d.ts
@@ -0,0 +1,153 @@
+import { BasePlugin } from '@cool-midway/plugin-cli';
+interface TemplateDataItem {
+  value: string;
+  color?: string;
+}
+interface TemplateData {
+  [key: string]: TemplateDataItem;
+}
+interface MiniProgramParams {
+  appid?: string;
+  pagepath?: string;
+}
+interface SendTemplateMessageParams {
+  touser: string;
+  template_id: string;
+  url?: string;
+  miniprogram?: MiniProgramParams;
+  data: TemplateData;
+  client_msg_id?: string;
+}
+interface SendResult {
+  msgid: number;
+  errcode: number;
+  errmsg: string;
+}
+interface MassSendFilter {
+  is_to_all?: boolean;
+  tag_id?: string;
+}
+interface MassSendByOpenIdParams {
+  touser: string[];
+  msgtype: string;
+  mpnews?: {
+    media_id: string;
+  };
+  text?: {
+    content: string;
+  };
+  voice?: {
+    media_id: string;
+  };
+  image?: {
+    media_ids: string[];
+    recommend?: string;
+    title?: string;
+    need_open_comment?: number;
+    only_fans_can_comment?: number;
+  };
+  mpvideo?: {
+    media_id: string;
+    title?: string;
+    description?: string;
+  };
+  wxcard?: {
+    card_id: string;
+  };
+  send_ignore_reprint?: number;
+  clientmsgid?: string;
+}
+interface MassSendByTagParams {
+  filter: MassSendFilter;
+  msgtype: string;
+  mpnews?: {
+    media_id: string;
+  };
+  text?: {
+    content: string;
+  };
+  voice?: {
+    media_id: string;
+  };
+  image?: {
+    media_ids: string[];
+    recommend?: string;
+    title?: string;
+    need_open_comment?: number;
+    only_fans_can_comment?: number;
+  };
+  mpvideo?: {
+    media_id: string;
+    title?: string;
+    description?: string;
+  };
+  wxcard?: {
+    card_id: string;
+  };
+  clientmsgid?: string;
+}
+interface MassSendResult {
+  type: string;
+  msg_id: number;
+  msg_data_id: number;
+  errcode: number;
+  errmsg: string;
+}
+interface SubscribeDataItem {
+  value: string;
+}
+interface SubscribeData {
+  [key: string]: SubscribeDataItem;
+}
+interface SendSubscribeMsgParams {
+  template_id: string;
+  touser: string;
+  page?: string;
+  data: SubscribeData;
+  miniprogram_state: string;
+  lang?: string;
+}
+interface SubscribeResult {
+  errcode: number;
+  errmsg: string;
+}
+interface OneTimeMiniProgram {
+  appid: string;
+  pagepath: string;
+}
+interface OneTimeMsgData {
+  content: {
+    value: string;
+    color?: string;
+  };
+}
+interface SendOneTimeSubscribeParams {
+  touser: string;
+  template_id: string;
+  url?: string;
+  miniprogram?: OneTimeMiniProgram;
+  scene: string;
+  title: string;
+  data: OneTimeMsgData;
+}
+interface OneTimeSubscribeResult {
+  errcode: number;
+  errmsg: string;
+}
+export declare class CoolPlugin extends BasePlugin {
+  private getAccessToken;
+  private handleApiError;
+  private validateMassSendParams;
+  massSendByOpenId(params: MassSendByOpenIdParams): Promise<MassSendResult>;
+  massSendByTag(params: MassSendByTagParams): Promise<MassSendResult>;
+  sendTemplateMessage(params: SendTemplateMessageParams): Promise<SendResult>;
+  batchSendTemplateMessage(
+    paramsList: SendTemplateMessageParams[],
+  ): Promise<SendResult[]>;
+  sendSubscribeMsg(params: SendSubscribeMsgParams): Promise<SubscribeResult>;
+  sendOneTimeSubscribeMsg(
+    params: SendOneTimeSubscribeParams,
+  ): Promise<OneTimeSubscribeResult>;
+}
+export declare const Plugin: typeof CoolPlugin;
+export {};

--
Gitblit v1.9.1