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/user/service/wx.ts |  129 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 125 insertions(+), 4 deletions(-)

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

--
Gitblit v1.9.1