ith5cn 1 kuukausi sitten
commit
6978bff89a

+ 9 - 0
.env

@@ -0,0 +1,9 @@
+SDK_VERSION='1.0.0'
+SDK_AGENT_ID_IOS='1000'
+SDK_AGENT_ID_ANDROID='1000'
+SDK_SITE_ID_IOS='1000'
+SDK_SITE_ID_ANDROID='1000'
+
+SDK_VITE_KEY=''
+
+SDK_API_BASE='http://192.168.10.12'

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+.idea
+.DS_Store
+node_modules
+
+/dist

+ 15 - 0
.vscode/launch.json

@@ -0,0 +1,15 @@
+{
+    // 使用 IntelliSense 了解相关属性。 
+    // 悬停以查看现有属性的描述。
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-chrome",
+            "request": "launch",
+            "name": "Launch Chrome against localhost",
+            "url": "http://localhost:8080",
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}

+ 11 - 0
api/auth.api.ts

@@ -0,0 +1,11 @@
+import httpClient from "../utils/httpClient";
+import { UserInfoInterface } from "../store/models/user.model";
+
+/**
+ * 服务端登录
+ * @param data 
+ * @returns 
+ */
+export const serverLoginApi = (data: any): Promise<UserInfoInterface> => {
+  return httpClient.post<UserInfoInterface>("/sdk/auth/third_login",data);
+};

+ 7 - 0
api/init.api.ts

@@ -0,0 +1,7 @@
+
+import { DNSDKConfig } from "../store/models/dnsdkConfig.model"
+import httpClient from "../utils/httpClient"
+
+export const getDnsdkConfigApi =async () => {
+    return await httpClient.post<DNSDKConfig>("/sdk/wx/dn_sdk_config")
+}

+ 27 - 0
api/order.api.ts

@@ -0,0 +1,27 @@
+import httpClient from "../utils/httpClient";
+
+/**
+ * 创建订单
+ * @param data 
+ * @returns 
+ */
+export const createOrderApi = (data: any): Promise<any> => {
+    return httpClient.post("/payment/order/create", data);
+};
+
+/**
+ * 订单支付
+ * @param data 
+ * @returns 
+ */
+export const paymentApi = (data: any): Promise<any> => {
+    return httpClient.post("/payment/order/pay", data);
+};
+
+
+/**
+ * 订单回调
+ */
+export const orderNotifyApi = (payChannelId: any, data: any): Promise<any> => {
+    return httpClient.post(`/payment/notify/${payChannelId}`, data);
+};

+ 5 - 0
api/report.api.ts

@@ -0,0 +1,5 @@
+import httpClient from "../utils/httpClient";
+
+export const reportApi = async (data: any): Promise<any> => {
+  return await httpClient.post("/sdk/role/report", data);
+};

+ 5 - 0
api/safe.api.ts

@@ -0,0 +1,5 @@
+import httpClient from "../utils/httpClient";
+
+export const secCheckTextApi = (data: any): Promise<any> => {
+  return httpClient.post("/sdk/wx/msg_sec_check", data);
+};

+ 6 - 0
api/share.api.ts

@@ -0,0 +1,6 @@
+import { ShareConfig } from "../store/models/shareConfig.model";
+import httpClient from "../utils/httpClient";
+
+export const getShareConfigApi = () => {
+    return httpClient.get<ShareConfig[]>("/sdk/wx/share_config");
+}

+ 28 - 0
core/auth/index.ts

@@ -0,0 +1,28 @@
+import { serverLogin } from "./server/server-auth"
+import { silentLogin } from "./tt/tt-auth"
+import UserInfoModel from "../../store/models/user.model"
+
+export const authLogin = async () => {
+    // 抖音登录
+    const dyLoginResult = await silentLogin()
+    // 服务端登录
+    const loginInfo = await serverLogin(dyLoginResult)
+
+    return Promise.resolve(loginInfo)
+}
+
+/**
+ * 获取当前登录用户信息
+ */
+export const getCurrentUserInfo = () => {
+    try {
+        let userInfo = UserInfoModel.getInstance().get();
+        let launchOptions = tt.getLaunchOptionsSync();
+        let query:any = launchOptions.query;
+        const share_ext = query.share_ext;
+        userInfo.share_ext = share_ext ? share_ext : "";
+        return Promise.resolve(userInfo);
+    } catch (error) {
+        return Promise.reject(error);
+    }
+}

+ 57 - 0
core/auth/server/server-auth.ts

@@ -0,0 +1,57 @@
+import { serverLoginApi } from "../../../api/auth.api";
+import UserInfoModel, { UserInfoInterface } from "../../../store/models/user.model";
+import { silentLogin } from "../tt/tt-auth";
+import { to } from "../../../utils/helper";
+import URLQueryModel from "../../../store/models/urlQuery.model";
+
+/**
+ * 服务端登录
+ * @param dyloginInfo 抖音登录信息
+ * @returns Promise
+ */
+export const serverLogin = (dyloginInfo) => {
+  return new Promise(async (resolve, reject) => {
+    
+    let params = {
+      channel: "dyxyx",
+      code: dyloginInfo.code
+    };
+
+    const serverLoginRes: UserInfoInterface = await serverLoginApi(params)
+
+    //用户态存缓存下次进游戏可以直接拿
+    UserInfoModel.getInstance().save(serverLoginRes);
+
+    // 弹框公告
+    if (serverLoginRes.pop_content) {
+      tt.showModal({
+        title: serverLoginRes.pop_title ? serverLoginRes.pop_title : "登录提示",
+        content: serverLoginRes.pop_content,
+        complete(res) {
+          console.log(res);
+        },
+      });
+    }
+
+    resolve(serverLoginRes);
+  });
+};
+
+
+/**
+ * @description: 重新登录
+ * @return {*}
+ */
+export const relogin = async () => {
+  let err, res;
+  //静默授权
+  [err, res] = await to(silentLogin());
+  if (err) {
+    return Promise.reject(err);
+  }
+  [err, res] = await to(serverLogin(res));
+  if (err) {
+    return Promise.reject(err);
+  }
+  return Promise.resolve();
+};

+ 22 - 0
core/auth/tt/tt-auth.ts

@@ -0,0 +1,22 @@
+/**
+ * 静默获取用户信息
+ * @returns Promise
+ */
+export const silentLogin = () => {
+  return new Promise((resolve, reject) => {
+    // 执行登录
+    tt.login({
+      success(loginRes) {
+        let dyLoginReUserInfo = {
+          code: loginRes.code
+        };
+        resolve(dyLoginReUserInfo);
+      },
+      fail(error) {
+        // 登录失败异常
+        reject(error.errMsg);
+      },
+    });
+  });
+};
+

+ 19 - 0
core/init/index.ts

@@ -0,0 +1,19 @@
+import GameConfigModel, { GameConfig } from "../../store/models/gameConfig.model";
+import { checkMissingParams } from "../../utils/helper";
+import { openSystemShare } from "../share/index";
+import { getUrlQuery } from "./query/url-query";
+
+export const init = async (gameConfig: GameConfig) => {
+    const missingKeys = checkMissingParams(gameConfig, ['mini_program_id', 'version', 'game_id', 'game_name']);
+
+    if (missingKeys.length > 0) {
+        throw new Error(`DYSDK init error: missing params: ${missingKeys.join(', ')}`);
+    }
+
+   
+    GameConfigModel.getInstance().save(gameConfig);
+    await getUrlQuery() // 获取url参数
+    await openSystemShare() //开启系统分享
+
+    return Promise.resolve()
+}

+ 27 - 0
core/init/query/url-query.ts

@@ -0,0 +1,27 @@
+import { setStorage } from "../../../store";
+import URLQueryModel, { URLQuery } from "../../../store/models/urlQuery.model";
+
+/**
+ * 获取url中的query参数
+ * @returns Promise<URLQuery>
+ */
+export const getUrlQuery = () => {
+   return new Promise((resolve, reject) => {
+      // 获取url中的query参数
+      let launchOptions = tt.getLaunchOptionsSync();
+      let query:any = launchOptions.query;
+      let scene = launchOptions.scene;
+
+      let urlQuery: URLQuery = {
+         agent_id: Number(query.agent_id),
+         site_id: Number(query.site_id),
+         queryStr: JSON.stringify(query),
+         gdt_vid: query.gdt_vid,
+         weixinadinfo: query.weixinadinfo
+      }
+
+      URLQueryModel.getInstance().save(urlQuery);
+      setStorage("launchScene", scene ? scene : 0);
+      resolve(urlQuery)
+   })
+}

+ 165 - 0
core/payment/index.tsx

@@ -0,0 +1,165 @@
+import { createOrderApi } from "../../api/order.api";
+import { throttle } from "../../utils/helper";
+import { getSystemInfo } from "../../utils/wechat";
+
+
+/**
+ * @description: 内部支付函数
+ * @param {*} payParams
+ * @return {*}
+ */
+const _payment = async (payParams) => {
+
+    try {
+        const orderInfo = {
+            cp_order_id: payParams.cp_order_id,
+            money: payParams.money, // 元单位
+            role_id: payParams.role_id,
+            role_name: payParams.role_name,
+            server_id: payParams.server_id,
+            server_name: payParams.server_name,
+            ext: payParams.ext ? payParams.ext : "",
+            role_level: payParams.role_level,
+            product_id: payParams.product_id,
+            product_name: payParams.product_name,
+        };
+
+        tt.showLoading({
+            title: "支付中...",
+            mask: true,
+        });
+
+        // 下单
+        const createOrderResult = await createOrderApi(orderInfo);
+
+        tt.hideLoading({});
+        let dyPayConfig = typeof createOrderResult.dy_pay_config !== "undefined" ? createOrderResult.dy_pay_config : {}
+        await requestDyPayment(orderInfo, dyPayConfig);
+        return Promise.resolve(null);
+    } catch (error) {
+        return Promise.reject(error);
+    }
+};
+/**
+ * @description: 安卓虚拟支付
+ * @param {object}
+ * @return {*}
+ */
+export const requestGamePayment = (data) => {
+  return new Promise((resolve, reject) => {
+    console.log("虚拟支付开始:", data);
+    tt.requestGamePayment({
+      ...data,
+      success: (res) => {
+        resolve(res);
+      },
+      fail: (error) => {
+        console.log("虚拟支付失败:", error);
+        reject(error);
+      },
+    });
+  });
+};
+
+
+/**
+ * @description: iOS 虚拟支付 发起钻石支付
+ * @param {object}
+ * @return {*}
+ */
+export const openAwemeCustomerService = (data) => {
+  return new Promise((resolve, reject) => {
+    console.log("发起钻石支付:", data);
+    tt.openAwemeCustomerService({
+      ...data,
+      success: (res) => {
+        resolve(res);
+      },
+      fail: (error) => {
+        console.log("钻石支付失败:", error);
+        reject(error);
+      },
+    });
+  });
+};
+
+
+
+/**
+ * 拉起抖音支付
+ */
+const requestDyPayment = async (orderInfo, dyPayConfig) => {
+     let platform = await getSystemInfo("platform");
+      try {
+        var setGoodType = 0;
+        let res;
+        // 小额支付描述 https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/open-ability/payment/micropayment-guide
+        // 安卓 requestGamePayment
+        if(platform==="android"){
+        // 判断是否小额支付
+        if (tt.canIUse('requestGamePayment.object.goodType')) {
+            setGoodType = 2;
+            res = await requestGamePayment({
+            goodType: setGoodType, // 道具直购
+            orderAmount:orderInfo.money * 100, // 单位分
+            goodName: orderInfo.product_name,
+            currencyType: "CNY",
+            zoneId: String(dyPayConfig.zoneId),
+            customId: orderInfo.cp_order_id,
+            mode: "game",
+            env: 0,
+            platform: platform, // 目前仅为"android"
+            extraInfo: String(setGoodType)
+            });
+        }else{
+            res = await requestGamePayment({
+            mode: "game",
+            env: 0,
+            platform: platform, // 目前仅为"android"
+            currencyType: "CNY",
+            buyQuantity: dyPayConfig.buyQuantity,
+            zoneId: String(dyPayConfig.zoneId),
+            customId: orderInfo.cp_order_id,
+            });
+        }
+
+    }else{
+      // 判断是否小额支付
+      if (tt.canIUse('openAwemeCustomerService.object.goodType')) {
+        setGoodType = 2;
+        // iOS openAwemeCustomerService
+        res = await openAwemeCustomerService({
+          goodType: setGoodType, // 道具直购
+          orderAmount: orderInfo.money * 100, // 单位分
+          goodName: orderInfo.product_name,
+          currencyType: "DIAMOND",
+          zoneId: String(dyPayConfig.zoneId),
+          customId: orderInfo.cp_order_id,
+          extraInfo: String(setGoodType)
+        });
+      }else{
+        // iOS openAwemeCustomerService
+        res = await openAwemeCustomerService({
+          zoneId: String(dyPayConfig.zoneId),
+          customId: orderInfo.cp_order_id,
+          currencyType: "DIAMOND",
+          buyQuantity: dyPayConfig.buyQuantity,
+        });
+      }
+    }
+    return Promise.resolve();
+  } catch (error) {
+    return Promise.reject(error);
+  }
+}
+
+
+/**
+ * @description:拉起支付(节流版本,避免短时间内重复点击)
+ * @param {*} payParams
+ * @return {*}
+ */
+export const payment = throttle(_payment, 2000);
+
+
+

