Bläddra i källkod

添加微信工具类

chenritian 2 veckor sedan
förälder
incheckning
5d43a6406e

+ 17 - 1
entry/oh-package-lock.json5

@@ -6,10 +6,19 @@
   "lockfileVersion": 3,
   "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
   "specifiers": {
+    "@abner/log@^1.0.5": "@abner/log@1.0.5",
     "@ibestservices/ibest-ui@^2.1.2": "@ibestservices/ibest-ui@2.2.5",
-    "@ohos/axios@^2.2.4": "@ohos/axios@2.2.7"
+    "@ohos/axios@^2.2.4": "@ohos/axios@2.2.7",
+    "@tencent/wechat_open_sdk@^1.0.14": "@tencent/wechat_open_sdk@1.0.16"
   },
   "packages": {
+    "@abner/log@1.0.5": {
+      "name": "",
+      "version": "1.0.5",
+      "integrity": "sha512-ifkzK9CKO4Rq6+BrYqT0NE5pkFfONWgBtkPTXSqJHn4vzXwEkVIWUl0v8fmgtHE+8XALi3AiBGu0ldqkrgsZ7Q==",
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@abner/log/-/log-1.0.5.har",
+      "registryType": "ohpm"
+    },
     "@ibestservices/ibest-ui@2.2.5": {
       "name": "",
       "version": "2.2.5",
@@ -23,6 +32,13 @@
       "integrity": "sha512-oiGb0GheCk6h7MiqZvYPsVOdyLorq7e+PU26EHeBgX5r3rAkVbDR7bCF3n5rDU9mb5R/mgckA/um1ZgYGJ2EBQ==",
       "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/axios/-/axios-2.2.7.har",
       "registryType": "ohpm"
+    },
+    "@tencent/wechat_open_sdk@1.0.16": {
+      "name": "",
+      "version": "1.0.16",
+      "integrity": "sha512-/vqcsHFQlQ1UWybijbRTWlT+9BqRUcE/FWsYayEoVwreOeGJ/pdxNHZFkU0EManYRxnqzLoL4SB8HGJNdsm9TA==",
+      "resolved": "https://ohpm.openharmony.cn/ohpm/@tencent/wechat_open_sdk/-/wechat_open_sdk-1.0.16.har",
+      "registryType": "ohpm"
     }
   }
 }

+ 3 - 1
entry/oh-package.json5

@@ -7,7 +7,9 @@
   "license": "",
   "dependencies": {
     "@ibestservices/ibest-ui": "^2.1.2",
-    "@ohos/axios": "^2.2.4"
+    "@ohos/axios": "^2.2.4",
+    "@tencent/wechat_open_sdk": "^1.0.14",
+    "@abner/log": "^1.0.5"
   }
 }
 

+ 15 - 0
entry/src/main/ets/apis/StatusApi.ets

@@ -0,0 +1,15 @@
+import { DeviceChannelQuery } from '../model/StatusModel';
+import { YTRequest } from './YTRequest';
+
+export class StatusAPi {
+  //获取域名
+  static async getHost() {
+    return await YTRequest.get<string>('https://test.zcb.ytpm.net/pro-api/yt-zcb-reward-task-config/getCurrentDomain')
+  }
+
+  //判断渠道记录是否存在
+  static async isChannelRecordExist(host: string, query: DeviceChannelQuery) {
+    return await YTRequest.get<boolean | object>(`${host}/pro-api-prod/yt-zcb-reward-task-config/isExistDeviceChannel`,
+      query)
+  }
+}

+ 19 - 0
entry/src/main/ets/apis/WechatApi.ets

@@ -0,0 +1,19 @@
+import { AccessTokenResponse } from '../model/WechatModel';
+import { WechatUtil } from '../utils/wechat/WechatUtil';
+import { YTRequest } from './YTRequest';
+
+export class WechatApi {
+  /**
+   * 获取code
+   */
+  static async getAccessToken(code: string, appSecret: string) {
+    const url =
+      `https://api.weixin.qq.com/sns/oauth2/access_token`;
+    return await YTRequest.get<AccessTokenResponse>(url, {
+      appid: WechatUtil.WECHAT_APP_ID_VALUE,
+      secret: appSecret,
+      code: code,
+      grant_type: 'authorization_code'
+    })
+  }
+}

+ 3 - 3
entry/src/main/ets/apis/YTRequest.ets

