Explorar el Código

Merge remote-tracking branch 'origin/master'

onecold hace 9 meses
padre
commit
4a8786b9f8
Se han modificado 2 ficheros con 340 adiciones y 0 borrados
  1. 136 0
      entry/src/main/ets/common/util/UserUtil.ets
  2. 204 0
      entry/src/main/ets/pages/UserCenter.ets

+ 136 - 0
entry/src/main/ets/common/util/UserUtil.ets

@@ -8,6 +8,7 @@ import { hilog } from "@kit.PerformanceAnalysisKit";
 import { FileUtil } from "@pura/harmony-utils";
 import { fileUri, picker } from "@kit.CoreFileKit";
 import { VipPage } from "../../pages/VipPage";
+import { MD5 } from '@pura/harmony-utils';
 
 // 用户相关接口定义
 export interface WechatUserInfo {
@@ -137,6 +138,25 @@ export interface UserInfoApiResponse {
   data: UserInfoApiData;
 }
 
+// 用户注销接口响应数据
+export interface DeleteAccountData {
+  message: string;
+}
+
+// 用户注销接口响应
+export interface DeleteAccountResponse {
+  code: number;
+  msg: string;
+  data: DeleteAccountData;
+}
+
+// 用户注销请求参数
+export interface DeleteAccountRequest {
+  token: string;
+  timestamp: number;
+  sign: string;
+}
+
 export default class UserUtil {
   // 微信是否安装
   static async isHasWX(): Promise<boolean> {
@@ -457,4 +477,120 @@ export default class UserUtil {
       return false;
     }
   }
+
+  /**
+   * 用户注销
+   * 删除用户账户并进行匿名化处理
+   * @param secretKey 服务端提供的密钥
+   * @returns Promise<boolean> 注销是否成功
+   */
+  static async deleteAccount(): Promise<boolean> {
+    try {
+      const token = PreferencesUtil.getStringSync('userToken', '');
+      if (!token) {
+        ToastUtil.showToast('用户未登录');
+        return false;
+      }
+
+      // 获取当前时间戳(秒)
+      const timestamp = Math.floor(Date.now() / 1000);
+
+      // 签名密钥,与服务端保持一致
+      const SIGN_KEY = 'pay_ss5_xyz_delete_account_sign_key_2023';
+      
+      // 将token、时间戳和密钥拼接
+      const data = token + timestamp.toString() + SIGN_KEY;
+      
+      // 生成MD5签名
+      const sign = await MD5.digestSync(data);
+
+      LogUtil.debug("UserUtil", `注销请求参数: token=${token.substring(0, 10)}..., timestamp=${timestamp}, sign=${sign.substring(0, 10)}...`);
+
+      // 构造请求参数
+      const requestParams: DeleteAccountRequest = {
+        token: token,
+        timestamp: timestamp,
+        sign: sign
+      };
+
+      // 发送POST请求
+      const httpRequest = http.createHttp();
+      const options: http.HttpRequestOptions = {
+        method: http.RequestMethod.POST,
+        readTimeout: 10000,
+        connectTimeout: 10000,
+        header: {
+          'Content-Type': 'application/json'
+        },
+        extraData: JSON.stringify(requestParams)
+      };
+
+      const response: http.HttpResponse = await httpRequest.request('https://pay.ss5.xyz/user/delete_account', options);
+
+      if (response.responseCode === 200) {
+        const res = response.result as string;
+        LogUtil.debug("UserUtil", `注销响应: ${res}`);
+
+        const responseData: DeleteAccountResponse = JSON.parse(res) as DeleteAccountResponse;
+
+        if (responseData.code === 0) {
+          ToastUtil.showToast('账户注销成功');
+          LogUtil.info("UserUtil", '账户注销成功,开始清除本地数据');
+
+          // 清除本地登录状态和用户信息
+          UserUtil.logout();
+
+          // 额外清除一些可能的数据
+          PreferencesUtil.deleteSync('isNoble');
+          PreferencesUtil.deleteSync('nobleExpireDate');
+
+          return true;
+        } else {
+          // 处理具体的错误码
+          let errorMessage = '注销失败';
+          switch (responseData.code) {
+            case 40001:
+              errorMessage = 'token不能为空';
+              break;
+            case 40002:
+            case 40003:
+              errorMessage = 'token无效或已过期';
+              break;
+            case 40004:
+              errorMessage = '用户不存在';
+              break;
+            case 40005:
+              errorMessage = '时间戳不能为空';
+              break;
+            case 40006:
+              errorMessage = '请求已过期,请重新发起';
+              break;
+            case 40007:
+              errorMessage = '签名不能为空';
+              break;
+            case 40008:
+              errorMessage = '签名验证失败';
+              break;
+            case 50001:
+              errorMessage = '系统错误';
+              break;
+            default:
+              errorMessage = responseData.msg || '未知错误';
+          }
+          ToastUtil.showToast(errorMessage);
+          LogUtil.error("UserUtil", `账户注销失败: ${errorMessage}, 错误码: ${responseData.code}`);
+          return false;
+        }
+      } else {
+        ToastUtil.showToast('注销请求失败');
+        LogUtil.error("UserUtil", `注销请求失败,HTTP状态码: ${response.responseCode}`);
+        return false;
+      }
+    } catch (error) {
+      const errorMessage = error instanceof Error ? error.message : '注销异常';
+      ToastUtil.showToast('注销异常:' + errorMessage);
+      LogUtil.error("UserUtil", `账户注销异常: ${errorMessage}`);
+      return false;
+    }
+  }
 }

+ 204 - 0
entry/src/main/ets/pages/UserCenter.ets

@@ -22,6 +22,7 @@ import json from '@ohos.util.json';
 import UserUtil, { VipFeature, VipPlanApi, VipPlanListApiResponse, WeChatPrepayId } from '../common/util/UserUtil';
 import { Utility } from '../common/util/Utility';
 import { pinyin4js } from '@ohos/pinyin4js';
+import { CustomContentDialog } from '@kit.ArkUI';
 
 
 // 微信支付相关工具方法
@@ -196,9 +197,36 @@ export struct UserCenter {
   @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
     ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
   @State showWxLogin: boolean = false;
+  @State appIdentifier: string = '';
   private wxApi = WXApi
   private wxEventHandler = WXEventHandler
 
+  // 注销功能相关
+  deleteAccountDialogController: CustomDialogController = new CustomDialogController({
+    builder: CustomContentDialog({
+      primaryTitle: '注销账户',
+      contentBuilder: () => {
+        this.deleteAccountDialogContent();
+      },
+      buttons: [
+        {
+          value: '取消',
+          action: () => {
+            this.deleteAccountDialogController.close();
+          }
+        },
+        {
+          value: '确认注销',
+          action: () => {
+            this.handleDeleteAccount();
+          }
+        }
+      ]
+    }),
+    autoCancel: true,
+    alignment: DialogAlignment.Center
+  })
+
   constructor() {
     super();
   }
@@ -252,6 +280,7 @@ export struct UserCenter {
     Utility.getAppName(getContext(this)).then((appName:string)=>{
       this.appName = appName
     })
+    this.getAppIdentifier(); // 获取应用标识符
     this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
     let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
     this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
@@ -451,6 +480,11 @@ export struct UserCenter {
           }
           this.buildVipFeatures()
 
+          // 注销功能区域(仅在登录时显示)
+          if (this.isLogin) {
+            this.buildDeleteAccountSection()
+          }
+
           // this.buildFunctionMenu()
         }
         .width('100%')
@@ -731,6 +765,74 @@ export struct UserCenter {
     })
   }
 