+ 32 - 0
core/role/index.ts

@@ -0,0 +1,32 @@
+import { checkMissingParams } from "../../utils/helper";
+import { RoleData } from "./role.interface";
+import DnSdk from "../../lib/DnSdk.js";
+import { reportApi } from "../../api/report.api";
+
+export const reportRole = async (roleData: RoleData) => {
+  const missingKeys = checkMissingParams(roleData, ["data_type",
+    "server_id",
+    "server_name",
+    "role_id",
+    "role_name",
+    "role_level"]);
+
+  if (missingKeys.length > 0) {
+    throw new Error(`DYSDK reportRole error: missing params: ${missingKeys.join(', ')}`);
+  }
+
+  const reportData = {
+    data_type: roleData.data_type,
+    server_id: roleData.server_id,
+    server_name: roleData.server_name,
+    role_id: roleData.role_id,
+    role_name: roleData.role_name,
+    role_level: roleData.role_level,
+  };
+
+  // 上报数据
+  await reportApi(reportData);
+
+
+  return Promise.resolve();
+}

+ 9 - 0
core/role/role.interface.ts

@@ -0,0 +1,9 @@
+export interface RoleData {
+    data_type: number;
+    server_id: number;
+    server_name: string;
+    role_id: string;
+    role_name: string;
+    role_level: number;
+    event_type?: string
+}

+ 111 - 0
core/share/index.ts

@@ -0,0 +1,111 @@
+import { getShareConfigApi } from "../../api/share.api";
+import ShareConfigModel from "../../store/models/shareConfig.model";
+
+const shareConfigModel = ShareConfigModel.getInstance();
+
+/**
+ * 初始化分享配置
+ */
+export const initShareConfig = async () => {
+   try {
+     shareConfigModel.clear();
+    const shareConfigData = await getShareConfigApi()
+
+    if(shareConfigData.length){
+        // 从数组中随机选择一个分享配置
+        const randomShareConfig = shareConfigData[Math.floor(Math.random() * shareConfigData.length)];
+
+        shareConfigModel.save(randomShareConfig)
+    }
+
+
+    return Promise.resolve()
+   } catch (error) {
+    return Promise.reject(error)
+   }
+}
+
+/**
+ * 开启系统分享
+ */
+export const openSystemShare = async () => {
+    const shareConfig = shareConfigModel.get()
+    if (!shareConfig) {
+        return
+    }
+    try {
+        // 开启分享按钮
+        tt.showShareMenu({});
+        await initShareConfig();
+        let shareData = generateShareData({});
+        tt.onShareAppMessage(() => {
+            return shareData;
+        });
+        return Promise.resolve();
+    } catch (error) {
+        return Promise.reject(error);
+    }
+}
+
+
+
+/**
+ * 主动发起分享
+ */
+export const share = async(data) => {
+    const shareConfig = shareConfigModel.get()
+    if (!shareConfig) {
+        return
+    }
+    try {
+        await initShareConfig();
+        // 发起分享
+        let shareData = generateShareData(data);
+        tt.shareAppMessage(shareData);
+        return Promise.resolve();
+    } catch (error) {
+        return Promise.reject(error);
+    }
+}
+
+/**
+ *  主动监听研发透传参数
+ */
+export const onShareAppMessage = (data,menus=['shareAppMessage']) => {
+    console.log("onShareAppMessage入参" + JSON.stringify(data));
+    //先off,再on
+    tt.offShareAppMessage();
+    let shareData = generateShareData(data);
+    tt.showShareMenu({});
+    tt.onShareAppMessage(() => {
+        return shareData;
+    });
+};
+
+
+/**
+ * 构建分享数据
+ * @param data 
+ * @returns 
+ */
+
+const generateShareData = (data) => {
+    let shareConfigInfo = shareConfigModel.get();
+    let shareData = {
+        title: shareConfigInfo.title,
+        imageUrl: shareConfigInfo.imageUrl,
+        query: shareConfigInfo.query,
+    };
+    let share_ext = "";
+    // 记录分享透传参数
+    if (data.hasOwnProperty("title") && data.title) shareData.title = data.title;
+    if (data.hasOwnProperty("imageUrl") && data.imageUrl) shareData.imageUrl = data.imageUrl;
+    if (data.hasOwnProperty("ext") && data.ext) share_ext = data.ext;
+
+    //前面这个share_ext是研发透传进来的,后面这个是服务端传进来的
+    shareData.query += shareConfigInfo.share_ext;
+    if (share_ext) {
+        shareData.query += `&share_ext=` + share_ext;
+    }
+    return shareData;
+};

+ 147 - 0
game.js

@@ -0,0 +1,147 @@
+import {
+  init,
+  authLogin,
+  share,
+  onShareAppMessage,
+  reportRole,
+  payment,
+  getCurrentUserInfo,
+  safeTextCheck
+} from "./dist/gameSdk.1.0.0.js"
+
+// 初始化skd
+(async () => {
+  try {
+    let data = {
+      version: "1.0.1", // 游戏版本
+      mini_program_id: "wxf290757236471ea9", //测试号id
+      game_id: "311", // 游戏id
+      game_name: "测试游戏", // 游戏名称
+    };
+    // 静默授权
+    const res = await init(data);
+    console.log("init", res);
+    tt.showToast({
+      title: "进入游戏成功",
+    });
+  } catch (error) {
+
+  }
+})();
+
+const handleLogin = async () => {
+  const res = await authLogin()
+  console.log("登录返回", res)
+}
+
+// 角色上报
+const handleRoleDataReport = async () => {
+  const res = await reportRole({
+    data_type: 1,
+    server_id: 1,
+    server_name: "测试服务器",
+    role_id: "1",
+    role_name: "测试角色",
+    role_level: 1,
+  })
+
+  console.log("reportRole", res);
+}
+
+//主动分享
+const handleShareAppMessage = async () => {
+  try {
+    let info = {
+      title: "分享测试",
+      imageUrl: "https://xxxx.com/hd/img/632-2.jpg",
+      ext: "&ceshi=linquan321",
+    };
+    await share(info);
+    tt.showToast({
+      title: "分享成功",
+    });
+  } catch (error) {
+    console.log(error);
+  }
+};
+
+//主动监听
+const handleOnShareAppMessage = () => {
+  onShareAppMessage({
+    title: "测试",
+    imageUrl: "https://xxxx.com/hd/img/632-2.jpg",
+    ext: "&ceshi=test",
+  });
+};
+
+// 支付
+const handleMakePayment = async () => {
+  let params = {
+    amt: 1,
+    cp_order_id: new Date().getTime(),
+    server_id: 1,
+    server_name: "测试1服",
+    role_id: "10000111",
+    role_name: "小林",
+    role_level: 1,
+    ext: "aaaaaaa",
+    product_id: "-1",
+    product_name: "钻石",
+  };
+  await payment(params)
+}
+
+// 获取当前用户信息
+const handleLoginUserInfo = async () => {
+  const res = await getCurrentUserInfo()
+  console.log("getCurrentUserInfo", res)
+}
+
+// 文本内容安全检测
+const handleSecCheckText = async () => {
+  const res = await safeTextCheck({ content: "毛泽东", scene: 1 })
+  console.log(res)
+}
+
+function newButtonMap(name, callback, x, y, w = 200, h = 30) {
+  return {
+    name: name,
+    callback: callback,
+    x: x,
+    y: y,
+    w: w,
+    h: h,
+  };
+}
+let buttonList = [
+  newButtonMap("登录", handleLogin, 10, 60),
+  newButtonMap("上报用户角色", handleRoleDataReport, 10, 110),
+  newButtonMap("生成1元订单", handleMakePayment, 10, 160),
+  newButtonMap("主动分享", handleShareAppMessage, 10, 260),
+  newButtonMap("获取平台登陆用户信息", handleLoginUserInfo, 10, 210),
+  newButtonMap("监听分享传参", handleOnShareAppMessage, 10, 310),
+  newButtonMap("文本内容安全检测", handleSecCheckText, 10, 360),
+];
+
+const canvas = tt.createCanvas();
+const ctx = canvas.getContext("2d");
+
+ctx.fillStyle = "#ff0000";
+ctx.font = "14px Arial";
+ctx.strokeStyle = "#ff0000";
+for (let k in buttonList) {
+  ctx.fillText(buttonList[k].name, 20, buttonList[k].y + 20);
+}
+tt.onTouchStart(function (a) {
+  let cur = a.touches[0];
+  for (let k in buttonList) {
+    if (
+      cur.clientX >= buttonList[k].x &&
+      cur.clientX <= buttonList[k].x + buttonList[k].w &&
+      cur.clientY >= buttonList[k].y &&
+      cur.clientY <= buttonList[k].y + buttonList[k].h
+    ) {
+      buttonList[k].callback();
+    }
+  }
+});

+ 4 - 0
game.json

@@ -0,0 +1,4 @@
+{
+    "deviceOrientation": "portrait",
+    "showStatusBar": false
+}

+ 5 - 0
index.ts

@@ -0,0 +1,5 @@
+export { init } from "./core/init/index";
+export { authLogin, getCurrentUserInfo } from "./core/auth/index";
+export { share, onShareAppMessage } from "./core/share/index";
+export { reportRole } from "./core/role/index";
+export { payment } from "./core/payment/index"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
lib/crypto-js.min.js


+ 683 - 0
lib/md5.js