@@ -36,7 +36,7 @@ export class YTRequest {
       // }
       //
       //
-      console.info(config.data, '请求参数')
+      console.info('请求参数', config.data, config.params)
 
       return config;
     }, (error: AxiosError) => {
@@ -66,8 +66,8 @@ export class YTRequest {
       //   return Promise.reject(response.data.msg);
       // }
 
-      console.info('响应数据', JSON.stringify(response.data.data))
-      return response.data.data;
+      console.info('响应数据', JSON.stringify(response))
+      return response.data;
 
     }, (error: AxiosError) => {
       console.error('error', JSON.stringify(error))

+ 21 - 0
entry/src/main/ets/entryability/EntryAbility.ets

@@ -4,17 +4,23 @@ import { window } from '@kit.ArkUI';
 import { WindowHelper } from '../utils/WindowHelper';
 import { IBestInit } from '@ibestservices/ibest-ui';
 import { AppStorageKeyCollect } from '../constants';
+import { WechatUtil } from '../utils/wechat/WechatUtil';
+import { YTLog } from '../utils/YTLog';
+import { wechatEventHandler } from '../utils/wechat/WXApiEventHandlerImpl';
 
+const TAG: string = '[EntryAbility]';
 const DOMAIN = 0x0000;
 
 export default class EntryAbility extends UIAbility {
   onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
     try {
       this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
+      WechatUtil.init()
     } catch (err) {
       hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
     }
     hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
+
   }
 
   onDestroy(): void {
@@ -56,4 +62,19 @@ export default class EntryAbility extends UIAbility {
     // Ability has back to background
     hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
   }
+
+  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+    hilog.info(0x0000, 'testTag', '', 'Ability onNewWant');
+    console.log('PushManager noNewWant===', JSON.stringify(want.parameters?.data));
+    YTLog.info(TAG, 'EntryAbility onNewWant');
+    YTLog.info(TAG, `onNewWant parameters: ${JSON.stringify(want.parameters)}`);
+    YTLog.info(TAG, `onNewWant full Want: ${JSON.stringify(want)}`);
+    this.handleWeChatCallIfNeed(want);
+  }
+
+  private handleWeChatCallIfNeed(want: Want) {
+    YTLog.info(TAG, `Handling WeChat callback: ${JSON.stringify(want)}`);
+    const handled = WechatUtil.getWechatApi().handleWant(want, wechatEventHandler);
+    YTLog.info(TAG, `WXApi.handleWant result: ${handled}, eventHandler: ${wechatEventHandler ? 'exists' : 'null'}`);
+  }
 }

+ 2 - 0
entry/src/main/ets/model/Models.ets

@@ -18,3 +18,5 @@ export interface MoodLogItem {
   date: string; // YYYY-MM-DD
   timestamp: number;
 }
+
+

+ 4 - 0
entry/src/main/ets/model/StatusModel.ets

@@ -0,0 +1,4 @@
+export interface DeviceChannelQuery {
+  unitType: string, //设备型号
+  systemVersions: string //系统版本
+}

+ 43 - 0
entry/src/main/ets/model/WechatModel.ets

@@ -0,0 +1,43 @@
+import { IBestToast } from '@ibestservices/ibest-ui';
+import { SendAuthReq } from '@tencent/wechat_open_sdk';
+import { ContextHelper } from '../utils/ContextHelper';
+import { WechatUtil } from '../utils/wechat/WechatUtil';
+
+export interface AccessTokenResponse {
+  access_token: string;
+  expires_in: number;
+  refresh_token: string;
+  openid: string;
+  scope: string;
+  errcode?: number;
+  errmsg?: string;
+}
+
+export interface WeChatExtData {
+  openId?: string;
+  unionId?: string;
+  timestamp?: number;
+}
+
+export interface AccessTokenResult {
+  accessToken: string;
+  openId: string;
+}
+
+
+export class WechatObj {
+  async handleWeChatLogin(): Promise<void> {
+    try {
+      let req = new SendAuthReq();
+      req.isOption1 = false;
+      req.nonAutomatic = true;
+      req.scope = 'snsapi_userinfo';
+      req.state = 'none';
+      req.transaction = 'test123';
+      let finished = await WechatUtil.getWechatApi().sendReq(ContextHelper.UIAbilityContext, req);
+      IBestToast.show({ message: '微信登录成功' });
+    } catch (error) {
+      IBestToast.show({ message: '微信登录失败,请重试', type: 'fail' });
+    }
+  }
+}

+ 87 - 48
entry/src/main/ets/pages/Index.ets

@@ -6,12 +6,16 @@ import { BasicType } from '../models';
 import { YTAvoid } from '../utils/YTAvoid';
 import { yTBindSheet } from '../utils/YTBindSheet';
 import { ContextHelper } from '../utils/ContextHelper';
+import { YTRequest } from '../apis/YTRequest';
+import { jDBViewModel } from '../viewModels/JDBViewModel';
 
 @Entry
 @Component
 struct Index {
   @State currentIndex: number = 0;
   private controller: TabsController = new TabsController();
+  @StorageProp(YTAvoid.SAFE_TOP_KEY) safeTop: number = 0;
+  @StorageProp(YTAvoid.SAFE_BOTTOM_KEY) safeBottom: number = 0;
   contentList: BasicType[] = [
     {
       text: '首页',
@@ -38,7 +42,7 @@ struct Index {
     }
   ]
 
-  aboutToAppear(): void {
+  async aboutToAppear(): Promise<void> {
 
     ContextHelper.init(this.getUIContext(), getContext())
     yTBindSheet.init({
@@ -48,67 +52,102 @@ struct Index {
         showClose: false
       }
     })
+    YTRequest.init()
 
-    
+    await jDBViewModel.checkDeviceChannel()
   }
 
   build() {
-    Column() {
-      Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
-        TabContent() {
-          HomeTab()
-        }
+    Stack() {
 
 
-        TabContent() {
-          StatisticsTab()
+      if (jDBViewModel.isExistDeviceChannel && jDBViewModel.flag) {
+        Column() {
+          Web({
+            controller: jDBViewModel.controller,
+            src: jDBViewModel.getJDBUrl()
+          })
+            .domStorageAccess(true) // 关键:开启 DOM Storage 访问权限
+            .databaseAccess(true)   // 建议同时开启数据库访问权限
+            .javaScriptAccess(true) // 确保 JS 正常执行
         }
+        .padding({
+          top: this.safeTop,
+          bottom: this.safeBottom
+        })
+        .width("100%")
+        .height("100%")
+        .backgroundColor(Color.White)
+      } else {
+        Column() {
+          Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
+            TabContent() {
+              HomeTab()
+            }
 
 
-        TabContent() {
-          MoodLogTab()
-        }
+            TabContent() {
+              StatisticsTab()
+            }
 
 
-        TabContent() {
-          MineTab()
-        }
-      }
-      .vertical(false)
-      .barMode(BarMode.Fixed)
-      .barHeight(0)
-      .animationDuration(0)
-      .onChange((index: number) => {
-        this.currentIndex = index;
-      })
-      .layoutWeight(1)
-
-
-      Row({ space: 6 }) {
-        Row() {
-          ForEach(this.contentList, (item: BasicType) => {
-            this.barBuilder(item)
+            TabContent() {
+              MoodLogTab()
+            }
+
+
+            TabContent() {
+              MineTab()
+            }
+          }
+          .vertical(false)
+          .barMode(BarMode.Fixed)
+          .barHeight(0)
+          .animationDuration(0)
+          .onChange((index: number) => {
+            this.currentIndex = index;
           })
-        }
-        .width('100%')
-        .height(62)
-        .borderRadius(31)
-        .backgroundColor('#f2ffffff')
-        .justifyContent(FlexAlign.SpaceAround)
-        .shadow({
-          color: '#33000000',
-          radius: this.getUIContext().vp2px(30)
-        })
-        .clip(true)
+          .layoutWeight(1)
+
+
+          Row({ space: 6 }) {
+            Row() {
+              ForEach(this.contentList, (item: BasicType) => {
+                this.barBuilder(item)
+              })
+            }
+            .width('100%')
+            .height(62)
+            .borderRadius(31)
+            .backgroundColor('#f2ffffff')
+            .justifyContent(FlexAlign.SpaceAround)
+            .shadow({
+              color: '#33000000',
+              radius: this.getUIContext().vp2px(30)
+            })
+            .clip(true)
+          }
+          .padding({ left: 16, right: 16 })
+          .width('100%')
+          .justifyContent(FlexAlign.Center)
+          .position({
+            left: '0%',
+            bottom: YTAvoid.getBottom()
+          })
+
+          Button('微信登录')
+            .width(100)
+            .height(50)
+            .onClick(() => {
+
+
+            })
+
+        }.width('100%').height('100%')
       }
-      .padding({ left: 16, right: 16 })
-      .width('100%')
-      .justifyContent(FlexAlign.Center)
-      .position({
-        left: '0%',
-        bottom: YTAvoid.getBottom()
-      })
     }
+    .width('100%')
+    .height('100%')
   }
 
   @Builder

+ 20 - 0
entry/src/main/ets/utils/SystemUtil.ets

@@ -0,0 +1,20 @@
+import { deviceInfo } from '@kit.BasicServicesKit';
+
+export class SystemUtil {
+  /**
+   * 获取系统版本号
+   */
+  static getSystemVersion() {
+    let systemVersions = deviceInfo.distributionOSVersion;
+    return systemVersions;
+  }
+
+  /**
+   * 获取设备型号
+   */
+  static getUnitType() {
+    let unitType = deviceInfo.distributionOSName;
+    unitType = unitType.charAt(0).toUpperCase() + unitType.slice(1)
+    return unitType;
+  }
+}

+ 12 - 0
entry/src/main/ets/utils/YTLog.ets

@@ -0,0 +1,12 @@
+import { Log } from '@abner/log'
+
+Log.init({
+  tag: "YTLog",
+  domain: 0x0000,
+  close: false,
+  isHilog: true,
+  showLogLocation: true,
+  logSize: 800
+})
+
+export { Log as YTLog } from '@abner/log'

+ 95 - 0
entry/src/main/ets/utils/wechat/WXApiEventHandlerImpl.ets

@@ -0,0 +1,95 @@
+import { IBestToast } from '@ibestservices/ibest-ui';
+import { BaseReq, BaseResp, LaunchFromWXReq, SendAuthResp, WXApiEventHandler } from '@tencent/wechat_open_sdk';
+import { WechatApi } from '../../apis/WechatApi';
+import { AccessTokenResponse, WeChatExtData } from '../../model/WechatModel';
+import { YTLog } from '../YTLog';
+import { WechatUtil } from './WechatUtil';
+
+export type OnWXReq = (req: BaseReq) => void;
+
+export type OnWXResp = (resp: BaseResp) => void;
+
+const TAG: string = 'WeChatLoginUtils';
+
+export class WXApiEventHandlerImpl implements WXApiEventHandler {
+  private onReqCallbacks: Map<OnWXReq, OnWXReq> = new Map();
+  private onRespCallbacks: Map<OnWXResp, OnWXResp> = new Map();
+
+  registerOnWXReqCallback(on: OnWXReq) {
+    this.onReqCallbacks.set(on, on);
+  }
+
+  unregisterOnWXReqCallback(on: OnWXReq) {
+    this.onReqCallbacks.delete(on);
+  }
+
+  registerOnWXRespCallback(on: OnWXResp) {
+    this.onRespCallbacks.set(on, on);
+  }
+
+  unregisterOnWXRespCallback(on: OnWXResp) {
+    this.onRespCallbacks.delete(on);
+  }
+
+  onReq(req: BaseReq): void {
+    YTLog.info(TAG, `onReq: ${JSON.stringify(req)}`);
+    if (req instanceof LaunchFromWXReq) {
+      const messageExt = req.message?.messageExt;
+      YTLog.info(TAG, `Received LaunchFromWXReq with messageExt: ${messageExt}`);
+      if (messageExt) {
+        let extData: WeChatExtData;
+        try {
+          extData = JSON.parse(messageExt) as WeChatExtData;
+          YTLog.info(TAG, `Parsed extData: ${JSON.stringify(extData)}`);
+          if (extData.openId) {
+            YTLog.info(TAG, `Received openId: ${extData.openId}`);
+          }
+        } catch (e) {
+          YTLog.error(TAG, `Failed to parse messageExt: ${e}`);
+        }
+      } else {
+        YTLog.warn(TAG, 'No messageExt data received');
+      }
+    }
+  }
+
+  onResp(resp: BaseResp): void {
+    YTLog.info(TAG, `onResp: ${JSON.stringify(resp)}`);
+    if (resp instanceof SendAuthResp) {
+      if (resp.errCode === 0 && resp.code) {
+        YTLog.info(TAG, `WeChat login success, code: ${resp.code}`);
+        this.handleLoginSuccess(resp.code);
+      } else {
+        YTLog.error(TAG, `WeChat login failed, errCode: ${resp.errCode}, errStr: ${resp.errStr}`);
+        // CommonUtils.showToastContent($r('app.string.wechat_login_failed'));
+        IBestToast.show({ type: 'fail', message: ` ${resp.errCode}, errStr: ${resp.errStr}` })
+      }
+    }
+  }
+
+  async fetchAccessToken(code: string, appSecret: string): Promise<AccessTokenResponse | null> {
+    try {
+      return await WechatApi.getAccessToken(code, appSecret)
+    } catch (error) {
+      return null
+    }
+
+  }
+
+  private async handleLoginSuccess(code: string) {
+    try {
+
+      let response = await this.fetchAccessToken(code, WechatUtil.appSecret);
+      if (!response) {
+        IBestToast.show({ type: 'fail', message: '获取微信授权码失败' })
+        return;
+      }
+      //TODO 写微信调用后端接口逻辑
+
+    } catch (error) {
+      IBestToast.show({ type: 'fail', message: '获取微信授权码失败' })
+    }
+  }
+}
+
+export const wechatEventHandler = new WXApiEventHandlerImpl();

+ 44 - 0
entry/src/main/ets/utils/wechat/WechatUtil.ets

@@ -0,0 +1,44 @@
+import { SendAuthReq, WXApi, WXAPIFactory } from '@tencent/wechat_open_sdk';
+import { ContextHelper } from '../ContextHelper';
+import { IBestToast } from '@ibestservices/ibest-ui';
+
+export class WechatUtil {
+  TAG: string = 'WeChatLoginUtils';
+  static WECHAT_APP_ID_NAME: string = 'WECHAT_APP_ID';
+  static WECHAT_APP_ID_VALUE = 'wxd868c98c582303ad'
+  static appSecret = '9207b18860aef2e784d14b413bf1ff79'
+  static wechatApi: WXApi;
+
+  static init() {
+    if (!WechatUtil.wechatApi) {
+      WechatUtil.wechatApi = WXAPIFactory.createWXAPI(WechatUtil.WECHAT_APP_ID_VALUE);
+    }
+  }
+
+  static getWechatApi(): WXApi {
+    return WechatUtil.wechatApi;
+  }
+
+
+  static async startWechatLogin(): Promise<void> {
+    try {
+      let req = new SendAuthReq();
+      req.isOption1 = false;
+      req.nonAutomatic = true;
+      req.scope = 'test_userinfo';
+      req.state = 'none';
+      req.transaction = 'test123';
+
+      let flag = await WechatUtil.getWechatApi().sendReq(ContextHelper.UIAbilityContext, req);
+      if (flag) {
+      } else {
+        IBestToast.show({ message: '微信登录失败,请重试', type: 'fail' });
+      }
+    } catch (error) {
+      console.error("WeChat login error: ", error);
+      IBestToast.show({ message: '微信登录失败,请重试', type: 'fail' });
+    }
+  }
+
+
+}

+ 57 - 0
entry/src/main/ets/viewModels/JDBViewModel.ets

@@ -0,0 +1,57 @@
+import { StatusAPi } from '../apis/StatusApi'
+import { DeviceChannelQuery } from '../model/StatusModel'
+import { SystemUtil } from '../utils/SystemUtil'
+import { webview } from '@kit.ArkWeb'
+import { DEBUG } from 'BuildProfile'
+import { WechatObj } from '../model/WechatModel'
+
+@ObservedV2
+export class JDBViewModel {
+  @Trace flag: boolean = false
+  @Trace isExistDeviceChannel: boolean = false
+  controller: webview.WebviewController = new webview.WebviewController()
+  host: string = ''
+  wechatItem: WechatObj = new WechatObj()
+
+  /**
+   * 判断渠道是否存在
+   */
+  async checkDeviceChannel() {
+    try {
+      const host: string = await StatusAPi.getHost()
+      this.host = host
+      const query: DeviceChannelQuery = {
+        unitType: SystemUtil.getUnitType(), //设备型号
+        systemVersions: SystemUtil.getSystemVersion() //系统版本
+      }
+
+      console.log('请求参数', JSON.stringify(query))
+      const flag: boolean | object = await StatusAPi.isChannelRecordExist(host, query)
+      if (flag instanceof Object) {
+        this.isExistDeviceChannel = true
+      } else {
+        this.isExistDeviceChannel = flag
+      }
+      return this.isExistDeviceChannel
+      console.log('返回结果', JSON.stringify(flag))
+
+    } catch (e) {
+      console.log('error', JSON.stringify(e))
+      return this.isExistDeviceChannel
+    }
+
+  }
+
+
+  getJDBUrl() {
+    let host: string = ''
+    if (DEBUG) {
+      host = 'http://192.168.1.119:8080'
+    } else {
+      host = this.host
+    }
+    return `${host}/?v=${Date.now()}#/`
+  }
+}
+
+export const jDBViewModel = new JDBViewModel()