js脚本如何对接泡椒云网络验证?

js脚本如何对接泡椒云网络验证?auto jspro9 如何对接泡椒云网络验证 js 对接

大家好,欢迎来到IT知识分享网。

步骤

实例代码

下面代码需要你修改三处:AppKey、AppSecret和卡密,为了代码简洁,你可以把泡椒云的sdk封装成一个js模块文件调用

/*泡椒云的 sdk模块 */ const PJYSDK = (function () { 
    function PJYSDK(app_key, app_secret) { 
    http.__okhttp__.setMaxRetries(0); http.__okhttp__.setTimeout(5 * 1000); this.event = events.emitter(); this.debug = true; this._lib_version = "v1.13"; this._protocol = "http"; this._hosts = ["api3.paojiaoyun.com", "api2.paojiaoyun.com", "api.paojiaoyun.com"]; this._host = this._hosts[0]; this._device_id = this.getDeviceID(); this._retry_count = 9; this._switch_count = 0; this._app_key = app_key; this._app_secret = app_secret; this._card = null; this._username = null; this._password = null; this._token = null; this.is_trial = false; // 是否是试用用户 this.login_result = { 
    card_type: "", expires: "", expires_ts: 0, config: "", }; this._auto_heartbeat = true; // 是否自动开启心跳任务 this._heartbeat_gap = 120 * 1000; // 默认120秒 this._heartbeat_task = null; this._heartbeat_ret = { 
    code: -9, message: "还未开始验证" }; this._prev_nonce = null; this._is_ping = false; } PJYSDK.prototype.SetBackupHosts = function (hosts) { 
    // 设置备用 api host this._hosts.concat(hosts); }; PJYSDK.prototype.switchHost = function () { 
    // 切换备用 api host this._switch_count++; this._host = this._hosts[this._switch_count % this._hosts.length]; }; PJYSDK.prototype.SetCard = function (card) { 
    this._card = card.trim(); }; PJYSDK.prototype.SetUser = function (username, password) { 
    this._username = username.trim(); this._password = password; }; PJYSDK.prototype.getDeviceID = function () { 
    let id = device.serial; if (id == null || id == "" || id == "unknown") { 
    id = device.getAndroidId(); } if (id == null || id == "" || id == "unknown") { 
    id = device.getIMEI(); } return id; }; PJYSDK.prototype.MD5 = function (str) { 
    try { 
    let digest = java.security.MessageDigest.getInstance("md5"); let result = digest.digest(new java.lang.String(str).getBytes("UTF-8")); let buffer = new java.lang.StringBuffer(); for (let index = 0; index < result.length; index++) { 
    let b = result[index]; let number = b & 0xff; let str = java.lang.Integer.toHexString(number); if (str.length == 1) { 
    buffer.append("0"); } buffer.append(str); } return buffer.toString(); } catch (error) { 
    alert(error); return ""; } }; PJYSDK.prototype.getTimestamp = function () { 
    try { 
    let res = http.get("http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp"); let data = res.body.json(); return Math.floor(data["data"]["t"] / 1000) - 3; } catch (error) { 
    try { 
    let res = http.get("https://tptm.hd.mi.com/gettimestamp"); let data = res.body.string(); return parseInt(data.replace("var servertime=", "")) - 3; } catch (error) { 
    return Math.floor(new Date().getTime() / 1000) - 3; } } }; PJYSDK.prototype._draw_cc_params = function (body) { 
    if (!body) return ""; start = body.indexOf("?"); if (start < 0) return ""; end = body.indexOf('";'); if (end < 0 || end < start) return ""; return body.substring(start, end); }; PJYSDK.prototype.Ping = function () { 
    if (this._is_ping) return; try { 
    let path = "/v1/ping"; let url = this._protocol + "://" + this._host + path; let resp = http.get(url); let body = resp.body.string(); if (body == "Pong") { 
    log("api连接成功"); this._is_ping = true; return; } let params = this._draw_cc_params(body); if (params) { 
    let resp2 = http.get(url + params); if (resp2.body.string() == "Pong") { 
    log("api连接成功"); this._is_ping = true; } } else { 
    this.switchHost(); } } catch (error) { 
    this.switchHost(); } }; PJYSDK.prototype.genNonce = function () { 
    const ascii_str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0"; let tmp = ""; for (let i = 0; i < 20; i++) { 
    tmp += ascii_str.charAt(Math.round(Math.random() * ascii_str.length)); } return this.MD5(this.getDeviceID() + this._prev_nonce + new Date().getTime() + tmp); }; PJYSDK.prototype.joinParams = function (params) { 
    let ps = []; for (let k in params) { 
    ps.push(k + "=" + params[k]); } ps.sort(); return ps.join("&"); }; PJYSDK.prototype.CheckRespSign = function (resp) { 
    if (resp.code != 0 && resp.nonce === "" && resp.sign === "") { 
    return resp; } let ps = ""; if (resp["result"]) { 
    ps = this.joinParams(resp["result"]); } let s = resp["code"] + resp["message"] + ps + resp["nonce"] + this._app_secret; let sign = this.MD5(s); if (sign === resp["sign"]) { 
    if (this._prev_nonce === null) { 
    this._prev_nonce = resp["nonce"]; return { 
    code: 0, message: "OK" }; } else { 
    if (resp["nonce"] > this._prev_nonce) { 
    this._prev_nonce = resp["nonce"]; return { 
    code: 0, message: "OK" }; } else { 
    return { 
    code: -98, message: "CRS:nonce校验失败" }; } } } return { 
    code: -99, message: "CRS:签名校验失败" }; }; PJYSDK.prototype.retry_fib = function (num) { 
    if (num > 9) { 
    return 34; } let a = 0; let b = 1; for (let i = 0; i < num; i++) { 
    let tmp = a + b; a = b; b = tmp; } return a; }; PJYSDK.prototype._debug = function (path, params, result) { 
    if (this.debug) { 
    log("\n" + path, "\nparams:", params, "\nresult:", result); } }; PJYSDK.prototype.Request = function (method, path, params) { 
    this.Ping(); // 构建公共参数 params["app_key"] = this._app_key; method = method.toUpperCase(); let max_retries = this._retry_count; let retries_count = 0; let data = { 
    code: -1, message: "连接服务器失败" }; do { 
    let url = this._protocol + "://" + this._host + path; retries_count++; let sec = this.retry_fib(retries_count); delete params["sign"]; params["nonce"] = this.genNonce(); params["timestamp"] = this.getTimestamp(); let ps = this.joinParams(params); let s = method + this._host + path + ps + this._app_secret; let sign = this.MD5(s); params["sign"] = sign; let resp, body; try { 
    if (method === "GET") { 
    resp = http.get(url + "?" + ps + "&sign=" + sign); } else { 
    // POST resp = http.post(url, params); } body = resp.body.string(); data = JSON.parse(body); this._debug(method + "-" + path + ":", params, data); let crs = this.CheckRespSign(data); if (crs.code !== 0) { 
    return crs; } else { 
    return data; } } catch (error) { 
    if (this.debug) { 
    log("[*] request error: ", error, sec + "s后重试"); } this._debug(method + "-" + path + ":", params, body); this.switchHost(); sleep(sec * 1000); } } while (retries_count < max_retries); return data; }; /* 通用 */ PJYSDK.prototype.GetHeartbeatResult = function () { 
    return this._heartbeat_ret; }; PJYSDK.prototype.GetTimeRemaining = function () { 
    let g = this.login_result.expires_ts - this.getTimestamp(); if (g < 0) { 
    return 0; } return g; }; /* 卡密相关 */ PJYSDK.prototype.CardLogin = function () { 
    // 卡密登录 if (!this._card) { 
    return { 
    code: -4, message: "请先设置卡密" }; } let method = "POST"; let path = "/v1/card/login"; let data = { 
    card: this._card, device_id: this._device_id }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this._token = ret.result.token; this.login_result = ret.result; if (this._auto_heartbeat) { 
    this._startCardHeartbeat(); } } return ret; }; PJYSDK.prototype.CardHeartbeat = function () { 
    // 卡密心跳,默认会自动调用 if (!this._token) { 
    return { 
    code: -2, message: "请在卡密登录成功后调用" }; } let method = "POST"; let path = "/v1/card/heartbeat"; let data = { 
    card: this._card, token: this._token }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this.login_result.expires = ret.result.expires; this.login_result.expires_ts = ret.result.expires_ts; } return ret; }; PJYSDK.prototype._startCardHeartbeat = function () { 
    // 开启卡密心跳任务 if (this._heartbeat_task) { 
    this._heartbeat_task.interrupt(); this._heartbeat_task = null; } this._heartbeat_task = threads.start(function () { 
    setInterval(function () { 
   }, 10000); }); this._heartbeat_ret = this.CardHeartbeat(); this._heartbeat_task.setInterval( (self) => { 
    self._heartbeat_ret = self.CardHeartbeat(); if (self._heartbeat_ret.code != 0) { 
    self.event.emit("heartbeat_failed", self._heartbeat_ret); } }, this._heartbeat_gap, this ); this._heartbeat_task.setInterval( (self) => { 
    if (self.GetTimeRemaining() == 0) { 
    self.event.emit("heartbeat_failed", { 
    code: 10210, message: "卡密已过期!" }); } }, 1000, this ); }; PJYSDK.prototype.CardLogout = function () { 
    // 卡密退出登录 this._heartbeat_ret = { 
    code: -9, message: "还未开始验证" }; if (this._heartbeat_task) { 
    // 结束心跳任务 this._heartbeat_task.interrupt(); this._heartbeat_task = null; } if (!this._token) { 
    return { 
    code: 0, message: "OK" }; } let method = "POST"; let path = "/v1/card/logout"; let data = { 
    card: this._card, token: this._token }; let ret = this.Request(method, path, data); // 清理 this._token = null; this.login_result = { 
    card_type: "", expires: "", expires_ts: 0, config: "", }; return ret; }; PJYSDK.prototype.CardUnbindDevice = function () { 
    // 卡密解绑设备,需开发者后台配置 if (!this._token) { 
    return { 
    code: -2, message: "请在卡密登录成功后调用" }; } let method = "POST"; let path = "/v1/card/unbind_device"; let data = { 
    card: this._card, device_id: this._device_id, token: this._token }; return this.Request(method, path, data); }; PJYSDK.prototype.SetCardUnbindPassword = function (password) { 
    // 自定义设置解绑密码 if (!this._token) { 
    return { 
    code: -2, message: "请在卡密登录成功后调用" }; } let method = "POST"; let path = "/v1/card/unbind_password"; let data = { 
    card: this._card, password: password, token: this._token }; return this.Request(method, path, data); }; PJYSDK.prototype.CardUnbindDeviceByPassword = function (password) { 
    // 用户通过解绑密码解绑设备 let method = "POST"; let path = "/v1/card/unbind_device/by_password"; let data = { 
    card: this._card, password: password }; return this.Request(method, path, data); }; PJYSDK.prototype.CardRecharge = function (card, use_card) { 
    // 以卡充卡 let method = "POST"; let path = "/v1/card/recharge"; let data = { 
    card: card, use_card: use_card }; return this.Request(method, path, data); }; /* 用户相关 */ PJYSDK.prototype.UserRegister = function (username, password, card) { 
    // 用户注册(通过卡密) let method = "POST"; let path = "/v1/user/register"; let data = { 
    username: username, password: password, card: card, device_id: this._device_id }; return this.Request(method, path, data); }; PJYSDK.prototype.UserLogin = function () { 
    // 用户账号登录 if (!this._username || !this._password) { 
    return { 
    code: -4, message: "请先设置用户账号密码" }; } let method = "POST"; let path = "/v1/user/login"; let data = { 
    username: this._username, password: this._password, device_id: this._device_id }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this._token = ret.result.token; this.login_result = ret.result; if (this._auto_heartbeat) { 
    this._startUserHeartbeat(); } } return ret; }; PJYSDK.prototype.UserHeartbeat = function () { 
    // 用户心跳,默认会自动开启 if (!this._token) { 
    return { 
    code: -2, message: "请在用户登录成功后调用" }; } let method = "POST"; let path = "/v1/user/heartbeat"; let data = { 
    username: this._username, token: this._token }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this.login_result.expires = ret.result.expires; this.login_result.expires_ts = ret.result.expires_ts; } return ret; }; PJYSDK.prototype._startUserHeartbeat = function () { 
    // 开启用户心跳任务 if (this._heartbeat_task) { 
    this._heartbeat_task.interrupt(); this._heartbeat_task = null; } this._heartbeat_task = threads.start(function () { 
    setInterval(function () { 
   }, 10000); }); this._heartbeat_ret = this.UserHeartbeat(); this._heartbeat_task.setInterval( (self) => { 
    self._heartbeat_ret = self.UserHeartbeat(); if (self._heartbeat_ret.code != 0) { 
    self.event.emit("heartbeat_failed", self._heartbeat_ret); } }, this._heartbeat_gap, this ); this._heartbeat_task.setInterval( (self) => { 
    if (self.GetTimeRemaining() == 0) { 
    self.event.emit("heartbeat_failed", { 
    code: 10250, message: "用户已到期!" }); } }, 1000, this ); }; PJYSDK.prototype.UserLogout = function () { 
    // 用户退出登录 this._heartbeat_ret = { 
    code: -9, message: "还未开始验证" }; if (this._heartbeat_task) { 
    // 结束心跳任务 this._heartbeat_task.interrupt(); this._heartbeat_task = null; } if (!this._token) { 
    return { 
    code: 0, message: "OK" }; } let method = "POST"; let path = "/v1/user/logout"; let data = { 
    username: this._username, token: this._token }; let ret = this.Request(method, path, data); // 清理 this._token = null; this.login_result = { 
    card_type: "", expires: "", expires_ts: 0, config: "", }; return ret; }; PJYSDK.prototype.UserChangePassword = function (username, password, new_password) { 
    // 用户修改密码 let method = "POST"; let path = "/v1/user/password"; let data = { 
    username: username, password: password, new_password: new_password }; return this.Request(method, path, data); }; PJYSDK.prototype.UserRecharge = function (username, card) { 
    // 用户通过卡密充值 let method = "POST"; let path = "/v1/user/recharge"; let data = { 
    username: username, card: card }; return this.Request(method, path, data); }; PJYSDK.prototype.UserUnbindDevice = function () { 
    // 用户解绑设备,需开发者后台配置 if (!this._token) { 
    return { 
    code: -2, message: "请在用户登录成功后调用" }; } let method = "POST"; let path = "/v1/user/unbind_device"; let data = { 
    username: this._username, device_id: this._device_id, token: this._token }; return this.Request(method, path, data); }; /* 配置相关 */ PJYSDK.prototype.GetCardConfig = function () { 
    // 获取卡密配置 let method = "GET"; let path = "/v1/card/config"; let data = { 
    card: this._card }; return this.Request(method, path, data); }; PJYSDK.prototype.UpdateCardConfig = function (config) { 
    // 更新卡密配置 let method = "POST"; let path = "/v1/card/config"; let data = { 
    card: this._card, config: config }; return this.Request(method, path, data); }; PJYSDK.prototype.GetUserConfig = function () { 
    // 获取用户配置 let method = "GET"; let path = "/v1/user/config"; let data = { 
    user: this._username }; return this.Request(method, path, data); }; PJYSDK.prototype.UpdateUserConfig = function (config) { 
    // 更新用户配置 let method = "POST"; let path = "/v1/user/config"; let data = { 
    username: this._username, config: config }; return this.Request(method, path, data); }; /* 软件相关 */ PJYSDK.prototype.GetSoftwareConfig = function () { 
    // 获取软件配置 let method = "GET"; let path = "/v1/software/config"; return this.Request(method, path, { 
   }); }; PJYSDK.prototype.GetSoftwareNotice = function () { 
    // 获取软件通知 let method = "GET"; let path = "/v1/software/notice"; return this.Request(method, path, { 
   }); }; PJYSDK.prototype.GetSoftwareLatestVersion = function (current_ver) { 
    // 获取软件最新版本 let method = "GET"; let path = "/v1/software/latest_ver"; let data = { 
    version: current_ver }; return this.Request(method, path, data); }; /* 试用功能 */ PJYSDK.prototype.TrialLogin = function () { 
    // 试用登录 let method = "POST"; let path = "/v1/trial/login"; let data = { 
    device_id: this._device_id }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this.is_trial = true; this.login_result = ret.result; if (this._auto_heartbeat) { 
    this._startTrialHeartbeat(); } } return ret; }; PJYSDK.prototype.TrialHeartbeat = function () { 
    // 试用心跳,默认会自动调用 let method = "POST"; let path = "/v1/trial/heartbeat"; let data = { 
    device_id: this._device_id }; let ret = this.Request(method, path, data); if (ret.code == 0) { 
    this.login_result.expires = ret.result.expires; this.login_result.expires_ts = ret.result.expires_ts; } return ret; }; PJYSDK.prototype._startTrialHeartbeat = function () { 
    // 开启试用心跳任务 if (this._heartbeat_task) { 
    this._heartbeat_task.interrupt(); this._heartbeat_task = null; } this._heartbeat_task = threads.start(function () { 
    setInterval(function () { 
   }, 10000); }); this._heartbeat_ret = this.TrialHeartbeat(); this._heartbeat_task.setInterval( (self) => { 
    self._heartbeat_ret = self.TrialHeartbeat(); if (self._heartbeat_ret.code != 0) { 
    self.event.emit("heartbeat_failed", self._heartbeat_ret); } }, this._heartbeat_gap, this ); this._heartbeat_task.setInterval( (self) => { 
    if (self.GetTimeRemaining() == 0) { 
    self.event.emit("heartbeat_failed", { 
    code: 10407, message: "试用已到期!" }); } }, 1000, this ); }; PJYSDK.prototype.TrialLogout = function () { 
    // 试用退出登录,没有http请求,只是清理本地记录 this.is_trial = false; this._heartbeat_ret = { 
    code: -9, message: "还未开始验证" }; if (this._heartbeat_task) { 
    // 结束心跳任务 this._heartbeat_task.interrupt(); this._heartbeat_task = null; } // 清理 this._token = null; this.login_result = { 
    card_type: "", expires: "", expires_ts: 0, config: "", }; return { 
    code: 0, message: "OK" }; }; /* 高级功能 */ PJYSDK.prototype.GetRemoteVar = function (key) { 
    // 获取远程变量 let method = "GET"; let path = "/v1/af/remote_var"; let data = { 
    key: key }; return this.Request(method, path, data); }; PJYSDK.prototype.GetRemoteData = function (key) { 
    // 获取远程数据 let method = "GET"; let path = "/v1/af/remote_data"; let data = { 
    key: key }; return this.Request(method, path, data); }; PJYSDK.prototype.CreateRemoteData = function (key, value) { 
    // 创建远程数据 let method = "POST"; let path = "/v1/af/remote_data"; let data = { 
    action: "create", key: key, value: value }; return this.Request(method, path, data); }; PJYSDK.prototype.UpdateRemoteData = function (key, value) { 
    // 修改远程数据 let method = "POST"; let path = "/v1/af/remote_data"; let data = { 
    action: "update", key: key, value: value }; return this.Request(method, path, data); }; PJYSDK.prototype.DeleteRemoteData = function (key) { 
    // 删除远程数据 let method = "POST"; let path = "/v1/af/remote_data"; let data = { 
    action: "delete", key: key }; return this.Request(method, path, data); }; PJYSDK.prototype.CallRemoteFunc = function (func_name, params) { 
    // 执行远程函数 let method = "POST"; let path = "/v1/af/call_remote_func"; let ps = JSON.stringify(params); let data = { 
    func_name: func_name, params: ps }; let ret = this.Request(method, path, data); if (ret.code == 0 && ret.result.return) { 
    ret.result = JSON.parse(ret.result.return); } return ret; }; return PJYSDK; })(); /* 上面的代码无需修改 你也可以把它封装为模块调用 */ /* 下面开始调用 */ // AppKey 和 AppSecret 在泡椒云开发者后台获取 let pjysdk = new PJYSDK("cldeb3rdqusrt119pv20", "VRaGssW25AVlhc3ODt2G4weycMQB152e"); pjysdk.debug = true; /* 这个是卡密 */ pjysdk.SetCard("cloudyNL3Xvx97wfWDizprRyN"); // 监听心跳失败事件 pjysdk.event.on("heartbeat_failed", function (hret) { 
    toastLog(hret.message); if (hret.code === 10214) { 
    sleep(200); exit(); // 退出脚本 return; } log("心跳失败,尝试重登..."); sleep(2000); let login_ret = pjysdk.CardLogin(); if (login_ret.code == 0) { 
    log("重登成功"); } else { 
    toastLog(login_ret.message); // 重登失败 sleep(200); exit(); // 退出脚本 } }); // 当脚本正常或者异常退出时会触发exit事件 events.on("exit", function () { 
    pjysdk.CardLogout(); // 调用退出登录 log("结束运行"); }); let login_ret = pjysdk.CardLogin(); if (login_ret.code == 0) { 
    // 登录成功,后面写你的业务代码 } else { 
    // 登录失败提示 toast(login_ret.message); } 

运行测试

在这里插入图片描述输出结果

11-20 20:45:22.205/D: POST-/v1/card/login: params: { 
    card: 'cloudyNL3Xvx97wfWDizprRyN', device_id: '44993ef5a3f44521', app_key: 'cldeb3rdqusrt119pv20', nonce: 'd2bbb57323febd8d8ab0201b', timestamp: , sign: 'a421b1cd8e0f9a978a52d5ccc8a4f983' } result: { 
    code: 0, message: 'ok', result: { 
    card_type: '天卡', token: 'XjgHrNwqa1whKyujwLxU', expires: '2023-11-21 12:53:36', expires_ts: , config: '', server_time:  }, nonce: 'cldl9njdqusp0rk3c9c0', sign: '4865ac8f89c3be1dfd21836cc' } 

api文档

pjysdk.SetCard(card) 
pjysdk.SetUser(username, password) 
let ret = pjysdk.getTimestamp() 
let ret = pjysdk.GetHeartbeatResult() 
let ret = pjysdk.GetTimeRemaining() 
let ret = pjysdk.CardLogin() 
let ret = pjysdk.CardLogout() 
let ret = pjysdk.CardUnbindDevice() 
let ret = pjysdk.SetCardUnbindPassword(password) 
let ret = pjysdk.CardUnbindDeviceByPassword(password) 
let ret = pjysdk.CardRecharge(card, use_card) 
let ret = pjysdk.UserRegister(username, password, card) 
let ret = pjysdk.UserLogin() 
let ret = pjysdk.UserLogout() 
let ret = pjysdk.UserChangePassword(username, password, new_password) 
let ret = pjysdk.UserRecharge(username, card) 
let ret = pjysdk.UserUnbindDevice() 
let ret = pjysdk.TrialLogin() 
let ret = pjysdk.TrialLogout() 
let ret = pjysdk.GetCardConfig() 
let ret = pjysdk.UpdateCardConfig(config) 
let ret = pjysdk.GetUserConfig() 
let ret = pjysdk.UpdateUserConfig(config) 
let ret = pjysdk.GetSoftwareConfig() 
let ret = pjysdk.GetSoftwareNotice() 
let ret = pjysdk.GetSoftwareLatestVersion(current_ver) 
let ret = pjysdk.GetRemoteVar(key) 
let ret = pjysdk.GetRemoteData(key) 
let ret = pjysdk.CreateRemoteData(key, value) 
let ret = pjysdk.UpdateRemoteData(key, value) 
let ret = pjysdk.DeleteRemoteData(key) 
let ret = pjysdk.CallRemoteFunc(func_name, params) 
function add(a, b) { 
    return a + b; } 

脚本中调用:

let ret = pjysdk.CallRemoteFunc("add", [1, 2]) if (ret.code == 0) { 
    log(ret.result.return); } else { 
    log(ret.message); } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/137640.html

(0)
上一篇 2025-06-18 21:10
下一篇 2025-06-18 21:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信