@@ -0,0 +1,683 @@
+/**
+ * [js-md5]{@link https://github.com/emn178/js-md5}
+ *
+ * @namespace md5
+ * @version 0.7.3
+ * @author Chen, Yi-Cyuan [emn178@gmail.com]
+ * @copyright Chen, Yi-Cyuan 2014-2017
+ * @license MIT
+ */
+(function () {
+    'use strict';
+
+    var ERROR = 'input is invalid type';
+    var WINDOW = typeof window === 'object';
+    var root = WINDOW ? window : {};
+    if (root.JS_MD5_NO_WINDOW) {
+        WINDOW = false;
+    }
+    var WEB_WORKER = !WINDOW && typeof self === 'object';
+    var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+    if (NODE_JS) {
+        root = global;
+    } else if (WEB_WORKER) {
+        root = self;
+    }
+    var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
+    var AMD = typeof define === 'function' && define.amd;
+    var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+    var HEX_CHARS = '0123456789abcdef'.split('');
+    var EXTRA = [128, 32768, 8388608, -2147483648];
+    var SHIFT = [0, 8, 16, 24];
+    var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
+    var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+    var blocks = [], buffer8;
+    if (ARRAY_BUFFER) {
+        var buffer = new ArrayBuffer(68);
+        buffer8 = new Uint8Array(buffer);
+        blocks = new Uint32Array(buffer);
+    }
+
+    if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
+        Array.isArray = function (obj) {
+            return Object.prototype.toString.call(obj) === '[object Array]';
+        };
+    }
+
+    if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
+        ArrayBuffer.isView = function (obj) {
+            return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+        };
+    }
+
+    /**
+     * @method hex
+     * @memberof md5
+     * @description Output hash as hex string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} Hex string
+     * @example
+     * md5.hex('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * md5('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method digest
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.digest('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method array
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.array('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method arrayBuffer
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.buffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method base64
+     * @memberof md5
+     * @description Output hash as base64 string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} base64 string
+     * @example
+     * md5.base64('The quick brown fox jumps over the lazy dog');
+     */
+    var createOutputMethod = function (outputType) {
+        return function (message) {
+            return new Md5(true).update(message)[outputType]();
+        };
+    };
+
+    /**
+     * @method create
+     * @memberof md5
+     * @description Create Md5 object
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.create();
+     */
+    /**
+     * @method update
+     * @memberof md5
+     * @description Create and update Md5 object
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.update('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * var hash = md5.create();
+     * hash.update('The quick brown fox jumps over the lazy dog');
+     */
+    var createMethod = function () {
+        var method = createOutputMethod('hex');
+        if (NODE_JS) {
+            method = nodeWrap(method);
+        }
+        method.create = function () {
+            return new Md5();
+        };
+        method.update = function (message) {
+            return method.create().update(message);
+        };
+        for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+            var type = OUTPUT_TYPES[i];
+            method[type] = createOutputMethod(type);
+        }
+        return method;
+    };
+
+    var nodeWrap = function (method) {
+        var crypto = eval("require('crypto')");
+        var Buffer = eval("require('buffer').Buffer");
+        var nodeMethod = function (message) {
+            if (typeof message === 'string') {
+                return crypto.createHash('md5').update(message, 'utf8').digest('hex');
+            } else {
+                if (message === null || message === undefined) {
+                    throw ERROR;
+                } else if (message.constructor === ArrayBuffer) {
+                    message = new Uint8Array(message);
+                }
+            }
+            if (Array.isArray(message) || ArrayBuffer.isView(message) ||
+                message.constructor === Buffer) {
+                return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
+            } else {
+                return method(message);
+            }
+        };
+        return nodeMethod;
+    };
+
+    /**
+     * Md5 class
+     * @class Md5
+     * @description This is internal class.
+     * @see {@link md5.create}
+     */
+    function Md5(sharedMemory) {
+        if (sharedMemory) {
+            blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                    blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            this.blocks = blocks;
+            this.buffer8 = buffer8;
+        } else {
+            if (ARRAY_BUFFER) {
+                var buffer = new ArrayBuffer(68);
+                this.buffer8 = new Uint8Array(buffer);
+                this.blocks = new Uint32Array(buffer);
+            } else {
+                this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+            }
+        }
+        this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
+        this.finalized = this.hashed = false;
+        this.first = true;
+    }
+
+    /**
+     * @method update
+     * @memberof Md5
+     * @instance
+     * @description Update hash
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @see {@link md5.update}
+     */
+    Md5.prototype.update = function (message) {
+        if (this.finalized) {
+            return;
+        }
+
+        var notString, type = typeof message;
+        if (type !== 'string') {
+            if (type === 'object') {
+                if (message === null) {
+                    throw ERROR;
+                } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
+                    message = new Uint8Array(message);
+                } else if (!Array.isArray(message)) {
+                    if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
+                        throw ERROR;
+                    }
+                }
+            } else {
+                throw ERROR;
+            }
+            notString = true;
+        }
+        var code, index = 0, i, length = message.length, blocks = this.blocks;
+        var buffer8 = this.buffer8;
+
+        while (index < length) {
+            if (this.hashed) {
+                this.hashed = false;
+                blocks[0] = blocks[16];
+                blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                    blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                        blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                            blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            }
+
+            if (notString) {
+                if (ARRAY_BUFFER) {
+                    for (i = this.start; index < length && i < 64; ++index) {
+                        buffer8[i++] = message[index];
+                    }
+                } else {
+                    for (i = this.start; index < length && i < 64; ++index) {
+                        blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
+                    }
+                }
+            } else {
+                if (ARRAY_BUFFER) {
+                    for (i = this.start; index < length && i < 64; ++index) {
+                        code = message.charCodeAt(index);
+                        if (code < 0x80) {
+                            buffer8[i++] = code;
+                        } else if (code < 0x800) {
+                            buffer8[i++] = 0xc0 | (code >> 6);
+                            buffer8[i++] = 0x80 | (code & 0x3f);
+                        } else if (code < 0xd800 || code >= 0xe000) {
+                            buffer8[i++] = 0xe0 | (code >> 12);
+                            buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+                            buffer8[i++] = 0x80 | (code & 0x3f);
+                        } else {
+                            code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+                            buffer8[i++] = 0xf0 | (code >> 18);
+                            buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);
+                            buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+                            buffer8[i++] = 0x80 | (code & 0x3f);
+                        }
+                    }
+                } else {
+                    for (i = this.start; index < length && i < 64; ++index) {
+                        code = message.charCodeAt(index);
+                        if (code < 0x80) {
+                            blocks[i >> 2] |= code << SHIFT[i++ & 3];
+                        } else if (code < 0x800) {
+                            blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                        } else if (code < 0xd800 || code >= 0xe000) {
+                            blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                        } else {
+                            code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+                            blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+                            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                        }
+                    }
+                }
+            }
+            this.lastByteIndex = i;
+            this.bytes += i - this.start;
+            if (i >= 64) {
+                this.start = i - 64;
+                this.hash();
+                this.hashed = true;
+            } else {
+                this.start = i;
+            }
+        }
+        if (this.bytes > 4294967295) {
+            this.hBytes += this.bytes / 4294967296 << 0;
+            this.bytes = this.bytes % 4294967296;
+        }
+        return this;
+    };
+
+    Md5.prototype.finalize = function () {
+        if (this.finalized) {
+            return;
+        }
+        this.finalized = true;
+        var blocks = this.blocks, i = this.lastByteIndex;
+        blocks[i >> 2] |= EXTRA[i & 3];
+        if (i >= 56) {
+            if (!this.hashed) {
+                this.hash();
+            }
+            blocks[0] = blocks[16];
+            blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                    blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+        }
+        blocks[14] = this.bytes << 3;
+        blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
+        this.hash();
+    };
+
+    Md5.prototype.hash = function () {
+        var a, b, c, d, bc, da, blocks = this.blocks;
+
+        if (this.first) {
+            a = blocks[0] - 680876937;
+            a = (a << 7 | a >>> 25) - 271733879 << 0;
+            d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
+            b = (b << 22 | b >>> 10) + c << 0;
+        } else {
+            a = this.h0;
+            b = this.h1;
+            c = this.h2;
+            d = this.h3;
+            a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
+            a = (a << 7 | a >>> 25) + b << 0;
+            d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
+            b = (b << 22 | b >>> 10) + c << 0;
+        }
+
+        a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
+        b = (b << 20 | b >>> 12) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[5] - 378558;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[8] - 2022574463;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[11] + 1839030562;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[14] - 35309556;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[1] - 1530992060;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[4] + 1272893353;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[7] - 155497632;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[10] - 1094730640;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[13] + 681279174;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[0] - 358537222;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[3] - 722521979;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[6] + 76029189;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[9] - 640364487;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[12] - 421815835;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[15] + 530742520;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[2] - 995338651;
+        b = (b << 23 | b >>> 9) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[0] - 198630844;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[5] - 57434055;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[10] - 1051523;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[15] - 30611744;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[4] - 145523070;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[2] + 718787259;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[9] - 343485551;
+        b = (b << 21 | b >>> 11) + c << 0;
+
+        if (this.first) {
+            this.h0 = a + 1732584193 << 0;
+            this.h1 = b - 271733879 << 0;
+            this.h2 = c - 1732584194 << 0;
+            this.h3 = d + 271733878 << 0;
+            this.first = false;
+        } else {
+            this.h0 = this.h0 + a << 0;
+            this.h1 = this.h1 + b << 0;
+            this.h2 = this.h2 + c << 0;
+            this.h3 = this.h3 + d << 0;
+        }
+    };
+
+    /**
+     * @method hex
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.hex();
+     */
+    Md5.prototype.hex = function () {
+        this.finalize();
+
+        var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+
+        return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
+            HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
+            HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
+            HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
+            HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
+            HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
+            HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
+            HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
+            HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
+            HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
+            HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
+            HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
+            HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
+            HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
+            HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
+            HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];
+    };
+
+    /**
+     * @method toString
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.toString();
+     */
+    Md5.prototype.toString = Md5.prototype.hex;
+
+    /**
+     * @method digest
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.digest}
+     * @example
+     * hash.digest();
+     */
+    Md5.prototype.digest = function () {
+        this.finalize();
+
+        var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+        return [
+            h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,
+            h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,
+            h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,
+            h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF
+        ];
+    };
+
+    /**
+     * @method array
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.array}
+     * @example
+     * hash.array();
+     */
+    Md5.prototype.array = Md5.prototype.digest;
+
+    /**
+     * @method arrayBuffer
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.arrayBuffer}
+     * @example
+     * hash.arrayBuffer();
+     */
+    Md5.prototype.arrayBuffer = function () {
+        this.finalize();
+
+        var buffer = new ArrayBuffer(16);
+        var blocks = new Uint32Array(buffer);
+        blocks[0] = this.h0;
+        blocks[1] = this.h1;
+        blocks[2] = this.h2;
+        blocks[3] = this.h3;
+        return buffer;
+    };
+
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.buffer}
+     * @example
+     * hash.buffer();
+     */
+    Md5.prototype.buffer = Md5.prototype.arrayBuffer;
+
+    /**
+     * @method base64
+     * @memberof Md5
+     * @instance
+     * @description Output hash as base64 string
+     * @returns {String} base64 string
+     * @see {@link md5.base64}
+     * @example
+     * hash.base64();
+     */
+    Md5.prototype.base64 = function () {
+        var v1, v2, v3, base64Str = '', bytes = this.array();
+        for (var i = 0; i < 15;) {
+            v1 = bytes[i++];
+            v2 = bytes[i++];
+            v3 = bytes[i++];
+            base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+                BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +
+                BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +
+                BASE64_ENCODE_CHAR[v3 & 63];
+        }
+        v1 = bytes[i];
+        base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+            BASE64_ENCODE_CHAR[(v1 << 4) & 63] +
+            '==';
+        return base64Str;
+    };
+
+    var exports = createMethod();
+
+    if (COMMON_JS) {
+        module.exports = exports;
+    } else {
+        /**
+         * @method md5
+         * @description Md5 hash function, export to global in browsers.
+         * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+         * @returns {String} md5 hashes
+         * @example
+         * md5(''); // d41d8cd98f00b204e9800998ecf8427e
+         * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
+         * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
+         *
+         * // It also supports UTF-8 encoding
+         * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
+         *
+         * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
+         * md5([]); // d41d8cd98f00b204e9800998ecf8427e
+         * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
+         */
+        root.md5 = exports;
+        if (AMD) {
+            define(function () {
+                return exports;
+            });
+        }
+    }
+})();