+  // 注销功能区域
+  @Builder
+  buildDeleteAccountSection(): void {
+    Column() {
+      Row() {
+        SymbolGlyph($r('sys.symbol.exclamationmark_triangle_fill'))
+          .fontSize(24)
+          .fontColor([Color.Red])
+          .margin({ left: 16 })
+        Text('危险操作')
+          .fontSize(18)
+          .fontWeight(FontWeight.Bold)
+          .fontColor(Color.Red)
+          .margin({ left: 12 })
+          .layoutWeight(1)
+      }
+      .width('100%')
+      .margin({ bottom: 16 })
+
+      Button() {
+        Row() {
+          SymbolGlyph($r('sys.symbol.trash'))
+            .fontSize(20)
+            .fontColor([Color.Red])
+            .alignSelf(ItemAlign.Center)
+            .margin({ left: 12 })
+          Text('注销账户')
+            .margin({ left: 8 })
+            .fontSize(15)
+            .fontColor(Color.Red)
+            .fontWeight(FontWeight.Medium)
+            .layoutWeight(1)
+          Image($r('app.media.arrow_right'))
+            .width(22)
+            .height(22)
+            .margin({ right: 16 })
+            .fillColor([Color.Red])
+        }
+      }
+      .backgroundColor(Color.Transparent)
+      .height(55)
+      .width('100%')
+      .clickEffect({ level: ClickEffectLevel.HEAVY })
+      .onClick(() => {
+        this.deleteAccountDialogController.open();
+      })
+    }
+    .width('100%')
+    .backgroundColor($r('app.color.user_center_card_background'))
+    .borderRadius(24)
+    .margin({
+      left: 16,
+      right: 16,
+      top: 10,
+      bottom: 20
+    })
+    .padding({
+      top: 20,
+      bottom: 20
+    })
+    .shadow({
+      radius: 8,
+      color: 0x11000000,
+      offsetX: 0,
+      offsetY: 2
+    })
+  }
+
   // 支付方式选择弹窗内容
   @Builder
   payDialogContentBuilder(): void {
@@ -1216,4 +1318,106 @@ export struct UserCenter {
       }
     }
   }
