123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- // 全局状态变量
- let tempTimer = 0;
- let client = null;
- let util = null;
- let mDeviceEvent = null;
- let crypto = null;
- let md5 = null;
- let aesjs = null;
- const timeOut = 20; // 超时时间
- let timeId = "";
- let sequenceControl = 0;
- // let sequenceNumber = -1; // 此变量在代码中未被使用,可以移除
- // _self 对象用于维护单个连接会话的状态
- let _self = {
- data: {
- deviceId: null,
- isConnected: false,
- failure: false,
- value: 0,
- desc: "请耐心等待...",
- isChecksum: true,
- isEncrypt: true,
- flagEnd: false,
- defaultData: 1,
- ssidType: 2,
- passwordType: 3,
- meshIdType: 3,
- ssid: "",
- password: "",
- meshId: "",
- uuid: "",
- serviceId: "",
- processList: [],
- result: [],
- service_uuid: "0000FFFF-0000-1000-8000-00805F9B34FB",
- characteristic_write_uuid: "0000FF01-0000-1000-8000-00805F9B34FB",
- characteristic_read_uuid: "0000FF02-0000-1000-8000-00805F9B34FB",
- customData: null,
- md5Key: 0,
- }
- };
- // =================================================================
- // 辅助/工具函数 (已优化)
- // =================================================================
- /**
- * 将 ArrayBuffer 转换为十六进制字符串
- * @param {ArrayBuffer} buffer
- * @returns {string}
- */
- function buf2hex(buffer) {
- return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
- }
- /**
- * [优化] 将 ArrayBuffer (字节数组) 转换为字符串。
- * 使用 TextDecoder API,性能更高且能正确处理 UTF-8 编码。
- * @param {ArrayBuffer} buffer
- * @returns {string}
- */
- function buf2string(buffer) {
- try {
- const dataView = new DataView(buffer);
- // 微信小程序环境支持 TextDecoder
- const decoder = new TextDecoder('utf-8');
- return decoder.decode(dataView);
- } catch (e) {
- // 降级处理,以防极旧环境不支持
- const arr = Array.prototype.map.call(new Uint8Array(buffer), x => x);
- let str = '';
- for (let i = 0; i < arr.length; i++) {
- str += String.fromCharCode(arr[i]);
- }
- return decodeURIComponent(escape(str));
- }
- }
- /**
- * [优化] 将字符串转换为 UTF-8 编码的字节数组。
- * 使用 TextEncoder API,性能远超手动循环,且编码更标准。
- * @param {string} str
- * @returns {number[]}
- */
- function stringToUtf8Bytes(str) {
- try {
- // 微信小程序环境支持 TextEncoder
- const encoder = new TextEncoder();
- return Array.from(encoder.encode(str));
- } catch (e) {
- // 降级处理
- return unescape(encodeURIComponent(str)).split("").map(val => val.charCodeAt());
- }
- }
- /**
- * [优化] 判断并处理返回的数据帧,提取负载数据。
- * 简化了原有的 if/else 重复逻辑。
- * @param {string[]} fragNum - 帧控制字段的二进制数组
- * @param {number[]} list - 原始数据包
- * @param {any} md5Key - MD5密钥
- * @returns {number[]} - 提取出的负载数据
- */
- function parsePayload(fragNum, list, md5Key) {
- let payload = list;
- // 步骤1: 如果有校验和,则移除末尾的校验位
- if (fragNum[6] == "1") {
- payload = payload.slice(0, payload.length - 2);
- }
- // 步骤2: 根据是否加密进行解密 (当前代码中解密逻辑未实现,仅预留位置)
- if (fragNum[7] == "1") {
- // 返回数据已加密
- // let iv = this.generateAESIV(parseInt(payload[2], 16));
- // 在此处添加解密逻辑...
- }
- // 步骤3: 根据是否分包,移除帧头,提取有效数据
- if (fragNum[3] == "0") { // 未分包
- _self.data.flagEnd = true;
- return payload.slice(4);
- } else { // 分包
- _self.data.flagEnd = false;
- return payload.slice(6);
- }
- }
- // =================================================================
- // BLE 核心写入函数
- // =================================================================
- /**
- * 发送安全协商数据 (P, G, K)
- * @param {string} deviceId
- * @param {string} serviceId
- * @param {string} characteristicId
- * @param {object} client - DH 客户端实例
- * @param {number[]} kBytes - 公钥
- * @param {number[]} pBytes - P
- * @param {number[]} gBytes - G
- * @param {number[]|null} data - 分包后的剩余数据
- */
- function getSecret(deviceId, serviceId, characteristicId, client, kBytes, pBytes, gBytes, data) {
- let obj = {};
- let frameControl = 0;
- sequenceControl = parseInt(sequenceControl) + 1;
- if (!util._isEmpty(data)) {
- obj = util.isSubcontractor(data, true, sequenceControl);
- frameControl = util.getFrameCTRLValue(false, true, util.DIRECTION_OUTPUT, false, obj.flag);
- } else {
- let payload = [];
- payload.push(util.NEG_SET_SEC_ALL_DATA);
-
- const pLength = pBytes.length;
- payload.push((pLength >> 8) & 0xff, pLength & 0xff, ...pBytes);
- const gLength = gBytes.length;
- payload.push((gLength >> 8) & 0xff, gLength & 0xff, ...gBytes);
- const kLength = kBytes.length;
- payload.push((kLength >> 8) & 0xff, kLength & 0xff, ...kBytes);
- obj = util.isSubcontractor(payload, true, sequenceControl);
- frameControl = util.getFrameCTRLValue(false, true, util.DIRECTION_OUTPUT, false, obj.flag);
- }
- const value = util.writeData(util.PACKAGE_VALUE, util.SUBTYPE_NEG, frameControl, sequenceControl, obj.len, obj.lenData);
- const typedArray = new Uint8Array(value);
- wx.writeBLECharacteristicValue({
- deviceId: deviceId,
- serviceId: serviceId,
- characteristicId: characteristicId,
- value: typedArray.buffer,
- success: function (res) {
- if (obj.flag) { // 如果有剩余数据,则继续发送
- getSecret(deviceId, serviceId, characteristicId, client, kBytes, pBytes, gBytes, obj.laveData);
- }
- },
- fail: function (res) {
- console.error("getSecret failed:", res, { deviceId, serviceId, characteristicId });
- }
- });
- }
- /**
- * [新增] 通用的数据包发送函数,用于取代原先多个重复的 writeXXX 函数
- * @param {number} subType - 包的子类型
- * @param {number[]} dataPayload - 要发送的原始数据负载
- * @param {function} onSuccess - 发送成功(且无分包)后的回调函数
- */
- function sendBlufiDataPacket(subType, dataPayload, onSuccess) {
- const deviceId = _self.data.deviceId;
- const serviceId = _self.data.service_uuid;
- const characteristicId = _self.data.characteristic_write_uuid;
- function writePacket(remainingData) {
- sequenceControl = parseInt(sequenceControl) + 1;
-
- // 检查是否需要分包
- const obj = util.isSubcontractor(remainingData, _self.data.isChecksum, sequenceControl, _self.data.isEncrypt);
- const frameControl = util.getFrameCTRLValue(_self.data.isEncrypt, _self.data.isChecksum, util.DIRECTION_OUTPUT, false, obj.flag);
-
- // 加密数据
- const encryptedData = util.encrypt(aesjs, _self.data.md5Key, sequenceControl, obj.lenData, true);
-
- // 组装最终数据包
- const value = util.writeData(util.PACKAGE_VALUE, subType, frameControl, sequenceControl, obj.len, encryptedData);
- const typedArray = new Uint8Array(value);
- wx.writeBLECharacteristicValue({
- deviceId: deviceId,
- serviceId: serviceId,
- characteristicId: characteristicId,
- value: typedArray.buffer,
- success: function (res) {
- if (obj.flag) {
- // 如果有分包,继续发送剩余数据
- writePacket(obj.laveData);
- } else if (onSuccess) {
- // 发送完成,执行成功回调
- onSuccess();
- }
- },
- fail: function (res) {
- console.error(`sendBlufiDataPacket failed for subType ${subType}:`, res);
- }
- });
- }
- // 启动第一次写入
- writePacket(dataPayload);
- }
- function writeDeviceRouterInfoStart() {
- const payload = [_self.data.defaultData];
- const onSuccess = () => writeRouterSsid();
- // 注意:此处的 subType 需要根据你的 util 库定义来确定
- sendBlufiDataPacket(util.SUBTYPE_WIFI_MODEl, payload, onSuccess);
- }
- function writeRouterSsid() {
- const ssidData = stringToUtf8Bytes(_self.data.ssid);
- const onSuccess = () => writeDevicePwd();
- sendBlufiDataPacket(util.SUBTYPE_SET_SSID, ssidData, onSuccess);
- }
- function writeDevicePwd() {
- const pwdData = stringToUtf8Bytes(_self.data.password);
- const onSuccess = () => writeDeviceEnd();
- sendBlufiDataPacket(util.SUBTYPE_SET_PWD, pwdData, onSuccess);
- }
- function writeCutomsData() {
- const customDataBytes = stringToUtf8Bytes(_self.data.customData);
- // 自定义数据发送后通常没有下一步,所以 onSuccess 为 null
- sendBlufiDataPacket(util.SUBTYPE_CUSTOM_DATA, customDataBytes, null);
- }
- /**
- * 发送结束指令
- */
- function writeDeviceEnd() {
- sequenceControl = parseInt(sequenceControl) + 1;
- const frameControl = util.getFrameCTRLValue(_self.data.isEncrypt, false, util.DIRECTION_OUTPUT, false, false);
- const value = util.writeData(_self.data.PACKAGE_CONTROL_VALUE, util.SUBTYPE_END, frameControl, sequenceControl, 0, null);
- const typedArray = new Uint8Array(value);
- wx.writeBLECharacteristicValue({
- deviceId: _self.data.deviceId,
- serviceId: _self.data.service_uuid,
- characteristicId: _self.data.characteristic_write_uuid,
- value: typedArray.buffer,
- success: function (res) {},
- fail: function (res) {}
- });
- }
- /**
- * 发送获取附近Wi-Fi列表的指令
- */
- function writeGetNearRouterSsid() {
- const deviceId = _self.data.deviceId;
- const serviceId = _self.data.service_uuid;
- const characteristicId = _self.data.characteristic_write_uuid;
- sequenceControl = parseInt(sequenceControl) + 1;
- const frameControl = util.getFrameCTRLValue(_self.data.isEncrypt, false, util.DIRECTION_OUTPUT, false, false);
- const value = util.writeData(util.PACKAGE_CONTROL_VALUE, util.SUBTYPE_WIFI_NEG, frameControl, sequenceControl, 0, null);
- const typedArray = new Uint8Array(value);
- wx.writeBLECharacteristicValue({
- deviceId: deviceId,
- serviceId: serviceId,
- characteristicId: characteristicId,
- value: typedArray.buffer,
- success: function (res) {},
- fail: function (res) {}
- });
- }
- // =================================================================
- // 事件监听与主逻辑
- // =================================================================
- function init() {
- // 加载依赖
- mDeviceEvent = require('./xBlufi.js');
- util = require('@/utils/blufi/util.js');
- crypto = require('@/utils/blufi/crypto/crypto-dh.js');
- md5 = require('@/utils/blufi/crypto/md5.min.js');
- aesjs = require('@/utils/blufi/crypto/aes.js');
- // 监听蓝牙连接状态变化
- wx.onBLEConnectionStateChange(function (res) {
- const obj = {
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_STATUS_CONNECTED,
- 'result': res.connected,
- 'data': res
- };
- mDeviceEvent.notifyDeviceMsgEvent(obj);
- });
- // 监听开始扫描蓝牙设备事件
- mDeviceEvent.listenStartDiscoverBle(true, function (options) {
- if (options.isStart) {
- wx.closeBluetoothAdapter({
- complete: function (res) {
- wx.openBluetoothAdapter({
- success: function (res) {
- wx.startBluetoothDevicesDiscovery({
- allowDuplicatesKey: true,
- success: function (res) {
- const devicesList = [];
- const foundDevices = new Set(); // 使用 Set 来快速去重
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START,
- 'result': true,
- 'data': res
- });
- wx.onBluetoothDeviceFound(function (deviceRes) {
- const device = deviceRes.devices[0];
- if (device && device.deviceId && !foundDevices.has(device.deviceId)) {
- foundDevices.add(device.deviceId);
- devicesList.push({
- ...device,
- advertisData: device.advertisData ? buf2hex(device.advertisData) : ''
- });
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS,
- 'result': true,
- 'data': [...devicesList] // 发送列表的副本
- });
- }
- });
- },
- fail: function (res) {
- mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START, 'result': false, 'data': res });
- }
- });
- },
- fail: function (res) {
- mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START, 'result': false, 'data': res });
- }
- });
- }
- });
- } else {
- wx.stopBluetoothDevicesDiscovery({
- success: function (res) {
- mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_STOP, 'result': true, 'data': res });
- },
- fail: function (res) {
- mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_STOP, 'result': false, 'data': res });
- }
- });
- }
- });
- // 监听连接/断开蓝牙设备事件 (按要求,此函数未修改)
- mDeviceEvent.listenConnectBle(true, function (options) {
- //console.log("我要连接?", (options.isStart))
- if (options.isStart)
- wx.createBLEConnection({
- deviceId: options.deviceId,
- success: function (res) {
- wx.setBLEMTU({
- deviceId: options.deviceId,
- mtu: 128
- })
- _self.data.deviceId = options.deviceId
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CONNECTED,
- 'result': true,
- 'data': {
- deviceId: options.deviceId,
- name: options.name
- },
- });
- },
- fail: function (res) {
- _self.data.deviceId = null
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CONNECTED,
- 'result': false,
- 'data': res,
- });
- }
- });
- else wx.closeBLEConnection({
- deviceId: options.deviceId,
- success: function (res) {
- console.log('断开成功')
- _self.data.deviceId = null
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CLOSE_CONNECTED,
- 'result': true,
- 'data': {
- deviceId: options.deviceId,
- name: options.name
- }
- });
- },
- fail: function (res) {
- _self.data.deviceId = null
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CLOSE_CONNECTED,
- 'result': false,
- 'data': res,
- });
- }
- })
- });
- // 监听初始化ESP32(BluFi流程)事件
- mDeviceEvent.listenInitBleEsp32(true, function (options) {
- // 重置状态
- sequenceControl = 0;
- _self.data.result = [];
- _self.data.deviceId = options.deviceId;
-
- wx.getBLEDeviceServices({
- deviceId: options.deviceId,
- success: function (res) {
- const service = res.services.find(s => s.uuid === _self.data.service_uuid);
- if (!service) {
- throw new Error("Blufi service not found");
- }
-
- const serviceId = service.uuid;
- wx.getBLEDeviceCharacteristics({
- deviceId: options.deviceId,
- serviceId: serviceId,
- success: function (charRes) {
- const writeChar = charRes.characteristics.find(c => c.uuid === _self.data.characteristic_write_uuid);
- const readChar = charRes.characteristics.find(c => c.uuid === _self.data.characteristic_read_uuid);
- if (!writeChar || !readChar) {
- throw new Error("Blufi characteristics not found");
- }
-
- _self.data.serviceId = serviceId;
- _self.data.uuid = writeChar.uuid;
- wx.notifyBLECharacteristicValueChange({
- state: true,
- deviceId: options.deviceId,
- serviceId: serviceId,
- characteristicId: readChar.uuid,
- success: function (notifyRes) {
- // 启用通知成功,开始安全协商
- client = util.blueDH(util.DH_P, util.DH_G, crypto);
- const kBytes = util.uint8ArrayToArray(client.getPublicKey());
- const pBytes = util.hexByInt(util.DH_P);
- const gBytes = util.hexByInt(util.DH_G);
- const pgkLength = pBytes.length + gBytes.length + kBytes.length + 6;
- const data = [
- util.NEG_SET_SEC_TOTAL_LEN,
- (pgkLength >> 8) & 0xff,
- pgkLength & 0xff
- ];
-
- const frameControl = util.getFrameCTRLValue(false, false, util.DIRECTION_OUTPUT, false, false);
- const value = util.writeData(util.PACKAGE_VALUE, util.SUBTYPE_NEG, frameControl, sequenceControl, data.length, data);
- const typedArray = new Uint8Array(value);
- wx.writeBLECharacteristicValue({
- deviceId: options.deviceId,
- serviceId: serviceId,
- characteristicId: writeChar.uuid,
- value: typedArray.buffer,
- success: function (writeRes) {
- getSecret(options.deviceId, serviceId, writeChar.uuid, client, kBytes, pBytes, gBytes, null);
- },
- fail: (err) => mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT, 'result': false, 'data': err })
- });
- // 监听从设备返回的数据
- wx.onBLECharacteristicValueChange(function (changeRes) {
- const list2 = (util.ab2hex(changeRes.value));
- if (list2.length < 4) { return; }
- const val = parseInt(list2[0], 16);
- const type = val & 3;
- const subType = val >> 2;
-
- // 使用优化后的 payload 解析函数
- const fragNum = util.hexToBinArray(list2[1]);
- const payload = parsePayload(fragNum, list2, _self.data.md5Key);
- _self.data.result = _self.data.result.concat(payload);
- if (_self.data.flagEnd) {
- const resultData = [..._self.data.result];
- _self.data.result = []; // 清空缓存
-
- if (type == 1) { // Type: Data
- console.log("Receive data, subType: ", subType);
- switch (subType) {
- case util.SUBTYPE_NEGOTIATION_NEG: // 安全协商响应
- const arr = util.hexByInt(resultData.join(""));
- const clientSecret = client.computeSecret(new Uint8Array(arr));
- _self.data.md5Key = md5.array(clientSecret);
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT,
- 'result': true,
- 'data': { deviceId: options.deviceId, serviceId, characteristicId: writeChar.uuid }
- });
- break;
- case 15: // Wi-Fi连接状态报告
- const stationStatus = String.fromCharCode(...resultData.map(hex => parseInt(hex, 16)));
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CONNECT_ROUTER_RESULT,
- 'result': resultData.length > 3,
- 'data': { 'progress': 100, 'status': stationStatus }
- });
- break;
- case 17: // 附近Wi-Fi列表
- getList(resultData);
- break;
- case 19: // 自定义数据
- const customDataStr = String.fromCharCode(...resultData.map(hex => parseInt(hex, 16)));
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_RECIEVE_CUSTON_DATA,
- 'result': true,
- 'data': customDataStr
- });
- break;
- default:
- console.log("Unhandled data subType:", subType);
- break;
- }
- } else {
- console.error("Received an error type frame:", type);
- }
- }
- });
- },
- fail: (err) => mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT, 'result': false, 'data': err })
- });
- },
- fail: (err) => mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT, 'result': false, 'data': err })
- });
- },
- fail: (err) => mDeviceEvent.notifyDeviceMsgEvent({ 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT, 'result': false, 'data': err })
- });
- });
- // 监听发送SSID和密码事件
- mDeviceEvent.listenSendRouterSsidAndPassword(true, function (options) {
- _self.data.password = options.password;
- _self.data.ssid = options.ssid;
- writeDeviceRouterInfoStart();
- });
- // 监听发送自定义数据事件
- mDeviceEvent.listenSendCustomData(true, function (options) {
- _self.data.customData = options.customData;
- writeCutomsData();
- });
-
- // 监听发送获取附近Wi-Fi指令事件
- mDeviceEvent.listenSendGetNearRouterSsid(true, function(options) {
- writeGetNearRouterSsid();
- });
- }
- /**
- * [优化] 处理Wi-Fi列表数据
- * 使用循环和索引替代递归和splice,性能更高。
- * @param {number[]} arr - 包含所有Wi-Fi信息的字节数组
- */
- function getList(arr) {
- let currentIndex = 0;
- while (currentIndex < arr.length) {
- const len = parseInt(arr[currentIndex], 16);
- if (len <= 0) {
- // 长度异常,跳出循环
- break;
- }
-
- // 检查数据是否完整
- if (currentIndex + 1 + len > arr.length) {
- console.error("Incomplete Wi-Fi data frame.");
- break;
- }
- const rssi = parseInt(arr[currentIndex + 1], 16);
- const nameBytes = arr.slice(currentIndex + 2, currentIndex + 1 + len);
- const name = String.fromCharCode(...nameBytes.map(hex => parseInt(hex, 16)));
-
- mDeviceEvent.notifyDeviceMsgEvent({
- 'type': mDeviceEvent.XBLUFI_TYPE.TYPE_CONNECT_NEAR_ROUTER_LISTS,
- 'result': true,
- 'data': { "rssi": rssi, "SSID": decodeURIComponent(escape(name)) } // 保持和原逻辑一致的解码方式
- });
-
- // 移动索引到下一个Wi-Fi信息的开头
- currentIndex += (1 + len);
- }
- }
- /****************************** 对外导出 ***************************************/
- module.exports = {
- init: init,
- };
|