+ 19 - 0
package.json

@@ -0,0 +1,19 @@
+{
+	"scripts": {
+		"build": "vite build",
+		"dev": "vite build --watch"
+	},
+	"type": "module",
+	"dependencies": {
+		"@dn-sdk/minigame": "^1.5.6",
+		"crypto-js": "^4.2.0",
+		"md5": "^2.3.0",
+		"vite": "^7.3.1"
+	},
+	"devDependencies": {
+		"@douyin-microapp/typings": "^1.3.1",
+		"minigame-api-typings": "^3.8.16",
+		"miniprogram-api-typings": "^5.0.0",
+		"terser": "^5.46.0"
+	}
+}

+ 804 - 0
pnpm-lock.yaml

@@ -0,0 +1,804 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@dn-sdk/minigame':
+        specifier: ^1.5.6
+        version: 1.5.6
+      crypto-js:
+        specifier: ^4.2.0
+        version: 4.2.0
+      md5:
+        specifier: ^2.3.0
+        version: 2.3.0
+      vite:
+        specifier: ^7.3.1
+        version: 7.3.1(terser@5.46.0)
+    devDependencies:
+      '@douyin-microapp/typings':
+        specifier: ^1.3.1
+        version: 1.3.1
+      minigame-api-typings:
+        specifier: ^3.8.16
+        version: 3.8.16
+      miniprogram-api-typings:
+        specifier: ^5.0.0
+        version: 5.0.0
+      terser:
+        specifier: ^5.46.0
+        version: 5.46.0
+
+packages:
+
+  '@dn-sdk/minigame@1.5.6':
+    resolution: {integrity: sha512-0fTl8AWA9KP3I3MvC8R3t7e0aOnFm7mkJ3tsDvhs/UcXQ1u9tMpkHKSkrCqbXBJWTwBXQ7RCYcjIjSuZM8L9HQ==}
+
+  '@douyin-microapp/typings@1.3.1':
+    resolution: {integrity: sha512-TuooIb1SYqMQ6FcC2Ux5wm5/2VH9hKO6bHIHxlKhcpW9fBp8FXHn7X/wfpVi0SaKNK15zA+B8JN2HSsaJ/wjUw==}
+
+  '@esbuild/aix-ppc64@0.27.2':
+    resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.27.2':
+    resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.27.2':
+    resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.27.2':
+    resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.27.2':
+    resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.27.2':
+    resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.27.2':
+    resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.27.2':
+    resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.27.2':
+    resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.27.2':
+    resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.27.2':
+    resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.27.2':
+    resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.27.2':
+    resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.27.2':
+    resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.27.2':
+    resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.27.2':
+    resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.27.2':
+    resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.27.2':
+    resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.27.2':
+    resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.27.2':
+    resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.27.2':
+    resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openharmony-arm64@0.27.2':
+    resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@esbuild/sunos-x64@0.27.2':
+    resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.27.2':
+    resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.27.2':
+    resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.27.2':
+    resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@jridgewell/gen-mapping@0.3.13':
+    resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/source-map@0.3.11':
+    resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
+
+  '@jridgewell/sourcemap-codec@1.5.5':
+    resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+  '@jridgewell/trace-mapping@0.3.31':
+    resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+  '@rollup/rollup-android-arm-eabi@4.57.1':
+    resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.57.1':
+    resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.57.1':
+    resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.57.1':
+    resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.57.1':
+    resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.57.1':
+    resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+    resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+    resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.57.1':
+    resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.57.1':
+    resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loong64-gnu@4.57.1':
+    resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-loong64-musl@4.57.1':
+    resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+    resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-ppc64-musl@4.57.1':
+    resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+    resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-musl@4.57.1':
+    resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-s390x-gnu@4.57.1':
+    resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.57.1':
+    resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.57.1':
+    resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-openbsd-x64@4.57.1':
+    resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@rollup/rollup-openharmony-arm64@4.57.1':
+    resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@rollup/rollup-win32-arm64-msvc@4.57.1':
+    resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.57.1':
+    resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-gnu@4.57.1':
+    resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.57.1':
+    resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==}
+    cpu: [x64]
+    os: [win32]
+
+  '@types/estree@1.0.8':
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+  acorn@8.15.0:
+    resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  charenc@0.0.2:
+    resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
+
+  commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  crypt@0.0.2:
+    resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
+
+  crypto-js@4.2.0:
+    resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
+
+  esbuild@0.27.2:
+    resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  fdir@6.5.0:
+    resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      picomatch: ^3 || ^4
+    peerDependenciesMeta:
+      picomatch:
+        optional: true
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  is-buffer@1.1.6:
+    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
+
+  md5@2.3.0:
+    resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
+
+  minigame-api-typings@3.8.16:
+    resolution: {integrity: sha512-xSxV1onUDkwBRRqxAQgu+Xct1Ose74Ol2ESefE/hUpxH+7oY/eAFUvEaQB8hKL/2TZNx4RMIduEZv4Z5cAuvYg==}
+
+  miniprogram-api-typings@5.0.0:
+    resolution: {integrity: sha512-KQ1ZDk0cK3JrAaZtlFNlC/De/HwqhtToPZTzIYvFzi4FaW0Z8raIBN/qpjZ5QMiBaBNl/hJwTWwD+tfp8BwCZw==}
+
+  nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@4.0.3:
+    resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+    engines: {node: '>=12'}
+
+  postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  rollup@4.57.1:
+    resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  terser@5.46.0:
+    resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  tinyglobby@0.2.15:
+    resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+    engines: {node: '>=12.0.0'}
+
+  vite@7.3.1:
+    resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
+    engines: {node: ^20.19.0 || >=22.12.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^20.19.0 || >=22.12.0
+      jiti: '>=1.21.0'
+      less: ^4.0.0
+      lightningcss: ^1.21.0
+      sass: ^1.70.0
+      sass-embedded: ^1.70.0
+      stylus: '>=0.54.8'
+      sugarss: ^5.0.0
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+snapshots:
+
+  '@dn-sdk/minigame@1.5.6': {}
+
+  '@douyin-microapp/typings@1.3.1': {}
+
+  '@esbuild/aix-ppc64@0.27.2':
+    optional: true
+
+  '@esbuild/android-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/android-arm@0.27.2':
+    optional: true
+
+  '@esbuild/android-x64@0.27.2':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/darwin-x64@0.27.2':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-arm@0.27.2':
+    optional: true
+
+  '@esbuild/linux-ia32@0.27.2':
+    optional: true
+
+  '@esbuild/linux-loong64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.27.2':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.27.2':
+    optional: true
+
+  '@esbuild/linux-s390x@0.27.2':
+    optional: true
+
+  '@esbuild/linux-x64@0.27.2':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.27.2':
+    optional: true
+
+  '@esbuild/openharmony-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/sunos-x64@0.27.2':
+    optional: true
+
+  '@esbuild/win32-arm64@0.27.2':
+    optional: true
+
+  '@esbuild/win32-ia32@0.27.2':
+    optional: true
+
+  '@esbuild/win32-x64@0.27.2':
+    optional: true
+
+  '@jridgewell/gen-mapping@0.3.13':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.5
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/source-map@0.3.11':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.13
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/sourcemap-codec@1.5.5': {}
+
+  '@jridgewell/trace-mapping@0.3.31':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.5
+
+  '@rollup/rollup-android-arm-eabi@4.57.1':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-darwin-x64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-loong64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-loong64-musl@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-ppc64-musl@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-musl@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.57.1':
+    optional: true
+
+  '@rollup/rollup-openbsd-x64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-openharmony-arm64@4.57.1':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.57.1':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.57.1':
+    optional: true
+
+  '@rollup/rollup-win32-x64-gnu@4.57.1':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.57.1':
+    optional: true
+
+  '@types/estree@1.0.8': {}
+
+  acorn@8.15.0: {}
+
+  buffer-from@1.1.2: {}
+
+  charenc@0.0.2: {}
+
+  commander@2.20.3: {}
+
+  crypt@0.0.2: {}
+
+  crypto-js@4.2.0: {}
+
+  esbuild@0.27.2:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.27.2
+      '@esbuild/android-arm': 0.27.2
+      '@esbuild/android-arm64': 0.27.2
+      '@esbuild/android-x64': 0.27.2
+      '@esbuild/darwin-arm64': 0.27.2
+      '@esbuild/darwin-x64': 0.27.2
+      '@esbuild/freebsd-arm64': 0.27.2
+      '@esbuild/freebsd-x64': 0.27.2
+      '@esbuild/linux-arm': 0.27.2
+      '@esbuild/linux-arm64': 0.27.2
+      '@esbuild/linux-ia32': 0.27.2
+      '@esbuild/linux-loong64': 0.27.2
+      '@esbuild/linux-mips64el': 0.27.2
+      '@esbuild/linux-ppc64': 0.27.2
+      '@esbuild/linux-riscv64': 0.27.2
+      '@esbuild/linux-s390x': 0.27.2
+      '@esbuild/linux-x64': 0.27.2
+      '@esbuild/netbsd-arm64': 0.27.2
+      '@esbuild/netbsd-x64': 0.27.2
+      '@esbuild/openbsd-arm64': 0.27.2
+      '@esbuild/openbsd-x64': 0.27.2
+      '@esbuild/openharmony-arm64': 0.27.2
+      '@esbuild/sunos-x64': 0.27.2
+      '@esbuild/win32-arm64': 0.27.2
+      '@esbuild/win32-ia32': 0.27.2
+      '@esbuild/win32-x64': 0.27.2
+
+  fdir@6.5.0(picomatch@4.0.3):
+    optionalDependencies:
+      picomatch: 4.0.3
+
+  fsevents@2.3.3:
+    optional: true
+
+  is-buffer@1.1.6: {}
+
+  md5@2.3.0:
+    dependencies:
+      charenc: 0.0.2
+      crypt: 0.0.2
+      is-buffer: 1.1.6
+
+  minigame-api-typings@3.8.16: {}
+
+  miniprogram-api-typings@5.0.0: {}
+
+  nanoid@3.3.11: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@4.0.3: {}
+
+  postcss@8.5.6:
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  rollup@4.57.1:
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.57.1
+      '@rollup/rollup-android-arm64': 4.57.1
+      '@rollup/rollup-darwin-arm64': 4.57.1
+      '@rollup/rollup-darwin-x64': 4.57.1
+      '@rollup/rollup-freebsd-arm64': 4.57.1
+      '@rollup/rollup-freebsd-x64': 4.57.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.57.1
+      '@rollup/rollup-linux-arm-musleabihf': 4.57.1
+      '@rollup/rollup-linux-arm64-gnu': 4.57.1
+      '@rollup/rollup-linux-arm64-musl': 4.57.1
+      '@rollup/rollup-linux-loong64-gnu': 4.57.1
+      '@rollup/rollup-linux-loong64-musl': 4.57.1
+      '@rollup/rollup-linux-ppc64-gnu': 4.57.1
+      '@rollup/rollup-linux-ppc64-musl': 4.57.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.57.1
+      '@rollup/rollup-linux-riscv64-musl': 4.57.1
+      '@rollup/rollup-linux-s390x-gnu': 4.57.1
+      '@rollup/rollup-linux-x64-gnu': 4.57.1
+      '@rollup/rollup-linux-x64-musl': 4.57.1
+      '@rollup/rollup-openbsd-x64': 4.57.1
+      '@rollup/rollup-openharmony-arm64': 4.57.1
+      '@rollup/rollup-win32-arm64-msvc': 4.57.1
+      '@rollup/rollup-win32-ia32-msvc': 4.57.1
+      '@rollup/rollup-win32-x64-gnu': 4.57.1
+      '@rollup/rollup-win32-x64-msvc': 4.57.1
+      fsevents: 2.3.3
+
+  source-map-js@1.2.1: {}
+
+  source-map-support@0.5.21:
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+
+  source-map@0.6.1: {}
+
+  terser@5.46.0:
+    dependencies:
+      '@jridgewell/source-map': 0.3.11
+      acorn: 8.15.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
+  tinyglobby@0.2.15:
+    dependencies:
+      fdir: 6.5.0(picomatch@4.0.3)
+      picomatch: 4.0.3
+
+  vite@7.3.1(terser@5.46.0):
+    dependencies:
+      esbuild: 0.27.2
+      fdir: 6.5.0(picomatch@4.0.3)
+      picomatch: 4.0.3
+      postcss: 8.5.6
+      rollup: 4.57.1
+      tinyglobby: 0.2.15
+    optionalDependencies:
+      fsevents: 2.3.3
+      terser: 5.46.0

+ 36 - 0
project.config.json

@@ -0,0 +1,36 @@
+{
+  "setting": {
+    "es6": true,
+    "postcss": true,
+    "minified": true,
+    "uglifyFileName": false,
+    "enhance": true,
+    "packNpmRelationList": [],
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    },
+    "useCompilerPlugins": false,
+    "swc": true,
+    "disableSWC": false,
+    "compileWorklet": false,
+    "uploadWithSourceMap": true,
+    "packNpmManually": false,
+    "minifyWXSS": true,
+    "minifyWXML": true,
+    "localPlugins": false,
+    "disableUseStrict": false,
+    "condition": false
+  },
+  "compileType": "game",
+  "simulatorPluginLibVersion": {},
+  "packOptions": {
+    "ignore": [],
+    "include": []
+  },
+  "isGameTourist": false,
+  "appid": "wxf290757236471ea9",
+  "editorSetting": {},
+  "libVersion": "3.14.1"
+}