+
+  // 获取应用标识符
+  getAppIdentifier() {
+    let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION |
+    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO;
+    try {
+      bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {
+        hilog.info(0x0000, 'UserCenter', 'getBundleInfoForSelf successfully. Data: %{public}s', JSON.stringify(data));
+        let inde = JSON.stringify(data)
+        this.appIdentifier = inde.substring(inde.indexOf('appIdentifier'),inde.indexOf('"certificate'))
+        hilog.info(0x0000, 'UserCenter', 'appIdentifier   Data: %{public}s', this.appIdentifier);
+      }).catch((err: BusinessError) => {
+        hilog.error(0x0000, 'UserCenter', 'getBundleInfoForSelf failed. Cause: %{public}s', err.message);
+      });
+    } catch (err) {
+      let message = (err as BusinessError).message;
+      hilog.error(0x0000, 'UserCenter', 'getBundleInfoForSelf failed: %{public}s', message);
+    }
+  }
+
+  @Builder
+  deleteAccountDialogContent() {
+    Column() {
+      Text('注销账户后将无法恢复,所有相关数据将被永久删除。')
+        .fontSize(16)
+        .fontColor(Color.Red)
+        .textAlign(TextAlign.Start)
+        .width('100%')
+        .margin({ bottom: 20 })
+
+      // Text('包括但不限于:')
+      //   .fontSize(14)
+      //   .fontColor(Color.Gray)
+      //   .textAlign(TextAlign.Start)
+      //   .width('100%')
+      //   .margin({ bottom: 10 })
+      //
+      // Text('• 个人信息和设置(含订阅套餐)')
+      //   .fontSize(13)
+      //   .fontColor(Color.Gray)
+      //   .textAlign(TextAlign.Start)
+      //   .width('100%')
+      //   .margin({ left: 20, bottom: 5 })
+      //
+      // Text('• 收藏和播放列表')
+      //   .fontSize(13)
+      //   .fontColor(Color.Gray)
+      //   .textAlign(TextAlign.Start)
+      //   .width('100%')
+      //   .margin({ left: 20, bottom: 5 })
+      //
+      // Text('• 播放历史记录')
+      //   .fontSize(13)
+      //   .fontColor(Color.Gray)
+      //   .textAlign(TextAlign.Start)
+      //   .width('100%')
+      //   .margin({ left: 20, bottom: 15 })
+
+      Text('请确认您已了解此操作的后果。')
+        .fontSize(14)
+        .fontColor($r('app.color.text_color'))
+        .textAlign(TextAlign.Start)
+        .width('100%')
+    }
+    .width('100%')
+    .padding(20)
+  }
+
+  async handleDeleteAccount() {
+    try {
+
+      // 显示加载提示
+      ToastUtil.showToast('正在处理注销请求...');
+
+      // 调用注销API
+      const success = await UserUtil.deleteAccount();
+
+      if (success) {
+        // 更新页面状态
+        this.isLogin = false
+        this.userName = '未登录用户'
+        this.userAvatar = $r('app.media.icon_person2')
+        this.userAvatarUrl = ''
+        this.isVip = false
+        this.vipExpire = ''
+        this.hasActiveSubscription = false;
+        this.subscriptionName = '';
+        this.subscriptionEndDate = '';
+        this.isForever=false;
+
+
+        ToastUtil.showToast('账户已成功注销');
+        this.deleteAccountDialogController.close();
+
+        // 发送用户状态变更事件
+        emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
+      }
+    } catch (error) {
+      hilog.error(0x0000, 'UserCenter', '注销账户失败: %{public}s', error);
+      ToastUtil.showToast('注销失败,请稍后重试');
+    }
+  }
 }