+ 35 - 0
project.private.config.json

@@ -0,0 +1,35 @@
+{
+  "libVersion": "3.14.1",
+  "projectname": "wxxyx-sdk-new",
+  "setting": {
+    "urlCheck": false,
+    "coverView": true,
+    "lazyloadPlaceholderEnable": false,
+    "skylineRenderEnable": false,
+    "preloadBackgroundData": false,
+    "autoAudits": false,
+    "showShadowRootInWxmlPanel": true,
+    "compileHotReLoad": true,
+    "useApiHook": true,
+    "useApiHostProcess": true,
+    "useStaticServer": false,
+    "useLanDebug": false,
+    "showES6CompileOption": false,
+    "checkInvalidKey": true,
+    "ignoreDevUnusedFiles": true,
+    "bigPackageSizeSupport": false
+  },
+  "condition": {
+    "game": {
+      "list": [
+        {
+          "name": "投放",
+          "pathName": "",
+          "query": "agent_id=333&site_id=555&queryStr=222333",
+          "scene": null,
+          "launchMode": "defalut"
+        }
+      ]
+    }
+  }
+}

+ 27 - 0
store/index.ts

@@ -0,0 +1,27 @@
+/**
+ * Storage 模块导出
+ */
+export {
+  // 同步 API
+  setStorage,
+  getStorage,
+  removeStorage,
+  clearStorage,
+  hasStorage,
+  
+  // 异步 API
+  setStorageAsync,
+  getStorageAsync,
+  removeStorageAsync,
+  clearStorageAsync,
+  
+  // 工具方法
+  getStorageInfo,
+  getStorageInfoAsync,
+  cleanExpiredStorage,
+  clearMemoryCache,
+  getMemoryCacheSize,
+  
+  // 默认导出
+  default as storage,
+} from './storage';

+ 45 - 0
store/models/dnsdkConfig.model.ts

@@ -0,0 +1,45 @@
+import { getStorage, removeStorage, setStorage } from "../storage";
+
+export interface DNSDKConfig {
+    dn_source_id: number;
+    dn_source_secret: string;
+}
+
+const STORAGE_KEY = "dnsdkConfig"
+
+const DEFAULT_DNSDK_CONFIG: DNSDKConfig = {
+    dn_source_id: 0,
+    dn_source_secret: ""
+}
+
+class DNSDKConfigModel {
+    private static instance: DNSDKConfigModel | null = null;
+    private data: DNSDKConfig;
+
+    private constructor() {
+        this.data = getStorage<DNSDKConfig>(STORAGE_KEY, DEFAULT_DNSDK_CONFIG) ?? DEFAULT_DNSDK_CONFIG;
+    }
+
+    static getInstance(): DNSDKConfigModel {
+        if (!DNSDKConfigModel.instance) {
+            DNSDKConfigModel.instance = new DNSDKConfigModel();
+        }
+        return DNSDKConfigModel.instance;
+    }
+
+    get(): DNSDKConfig {
+        return this.data;
+    }
+
+    save(dnsdkConfig: DNSDKConfig): boolean {
+        this.data = dnsdkConfig;
+        return setStorage(STORAGE_KEY, dnsdkConfig);
+    }
+
+    clear(): boolean {
+        this.data = { ...DEFAULT_DNSDK_CONFIG };
+        return removeStorage(STORAGE_KEY);
+    }
+}
+
+export default DNSDKConfigModel;

+ 49 - 0
store/models/gameConfig.model.ts

@@ -0,0 +1,49 @@
+import { getStorage, removeStorage, setStorage } from "../storage";
+
+export interface GameConfig {
+    mini_program_id: string // 微信小游戏APPID
+    version: string // 游戏版本号
+    game_id: number // 云帆平台游戏ID
+    game_name: string // 游戏名称
+}
+
+const STORAGE_KEY = "gameConfig";
+
+const DEFAULT_CONFIG: GameConfig = {
+    mini_program_id: "",
+    version: "",
+    game_id: 0,
+    game_name: ""
+}
+
+class GameConfigModel {
+    private static instance: GameConfigModel | null = null;
+    private data: GameConfig;
+
+    private constructor() {
+        this.data = getStorage<GameConfig>(STORAGE_KEY, DEFAULT_CONFIG) ?? DEFAULT_CONFIG;
+    }
+
+    static getInstance(): GameConfigModel {
+        if (!GameConfigModel.instance) {
+            GameConfigModel.instance = new GameConfigModel();
+        }
+        return GameConfigModel.instance;
+    }
+
+    get(): GameConfig {
+        return this.data;
+    }
+
+    save(config: GameConfig): boolean {
+        this.data = config;
+        return setStorage(STORAGE_KEY, config);
+    }
+
+    clear(): boolean {
+        this.data = { ...DEFAULT_CONFIG };
+        return removeStorage(STORAGE_KEY);
+    }
+}
+
+export default GameConfigModel;

+ 76 - 0
store/models/params.model.ts

@@ -0,0 +1,76 @@
+import { generateUUID } from "../../utils/helper"
+import { getSystemInfo } from "../../utils/wechat"
+import { getStorage } from "../storage"
+import GameConfigModel from "./gameConfig.model"
+import URLQueryModel from "./urlQuery.model"
+import UserInfoModel from "./user.model"
+
+
+export interface ParamsInterface {
+    platform: 'ios' | 'android' | 'ohos' | 'ohos_pc' | 'windows' | 'mac' | 'devtools'
+    os: number
+    imei: string
+    model: string
+    brand: string
+    app_version: string
+    query: string
+    track_id: string
+    openid: string
+    sdk_version: string
+    sdk_version_code: number
+    agent_id: number
+    site_id: number
+    oaid: string
+    game_id: number
+    package_name: string | number
+    appid: string
+}
+
+export const getCommonParams = () => {
+
+    const userInfo = UserInfoModel.getInstance().get()
+    const gameConfig = GameConfigModel.getInstance().get()
+    const urlQuery = URLQueryModel.getInstance().get()
+    // const appBaseInfo = getAppBaseInfo()
+    // const gameConfig = getStorage<{ app_version: string, game_id: number, app_id: string }>("gameConfig") ?? { app_version: '', game_id: 0, app_id: '' }
+
+    const uuid = getStorage("imei") || generateUUID()
+    const platform = getSystemInfo('platform')
+    const model = getSystemInfo('model')
+    const brand = getSystemInfo('brand')
+    const os = platform === 'ios' ? 4 : 3; // 3(and-小程序)或4(ios-小程序)
+    const app_version = gameConfig.version
+    const { agent_id, queryStr, site_id } = urlQuery
+
+
+    let gameId = 0
+    let packageName: number | string = ''
+    if (userInfo.game_id) {
+        gameId = userInfo.game_id
+        packageName = userInfo.game_id
+    } else {
+        gameId = gameConfig.game_id
+        packageName = gameConfig.mini_program_id
+    }
+
+    const params: ParamsInterface = {
+        platform,
+        os,
+        imei: uuid as string,
+        model,
+        brand,
+        app_version,
+        query: queryStr,
+        track_id: "0",
+        openid: userInfo.openid,
+        sdk_version: import.meta.env.SDK_VERSION ?? '',
+        sdk_version_code: import.meta.env.SDK_VERSION ? Number(import.meta.env.SDK_VERSION.split('.').join('')) : 0,
+        agent_id: agent_id || (platform === 'ios' ? Number(import.meta.env.SDK_AGENT_ID_IOS) : Number(import.meta.env.SDK_AGENT_ID_ANDROID)),
+        site_id: site_id || (platform === 'ios' ? Number(import.meta.env.SDK_SITE_ID_IOS) : Number(import.meta.env.SDK_SITE_ID_ANDROID)),
+        oaid: "0",
+        game_id: gameId,
+        package_name: packageName,
+        appid: gameConfig.mini_program_id,
+    }
+    return params
+}

+ 47 - 0
store/models/shareConfig.model.ts

@@ -0,0 +1,47 @@
+import { getStorage, removeStorage, setStorage } from "../storage";
+
+export interface ShareConfig {
+    title: string,
+    imageUrl: string,
+    query: string,
+    share_ext : string //服务端需要拼接的参数
+}
+
+const STORAGE_KEY = "shareConfig";
+const DEFAULT_CONFIG: ShareConfig = {
+    title: "",
+    imageUrl: "",
+    query: "",
+    share_ext : ""
+}
+class ShareConfigModel {
+    private static instance: ShareConfigModel | null = null;
+    private data: ShareConfig;
+
+    private constructor() {
+        this.data = getStorage<ShareConfig>(STORAGE_KEY, DEFAULT_CONFIG) ?? DEFAULT_CONFIG;
+    }
+
+    static getInstance(): ShareConfigModel {
+        if (!ShareConfigModel.instance) {
+            ShareConfigModel.instance = new ShareConfigModel();
+        }
+        return ShareConfigModel.instance;
+    }
+
+    get(): ShareConfig {
+        return this.data;
+    }
+
+    save(config: ShareConfig): boolean {
+        this.data = config;
+        return setStorage(STORAGE_KEY, config);
+    }
+
+    clear(): boolean {
+        this.data = { ...DEFAULT_CONFIG };
+        return removeStorage(STORAGE_KEY);
+    }
+}
+
+export default ShareConfigModel;

+ 53 - 0
store/models/urlQuery.model.ts

@@ -0,0 +1,53 @@
+import { getStorage, removeStorage, setStorage } from "../storage"
+
+export interface URLQuery {
+    agent_id: number
+    site_id: number
+    queryStr: string
+    gdt_vid: string
+    weixinadinfo: string
+}
+
+const STORAGE_KEY = "urlQuery"
+
+const DEFAULT_URL_QUERY: URLQuery = {
+    agent_id: 0,
+    site_id: 0,
+    queryStr: "",
+    gdt_vid: "",
+    weixinadinfo: ""
+}
+
+
+class URLQueryModel {
+    private static instance: URLQueryModel | null = null;
+    private data: URLQuery;
+
+    private constructor() {
+        this.data = getStorage<URLQuery>(STORAGE_KEY, DEFAULT_URL_QUERY) ?? DEFAULT_URL_QUERY;
+    }
+
+    static getInstance(): URLQueryModel {
+        if (!URLQueryModel.instance) {
+            URLQueryModel.instance = new URLQueryModel();
+        }
+        return URLQueryModel.instance;
+    }
+
+    get(): URLQuery {
+        return this.data;
+    }
+
+    save(urlQuery: URLQuery): boolean {
+        this.data = urlQuery;
+        return setStorage(STORAGE_KEY, urlQuery);
+    }
+
+    clear(): boolean {
+        this.data = { ...DEFAULT_URL_QUERY };
+        return removeStorage(STORAGE_KEY);
+    }
+}
+
+
+export default URLQueryModel;

+ 92 - 0
store/models/user.model.ts

@@ -0,0 +1,92 @@
+import { getStorage, setStorage, removeStorage } from "../storage";
+
+/** SDK 登录用户信息接口 */
+export interface UserInfoInterface {
+  token: string;
+  user_name: string;
+  reg_time: string;
+  uid: number;
+  openid: string;
+  game_id: number;
+  pop_content?: string;
+  pop_title?: string;
+  is_first_login?: boolean;
+  is_reactive_user?: boolean;
+  share_ext?:string;
+}
+
+const STORAGE_KEY = "userInfo";
+
+const DEFAULT_INFO: UserInfoInterface = {
+  token: "",
+  user_name: "",
+  reg_time: "",
+  uid: 0,
+  openid: "",
+  game_id: 0,
+};
+
+/**
+ * SDK 登录用户信息模型
+ * 单例模式,自动从缓存加载/保存
+ */
+class UserInfoModel {
+  private static instance: UserInfoModel | null = null;
+  private data: UserInfoInterface;
+
+  private constructor() {
+    // 从缓存加载数据
+    this.data = getStorage<UserInfoInterface>(STORAGE_KEY, DEFAULT_INFO) ?? DEFAULT_INFO;
+  }
+
+  static getInstance(): UserInfoModel {
+    if (!UserInfoModel.instance) {
+      UserInfoModel.instance = new UserInfoModel();
+    }
+    return UserInfoModel.instance;
+  }
+
+  /** 获取用户信息 */
+  get(): UserInfoInterface {
+    let launchOptions = tt.getLaunchOptionsSync();
+    let query:any = launchOptions.query;
+    const share_ext = query.share_ext;
+    this.data.share_ext = share_ext ? share_ext : "";
+    return this.data;
+  }
+
+  /** 保存用户信息到缓存 */
+  save(info: UserInfoInterface): boolean {
+    this.data = info;
+    return setStorage(STORAGE_KEY, info);
+  }
+
+  /** 更新部分用户信息 */
+  update(partial: Partial<UserInfoInterface>): boolean {
+    this.data = { ...this.data, ...partial };
+    return setStorage(STORAGE_KEY, this.data);
+  }
+
+  /** 清除用户信息 */
+  clear(): boolean {
+    this.data = { ...DEFAULT_INFO };
+    return removeStorage(STORAGE_KEY);
+  }
+
+  /** 是否已登录 */
+  isLoggedIn(): boolean {
+    return !!this.data.token && this.data.uid > 0;
+  }
+
+  /** 获取 token */
+  getToken(): string {
+    return this.data.token;
+  }
+
+  /** 获取 uid */
+  getUid(): number {
+    return this.data.uid;
+  }
+}
+
+export default UserInfoModel;

+ 408 - 0
store/storage.ts

@@ -0,0 +1,408 @@
+/**
+ * 高可用微信小游戏 Storage 封装
+ * 
+ * 特性:
+ * - 同步/异步双支持
+ * - 自动 JSON 序列化/反序列化
+ * - 过期时间支持
+ * - 内存缓存层
+ * - 完善的异常处理
+ * - TypeScript 类型支持
+ */
+
+/** 存储数据的包装结构 */
+interface StorageWrapper<T> {
+  data: T;
+  /** 过期时间戳,null 表示永不过期 */
+  expireAt: number | null;
+  /** 创建时间戳 */
+  createAt: number;
+}
+
+/** Storage 配置选项 */
+interface StorageOptions {
+  /** 过期时间(毫秒),默认为 null 表示永不过期 */
+  expireTime?: number | null;
+  /** 是否启用内存缓存,默认为 true */
+  enableCache?: boolean;
+}
+
+/** 默认配置 */
+const DEFAULT_OPTIONS: Required<StorageOptions> = {
+  expireTime: null,
+  enableCache: true,
+};
+
+/** 内存缓存 */
+const memoryCache = new Map<string, StorageWrapper<unknown>>();
+
+/**
+ * 检查数据是否过期
+ */
+function isExpired<T>(wrapper: StorageWrapper<T>): boolean {
+  if (wrapper.expireAt === null) {
+    return false;
+  }
+  return Date.now() > wrapper.expireAt;
+}
+
+/**
+ * 创建存储包装对象
+ */
+function createWrapper<T>(data: T, expireTime: number | null): StorageWrapper<T> {
+  const now = Date.now();
+  return {
+    data,
+    expireAt: expireTime ? now + expireTime : null,
+    createAt: now,
+  };
+}
+
+/**
+ * 安全地解析 JSON
+ */
+function safeJsonParse<T>(str: string): StorageWrapper<T> | null {
+  try {
+    return JSON.parse(str) as StorageWrapper<T>;
+  } catch {
+    return null;
+  }
+}
+
+// ==================== 同步 API ====================
+
+/**
+ * 同步设置存储数据
+ * @param key 存储键名
+ * @param data 存储数据
+ * @param options 配置选项
+ * @returns 是否成功
+ */
+export function setStorage<T>(key: string, data: T, options?: StorageOptions): boolean {
+  const opts = { ...DEFAULT_OPTIONS, ...options };
+  const wrapper = createWrapper(data, opts.expireTime ?? null);
+  
+  try {
+    tt.setStorageSync(key, JSON.stringify(wrapper));
+    
+    // 更新内存缓存
+    if (opts.enableCache) {
+      memoryCache.set(key, wrapper as StorageWrapper<unknown>);
+    }
+    
+    return true;
+  } catch (error) {
+    console.error(`[Storage] setStorage failed for key "${key}":`, error);
+    return false;
+  }
+}
+
+/**
+ * 同步获取存储数据
+ * @param key 存储键名
+ * @param defaultValue 默认值(当数据不存在或过期时返回)
+ * @param options 配置选项
+ * @returns 存储的数据或默认值
+ */
+export function getStorage<T>(key: string, defaultValue?: T, options?: StorageOptions): T | undefined {
+  const opts = { ...DEFAULT_OPTIONS, ...options };
+  
+  // 优先从内存缓存读取
+  if (opts.enableCache && memoryCache.has(key)) {
+    const cached = memoryCache.get(key) as StorageWrapper<T>;
+    if (!isExpired(cached)) {
+      return cached.data;
+    }
+    // 过期则清除缓存
+    memoryCache.delete(key);
+    removeStorage(key);
+    return defaultValue;
+  }
+  
+  try {
+    const raw = tt.getStorageSync(key);
+    if (!raw) {
+      return defaultValue;
+    }
+    
+    const wrapper = safeJsonParse<T>(raw);
+    if (!wrapper) {
+      // 兼容旧数据格式(非包装格式)
+      return raw as T;
+    }
+    
+    // 检查是否过期
+    if (isExpired(wrapper)) {
+      removeStorage(key);
+      return defaultValue;
+    }
+    
+    // 更新内存缓存
+    if (opts.enableCache) {
+      memoryCache.set(key, wrapper as StorageWrapper<unknown>);
+    }
+    
+    return wrapper.data;
+  } catch (error) {
+    console.error(`[Storage] getStorage failed for key "${key}":`, error);
+    return defaultValue;
+  }
+}
+
+/**
+ * 同步删除存储数据
+ * @param key 存储键名
+ * @returns 是否成功
+ */
+export function removeStorage(key: string): boolean {
+  try {
+    tt.removeStorageSync(key);
+    memoryCache.delete(key);
+    return true;
+  } catch (error) {
+    console.error(`[Storage] removeStorage failed for key "${key}":`, error);
+    return false;
+  }
+}
+
+/**
+ * 同步清空所有存储数据
+ * @returns 是否成功
+ */
+export function clearStorage(): boolean {
+  try {
+    tt.clearStorageSync();
+    memoryCache.clear();
+    return true;
+  } catch (error) {
+    console.error('[Storage] clearStorage failed:', error);
+    return false;
+  }
+}
+
+/**
+ * 检查存储键是否存在且未过期
+ * @param key 存储键名
+ * @returns 是否存在
+ */
+export function hasStorage(key: string): boolean {
+  return getStorage(key) !== undefined;
+}
+
+// ==================== 异步 API ====================
+
+/**
+ * 异步设置存储数据
+ * @param key 存储键名
+ * @param data 存储数据
+ * @param options 配置选项
+ */
+export function setStorageAsync<T>(key: string, data: T, options?: StorageOptions): Promise<void> {
+  const opts = { ...DEFAULT_OPTIONS, ...options };
+  const wrapper = createWrapper(data, opts.expireTime ?? null);
+  
+  return new Promise((resolve, reject) => {
+    tt.setStorage({
+      key,
+      data: JSON.stringify(wrapper),
+      success: () => {
+        // 更新内存缓存
+        if (opts.enableCache) {
+          memoryCache.set(key, wrapper as StorageWrapper<unknown>);
+        }
+        resolve();
+      },
+      fail: (error) => {
+        console.error(`[Storage] setStorageAsync failed for key "${key}":`, error);
+        reject(error);
+      },
+    });
+  });
+}
+
+/**
+ * 异步获取存储数据
+ * @param key 存储键名
+ * @param defaultValue 默认值(当数据不存在或过期时返回)
+ * @param options 配置选项
+ */
+export function getStorageAsync<T>(key: string, defaultValue?: T, options?: StorageOptions): Promise<T | undefined> {
+  const opts = { ...DEFAULT_OPTIONS, ...options };
+  
+  // 优先从内存缓存读取
+  if (opts.enableCache && memoryCache.has(key)) {
+    const cached = memoryCache.get(key) as StorageWrapper<T>;
+    if (!isExpired(cached)) {
+      return Promise.resolve(cached.data);
+    }
+    // 过期则清除缓存
+    memoryCache.delete(key);
+    return removeStorageAsync(key).then(() => defaultValue);
+  }
+  
+  return new Promise((resolve) => {
+    tt.getStorage({
+      key,
+      success: (res) => {
+        const raw = res.data;
+        if (!raw) {
+          resolve(defaultValue);
+          return;
+        }
+        
+        const wrapper = safeJsonParse<T>(raw);
+        if (!wrapper) {
+          // 兼容旧数据格式
+          resolve(raw as T);
+          return;
+        }
+        
+        // 检查是否过期
+        if (isExpired(wrapper)) {
+          removeStorageAsync(key).then(() => resolve(defaultValue));
+          return;
+        }
+        
+        // 更新内存缓存
+        if (opts.enableCache) {
+          memoryCache.set(key, wrapper as StorageWrapper<unknown>);
+        }
+        
+        resolve(wrapper.data);
+      },
+      fail: (error) => {
+        console.error(`[Storage] getStorageAsync failed for key "${key}":`, error);
+        resolve(defaultValue);
+      },
+    });
+  });
+}
+
+/**
+ * 异步删除存储数据
+ * @param key 存储键名
+ */
+export function removeStorageAsync(key: string): Promise<void> {
+  return new Promise((resolve, reject) => {
+    tt.removeStorage({
+      key,
+      success: () => {
+        memoryCache.delete(key);
+        resolve();
+      },
+      fail: (error) => {
+        console.error(`[Storage] removeStorageAsync failed for key "${key}":`, error);
+        reject(error);
+      },
+    });
+  });
+}
+
+/**
+ * 异步清空所有存储数据
+ */
+export function clearStorageAsync(): Promise<void> {
+  return new Promise((resolve, reject) => {
+    tt.clearStorage({
+      success: () => {
+        memoryCache.clear();
+        resolve();
+      },
+      fail: (error) => {
+        console.error('[Storage] clearStorageAsync failed:', error);
+        reject(error);
+      },
+    });
+  });
+}
+
+// ==================== 工具方法 ====================
+
+/**
+ * 获取存储信息
+ */
+export function getStorageInfo(): any {
+  try {
+    return tt.getStorageInfoSync();
+  } catch (error) {
+    console.error('[Storage] getStorageInfo failed:', error);
+    return { keys: [], currentSize: 0, limitSize: 0 };
+  }
+}
+
+/**
+ * 异步获取存储信息
+ */
+export function getStorageInfoAsync(): Promise<any> {
+  return new Promise((resolve) => {
+    tt.getStorageInfo({
+      success: (res) => resolve(res),
+      fail: (error) => {
+        console.error('[Storage] getStorageInfoAsync failed:', error);
+        resolve({ keys: [], currentSize: 0, limitSize: 0 });
+      },
+    });
+  });
+}
+
+/**
+ * 清理所有过期数据
+ */
+export function cleanExpiredStorage(): void {
+  try {
+    const { keys } = tt.getStorageInfoSync();
+    keys.forEach((key) => {
+      try {
+        const raw = tt.getStorageSync(key);
+        if (raw) {
+          const wrapper = safeJsonParse(raw);
+          if (wrapper && isExpired(wrapper)) {
+            removeStorage(key);
+          }
+        }
+      } catch {
+        // 忽略单个 key 的错误
+      }
+    });
+  } catch (error) {
+    console.error('[Storage] cleanExpiredStorage failed:', error);
+  }
+}
+
+/**
+ * 清理内存缓存
+ */
+export function clearMemoryCache(): void {
+  memoryCache.clear();
+}
+
+/**
+ * 获取内存缓存大小
+ */
+export function getMemoryCacheSize(): number {
+  return memoryCache.size;
+}
+
+// ==================== 导出默认实例 ====================
+
+export default {
+  // 同步 API
+  set: setStorage,
+  get: getStorage,
+  remove: removeStorage,
+  clear: clearStorage,
+  has: hasStorage,
+  
+  // 异步 API
+  setAsync: setStorageAsync,
+  getAsync: getStorageAsync,
+  removeAsync: removeStorageAsync,
+  clearAsync: clearStorageAsync,
+  
+  // 工具方法
+  getInfo: getStorageInfo,
+  getInfoAsync: getStorageInfoAsync,
+  cleanExpired: cleanExpiredStorage,
+  clearCache: clearMemoryCache,
+  getCacheSize: getMemoryCacheSize,
+};

+ 9 - 0
tsconfig.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "target": "ES6",
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "lib": ["ES6"],
+    "types": ["vite/client", "@douyin-microapp/typings"]
+  }
+}

+ 76 - 0
tt-extensions.d.ts

@@ -0,0 +1,76 @@
+/**
+ * 补充 @douyin-microapp/typings 中缺失的抖音小程序 API 类型声明
+ * 这些 API 在运行时是可用的,但在 @douyin-microapp/typings@1.3.1 中未被声明
+ *
+ * 如需补充更多缺失的 API,直接在下方 TtExtensions 接口中添加即可,全项目生效。
+ */
+
+interface ShareAppMessageOptions {
+  title?: string;
+  imageUrl?: string;
+  query?: string;
+  channel?: string;
+  desc?: string;
+  extra?: Record<string, any>;
+  templateId?: string;
+}
+
+interface RequestGamePaymentOptions {
+  mode: string;
+  env: number;
+  platform?: string;
+  currencyType: string;
+  buyQuantity?: number;
+  zoneId?: string;
+  customId?: string;
+  goodType?: number;
+  orderAmount?: number;
+  goodName?: string;
+  extraInfo?: string;
+  success?: (res: any) => void;
+  fail?: (error: any) => void;
+  complete?: (res: any) => void;
+}
+
+interface OpenAwemeCustomerServiceOptions {
+  zoneId?: string;
+  customId?: string;
+  currencyType?: string;
+  buyQuantity?: number;
+  goodType?: number;
+  orderAmount?: number;
+  goodName?: string;
+  extraInfo?: string;
+  success?: (res: any) => void;
+  fail?: (error: any) => void;
+  complete?: (res: any) => void;
+}
+
+interface TtExtensions {
+  /** 主动拉起转发,进入选择通讯录界面 */
+  shareAppMessage: (options: ShareAppMessageOptions) => void;
+
+  /** 监听用户点击右上角菜单中的「转发」按钮时触发的事件 */
+  onShareAppMessage: (
+    callback: (options?: {
+      from?: string;
+      target?: any;
+      webViewUrl?: string;
+    }) => ShareAppMessageOptions
+  ) => void;
+
+  /** 取消监听用户点击右上角菜单中的「转发」按钮时触发的事件 */
+  offShareAppMessage: (callback?: (...args: any[]) => void) => void;
+
+  /** 安卓虚拟支付 */
+  requestGamePayment: (options: RequestGamePaymentOptions) => void;
+
+  /** iOS 拉起抖音客服支付 */
+  openAwemeCustomerService: (options: OpenAwemeCustomerServiceOptions) => void;
+}
+
+declare global {
+  const tt: typeof import("@douyin-microapp/typings/types/api") & TtExtensions;
+}
+
+export {};

+ 150 - 0
utils/encrypted.ts

@@ -0,0 +1,150 @@
+import GameConfigModel from "../store/models/gameConfig.model";
+import { generateRandomIV, sortObj } from "./helper";
+import md5 from 'md5';
+import CryptoJS from 'crypto-js';
+/**
+ * 生成签名
+ * @param params 
+ * @returns 
+ */
+export const generateSign = (params: {
+        time: number;
+        [key: string]: any;
+    }) => {
+    const gemeConfig = GameConfigModel.getInstance().get()
+
+    params.time = Math.floor(new Date().getTime() / 1000);
+    let newData = sortObj(params);
+    let signStr = "";
+    for (let k in newData) {
+        if (k === "sign") {
+          continue;
+        }
+        if (newData[k] === "") {
+          continue;
+        }
+        if (newData[k] === undefined) {
+          continue;
+        }
+        if (newData[k] === null) {
+          continue;
+        }
+        signStr = signStr + k + "=" + newData[k] + "&";
+    }
+    signStr =
+        signStr.substring(0, signStr.length - 1) +
+        "&secret=" +
+        gemeConfig.mini_program_id;
+    // console.log("加密原串:" + signStr);
+    params.sign = md5(signStr);
+    let newParams: any = {}
+    newParams.encrypt_data = encryptData(params);
+    return newParams;
+}
+
+
+/**
+ * 加密数据
+ * @param params 
+ * @returns 
+ */
+export const encryptData = (params: any) => {
+    console.log('加密前',params)
+    const key = import.meta.env.SDK_VITE_KEY;
+    // 将参数对象转换为 JSON 字符串
+    const data = JSON.stringify(params);
+
+    // 生成 16 字节的随机 IV (AES-256-CBC 需要 16 字节 IV)
+    const iv = generateRandomIV(16);
+
+    // 将密钥转换为 WordArray
+    // AES-256 需要 32 字节 (256 位) 的密钥
+    // 如果 key 长度不够,使用 MD5 哈希来生成 32 字节的密钥
+    let keyWordArray;
+    
+    if (key.length === 32) {
+      // 如果 key 正好是 32 字节,直接使用 UTF8 解析
+      keyWordArray = CryptoJS.enc.Utf8.parse(key);
+    } else {
+      // 否则使用 MD5 哈希生成 32 字节的密钥(MD5 生成 16 字节,但 crypto-js 会处理)
+      // 对于 AES-256,我们需要 32 字节,可以使用 SHA256 或者重复 MD5
+      keyWordArray = CryptoJS.SHA256(key);
+    }
+
+    // 使用 AES-256-CBC 加密,返回原始二进制数据
+    const encrypted = CryptoJS.AES.encrypt(data, keyWordArray, {
+      iv: iv,
+      mode: CryptoJS.mode.CBC,
+      padding: CryptoJS.pad.Pkcs7
+    });
+
+    // 将 IV 和密文拼接(都是 WordArray,直接拼接)
+    // PHP 中:$iv . $encrypted (直接拼接二进制数据)
+    const combined = iv.concat(encrypted.ciphertext);
+
+    // 将拼接后的数据转换为 Base64 编码
+    // PHP 中:base64_encode($iv . $encrypted)
+    const base64Encoded = CryptoJS.enc.Base64.stringify(combined);
+
+    // 返回加密后的数据,将原始参数替换为加密数据
+    return base64Encoded;
+}
+
+/**
+ * 解密数据
+ * @param encryptedData 
+ * @returns 
+ */
+export const decryptData = (encryptedData: string) => {
+    const key = import.meta.env.SDK_VITE_KEY;
+    try {
+      // 如果数据为空或无效,直接返回
+      if (!encryptedData || typeof encryptedData !== 'string') {
+        return encryptedData;
+      }
+
+      // Base64 解码
+      const combined = CryptoJS.enc.Base64.parse(encryptedData);
+
+      // 提取前 16 字节作为 IV(AES-256-CBC 需要 16 字节 IV)
+      const iv = CryptoJS.lib.WordArray.create(combined.words.slice(0, 4), 16);
+
+      // 提取剩余部分作为密文
+      const ciphertext = CryptoJS.lib.WordArray.create(combined.words.slice(4), combined.sigBytes - 16);
+
+      // 将密钥转换为 WordArray(与加密方法保持一致)
+      let keyWordArray;
+      if (key && key.length === 32) {
+        // 如果 key 正好是 32 字节,直接使用 UTF8 解析
+        keyWordArray = CryptoJS.enc.Utf8.parse(key);
+      } else if (key) {
+        // 否则使用 SHA256 哈希生成 32 字节的密钥
+        keyWordArray = CryptoJS.SHA256(key);
+      } else {
+        throw new Error('解密密钥不能为空');
+      }
+
+      // 创建 CipherParams 对象用于解密
+      const cipherParams = CryptoJS.lib.CipherParams.create({
+        ciphertext: ciphertext
+      });
+
+      // 使用 AES-256-CBC 解密
+      const decrypted = CryptoJS.AES.decrypt(cipherParams, keyWordArray, {
+        iv: iv,
+        mode: CryptoJS.mode.CBC,
+        padding: CryptoJS.pad.Pkcs7
+      });
+
+      // 将解密后的数据转换为 UTF8 字符串
+      const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
+
+      console.log("解密后的数据:", JSON.parse(decryptedStr));
+      // 解析 JSON 字符串并返回对象
+      return JSON.parse(decryptedStr);
+    } catch (error) {
+      console.error('解密失败:', error);
+      // 如果解密失败,返回原始数据
+      return encryptedData;
+    }
+}

+ 161 - 0
utils/helper.ts

@@ -0,0 +1,161 @@
+import CryptoJS from 'crypto-js';
+/**
+ * @description: UUID生成
+ * @return {string}
+ */
+export const generateUUID = () => {
+  const chars =
+    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  let result = "";
+  for (let i = 32; i > 0; --i) {
+    result += chars[Math.floor(Math.random() * chars.length)];
+  }
+  return result;
+};
+
+/**
+ * @description: 检查某个对象是否缺失必要参数
+ * @param {object} paramsObj
+ * @param {string[]} requiredParams
+ * @return {string[]} 缺失的参数数组
+ */
+export const checkMissingParams = (paramsObj: object, requiredParams: string[]): string[] => {
+  const missingParams: string[] = [];
+  requiredParams.forEach(param => {
+    if (!paramsObj[param as keyof typeof paramsObj]) {
+      missingParams.push(param);
+    }
+  });
+
+  return missingParams
+}
+
+/**
+ * @description: 对象排序
+ * @param {object} data
+ * @return {object}
+ */
+export const sortObj = (data: object) => {
+  let arr = [];
+  for (let key in data) {
+    arr.push(key);
+  }
+  arr = arr.sort();
+  let newData = {};
+  for (var i in arr) {
+    newData[arr[i]] = data[arr[i]];
+  }
+  return newData;
+};
+
+export const generateRandomIV = (length: number) => {
+  const words = [];
+  const now = Date.now();
+  // 使用性能计数器增加随机性(如果可用)
+  // @ts-ignore
+  const perfNow = (typeof performance !== 'undefined' && performance.now) ? performance.now() : 0;
+
+  for (let i = 0; i < length; i += 4) {
+    // 组合多个随机源以增加随机性
+    const random1 = Math.random() * 0xffffffff;
+    const random2 = Math.random() * 0xffffffff;
+    const timePart = (now + i) & 0xffffffff;
+    const perfPart = (perfNow * 1000 + i) & 0xffffffff;
+
+    // 使用异或运算组合多个随机源
+    let combined = (random1 ^ random2 ^ timePart ^ perfPart) >>> 0;
+
+    // 如果长度不是 4 的倍数,需要处理最后一个不完整的 word
+    if (i + 4 > length) {
+      // 只使用需要的字节数
+      const remainingBytes = length - i;
+      combined = combined >>> (32 - remainingBytes * 8);
+      combined = combined << (32 - remainingBytes * 8);
+    }
+
+    words.push(combined);
+  }
+
+  return CryptoJS.lib.WordArray.create(words, length);
+}
+
+/**
+* @description: js异步转同步优化
+* @param {*} promiseObj
+* @return {*}
+*/
+export const to = (promiseObj) => {
+  return promiseObj
+    .then((data) => {
+      return [null, data];
+    })
+    .catch((err) => [err]);
+};
+
+/**
+ * @description: 获取unix时间戳(秒)
+ * @return {number}
+ */
+export const unixTimestamp = () => {
+  return Math.floor(Date.now() / 1000);
+};
+
+/**
+ * @description: 加密
+ * @param {*} value
+ * @return {string}
+ */
+export const compile = (value) => {
+  value = JSON.stringify(value);
+  let result = String.fromCharCode(value.charCodeAt(0) + value.length);
+  for (let i = 1; i < value.length; i++) {
+    result += String.fromCharCode(
+      value.charCodeAt(i) + value.charCodeAt(i - 1)
+    );
+  }
+  return escape(result);
+};
+
+/**
+ * @description:解密
+ * @param {string} value
+ * @return {*}
+ */
+export const uncompile = (value) => {
+  value = unescape(value);
+  let result = String.fromCharCode(value.charCodeAt(0) - value.length);
+  for (let i = 1; i < value.length; i++) {
+    result += String.fromCharCode(
+      value.charCodeAt(i) - result.charCodeAt(i - 1)
+    );
+  }
+  result = JSON.parse(result);
+  return result;
+};
+/**
+ * @description: 节流函数
+ * @param {Function} func 要节流的函数
+ * @param {number} wait 等待时间(毫秒)
+ * @return {Function} 节流后的函数
+ */
+export const throttle = (func, wait = 2000) => {
+  let lastCall = 0;
+  let pendingPromise = null;
+
+  return function executedFunction(...args) {
+    const now = Date.now();
+
+    if (now - lastCall >= wait) {
+      // 可以执行,重置时间
+      lastCall = now;
+      pendingPromise = null;
+      return func.apply(this, args);
+    } else {
+      // 被节流,返回一个被拒绝的 Promise
+      if (!pendingPromise) {
+        pendingPromise = Promise.reject(new Error("点击太快,请稍后再试"));
+      }
+      return pendingPromise;
+    }
+  };
+};

+ 160 - 0
utils/httpClient.ts

@@ -0,0 +1,160 @@
+import { relogin } from "../core/auth/server/server-auth";
+import { getCommonParams } from "../store/models/params.model";
+import UserInfoModel from "../store/models/user.model";
+import { decryptData, generateSign } from "./encrypted";
+
+interface ApiOptions {
+  url: string;
+  data?: any;
+  method?: 'GET' | 'POST'
+}
+
+interface ApiResponse {
+  code: number;
+  data: any;
+  message: string;
+}
+
+
+const api = async <T>({url,data,method='GET'}:ApiOptions): Promise<T>=>{
+  try {
+    return await request<T>({url,data,method})
+  } catch (error) {
+    // 如果是401错误,尝试重新登录
+      if (error.code === 401) {
+        console.log("登录过期,尝试重新登录...");
+        try {
+          // 调用重新登录方法
+          await relogin();
+          console.log("重新登录成功,重新发起请求...");
+
+          // 重新获取登录信息
+          const newLoginInfo = UserInfoModel.getInstance().get();
+
+          // 重新发起请求
+          return new Promise((resolve, reject) => {
+            tt.request({
+              url: url,
+              header: {
+                "Content-Type": "application/x-www-form-urlencoded",
+                Authorization: `Bearer ${newLoginInfo.token}`,
+              },
+              method: method,
+              data,
+              success: (res:any) => {
+                if (res.statusCode === 200) {
+                   const responseData = res.data as ApiResponse;
+                  if (responseData.code === 200) {
+                    // 解密响应数据
+                    responseData.data = decryptData(responseData.data);
+                    resolve(responseData.data);
+                  } else {
+                    reject({
+                      code: responseData.code,
+                      msg: responseData.message,
+                    });
+                  }
+                } else {
+                  reject({
+                    code: -1,
+                    msg: `网络请求失败 ${res.statusCode}`,
+                  });
+                }
+              },
+              fail: (error) => {
+                reject({
+                  code: -1,
+                  msg: error.errMsg,
+                });
+              },
+            });
+          });
+        } catch (loginError) {
+          console.error("重新登录失败:", loginError);
+          // 重新登录失败,返回原始错误
+          throw error;
+        }
+      } else {
+        // 其他错误直接抛出
+        throw error;
+      }
+  }
+ 
+}
+
+
+const request = async <T>({url,data,method='GET'}:ApiOptions): Promise<T>=>{
+ return new Promise<T>((resolve,reject)=>{
+    const token = UserInfoModel.getInstance().getToken();
+    const commonParams = getCommonParams()
+    let  params = {
+      ...commonParams,
+      ...data
+    }
+     params = generateSign(params)
+    // signParams.sign = sign
+
+ 
+    tt.request({
+    url,
+    data: params,
+    method,
+    header: {
+      "Content-Type": "application/x-www-form-urlencoded",
+      Authorization:`Bearer ${token}`
+    },
+    success: (res: any) => {
+      if(res.statusCode === 200) {
+         const responseData = res.data as ApiResponse;
+         if (responseData.code === 200) {
+          responseData.data = decryptData(responseData.data);
+           resolve(responseData.data);
+         }else if(responseData.code === 401){
+           reject({
+                  code: 401,
+                  msg: responseData.message,
+                });
+         }else {
+          tt.showModal({
+              title: "网络错误",
+              showCancel:false,
+              content: `${url}:${responseData.message}`,
+            })
+                reject({
+                  code: responseData.code,
+                  msg: responseData.message,
+                });
+              }
+        return responseData;
+      } else {
+         
+          reject({
+            code: -1,
+            msg: `network error ${res.message}`,
+          });
+        }
+    },
+    fail: (error) => {
+      reject({
+        code: -1,
+        msg: `network error ${error.errMsg}`,
+      });
+    },
+  });
+  })
+}
+
+
+
+const get = <T>(url:string,data?:any): Promise<T>=>{
+  return api<T>({url:`${import.meta.env.SDK_API_BASE}${url}`,data,method:'GET'})
+}
+
+const post = <T>(url:string,data?:any): Promise<T>=>{
+  return api<T>({url:`${import.meta.env.SDK_API_BASE}${url}`,data,method:'POST'})
+}
+
+export default {
+  get,
+  post
+}

+ 13 - 0
utils/wechat.ts

@@ -0,0 +1,13 @@
+/**
+ * @description: 获取设备设置
+ * @param {string} key
+ * @return {*}
+ */
+export const getSystemInfo = (key: string) => {
+  const systemSetting = tt.getSystemInfoSync()
+  if (key && key in systemSetting) {
+    return systemSetting[key];
+  } else {
+    return systemSetting;
+  }
+};

+ 36 - 0
vite.config.js

@@ -0,0 +1,36 @@
+import { resolve } from 'path';
+import { defineConfig, loadEnv } from 'vite';
+
+export default defineConfig(({ mode }) => {
+  const env = loadEnv(mode, process.cwd(), '');
+  return {
+    envPrefix: ['VITE_', 'SDK_'],
+    build: {
+      lib: {
+        entry: resolve(__dirname, 'index.ts'),
+        name: 'TTGameSdk',
+        formats: ['es'],
+        fileName: (format) => `ttGameSdk.${env.SDK_VERSION}.js`
+      },
+      outDir: 'dist',
+      minify: 'terser',
+      terserOptions: {
+        compress: {
+          drop_console: false,
+        },
+        format: {
+          comments: false,
+        },
+      },
+      rollupOptions: {
+        external: [],
+      }
+    },
+    resolve: {
+      alias: {
+        '@Core': resolve(__dirname, 'core'),
+        '@Store': resolve(__dirname, 'store'),
+      }
+    }
+  };
+});

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä