From 3fa0238fbcfcc9aa310ad294267bb345677c8954 Mon Sep 17 00:00:00 2001 From: "COMPUTER\\EDY" <249929363@qq.com> Date: Tue, 7 Jul 2026 14:53:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9C=88=E5=8D=A1=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E5=92=8C=E4=BC=98=E5=8C=96=20=E6=B5=B7?= =?UTF-8?q?=E5=A4=96=E7=89=88app=20store=E4=BB=98=E8=B4=B9=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/Script/Revive.ts | 13 +- assets/Script/module/Pay/Utils.ts | 6 +- assets/Script/monthlyCard.ts | 11 +- assets/action_bundle/script/NewbieGift.ts | 15 +- assets/shop/img/30.png | Bin 0 -> 14643 bytes assets/shop/img/30.png.meta | 38 + assets/shop/prefab/monthlyCard.prefab | 582 ++++---- assets/shop/script/passCheck.ts | 4 +- assets/shop/script/shop.ts | 6 +- server/laf-cloud/functions/CDNKey.ts | 10 + server/laf-cloud/functions/CDNKey.yaml | 6 + server/laf-cloud/functions/TokenMgr.ts | 70 + server/laf-cloud/functions/TokenMgr.yaml | 6 + server/laf-cloud/functions/Utils.ts | 13 + server/laf-cloud/functions/Utils.yaml | 6 + server/laf-cloud/functions/__init__.ts | 6 + server/laf-cloud/functions/__init__.yaml | 6 + server/laf-cloud/functions/address.ts | 127 ++ server/laf-cloud/functions/address.yaml | 6 + server/laf-cloud/functions/canRebornGift.ts | 41 + server/laf-cloud/functions/canRebornGift.yaml | 6 + .../laf-cloud/functions/clearPHBDataById.ts | 23 + .../laf-cloud/functions/clearPHBDataById.yaml | 6 + server/laf-cloud/functions/clearPay.ts | 37 + server/laf-cloud/functions/clearPay.yaml | 6 + server/laf-cloud/functions/clearSrank.ts | 14 + server/laf-cloud/functions/clearSrank.yaml | 6 + server/laf-cloud/functions/comment.ts | 132 ++ server/laf-cloud/functions/comment.yaml | 6 + server/laf-cloud/functions/creatCDkTwo.ts | 38 + server/laf-cloud/functions/creatCDkTwo.yaml | 6 + server/laf-cloud/functions/creatCdk.ts | 15 + server/laf-cloud/functions/creatCdk.yaml | 6 + server/laf-cloud/functions/dayTaskInfo.ts | 110 ++ server/laf-cloud/functions/dayTaskInfo.yaml | 6 + server/laf-cloud/functions/drawHead.ts | 111 ++ server/laf-cloud/functions/drawHead.yaml | 6 + server/laf-cloud/functions/getAddressRank.ts | 12 + .../laf-cloud/functions/getAddressRank.yaml | 6 + server/laf-cloud/functions/getFilm.ts | 33 + server/laf-cloud/functions/getFilm.yaml | 6 + server/laf-cloud/functions/getHeadInfo.ts | 42 + server/laf-cloud/functions/getHeadInfo.yaml | 6 + server/laf-cloud/functions/getRank.ts | 38 + server/laf-cloud/functions/getRank.yaml | 6 + server/laf-cloud/functions/getSRank.ts | 18 + server/laf-cloud/functions/getSRank.yaml | 6 + server/laf-cloud/functions/getShareLv.ts | 32 + server/laf-cloud/functions/getShareLv.yaml | 6 + server/laf-cloud/functions/getTaskReward.ts | 80 ++ server/laf-cloud/functions/getTaskReward.yaml | 6 + server/laf-cloud/functions/getUserInfoByID.ts | 37 + .../laf-cloud/functions/getUserInfoByID.yaml | 6 + .../laf-cloud/functions/limitedTimeEvent.ts | 46 + .../laf-cloud/functions/limitedTimeEvent.yaml | 6 + server/laf-cloud/functions/login.ts | 391 ++++++ server/laf-cloud/functions/login.yaml | 6 + server/laf-cloud/functions/monthGetReward.ts | 33 + .../laf-cloud/functions/monthGetReward.yaml | 6 + .../laf-cloud/functions/monthRewardState.ts | 7 + .../laf-cloud/functions/monthRewardState.yaml | 6 + server/laf-cloud/functions/monthlyCard.ts | 118 ++ server/laf-cloud/functions/monthlyCard.yaml | 6 + server/laf-cloud/functions/passCheck.ts | 52 + server/laf-cloud/functions/passCheck.yaml | 6 + server/laf-cloud/functions/passCheckLv.ts | 43 + server/laf-cloud/functions/passCheckLv.yaml | 6 + server/laf-cloud/functions/passCheckTime.ts | 14 + server/laf-cloud/functions/passCheckTime.yaml | 6 + server/laf-cloud/functions/rank.ts | 88 ++ server/laf-cloud/functions/rank.yaml | 6 + server/laf-cloud/functions/readPem.ts | 12 + server/laf-cloud/functions/readPem.yaml | 6 + server/laf-cloud/functions/rebornGift.ts | 41 + server/laf-cloud/functions/rebornGift.yaml | 6 + server/laf-cloud/functions/redeem.ts | 100 ++ server/laf-cloud/functions/redeem.yaml | 6 + server/laf-cloud/functions/searchOrder.ts | 24 + server/laf-cloud/functions/searchOrder.yaml | 6 + .../functions/searchPlayerDataByLevel.ts | 56 + .../functions/searchPlayerDataByLevel.yaml | 6 + server/laf-cloud/functions/setForcedUpdate.ts | 29 + .../laf-cloud/functions/setForcedUpdate.yaml | 6 + server/laf-cloud/functions/setHeadArr.ts | 51 + server/laf-cloud/functions/setHeadArr.yaml | 6 + server/laf-cloud/functions/setUserLevel.ts | 62 + server/laf-cloud/functions/setUserLevel.yaml | 6 + server/laf-cloud/functions/setWinStreak.ts | 22 + server/laf-cloud/functions/setWinStreak.yaml | 6 + server/laf-cloud/functions/shareLvSuccess.ts | 45 + .../laf-cloud/functions/shareLvSuccess.yaml | 6 + server/laf-cloud/functions/shopDouble.ts | 59 + server/laf-cloud/functions/shopDouble.yaml | 6 + server/laf-cloud/functions/suRecall.ts | 7 + server/laf-cloud/functions/suRecall.yaml | 6 + server/laf-cloud/functions/test.ts | 295 ++++ server/laf-cloud/functions/test.yaml | 6 + server/laf-cloud/functions/userCoin.ts | 47 + server/laf-cloud/functions/userCoin.yaml | 6 + server/laf-cloud/functions/userData.ts | 103 ++ server/laf-cloud/functions/userData.yaml | 6 + server/laf-cloud/functions/userHealth.ts | 50 + server/laf-cloud/functions/userHealth.yaml | 6 + server/laf-cloud/functions/userLevel.ts | 278 ++++ server/laf-cloud/functions/userLevel.yaml | 6 + server/laf-cloud/functions/userPower.ts | 54 + server/laf-cloud/functions/userPower.yaml | 6 + server/laf-cloud/functions/userProp.ts | 96 ++ server/laf-cloud/functions/userProp.yaml | 6 + server/laf-cloud/functions/wx/KeFuInfo.ts | 226 +++ server/laf-cloud/functions/wx/KeFuInfo.yaml | 6 + server/laf-cloud/functions/wx/checkIos.ts | 38 + server/laf-cloud/functions/wx/checkIos.yaml | 6 + .../laf-cloud/functions/wx/getOrderReward.ts | 10 + .../functions/wx/getOrderReward.yaml | 6 + server/laf-cloud/functions/wx/getPayInfo.ts | 172 +++ server/laf-cloud/functions/wx/getPayInfo.yaml | 6 + server/laf-cloud/functions/wx/iosPayCall.ts | 5 + server/laf-cloud/functions/wx/iosPayCall.yaml | 6 + .../laf-cloud/functions/wx/iosgetPayInfo.ts | 157 +++ .../laf-cloud/functions/wx/iosgetPayInfo.yaml | 6 + server/laf-cloud/functions/wx/orderPaySig.ts | 51 + .../laf-cloud/functions/wx/orderPaySig.yaml | 6 + server/laf-cloud/functions/wx/payCallBack.ts | 125 ++ .../laf-cloud/functions/wx/payCallBack.yaml | 6 + .../laf-cloud/functions/wxPerformanceCall.ts | 6 + .../functions/wxPerformanceCall.yaml | 6 + server/laf-cloud/tsconfig.json | 44 + server/laf-cloud/types/global.d.ts | 1218 +++++++++++++++++ 129 files changed, 6075 insertions(+), 268 deletions(-) create mode 100644 assets/shop/img/30.png create mode 100644 assets/shop/img/30.png.meta create mode 100644 server/laf-cloud/functions/CDNKey.ts create mode 100644 server/laf-cloud/functions/CDNKey.yaml create mode 100644 server/laf-cloud/functions/TokenMgr.ts create mode 100644 server/laf-cloud/functions/TokenMgr.yaml create mode 100644 server/laf-cloud/functions/Utils.ts create mode 100644 server/laf-cloud/functions/Utils.yaml create mode 100644 server/laf-cloud/functions/__init__.ts create mode 100644 server/laf-cloud/functions/__init__.yaml create mode 100644 server/laf-cloud/functions/address.ts create mode 100644 server/laf-cloud/functions/address.yaml create mode 100644 server/laf-cloud/functions/canRebornGift.ts create mode 100644 server/laf-cloud/functions/canRebornGift.yaml create mode 100644 server/laf-cloud/functions/clearPHBDataById.ts create mode 100644 server/laf-cloud/functions/clearPHBDataById.yaml create mode 100644 server/laf-cloud/functions/clearPay.ts create mode 100644 server/laf-cloud/functions/clearPay.yaml create mode 100644 server/laf-cloud/functions/clearSrank.ts create mode 100644 server/laf-cloud/functions/clearSrank.yaml create mode 100644 server/laf-cloud/functions/comment.ts create mode 100644 server/laf-cloud/functions/comment.yaml create mode 100644 server/laf-cloud/functions/creatCDkTwo.ts create mode 100644 server/laf-cloud/functions/creatCDkTwo.yaml create mode 100644 server/laf-cloud/functions/creatCdk.ts create mode 100644 server/laf-cloud/functions/creatCdk.yaml create mode 100644 server/laf-cloud/functions/dayTaskInfo.ts create mode 100644 server/laf-cloud/functions/dayTaskInfo.yaml create mode 100644 server/laf-cloud/functions/drawHead.ts create mode 100644 server/laf-cloud/functions/drawHead.yaml create mode 100644 server/laf-cloud/functions/getAddressRank.ts create mode 100644 server/laf-cloud/functions/getAddressRank.yaml create mode 100644 server/laf-cloud/functions/getFilm.ts create mode 100644 server/laf-cloud/functions/getFilm.yaml create mode 100644 server/laf-cloud/functions/getHeadInfo.ts create mode 100644 server/laf-cloud/functions/getHeadInfo.yaml create mode 100644 server/laf-cloud/functions/getRank.ts create mode 100644 server/laf-cloud/functions/getRank.yaml create mode 100644 server/laf-cloud/functions/getSRank.ts create mode 100644 server/laf-cloud/functions/getSRank.yaml create mode 100644 server/laf-cloud/functions/getShareLv.ts create mode 100644 server/laf-cloud/functions/getShareLv.yaml create mode 100644 server/laf-cloud/functions/getTaskReward.ts create mode 100644 server/laf-cloud/functions/getTaskReward.yaml create mode 100644 server/laf-cloud/functions/getUserInfoByID.ts create mode 100644 server/laf-cloud/functions/getUserInfoByID.yaml create mode 100644 server/laf-cloud/functions/limitedTimeEvent.ts create mode 100644 server/laf-cloud/functions/limitedTimeEvent.yaml create mode 100644 server/laf-cloud/functions/login.ts create mode 100644 server/laf-cloud/functions/login.yaml create mode 100644 server/laf-cloud/functions/monthGetReward.ts create mode 100644 server/laf-cloud/functions/monthGetReward.yaml create mode 100644 server/laf-cloud/functions/monthRewardState.ts create mode 100644 server/laf-cloud/functions/monthRewardState.yaml create mode 100644 server/laf-cloud/functions/monthlyCard.ts create mode 100644 server/laf-cloud/functions/monthlyCard.yaml create mode 100644 server/laf-cloud/functions/passCheck.ts create mode 100644 server/laf-cloud/functions/passCheck.yaml create mode 100644 server/laf-cloud/functions/passCheckLv.ts create mode 100644 server/laf-cloud/functions/passCheckLv.yaml create mode 100644 server/laf-cloud/functions/passCheckTime.ts create mode 100644 server/laf-cloud/functions/passCheckTime.yaml create mode 100644 server/laf-cloud/functions/rank.ts create mode 100644 server/laf-cloud/functions/rank.yaml create mode 100644 server/laf-cloud/functions/readPem.ts create mode 100644 server/laf-cloud/functions/readPem.yaml create mode 100644 server/laf-cloud/functions/rebornGift.ts create mode 100644 server/laf-cloud/functions/rebornGift.yaml create mode 100644 server/laf-cloud/functions/redeem.ts create mode 100644 server/laf-cloud/functions/redeem.yaml create mode 100644 server/laf-cloud/functions/searchOrder.ts create mode 100644 server/laf-cloud/functions/searchOrder.yaml create mode 100644 server/laf-cloud/functions/searchPlayerDataByLevel.ts create mode 100644 server/laf-cloud/functions/searchPlayerDataByLevel.yaml create mode 100644 server/laf-cloud/functions/setForcedUpdate.ts create mode 100644 server/laf-cloud/functions/setForcedUpdate.yaml create mode 100644 server/laf-cloud/functions/setHeadArr.ts create mode 100644 server/laf-cloud/functions/setHeadArr.yaml create mode 100644 server/laf-cloud/functions/setUserLevel.ts create mode 100644 server/laf-cloud/functions/setUserLevel.yaml create mode 100644 server/laf-cloud/functions/setWinStreak.ts create mode 100644 server/laf-cloud/functions/setWinStreak.yaml create mode 100644 server/laf-cloud/functions/shareLvSuccess.ts create mode 100644 server/laf-cloud/functions/shareLvSuccess.yaml create mode 100644 server/laf-cloud/functions/shopDouble.ts create mode 100644 server/laf-cloud/functions/shopDouble.yaml create mode 100644 server/laf-cloud/functions/suRecall.ts create mode 100644 server/laf-cloud/functions/suRecall.yaml create mode 100644 server/laf-cloud/functions/test.ts create mode 100644 server/laf-cloud/functions/test.yaml create mode 100644 server/laf-cloud/functions/userCoin.ts create mode 100644 server/laf-cloud/functions/userCoin.yaml create mode 100644 server/laf-cloud/functions/userData.ts create mode 100644 server/laf-cloud/functions/userData.yaml create mode 100644 server/laf-cloud/functions/userHealth.ts create mode 100644 server/laf-cloud/functions/userHealth.yaml create mode 100644 server/laf-cloud/functions/userLevel.ts create mode 100644 server/laf-cloud/functions/userLevel.yaml create mode 100644 server/laf-cloud/functions/userPower.ts create mode 100644 server/laf-cloud/functions/userPower.yaml create mode 100644 server/laf-cloud/functions/userProp.ts create mode 100644 server/laf-cloud/functions/userProp.yaml create mode 100644 server/laf-cloud/functions/wx/KeFuInfo.ts create mode 100644 server/laf-cloud/functions/wx/KeFuInfo.yaml create mode 100644 server/laf-cloud/functions/wx/checkIos.ts create mode 100644 server/laf-cloud/functions/wx/checkIos.yaml create mode 100644 server/laf-cloud/functions/wx/getOrderReward.ts create mode 100644 server/laf-cloud/functions/wx/getOrderReward.yaml create mode 100644 server/laf-cloud/functions/wx/getPayInfo.ts create mode 100644 server/laf-cloud/functions/wx/getPayInfo.yaml create mode 100644 server/laf-cloud/functions/wx/iosPayCall.ts create mode 100644 server/laf-cloud/functions/wx/iosPayCall.yaml create mode 100644 server/laf-cloud/functions/wx/iosgetPayInfo.ts create mode 100644 server/laf-cloud/functions/wx/iosgetPayInfo.yaml create mode 100644 server/laf-cloud/functions/wx/orderPaySig.ts create mode 100644 server/laf-cloud/functions/wx/orderPaySig.yaml create mode 100644 server/laf-cloud/functions/wx/payCallBack.ts create mode 100644 server/laf-cloud/functions/wx/payCallBack.yaml create mode 100644 server/laf-cloud/functions/wxPerformanceCall.ts create mode 100644 server/laf-cloud/functions/wxPerformanceCall.yaml create mode 100644 server/laf-cloud/tsconfig.json create mode 100644 server/laf-cloud/types/global.d.ts diff --git a/assets/Script/Revive.ts b/assets/Script/Revive.ts index fbcf604e..6cc54e57 100644 --- a/assets/Script/Revive.ts +++ b/assets/Script/Revive.ts @@ -203,14 +203,7 @@ export default class Revive extends cc.Component { let systemType = "Android"; try { //@ts-ignore - let systemInfo = null; - if (cc.sys.platform === cc.sys.WECHAT_GAME) - //@ts-ignore - systemInfo = wx.getSystemInfoSync(); - - else if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) - //@ts-ignore - systemInfo = tt.getSystemInfoSync(); + const systemInfo = wx.getSystemInfoSync(); if (systemInfo.platform === 'ios') { systemType = "ios"; } @@ -263,7 +256,9 @@ export default class Revive extends cc.Component { console.log("错误原因:", res.errMsg); if (res.errMsg && res.errMsg.indexOf("fail App Store") !== -1) { console.log("走ios旧支付"); - this.iosOldPay(price, productId); + setTimeout(() => { + this.iosOldPay(price, productId); + }, 500); return; } MiniGameSdk.API.showToast("充值失败"); diff --git a/assets/Script/module/Pay/Utils.ts b/assets/Script/module/Pay/Utils.ts index 0a078ec1..c22ad872 100644 --- a/assets/Script/module/Pay/Utils.ts +++ b/assets/Script/module/Pay/Utils.ts @@ -243,7 +243,7 @@ export default class Utils { payment_num: iosPayInfo.payment_count, type: "ios", } - console.log("ios创建订单信息:", data); + console.log("________ios创建订单信息:", data); cc.fx.GameTool.shushu_Track("init_order", shushu_data); //@ts-ignore wx.openCustomerServiceConversation({ @@ -251,10 +251,10 @@ export default class Utils { showMessageCard: false, // 是否展示消息卡片 success() { callBack("success"); - //console.log('客服会话已successr'); + console.log('客服会话已successr'); }, fail() { - //console.log('客服会话已over'); + console.log('客服会话已over'); callBack("fail"); } }); diff --git a/assets/Script/monthlyCard.ts b/assets/Script/monthlyCard.ts index 7e40da63..778f330f 100644 --- a/assets/Script/monthlyCard.ts +++ b/assets/Script/monthlyCard.ts @@ -289,14 +289,7 @@ export default class NewClass extends cc.Component { let systemType = "Android"; try { //@ts-ignore - let systemInfo = null; - if (cc.sys.platform === cc.sys.WECHAT_GAME) - //@ts-ignore - systemInfo = wx.getSystemInfoSync(); - - else if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) - //@ts-ignore - systemInfo = tt.getSystemInfoSync(); + const systemInfo = wx.getSystemInfoSync(); if (systemInfo.platform === 'ios') { systemType = "ios"; } @@ -493,7 +486,7 @@ export default class NewClass extends cc.Component { this.iosPrice = price; this.iosProductId = productId; this.iosCount = 1; - console.log("准备跳客服回话:"); + console.log("准备跳客服回话:", iosPayInfo); console.log(this.iosProductId); Utils.GoKEFu(iosPayInfo, (res) => { if (res == "success") { diff --git a/assets/action_bundle/script/NewbieGift.ts b/assets/action_bundle/script/NewbieGift.ts index 6083b68c..fc691714 100644 --- a/assets/action_bundle/script/NewbieGift.ts +++ b/assets/action_bundle/script/NewbieGift.ts @@ -342,14 +342,7 @@ export default class NewbieGift extends cc.Component { let systemType = "Android"; try { //@ts-ignore - let systemInfo = null; - if (cc.sys.platform === cc.sys.WECHAT_GAME) - //@ts-ignore - systemInfo = wx.getSystemInfoSync(); - - else if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) - //@ts-ignore - systemInfo = tt.getSystemInfoSync(); + const systemInfo = wx.getSystemInfoSync(); if (systemInfo.platform === 'ios') { systemType = "ios"; } @@ -401,7 +394,9 @@ export default class NewbieGift extends cc.Component { console.log("错误原因:", res.errMsg); if (res.errMsg && res.errMsg.indexOf("fail App Store") !== -1) { console.log("走ios旧支付"); - this.iosOldPay(price, productId); + setTimeout(() => { + this.iosOldPay(price, productId); + }, 500); return; } MiniGameSdk.API.showToast("充值失败"); @@ -539,6 +534,8 @@ export default class NewbieGift extends cc.Component { this.iosPrice = price; this.iosProductId = productId; this.iosCount = 1; + console.log("准备跳客服回话:", iosPayInfo); + console.log(this.iosProductId); Utils.GoKEFu(iosPayInfo, (res) => { if (res == "success") { console.log("客服回话成功"); diff --git a/assets/shop/img/30.png b/assets/shop/img/30.png new file mode 100644 index 0000000000000000000000000000000000000000..cb86bd3565d281c369abf35f3aa896f1f0f5eb85 GIT binary patch literal 14643 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 { + this.iosOldPay(price, productId); + }, 500); return; } MiniGameSdk.API.showToast("充值失败"); diff --git a/assets/shop/script/shop.ts b/assets/shop/script/shop.ts index 9a7204eb..4052f195 100644 --- a/assets/shop/script/shop.ts +++ b/assets/shop/script/shop.ts @@ -629,7 +629,9 @@ export default class NewClass extends cc.Component { console.log("错误原因:", res.errMsg); if (res.errMsg && res.errMsg.indexOf("fail App Store") !== -1) { console.log("走ios旧支付"); - this.iosOldPay(price, productId); + setTimeout(() => { + this.iosOldPay(price, productId); + }, 500); return; } MiniGameSdk.API.showToast("充值失败"); @@ -891,7 +893,7 @@ export default class NewClass extends cc.Component { this.iosPrice = price; this.iosProductId = productId; this.iosCount = 1; - console.log("准备跳客服回话:"); + console.log("准备跳客服回话:", iosPayInfo); console.log(this.iosProductId); Utils.GoKEFu(iosPayInfo, (res) => { if (res == "success") { diff --git a/server/laf-cloud/functions/CDNKey.ts b/server/laf-cloud/functions/CDNKey.ts new file mode 100644 index 00000000..7c5ee5f7 --- /dev/null +++ b/server/laf-cloud/functions/CDNKey.ts @@ -0,0 +1,10 @@ +import cloud from '@lafjs/cloud' +const key:string="1.0.0"; +const testkey:string="1.0.0"; +export default async function (ctx: FunctionContext) { + const uid=ctx.body.uid; + if([].includes(uid)){ + return testkey; + } + return key; +} \ No newline at end of file diff --git a/server/laf-cloud/functions/CDNKey.yaml b/server/laf-cloud/functions/CDNKey.yaml new file mode 100644 index 00000000..42201692 --- /dev/null +++ b/server/laf-cloud/functions/CDNKey.yaml @@ -0,0 +1,6 @@ +name: CDNKey +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/TokenMgr.ts b/server/laf-cloud/functions/TokenMgr.ts new file mode 100644 index 00000000..61deda67 --- /dev/null +++ b/server/laf-cloud/functions/TokenMgr.ts @@ -0,0 +1,70 @@ +import cloud from '@lafjs/cloud'; +import * as crypto from 'crypto'; +const appid = 'wxdd145ced49158a1e'; // 微信小程序 AppId +const appsecret = '7f295245ffc4884b03306c64e30654db'; // 微信小程序 AppSecret + +const appid1 = "wxe69a671ea2c93af3"; // 广告微信小程序 AppId +const appsecret1 = '1eef42cd8810024eaea6dac961f9b3b0'; // 广告微信小程序 AppSecret +//定时获取Token +export default async function (ctx: FunctionContext) { + let res = await getWxToken(); + let res1= await getWxToken1(); + return { res: res } +} + +/**获取微信Token*/ +async function getWxToken() { + const api_url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`; + const ret = await cloud.fetch({ + url: api_url, + method: "get", + }); + if (ret?.data?.access_token) { + cloud.shared.set('wxaccess_token', ret.data.access_token); + return ret.data.access_token; + } else { + //access_token = null; + return null; + } +} +/**获取广告版本token*/ +async function getWxToken1() { + const api_url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid1}&secret=${appsecret1}`; + const ret = await cloud.fetch({ + url: api_url, + method: "get", + }); + if (ret?.data?.access_token) { + //console.log(ret.data.access_token); + cloud.shared.set('wxaccess_token1', ret.data.access_token); + PostToken(ret.data.access_token) + return ret.data.access_token; + } else { + //access_token = null; + return null; + } +} + +async function PostToken(wxToken) { + const token =`ZxCKV476ksJvu9j2bnqQIelYNeHP0XWz`; + const url = `https://backend.gravity-engine.com/event_center/api/v1/base/wx/access_token/refresh/?access_token=${token}`; + const time=Math.floor(Date.now()/1000); + const appkey =`zKUewiWbjqpbn7jzvrmPvhygftARptfwBxco69kXOsLuJnQrSDe5HgdYF0hEqNxk`; + const str: string = `app_key=${appkey}&wx_access_token=${wxToken}¤t_time=${time}`; + const sign: string = crypto.createHash('md5').update(str).digest('hex').toLowerCase(); + const data = { + "wx_access_token": wxToken, + "current_time": time, + "sign": sign + } + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + const token1 = await res.json() + //console.log(data11); + return "ok" +} \ No newline at end of file diff --git a/server/laf-cloud/functions/TokenMgr.yaml b/server/laf-cloud/functions/TokenMgr.yaml new file mode 100644 index 00000000..9435f0b9 --- /dev/null +++ b/server/laf-cloud/functions/TokenMgr.yaml @@ -0,0 +1,6 @@ +name: TokenMgr +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/Utils.ts b/server/laf-cloud/functions/Utils.ts new file mode 100644 index 00000000..960bd12b --- /dev/null +++ b/server/laf-cloud/functions/Utils.ts @@ -0,0 +1,13 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +export default class Utils { + static checkToken(token,token1){ + if(token==token1){ + //console.error("校验成功") + return true; + }else{ + //console.log("校验不成攻:"+token,token1) + return false; + } + } +} diff --git a/server/laf-cloud/functions/Utils.yaml b/server/laf-cloud/functions/Utils.yaml new file mode 100644 index 00000000..970276f7 --- /dev/null +++ b/server/laf-cloud/functions/Utils.yaml @@ -0,0 +1,6 @@ +name: Utils +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/__init__.ts b/server/laf-cloud/functions/__init__.ts new file mode 100644 index 00000000..ea556242 --- /dev/null +++ b/server/laf-cloud/functions/__init__.ts @@ -0,0 +1,6 @@ +import cloud from '@lafjs/cloud' + +export default async function (ctx: FunctionContext) { + // var teSDK = ThinkingData.initWithLoggingMode('LOG_DIRECTORY'); + // console.log(teSDK); +} diff --git a/server/laf-cloud/functions/__init__.yaml b/server/laf-cloud/functions/__init__.yaml new file mode 100644 index 00000000..5a548b75 --- /dev/null +++ b/server/laf-cloud/functions/__init__.yaml @@ -0,0 +1,6 @@ +name: __init__ +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/address.ts b/server/laf-cloud/functions/address.ts new file mode 100644 index 00000000..94e80940 --- /dev/null +++ b/server/laf-cloud/functions/address.ts @@ -0,0 +1,127 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + const address = ctx.body.address; + let dbname = "users"; + const gameName = ctx.body.gameName; + let db1 = "691093093cf0c4532ba4fd9c"; + if (gameName == "iaa") { + dbname = "usersAd"; + db1 = "691097ac3cf0c4532ba4fd9e"; + } + let ret1 = await db.collection(dbname).where({ _id: uid }).getOne(); + let oldAddress=ret1.data.address; + if(address==oldAddress){ + return; + } + const token1 = ctx.body.token; + if (ret1.data && ret1.data.token) { + let istoken = Utils.checkToken(token1, ret1.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let ret = await db.collection(dbname).where({ _id: uid }).update({ address:address}); + if(ret1.data.isWhite==false){ + let res = await rank(db1, uid, ret1, oldAddress, address); + } + + return {code:1,data:{address:address},msg:"ok"} +} + +async function rank(db1,uid,res1,oldaddress,address) { + let add = res1.data.addLevel; + if (add < 5) { + return; + } + let rankInfo = await db.collection("sRank").where({ _id: db1 }).getOne(); + let total = rankInfo.data.total; + if (total != null) { + total = JSON.parse(total); + } else { + total = {}; + } + if(total[oldaddress]){ + for (let i = 0; i < total[oldaddress].length; i++) { + if (total[oldaddress][i].id == uid) { + total[oldaddress].splice(i, 1) + } + } + } + if (total[address]) { + let isHave = false; + for (let i = 0; i < total[address].length; i++) { + if (total[address][i].id == uid) { + isHave = true; + break; + } + } + if (isHave == false) { + let length = total[address].length; + if (length < 10 || add > total[address][length - 1].addLevel) { + total[address].push({ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: res1.data?.levelAmount + }); + } + } + } else { + total[address] = [{ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: res1.data?.levelAmount + }]; + } + total[address].sort((a, b) => { + if (a.addLevel !== b.addLevel) { + return b.addLevel - a.addLevel; + } + return a.time - b.time; + }); + if (total[address].length >= 10) { + total[address] = total[address].slice(0, 10); + } + if (!total["role"]) { + total["role"] = {}; + } + if (total["role"][oldaddress]>0){ + total["role"][oldaddress] -= 1; + let length = total[oldaddress]?.length||0; + if (length < 10) { + total["role"][oldaddress] = length; + } + } + if (!total["role"][address]) { + total["role"][address] = 1; + } else { + total["role"][address] += 1; + let length = total[address]?.length||0; + if (length < 10) { + total["role"][address] = length; + }; + } + //console.log(address); + let ret1 = await db.collection("sRank").where({ _id: db1 }).update({ total: JSON.stringify(total) }); + let rank = -1; + for (let i = 0; i < total[address].length; i++) { + if (total[address][i].id == uid) { + rank = i + 1; + break; + } + } + return rank; +} \ No newline at end of file diff --git a/server/laf-cloud/functions/address.yaml b/server/laf-cloud/functions/address.yaml new file mode 100644 index 00000000..39c6017b --- /dev/null +++ b/server/laf-cloud/functions/address.yaml @@ -0,0 +1,6 @@ +name: address +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/canRebornGift.ts b/server/laf-cloud/functions/canRebornGift.ts new file mode 100644 index 00000000..79c3707e --- /dev/null +++ b/server/laf-cloud/functions/canRebornGift.ts @@ -0,0 +1,41 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + if (!uid) { + return { code: 0, data: null, msg: "参数错误" }; + } + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (res.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let rebornGiftTime = res.data.rebornGiftTime || 0; + let rebornGiftCount = res.data.rebornGiftCount || 0; + let times = getTodayStartTimestamp(); + if (rebornGiftTime < times) { + rebornGiftCount = 0; + } + if (rebornGiftCount >= 1) { + return { code: 0, data: null, msg: "今天购买次数已用完" }; + } + db.collection(dbname).where({ _id: uid }).update({ rebornGiftTime: rebornGiftTime, rebornGiftCount: rebornGiftCount }); + return { code: 1, data: { rebornGiftTime: rebornGiftTime, rebornGiftCount: rebornGiftCount }, msg: "可以购买" }; +} +function getTodayStartTimestamp() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date.getTime(); +} \ No newline at end of file diff --git a/server/laf-cloud/functions/canRebornGift.yaml b/server/laf-cloud/functions/canRebornGift.yaml new file mode 100644 index 00000000..de0150cd --- /dev/null +++ b/server/laf-cloud/functions/canRebornGift.yaml @@ -0,0 +1,6 @@ +name: canRebornGift +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/clearPHBDataById.ts b/server/laf-cloud/functions/clearPHBDataById.ts new file mode 100644 index 00000000..eb75460c --- /dev/null +++ b/server/laf-cloud/functions/clearPHBDataById.ts @@ -0,0 +1,23 @@ +import cloud from '@lafjs/cloud' +const db=cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let db1 = "691093093cf0c4532ba4fd9c"; + const uid=ctx.body.uid; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + db1 = "691097ac3cf0c4532ba4fd9e"; + } + let rankInfo = await db.collection("sRank").where({ _id: db1 }).getOne(); + let total = JSON.parse(rankInfo.data.total); + for(var key in total){ + for(let i=0;i=3){ + return { + code: 0, data: null, msg: "评论数已达上限" + } + } + for(let i=0;i5){ + let list1=[]; + for (let i = 0; i < comment.length; i++) { + if (comment[i].like > max) { + index1 = i; + max = comment[i].like; + } + } + list1.push(index1); + if(comment.length>50){ + while(list1.length<5){ + let index2=Math.floor(Math.random()*comment.length); + if(!list1.includes(index2)){ + list1.push(index2); + } + } + }else{ + let m=comment.length-1; + while(list1.length<5){ + if(!list1.includes(m)){ + list1.push(m); + m--; + } + } + } + for(let i=0;i taskTime) { + for (var key in task) { + task[key].value = 0; + task[key].state = 0; + } + } + switch (action) { + case 'save': + let task1 = ctx.body.task; + if (!task1) { + return { code: 0, data: null, msg: "请输入task数据" }; + } + task1 = JSON.parse(task1); + for (var key in task) { + task[key].value = task1[key].value; + } + for (var key in task) { + if (task[key].state == 0) { + if (task[key].value >= task[key].target) { + task[key].state = 1; + } + } + } + await db.collection(dbname).where({ _id: uid }).update({ task: JSON.stringify(task), taskTime: Date.now() + 8 * 3600000 }); + return { code: 1, data: { task: task }, msg: "用户数据更新成功" } + case 'read': + for (var key in task) { + if (!task[key].state) { + task[key].state = 0; + } + if (task[key].state != 2) { + if (task[key].value >= task[key].target) { + task[key].state = 1; + } + } + + } + return { code: 1, data: { task: task }, msg: "成功" } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } + +} diff --git a/server/laf-cloud/functions/dayTaskInfo.yaml b/server/laf-cloud/functions/dayTaskInfo.yaml new file mode 100644 index 00000000..9eda780f --- /dev/null +++ b/server/laf-cloud/functions/dayTaskInfo.yaml @@ -0,0 +1,6 @@ +name: dayTaskInfo +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/drawHead.ts b/server/laf-cloud/functions/drawHead.ts new file mode 100644 index 00000000..0a8943c0 --- /dev/null +++ b/server/laf-cloud/functions/drawHead.ts @@ -0,0 +1,111 @@ +import cloud from '@lafjs/cloud'; +import Utils from "@/Utils"; +const db = cloud.database(); +const level1 = [19, 20, 21, 22, 23]; +const level2 = [24, 25]; +const level3 = [26]; +/**头像抽卡 */ +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (!res.data) { + return { + code: 0, data: null, msg: "未获取到玩家信息" + } + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let film = res.data.film; + if (!film && film != 0) { + film = res.data.levelAmount; + } + + let headArr = res.data.headArr; + if (!headArr) { + headArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] ]; + } + + + let list1 = []; + let list2 = []; + let list3 = []; + for (let i = 0; i < level1.length; i++) { + if (!headArr.includes(level1[i])) { + list1.push(level1[i]); + } + } + for (let i = 0; i < level2.length; i++) { + if (!headArr.includes(level2[i])) { + list2.push(level2[i]); + } + } + for (let i = 0; i < level3.length; i++) { + if (!headArr.includes(level3[i])) { + list3.push(level3[i]); + } + } + let total = list1.length * 150 + list2.length * 50 + list3.length * 8; + if (total == 0) { + return { code: 0, data: null, msg: "已抽取所有头像" } + } + + let totalCount = level1.length + level2.length + level3.length; + // 计算 + let count = totalCount - (list1.length + list2.length + list3.length); + // 阶梯消耗表:索引0对应消耗5,索引1对应消耗10,以此类推 + let filmConsumeTable = [5, 10, 25, 35, 55, 70, 100, 120]; + + // 根据数量获取对应的消耗,数量超过7时取最后一个(120) + let filmCost = filmConsumeTable[Math.min(count, 7)]; + + film = Number(film) || 0; + if (film < filmCost) { + return { code: 0, data: { film: film, cost: filmCost }, msg: "代币不足,闯关获取更多代币!" } + } + // 检查通过后再扣除,余额始终以服务端数据为准。 + film -= filmCost; + + let num = Math.floor(Math.random() * total); + let list = []; + if (num < list1.length * 150) { + list = list1; + } else if (num < list1.length * 150 + list2.length * 50) { + list = list2; + } else { + list = list3; + } + let num2 = Math.floor(Math.random() * list.length); + if (list[num2] == 26) { + for (let i = 0; i < level1.length; i++) { + if (!headArr.includes(level1[i])) { + headArr.push(level1[i]); + } + } + for (let i = 0; i < level2.length; i++) { + if (!headArr.includes(level2[i])) { + headArr.push(level2[i]); + } + } + for (let i = 0; i < level3.length; i++) { + if (!headArr.includes(level3[i])) { + headArr.push(level3[i]); + } + } + } else { + if (!headArr.includes(list[num2])) { + headArr.push(list[num2]); + } + } + await db.collection(dbname).where({ _id: uid }).update({ headArr: headArr, film: film }); + return { code: 1, data: { headArr: headArr, reward: list[num2], film: film }, msg: "成功" }; +} diff --git a/server/laf-cloud/functions/drawHead.yaml b/server/laf-cloud/functions/drawHead.yaml new file mode 100644 index 00000000..60d2aed5 --- /dev/null +++ b/server/laf-cloud/functions/drawHead.yaml @@ -0,0 +1,6 @@ +name: drawHead +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/getAddressRank.ts b/server/laf-cloud/functions/getAddressRank.ts new file mode 100644 index 00000000..009650a1 --- /dev/null +++ b/server/laf-cloud/functions/getAddressRank.ts @@ -0,0 +1,12 @@ +import cloud from '@lafjs/cloud' +const db=cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const address=ctx.body.address; + const res3 = await db.collection("sRank").where({ name: address }).getOne(); + // if(!res3.data?.totoal){ + // console.log("获取排行榜失败:"+address); + // console.log(res3.data); + // } + return {code:1,data:res3.data?.totoal,msg:"ok"} +} diff --git a/server/laf-cloud/functions/getAddressRank.yaml b/server/laf-cloud/functions/getAddressRank.yaml new file mode 100644 index 00000000..d503acb4 --- /dev/null +++ b/server/laf-cloud/functions/getAddressRank.yaml @@ -0,0 +1,6 @@ +name: getAddressRank +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/getFilm.ts b/server/laf-cloud/functions/getFilm.ts new file mode 100644 index 00000000..3642d7c1 --- /dev/null +++ b/server/laf-cloud/functions/getFilm.ts @@ -0,0 +1,33 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +import Utils from "@/Utils"; +const level1 = [19, 20, 21, 22, 23]; +const level2 = [24, 25]; +const level3 = [26]; +/**获取代币 */ +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (!res.data) { + return { + code: 0, data: null, msg: "未获取到玩家信息" + } + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let film = res.data.film; + if (!film && film != 0) { + film = res.data.levelAmount; + } + return { code: 1, data: { film: film }, msg: "成功" }; +} diff --git a/server/laf-cloud/functions/getFilm.yaml b/server/laf-cloud/functions/getFilm.yaml new file mode 100644 index 00000000..882203be --- /dev/null +++ b/server/laf-cloud/functions/getFilm.yaml @@ -0,0 +1,6 @@ +name: getFilm +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/getHeadInfo.ts b/server/laf-cloud/functions/getHeadInfo.ts new file mode 100644 index 00000000..869e348f --- /dev/null +++ b/server/laf-cloud/functions/getHeadInfo.ts @@ -0,0 +1,42 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +import Utils from "@/Utils"; +const level1 = [18, 19, 20, 21, 22]; +const level2 = [23, 24]; +const level3 = [25]; +/**头像抽卡 */ +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (!res.data) { + return { + code: 0, data: null, msg: "未获取到玩家信息" + } + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let headArr = res.data.headArr; + if (!headArr) { + headArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] ]; + } + let film = res.data.film; + if (!film && film != 0) { + film = res.data.levelAmount; + } + + return { code: 1, data: { headArr: headArr, film: film }, msg: "成功" }; + // if(res.data.isWhite){ + // return { code: 1, data: { headArr: headArr, film: film }, msg: "成功" }; + // } + // else return { code: 1, data: { headArr: headArr}, msg: "成功" }; +} diff --git a/server/laf-cloud/functions/getHeadInfo.yaml b/server/laf-cloud/functions/getHeadInfo.yaml new file mode 100644 index 00000000..83f86646 --- /dev/null +++ b/server/laf-cloud/functions/getHeadInfo.yaml @@ -0,0 +1,6 @@ +name: getHeadInfo +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/getRank.ts b/server/laf-cloud/functions/getRank.ts new file mode 100644 index 00000000..1f901bf7 --- /dev/null +++ b/server/laf-cloud/functions/getRank.ts @@ -0,0 +1,38 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let uid=ctx.body.uid; + const gameName = ctx.body.gameName; + let res1; + if (gameName == "iaa") { + res1 = await db.collection("rank").where({ _id: "68e92b7835004ffb5ab9ee89" }).getOne(); + }else{ + res1 = await db.collection("rank").where({ _id: "689db6c5cc79706182bd6a9f" }).getOne(); + } + let rank=JSON.parse(res1.data.rank) + let list=[]; + if(rank.length<200){ + list=rank; + }else{ + for (let i = 0; i < 200; i++) { + // if(rank[i].useravatar==""){ + // rank[i].useravatar=Math.floor(Math.random()*4)+""; + // } + list.push(rank[i]); + } + } + + let index=-1; + for(let i=0;i taskTime) { + for (var key in task) { + task[key].value = 0; + task[key].state = 0; + } + } + await db.collection(dbname).where({ _id: userData._id }).update({ session_key: session_key, distinctId: distinctId, isDebug: isDebug, isFirst: false, unionid: unionid, task: JSON.stringify(task), taskTime: Date.now() + 8 * 3600000, token: token }); + } + if (userData?.freeze) { + return { code: 0, data: "账号封禁", msg: "账号封禁" }; + } + await delayExecution(0.1); + userData = await getUserData(dbname, openid); + let res = null; + if (userData.openid) { + res = await db.collection("order").where({ state: 1, openid: userData.openid }).get(); + } + let outTradeNo: any = []; + if (res.data && res.data.length > 0) { + for (let i = 0; i < res.data.length; i++) { + outTradeNo.push(res.data[i]); + } + } + if (dbname == "users") { + let ret = await getIosOrder(userData.openid, ctx); + outTradeNo = outTradeNo.concat(ret); + userData.outTradeNo = outTradeNo; + } + + if (!userData.onlyId) { + let ref = await db.collection("idcount").field({ count: 1 }).where({ _id: idname }).getOne(); + await db.collection("idcount").where({ _id: idname }).update({ count: ref.data.count + 1 }); + userData.onlyId = ref.data.count; + await db.collection(dbname).where({ _id: userData._id }).update({ session_key: session_key, onlyId: ref.data.count }); + } + let share = userData.shareLv; + if (share) { + userData.shareLv = JSON.parse(share); + } else { + userData.shareLv = []; + } + userData.careerAmount = userData.careerAmount || 0; + let rebornGiftTime = userData?.rebornGiftTime || 0; + let times = getTodayStartTimestamp(); + if (rebornGiftTime < times) { + userData.rebornGiftCount = 0; + } + if (userData.isWhite && !userData.film && userData.film != 0) { + userData.film = userData.levelAmount; + } + userData.canIos = canIos; + userData.version = version; + //console.log(userData) + //console.log("查询时间:"); + return { code: 1, data: userData } +} +function stringToHash(input: string, algorithm: 'sha1' | 'sha256' | 'sha512' = 'sha256'): string { + return createHash(algorithm).update(input).digest('hex'); +} +function getTodayUTCMidnightTimestamp(): number { + const now = new Date(); + // 1. 获取当前 UTC 时间 + const utcMidnight = Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + 0, 0, 0, 0 + ); + // 2. 加上 8 小时(北京时间 = UTC+8) + return utcMidnight + 8 * 60 * 60 * 1000; +} +const getClientIp = (req) => { + return ( + req.headers['x-forwarded-for']?.split(',')[0]?.trim() || // 代理链中的第一个 IP + req.headers['x-real-ip'] || // Nginx 配置的 real-ip + req.socket.remoteAddress // 原始 IP(可能是代理) + ); +}; + +const propName = { + "金币包1": "gold_1", + "金币包2": "gold_2", + "金币包3": "gold_3", + "金币包4": "gold_4", + "金币包5": "gold_5", + "金币包6": "gold_6", + "无限体力组合包1": "unlimited_health_bundle_1", + "无限体力组合包2": "unlimited_health_bundle_2", + "无限体力组合包3": "unlimited_health_bundle_3", + "局内购买冻结": "freeze_in_game", + "局内购买锤子": "hammer_in_game", + "局内购买魔棒": "wand_in_gamerefill", + "补满体力": "health" +}; + +function getTodayStartTimestamp() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date.getTime(); +} +async function getWXOpenId(code, dbname) { + let appid = "wxdd145ced49158a1e"; + let appsecret = "7f295245ffc4884b03306c64e30654db"; + if (dbname == "usersAd") { + appid = "wxe69a671ea2c93af3"; + appsecret = "1eef42cd8810024eaea6dac961f9b3b0"; + } + const api_url = `https://api.weixin.qq.com/sns/jscode2session`; + const param = `appid=${appid}&secret=${appsecret}&js_code=${code}&grant_type=authorization_code`; + let res = null; + try { + res = await cloud.fetch(`${api_url}?${param}`, { + headers: { + "Accept-Encoding": 'gzip' + } + }); + } catch { + + } + + + if (res == null || res.data.errcode > 0) { + return null + } + //console.log(res.data); + return [res?.data?.openid, res?.data?.session_key, res?.data?.unionid] +} +/**获取当前用户数据 */ +async function getUserData(dbname: string, openid: any, isFirst?) { + let { data } = await db.collection(dbname).where({ openid: openid }).get(); + if (data.length) { + return data[0]; + } + return null; +} + +async function delayExecution(seconds: number) { + return new Promise((resolve) => { + setTimeout(() => { + // 在这里编写需要延时执行的代码 + resolve("延时执行完成"); + }, seconds * 1000); + }); +} +async function getIosOrder(openid, ctx) { + let res = await db.collection("order").where({ state: 0, type: "ios", openid: openid }).get(); + let res1 = await db.collection("users").where({ openid: openid }).getOne(); + let list = []; + if (res.data.length > 0) { + for (let i = 0; i < res.data.length; i++) { + let ret = await getOrder(res.data[i].outTradeNo); + if (ret.trade_state == "SUCCESS") { + list.push(res.data[i]); + sead(res.data[i], res1.data, getClientIp(ctx)); + switch (res.data[i].itemid) { + case "month_Card": + const latestUser = await db.collection("users").where({ _id: res1.data._id }).getOne(); + const now = Date.now(); + const oldMonthCardTime = Number(latestUser.data?.monthCardTime) || 0; + const monthCardTime = Math.max(oldMonthCardTime, now) + 2592000000; + const monthCardReward = oldMonthCardTime > now ? (Number(latestUser.data?.monthCardReward) || 0) : 0; + await db.collection("users").where({ _id: res1.data._id }).update({ monthCardTime: monthCardTime, monthCardReward: monthCardReward, pay_user: true }); + await db.collection("order").where({ outTradeNo: res.data[i].outTradeNo }).update({ monthCardGranted: true }); + break; + case "reborn_Gift": + db.collection("users").where({ _id: res1.data._id }).update({ rebornGiftTime: Date.now(), rebornGiftCount: 1, pay_user: true }); + break; + case "starter_pack": + db.collection("users").where({ _id: res1.data._id }).update({ starter_packState: 1, pay_user: true }); + break; + default: + db.collection("users").where({ _id: res1.data._id }).update({ pay_user: true }); + break; + } + // 月卡补单确认成功后进入待客户端补发状态,避免下次登录重复累计。 + if (res.data[i].itemid === "month_Card") { + await db.collection("order").where({ outTradeNo: res.data[i].outTradeNo }).update({ state: 1 }); + } + } else { + if (Date.now() - res.data[i].time > 3 * 3600000) { + let ret = await db.collection('order').where({ outTradeNo: res.data[i].outTradeNo }).remove({ multi: true }); + } + } + } + } + return list; +} +async function sead(order, userInfo, ip) { + if (!userInfo.distinctId) { + return; + } + let id = '87d18958cea145f29d3265470ecd3486'; + if (userInfo.isDebug != "true") { + id = '121591378fc1423893deb12041413eb3'; + } + let teSDK = ThinkingData.initWithBatchMode(id, 'https://data.nika4fun.com/sync_data', { + dryRun: false, // report data to TE or not + deviceId: "123456789",//设备唯一标识 + }); + let trackEvent = { + accountId: order.openid, + distinctId: userInfo.distinctId,//访客id + event: 'payment', + time: new Date(Date.now() + 8 * 3600000), + ip: ip, + properties: { + order_id: order.outTradeNo, + pay_amount: order.goodsPrice, + payment_name: order.itemid, + payment_num: order.itemCount, + payment_type: "ios", + current_level: userInfo.lv, //当前关卡等级 number + current_health: userInfo.healthAmount, //当前体力值 + tmp_coin: userInfo.coinAmount,//当前金币 + version: "1.6", + user_id: userInfo._id, //用户id + pay_user: userInfo.pay_user + }, + callback(e) { + if (e) { + console.log(e); + } + } + }; + teSDK.track(trackEvent); + teSDK.close(); + return; +} +/**获取订单*/ +async function getOrder(outTradeNo) { + const url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437`; + const nonceStr = crypto.randomBytes(16).toString('hex'); + const timestamp = Math.floor(Date.now() / 1000).toString(); + let au = await sign(outTradeNo, nonceStr, timestamp); + let res = await fetch(url, { + method: 'GET', // 默认是 GET,可省略 + headers: { + 'Authorization': au, // 认证令牌 + 'Content-Type': 'application/json', // 声明请求体类型(GET 请求通常不需要) + 'Accept': 'application/json', // 声明期望的响应类型 + } + }) + const data = await res.json(); + return data; +} +async function sign(outTradeNo, nonceStr, timestamp) { + const data = `GET\n/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437\n${timestamp}\n${nonceStr}\n\n` + // 创建签名对象 + const sign = crypto.createSign('RSA-SHA256'); + // 更新签名内容 + sign.update(data); + // 获取存储桶 + const bucket = cloud.storage.bucket('j3k323ol-colorblock-oss'); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const privateKeyPem = await res.Body.transformToString(); + // 使用私钥签名(PKCS1 填充) + const signature = sign.sign({ + key: privateKeyPem, + padding: crypto.constants.RSA_PKCS1_PADDING + }, 'base64'); + // 构建Authorization头 + const authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`; + return authorization; +} + +/** + * 根据 IP 获取省份名称 + * @param {string} ip - 用户 IP 地址 + * @returns {string} 省份名称(如 "广东省"),未知时返回 "未知" + */ +function getProvinceByIP(ip) { + try { + const result = query.search(ip); + //console.log(result); + if (!result || !result.country.includes('中国')) { + return '其他'; + } + + // ip2region 返回的格式示例:"中国|0|广东省|深圳市|电信" + const parts = result.province; + const provinceCode = parts[2]; // 省份代码在第三部分 + + // 检查是否是已知的省份代码 + // if (PROVCE_CODE_MAP[provinceCode]) { + // return PROVINCE_CODE_MAP[provinceCode]; + // } + + // 如果未映射,返回原始代码(可能是数据库更新后的新代码) + return parts || '其他'; + } catch (error) { + console.error('IP 解析失败:', error); + return '其他'; + } +} diff --git a/server/laf-cloud/functions/login.yaml b/server/laf-cloud/functions/login.yaml new file mode 100644 index 00000000..4ada9f9d --- /dev/null +++ b/server/laf-cloud/functions/login.yaml @@ -0,0 +1,6 @@ +name: login +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/monthGetReward.ts b/server/laf-cloud/functions/monthGetReward.ts new file mode 100644 index 00000000..fd499a55 --- /dev/null +++ b/server/laf-cloud/functions/monthGetReward.ts @@ -0,0 +1,33 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let monthCardTime=Number(res.data.monthCardTime) || 0; + if(monthCardTimetimestamp){ + return { code: 0, data: { monthCardReward: monthCardReward},msg:"今天已经领取"}; + }else{ + await db.collection(dbname).where({ _id: uid }).update({ monthCardReward:Date.now()}) + return { code: 1, data: { monthCardReward:Date.now()},msg:"可以领取"}; + } +} diff --git a/server/laf-cloud/functions/monthGetReward.yaml b/server/laf-cloud/functions/monthGetReward.yaml new file mode 100644 index 00000000..f3db2641 --- /dev/null +++ b/server/laf-cloud/functions/monthGetReward.yaml @@ -0,0 +1,6 @@ +name: monthGetReward +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/monthRewardState.ts b/server/laf-cloud/functions/monthRewardState.ts new file mode 100644 index 00000000..62672218 --- /dev/null +++ b/server/laf-cloud/functions/monthRewardState.ts @@ -0,0 +1,7 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +export default async function (ctx: FunctionContext) { + // let uid=ctx.body.uid; + // await db.collection("users").where({ _id: uid }).update({ monthCardReward: Date.now() }) + // return {code:1,data:null,msg:"成功"} +} diff --git a/server/laf-cloud/functions/monthRewardState.yaml b/server/laf-cloud/functions/monthRewardState.yaml new file mode 100644 index 00000000..4a2f601c --- /dev/null +++ b/server/laf-cloud/functions/monthRewardState.yaml @@ -0,0 +1,6 @@ +name: monthRewardState +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/monthlyCard.ts b/server/laf-cloud/functions/monthlyCard.ts new file mode 100644 index 00000000..29ed5ed1 --- /dev/null +++ b/server/laf-cloud/functions/monthlyCard.ts @@ -0,0 +1,118 @@ +import cloud from '@lafjs/cloud'; +import Utils from "@/Utils"; + +const db = cloud.database(); +const MONTH_CARD_DURATION = 30 * 24 * 60 * 60 * 1000; + +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + + const res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token = ctx.body.token; + if (res.data && res.data.token) { + const isTokenValid = Utils.checkToken(token, res.data.token); + if (!isTokenValid) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + + switch (action) { + case "save": { + const now = Date.now(); + const oldMonthCardTime = Number(res.data?.monthCardTime) || 0; + const oldMonthCardReward = Number(res.data?.monthCardReward) || 0; + const outTradeNo = ctx.body.outTradeNo; + + // 测试账号校验月卡订单,并确保同一订单只延长一次。 + if (outTradeNo) { + const order = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + if (!order.data || + order.data.itemid !== "month_Card" || + order.data.openid !== res.data?.openid) { + return { code: 0, data: null, msg: "月卡订单校验失败" }; + } + if (order.data.state !== 2) { + return { code: 0, data: null, msg: "月卡订单尚未完成发货确认" }; + } + if (order.data.monthCardGranted === true) { + return { + code: 1, + data: { + monthCardTime: oldMonthCardTime, + monthCardReward: oldMonthCardReward, + alreadyGranted: true + }, + msg: "该月卡订单已续订" + }; + } + } + + // 有效期内续订:从原到期时间继续加30天; + // 已过期或首次购买:从当前时间加30天。 + const monthCardTime = Math.max(oldMonthCardTime, now) + MONTH_CARD_DURATION; + // 有效期内续订保留当天领取状态,避免同一天重复领取。 + const monthCardReward = oldMonthCardTime > now ? oldMonthCardReward : 0; + + await db.collection(dbname).where({ _id: uid }).update({ + monthCardTime: monthCardTime, + monthCardReward: monthCardReward + }); + + if (outTradeNo) { + await db.collection("order").where({ outTradeNo: outTradeNo }).update({ + monthCardGranted: true + }); + } + + return { + code: 1, + data: { + monthCardTime: monthCardTime, + monthCardReward: monthCardReward, + alreadyGranted: false + }, + msg: "月卡记录成功" + }; + } + + case "read": { + const monthCardTime = res.data?.monthCardTime || 0; + const monthCardReward = res.data?.monthCardReward || 0; + if (monthCardTime < Date.now()) { + return { + code: 0, + data: { + monthCardTime: monthCardTime, + monthCardReward: monthCardReward + }, + msg: "不在有效期" + }; + } + return { + code: 1, + data: { monthCardTime: monthCardTime }, + msg: "成功" + }; + } + + default: + return { + code: 400, + message: "无效的操作类型,请使用 save 或 read" + }; + } +} diff --git a/server/laf-cloud/functions/monthlyCard.yaml b/server/laf-cloud/functions/monthlyCard.yaml new file mode 100644 index 00000000..2b80db2c --- /dev/null +++ b/server/laf-cloud/functions/monthlyCard.yaml @@ -0,0 +1,6 @@ +name: monthlyCard +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/passCheck.ts b/server/laf-cloud/functions/passCheck.ts new file mode 100644 index 00000000..6c914fa3 --- /dev/null +++ b/server/laf-cloud/functions/passCheck.ts @@ -0,0 +1,52 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + switch (action) { + case 'save': + let passCheck =ctx.body.passCheck; + //console.log(passCheck); + if (!passCheck) { + return { code: 0, data: null, msg: "未获取到coinAmount" }; + } + let ret = await db.collection(dbname).where({ _id: uid }).update({passCheck:passCheck}); + return { code: 1, data: {passCheck:passCheck}, msg: "用户数据更新成功" } + case 'read': + let passCheck1 = res.data?.passCheck||null; + let time = await db.collection("idcount").where({ _id: "690c786db30b6944c56be7aa" }).getOne(); + let t = time.data.passcheckTime; + if (Date.now() >t+ 30*24*3600000) { + const endOfDay: any = new Date(); + endOfDay.setHours(16, 0, 0, 0); // 设置为明天的0点,等同于今天的24点 + t =endOfDay.getTime(); + await db.collection("idcount").where({ _id: "690c786db30b6944c56be7aa" }).update({ passcheckTime: t }); + } + return { code: 1, data: {passCheck:passCheck1,time:t}, msg: "成功" } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/passCheck.yaml b/server/laf-cloud/functions/passCheck.yaml new file mode 100644 index 00000000..0241cb56 --- /dev/null +++ b/server/laf-cloud/functions/passCheck.yaml @@ -0,0 +1,6 @@ +name: passCheck +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/passCheckLv.ts b/server/laf-cloud/functions/passCheckLv.ts new file mode 100644 index 00000000..69266c08 --- /dev/null +++ b/server/laf-cloud/functions/passCheckLv.ts @@ -0,0 +1,43 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + switch (action) { + case 'save': + let passCheckLv = ctx.body.passCheckLv; + if (!passCheckLv) { + return { code: 0, data: null, msg: "未获取到passCheckLv" }; + } + let ret = await db.collection(dbname).where({ _id: uid }).update({ passCheckLv: passCheckLv }); + return { code: 1, data: { passCheckLv: passCheckLv }, msg: "用户数据更新成功" } + case 'read': + let passCheckLv1 = res.data?.passCheckLv || null; + return { code: 1, data: { passCheckLv: passCheckLv1 }, msg: "成功" } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/passCheckLv.yaml b/server/laf-cloud/functions/passCheckLv.yaml new file mode 100644 index 00000000..d2ca38a1 --- /dev/null +++ b/server/laf-cloud/functions/passCheckLv.yaml @@ -0,0 +1,6 @@ +name: passCheckLv +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/passCheckTime.ts b/server/laf-cloud/functions/passCheckTime.ts new file mode 100644 index 00000000..ea16b2c9 --- /dev/null +++ b/server/laf-cloud/functions/passCheckTime.ts @@ -0,0 +1,14 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let time =await db.collection("idcount").where({ _id:"690c786db30b6944c56be7aa"}).getOne(); + let t=time.data.passcheckTime; + if(Date.now()>t+60*24*3600000){ + const endOfDay: any = new Date(); + endOfDay.setHours(0, 0, 0, 0); // 设置为明天的0点,等同于今天的24点 + t = endOfDay.getTime(); + await db.collection("idcount").where({ _id: "690c786db30b6944c56be7aa" }).update({ passcheckTime:t}); + } + return{code:1,data:{time:t},msg:"成功"} +} diff --git a/server/laf-cloud/functions/passCheckTime.yaml b/server/laf-cloud/functions/passCheckTime.yaml new file mode 100644 index 00000000..a32aa2ca --- /dev/null +++ b/server/laf-cloud/functions/passCheckTime.yaml @@ -0,0 +1,6 @@ +name: passCheckTime +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/rank.ts b/server/laf-cloud/functions/rank.ts new file mode 100644 index 00000000..6d5e4337 --- /dev/null +++ b/server/laf-cloud/functions/rank.ts @@ -0,0 +1,88 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +const CITY = { + "河北省": true, "山西省": true, "辽宁省": true, "吉林省": true, "黑龙江省": true, "江苏省": true, + "浙江省": true, "安徽省": true, "福建省": true, "江西省": true, "山东省": true, "河南省": true, "湖北省": true, + "湖南省": true, "广东省": true, "海南省": true, "四川省": true, "贵州省": true, "云南省": true, "陕西省": true, + "甘肃省": true, "青海省": true, "港澳台": true, "北京": true, "天津": true, "上海": true, "重庆": true, + "内蒙古": true, "广西": true, "西藏": true, "宁夏": true, "新疆": true, "其他": true +}; +export default async function (ctx: FunctionContext) { + let uid=ctx.body.uid; + let res=await sortBattle(uid); + // for(var key in CITY){ + // let res = await sortShengShi(key); + // } + return {code:1} +} +/**普通排行 + * @param userCollec + */ +async function sortBattle(uid) { + let res = await db.collection("users").aggregate().project({ + levelAmount: 1, // 1 表示包含该字段,其他字段默认排除 + endLevelNum: 1, + username:1, + useravatar:1, + useravatarIcon:1, + isWhite:1 + })//.addFields({ endLevelNum: { $toInt: "$endLevelNum" }}).addFields({ + // totalLevel: { $add: ["$levelAmount", "$endLevelNum"] }}) + .match({ + isWhite: false + }) + .sort({ + levelAmount:-1, + endLevelNum:-1 + //totalLevel: -1, + }).limit(1000).end(); + const res1 = await db.collection("rank").where({ _id:"689db6c5cc79706182bd6a9f"}) + .update({rank: JSON.stringify(res.data)}); + let res2 = await db.collection("usersAd").aggregate().project({ + levelAmount: 1, // 1 表示包含该字段,其他字段默认排除 + endLevelNum: 1, + username: 1, + useravatar: 1, + useravatarIcon: 1, + isWhite: 1 + }) + // .addFields({ endLevelNum: { $toInt: "$endLevelNum" } }).addFields({ + // totalLevel: { $add: ["$levelAmount", "$endLevelNum"] } + // }) + .match({ + isWhite: false + }) + .sort({ + levelAmount: -1, + endLevelNum: -1 + //totalLevel: -1, + }).limit(1000).end(); + const res3 = await db.collection("rank").where({ _id: "68e92b7835004ffb5ab9ee89" }) + .update({ rank: JSON.stringify(res2.data) }); + return true; +} + +// /**省市地区排名前100名*/ +// async function sortShengShi(address){ +// let res = await db.collection("users").aggregate().project({ +// addLevel:1, +// levelAmount:1, // 1 表示包含该字段,其他字段默认排除 +// endLevelNum: 1, +// username:1, +// useravatar:1, +// useravatarIcon: 1, +// isWhite: 1, +// address:1, +// }).addFields({ endLevelNum: { $toInt: "$endLevelNum" } }).addFields({ +// totalLevel: { $add: ["$levelAmount", "$endLevelNum"] } +// }).match({ +// address:address, +// isWhite: false, +// addLevel: {$gte:5}, +// }) +// .sort({ +// addLevel: -1, +// }).limit(100).end(); +// const res3 = await db.collection("sRank").where({ name: address }).update({ totoal: res.data }, { upsert: true }); +// return true; +// } \ No newline at end of file diff --git a/server/laf-cloud/functions/rank.yaml b/server/laf-cloud/functions/rank.yaml new file mode 100644 index 00000000..c3524050 --- /dev/null +++ b/server/laf-cloud/functions/rank.yaml @@ -0,0 +1,6 @@ +name: rank +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/readPem.ts b/server/laf-cloud/functions/readPem.ts new file mode 100644 index 00000000..5588cdb1 --- /dev/null +++ b/server/laf-cloud/functions/readPem.ts @@ -0,0 +1,12 @@ +import cloud from '@lafjs/cloud' +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const mjsonUrl = 'j3k323ol-colorblock-oss'; + // 获取存储桶 + const bucket = cloud.storage.bucket(mjsonUrl); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const data = await res.Body.transformToString() + cloud.shared.set("apiclient_key",data); + return data; +} diff --git a/server/laf-cloud/functions/readPem.yaml b/server/laf-cloud/functions/readPem.yaml new file mode 100644 index 00000000..4dc85c37 --- /dev/null +++ b/server/laf-cloud/functions/readPem.yaml @@ -0,0 +1,6 @@ +name: readPem +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/rebornGift.ts b/server/laf-cloud/functions/rebornGift.ts new file mode 100644 index 00000000..26d5b3b1 --- /dev/null +++ b/server/laf-cloud/functions/rebornGift.ts @@ -0,0 +1,41 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + if (!uid) { + return { code: 0, data: null, msg: "参数错误" }; + } + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (res.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let rebornGiftTime=res.data.rebornGiftTime||0; + let rebornGiftCount=res.data.rebornGiftCount||0; + let times=getTodayStartTimestamp(); + if(rebornGiftTime=1){ + return { code: 0, data: null, msg: "今天购买次数已用完" }; + } + db.collection(dbname).where({ _id: uid }).update({ rebornGiftTime:Date.now(),rebornGiftCount:1}); + return { code: 1, data: { rebornGiftTime:Date.now(),rebornGiftCount:rebornGiftCount+1},msg:"可以购买"}; +} +function getTodayStartTimestamp() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date.getTime(); +} diff --git a/server/laf-cloud/functions/rebornGift.yaml b/server/laf-cloud/functions/rebornGift.yaml new file mode 100644 index 00000000..c03a4ff0 --- /dev/null +++ b/server/laf-cloud/functions/rebornGift.yaml @@ -0,0 +1,6 @@ +name: rebornGift +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/redeem.ts b/server/laf-cloud/functions/redeem.ts new file mode 100644 index 00000000..2b3661ac --- /dev/null +++ b/server/laf-cloud/functions/redeem.ts @@ -0,0 +1,100 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +const CDKreward={ + wucaixiaoxiaole666:{ + 1001:1000, + 2001:1, + 2002:1, + 2003:1, + } +} +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + if (!uid) { + return { code: 0, data: null, msg: "参数错误" }; + } + let cdk= ctx.body.cdk; + if(!cdk){ + return {code:0, data:null, msg:"未获取到cdk"} + } + cdk = cdk.toLowerCase(); + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if (res.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let redeem = res.data.redeem || []; + if (redeem.includes(cdk)) { + return { code: 0, data: null, msg: "已获取奖励" }; + } + if (cdk.length == 5) { + let infos = await db.collection("cdk").where({ code: cdk }).getOne(); + if (infos.data == null) { + return { code: 0, data: null, msg: "cdk无效" } + } + let start = new Date(infos.data.start).getTime() - 8 * 3600000; + if (Date.now() < start) { + return { code: 0, data: null, msg: "活动未开始" } + } + let end = new Date(infos.data.end_time).getTime() - 8 * 3600000; + if (Date.now() > end) { + return { code: 0, data: null, msg: "活动已结束" } + } + redeem.push(cdk); + db.collection(dbname).where({ _id: uid }).update({ redeem: redeem }); + return { + code: 1, data: { + reward: { + 2001: 1, + 2002: 1, + 2003: 1, + } + }, msg: "可以购买" + }; + } + let info = await db.collection("idcount").where({ _id:"694e064b35004ffb5aba0dc6"}).getOne(); + let count= info.data.count; + if(count.includes(cdk)){ + let index=count.indexOf(cdk); + count.splice(index,1); + redeem.push(cdk); + db.collection(dbname).where({ _id: uid }).update({ redeem: redeem }); + await db.collection("idcount").where({ _id: "694e064b35004ffb5aba0dc6" }).update({count:count}); + let money=600; + let num=1; + if(cdk.length==6){ + money=1500; + num=3; + }else if(cdk.length==8){ + money=600; + num=1; + } + return { code: 1, data: { reward: { + 1001:money, + 2001: num, + 2002: num, + 2003: num, + } }, msg: "可以购买" }; + }else{ + let reward = CDKreward[cdk]; + if (!reward) { + return { code: 0, data: null, msg: "未获取到奖励信息" }; + } + redeem.push(cdk); + db.collection(dbname).where({ _id: uid }).update({ redeem: redeem }); + return { code: 1, data: { reward: reward }, msg: "可以购买" }; + } + +} diff --git a/server/laf-cloud/functions/redeem.yaml b/server/laf-cloud/functions/redeem.yaml new file mode 100644 index 00000000..97cdc86c --- /dev/null +++ b/server/laf-cloud/functions/redeem.yaml @@ -0,0 +1,6 @@ +name: redeem +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/searchOrder.ts b/server/laf-cloud/functions/searchOrder.ts new file mode 100644 index 00000000..f473131b --- /dev/null +++ b/server/laf-cloud/functions/searchOrder.ts @@ -0,0 +1,24 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let outTradeNo=ctx.body.outTradeNo; + const res = await db.collection("order").where({ outTradeNo:outTradeNo}).getOne(); + console.log(res) + return { data:res} +} +function formatMilliseconds(ms) { + // 创建一个新的Date对象 + const date = new Date(ms); + + // 获取各个时间部分 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,所以要加1 + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + // 组合成想要的格式 + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} \ No newline at end of file diff --git a/server/laf-cloud/functions/searchOrder.yaml b/server/laf-cloud/functions/searchOrder.yaml new file mode 100644 index 00000000..d0985b09 --- /dev/null +++ b/server/laf-cloud/functions/searchOrder.yaml @@ -0,0 +1,6 @@ +name: searchOrder +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/searchPlayerDataByLevel.ts b/server/laf-cloud/functions/searchPlayerDataByLevel.ts new file mode 100644 index 00000000..47156d6d --- /dev/null +++ b/server/laf-cloud/functions/searchPlayerDataByLevel.ts @@ -0,0 +1,56 @@ +import cloud from '@lafjs/cloud' +const { MongoClient} = require('mongodb'); +const db1 = cloud.database(); +export default async function (ctx: FunctionContext) { + const uri = 'mongodb://root:nlwrr6cr@dbconn.sealoshzh.site:48953/?directConnection=true'; + const dbName = 'sealaf-cno56wxmj7'; + // let res = await db1.collection("users").where({ totalLevel: { $gt: 10000 } }).update({ totalLevel:0},{multi:true}); + // return res; + let data=[]; + // for(let i=0;i item.name === collectionName); + if (!exists) { + console.log(`collection "${collectionName}" not found`); + return; + } + for(let i=0;i<3;i++){ + console.log(data[i]); + const user = await db.collection(collectionName).findOne({ _id:data[i]}); + let data1={ + endLevelNum: user.endLevelNum||0, + totalLevel: user.totalLevel||user.levelAmount, + userLevel: user.userLevel||0, + forcedUpdate: true, + } + if (user) { + //data1.coinAmount += 10000; + //console.log(data1); + let res22=await db1.collection("users").where({_id:data[i]}).update(data1); + //console.log(res22) + } + } + } catch (err) { + console.error('MongoDB error:', err); + } finally { + console.log("断开连接") + await client.close(); + } + // let res = await db1.collection("users").where({ endLevelNum:99999999999999 }).get(); + // let data=[]; + // for(let i=0;i20000){ + // data.push(res.data[i]._id); + // } + // } + // console.log(JSON.stringify(data)); +} diff --git a/server/laf-cloud/functions/searchPlayerDataByLevel.yaml b/server/laf-cloud/functions/searchPlayerDataByLevel.yaml new file mode 100644 index 00000000..95977ecd --- /dev/null +++ b/server/laf-cloud/functions/searchPlayerDataByLevel.yaml @@ -0,0 +1,6 @@ +name: searchPlayerDataByLevel +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/setForcedUpdate.ts b/server/laf-cloud/functions/setForcedUpdate.ts new file mode 100644 index 00000000..614c2456 --- /dev/null +++ b/server/laf-cloud/functions/setForcedUpdate.ts @@ -0,0 +1,29 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + //console.log(data); + let res1 = await db.collection(dbname).where({ _id: uid }).update({forcedUpdate:false }); + if (res.ok && res1.updated == 1) { + return { code: 1, data: null, msg: "用户数据更新成功" } + } else { + return { code: 0, data: null, msg: "用户数据更新失败" } + } +} diff --git a/server/laf-cloud/functions/setForcedUpdate.yaml b/server/laf-cloud/functions/setForcedUpdate.yaml new file mode 100644 index 00000000..074cd416 --- /dev/null +++ b/server/laf-cloud/functions/setForcedUpdate.yaml @@ -0,0 +1,6 @@ +name: setForcedUpdate +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/setHeadArr.ts b/server/laf-cloud/functions/setHeadArr.ts new file mode 100644 index 00000000..cd28f31e --- /dev/null +++ b/server/laf-cloud/functions/setHeadArr.ts @@ -0,0 +1,51 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let headArr = res.data?.headArr ?? []; + switch (action) { + case 'save': + let headNum = Number(ctx.body.headNum); + if (!headNum&&headNum!=0) { + return { code: 0, data: null, msg: "头像id错误" }; + } + if(headArr.includes(headNum)){ + return { code: 0, data: null, msg: "已保存头像" }; + } + headArr.push(headNum); + await db.collection(dbname).where({ _id: uid }).update({ headArr: headArr }); + return { code: 1, data: { headArr: headArr }, msg: "用户数据更新成功" } + case 'read': + + return { + code: 1, data: { headArr:headArr }, msg: "成功" + } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/setHeadArr.yaml b/server/laf-cloud/functions/setHeadArr.yaml new file mode 100644 index 00000000..bb2fe661 --- /dev/null +++ b/server/laf-cloud/functions/setHeadArr.yaml @@ -0,0 +1,6 @@ +name: setHeadArr +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/setUserLevel.ts b/server/laf-cloud/functions/setUserLevel.ts new file mode 100644 index 00000000..b8aa9b97 --- /dev/null +++ b/server/laf-cloud/functions/setUserLevel.ts @@ -0,0 +1,62 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let endLevelNum1 = res.data?.endLevelNum; + switch (action) { + case 'save': + let userLevel = ctx.body.userLevel; + if (!userLevel) { + return { code: 0, data: null, msg: "未获取到userLevel" }; + }; + userLevel=Number(userLevel); + let endLevelNum=ctx.body.endLevelNum; + if(!endLevelNum){ + endLevelNum=0; + } + if(endLevelNum10){ + console.error((endLevelNum-Number(endLevelNum1))+" 有作弊嫌疑 :"+uid); + return; + } + let totalLevel=userLevel+(endLevelNum/10000) + await db.collection(dbname).where({ _id: uid }).update({ userLevel:userLevel,endLevelNum:endLevelNum,totalLevel:totalLevel}); + return { code: 1, data: { userLevel: userLevel, endLevelNum: endLevelNum }, msg: "用户数据更新成功" } + case 'read': + let userLevel1 = res.data?.userLevel; + //let endLevelNum1=res.data?.endLevelNum; + return { + code: 1, data: { userLevel: userLevel1, endLevelNum: endLevelNum1 }, msg: "成功" + } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/setUserLevel.yaml b/server/laf-cloud/functions/setUserLevel.yaml new file mode 100644 index 00000000..d54f65bd --- /dev/null +++ b/server/laf-cloud/functions/setUserLevel.yaml @@ -0,0 +1,6 @@ +name: setUserLevel +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/setWinStreak.ts b/server/laf-cloud/functions/setWinStreak.ts new file mode 100644 index 00000000..5deecaa0 --- /dev/null +++ b/server/laf-cloud/functions/setWinStreak.ts @@ -0,0 +1,22 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let uid=ctx.body.uid; + let winStreak = ctx.body.winStreak; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let ret = await db.collection(dbname).where({ _id: uid }).update({ winStreak:winStreak }); + return { code: 1, data: { winStreak:winStreak }, msg: "用户数据更新成功" } +} diff --git a/server/laf-cloud/functions/setWinStreak.yaml b/server/laf-cloud/functions/setWinStreak.yaml new file mode 100644 index 00000000..02ec2700 --- /dev/null +++ b/server/laf-cloud/functions/setWinStreak.yaml @@ -0,0 +1,6 @@ +name: setWinStreak +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/shareLvSuccess.ts b/server/laf-cloud/functions/shareLvSuccess.ts new file mode 100644 index 00000000..86820581 --- /dev/null +++ b/server/laf-cloud/functions/shareLvSuccess.ts @@ -0,0 +1,45 @@ +import cloud from '@lafjs/cloud' +const db=cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + let otherUid=ctx.body.otherUid; + let uid=ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if(!uid){ + return {code:0,data:null,msg:"参数错误"}; + } + let shareLv=Number(ctx.body.shareLv); + if(!shareLv){ + return {code:0,data:null,msg:"参数错误"}; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + if(res.data==null){ + return {code:0,data:null,msg:"未获取到玩家数据"}; + } + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + let share=res.data.shareLv; + if(share){ + share=JSON.parse(share); + }else{ + share=[]; + } + if(share.length>0){ + if(share[0].lv total["all"][length - 1].addLevel) { + // total["all"].push({ + // id: uid, + // addLevel: add, + // time: Date.now(), + // username: res1.data?.username, + // useravatar: res1.data?.useravatar, + // useravatarIcon: res1.data?.useravatarIcon, + // levelAmount: levelAmount + // }); + // } + // } + // } else { + // total["all"] = [{ + // id: uid, + // addLevel: add, + // time: Date.now(), + // username: res1.data?.username, + // useravatar: res1.data?.useravatar, + // useravatarIcon: res1.data?.useravatarIcon, + // levelAmount: levelAmount + // }]; + // } + // total["all"].sort((a, b) => { + // if (a.addLevel !== b.addLevel) { + // return b.addLevel - a.addLevel; + // } + // return a.time - b.time; + // }); + // if (total["all"].length >= 10) { + // total["all"] = total["all"].slice(0, 10); + // } + // if (total[address]) { + // let isHave = false; + // for (let i = 0; i < total[address].length; i++) { + // if (total[address][i].id == uid) { + // isHave = true; + // total[address][i].useravatar = res1.data?.useravatar, + // total[address][i].username = res1.data?.username, + // total[address][i].useravatarIcon = res1.data?.useravatarIcon, + // total[address][i].addLevel = add; + // total[address][i].time = Date.now(); + // break; + // } + // } + // if (isHave == false) { + // let length = total[address].length; + // if (length < 10 || add > total[address][length - 1].addLevel) { + // total[address].push({ + // id: uid, + // addLevel: add, + // time: Date.now(), + // username: res1.data?.username, + // useravatar: res1.data?.useravatar, + // useravatarIcon: res1.data?.useravatarIcon, + // levelAmount: levelAmount + // }); + // } + // } + // } else { + // total[address] = [{ + // id: uid, + // addLevel: add, + // time: Date.now(), + // username: res1.data?.username, + // useravatar: res1.data?.useravatar, + // useravatarIcon: res1.data?.useravatarIcon, + // levelAmount: levelAmount + // }]; + // } + // total[address].sort((a, b) => { + // if (a.addLevel !== b.addLevel) { + // return b.addLevel - a.addLevel; + // } + // return a.time - b.time; + // }); + // if (total[address].length >= 10) { + // total[address] = total[address].slice(0, 10); + // } + // if (!total["role"]) { + // total["role"] = {}; + // } + // if (levelTJ == 0) { + + // if (!total["role"][address]) { + // total["role"][address] = 1; + // } else { + // total["role"][address] += 1; + // //console.log(address+"真实:"+total[address].length+"----"+total["role"][address]); + // let length = total[address].length; + // if (length < 10) { + // total["role"][address] = length; + // } + // //console.error(address + "真实:" + total[address].length + "----" + total["role"][address]) + // // const res3 = await db.collection("sRank").where({ name: address }).getOne(); + // // let length1=res3.data.totoal.length; + // // if(length1<100&&total["role"][address]>length1){ + // // total["role"][address]=length1; + // // } + // //console.error(address+":"+length); + // } + // } + // //console.log(address); + // let ret1 = await db.collection("sRank").where({ _id: db1 }).update({ total: JSON.stringify(total) }); + // let rank = -1; + // for (let i = 0; i < total[address].length; i++) { + // if (total[address][i].id == uid) { + // rank = i + 1; + // break; + // } + // } + // return rank; +} +function stringToHash(input: string, algorithm: 'sha1' | 'sha256' | 'sha512' = 'sha256'): string { + return createHash(algorithm).update(input).digest('hex'); +} + +async function updateAddLevel(){ + // const ret = await db1.collection('users').updateMany({ + // addLevel: { $exists: true } + // }, + // [ + // { + // $set: { + // addLevel: { $floor: ["$addLevel"] } + // } + // } + // ]); + let rankInfo = await db.collection("sRank").where({ _id: "691093093cf0c4532ba4fd9c" }).getOne(); + + let total = rankInfo.data.total; + total = JSON.parse(total); + for (var key in total) { + if (key !== "role") { + for (let i = 0; i < total[key].length; i++) { + total[key][i].addLevel = Math.floor(total[key][i].addLevel); + } + } + } + await db.collection("sRank").where({ _id: "691093093cf0c4532ba4fd9c" }).update({ total: JSON.stringify(total) }); +} +const propName = { + "金币包1": "gold_1", + "金币包2": "gold_2", + "金币包3": "gold_3", + "金币包4": "gold_4", + "金币包5": "gold_5", + "金币包6": "gold_6", + "无限体力组合包1": "unlimited_health_bundle_1", + "无限体力组合包2": "unlimited_health_bundle_2", + "无限体力组合包3": "unlimited_health_bundle_3", + "局内购买冻结": "freeze_in_game", + "局内购买锤子": "hammer_in_game", + "局内购买魔棒": "wand_in_gamerefill", + "补满体力": "health" +}; + +function getTodayStartTimestamp() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date.getTime(); +} +async function getWXOpenId(code) { + let appid = "wxdd145ced49158a1e"; + let appsecret = "7f295245ffc4884b03306c64e30654db"; + const api_url = `https://api.weixin.qq.com/sns/jscode2session`; + const param = `appid=${appid}&secret=${appsecret}&js_code=${code}&grant_type=authorization_code`; + let res = null; + try { + res = await cloud.fetch(`${api_url}?${param}`, { + headers: { + "Accept-Encoding": 'gzip' + } + }); + } catch { + + } + + + if (res == null || res.data.errcode > 0) { + return null + } + + return [res?.data?.openid, res?.data?.session_key] +} +/**获取当前用户数据 */ +async function getUserData(dbname: string, openid: any, isFirst?) { + let { data } = await db.collection(dbname).where({ openid: openid }).get(); + if (data.length) { + return data[0]; + } + return null; +} + +async function delayExecution(seconds: number) { + return new Promise((resolve) => { + setTimeout(() => { + // 在这里编写需要延时执行的代码 + resolve("延时执行完成"); + }, seconds * 1000); + }); +} +async function getIosOrder(openid) { + let res = await db.collection("order").where({ state:0,type: "ios",openid: openid }).get(); + let list = []; + if (res.data.length > 0) { + for (let i = 0; i < res.data.length; i++) { + let ret = await getOrder(res.data[i].outTradeNo); + if (ret.trade_state == "SUCCESS") { + list.push(res.data[i]); + } else { + console.log(Date.now()-res.data[i].time); + if (Date.now() - res.data[i].time > 3 * 3600000) { + let ret = await db.collection('order').where({ outTradeNo: res.data[i].outTradeNo }).remove({ multi: true }); + } + } + } + } + return list; +} + +/**获取订单*/ +async function getOrder(outTradeNo) { + const url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437`; + const nonceStr = crypto.randomBytes(16).toString('hex'); + const timestamp = Math.floor(Date.now() / 1000).toString(); + let au = await sign(outTradeNo, nonceStr, timestamp); + let res = await fetch(url, { + method: 'GET', // 默认是 GET,可省略 + headers: { + 'Authorization': au, // 认证令牌 + 'Content-Type': 'application/json', // 声明请求体类型(GET 请求通常不需要) + 'Accept': 'application/json', // 声明期望的响应类型 + } + }) + const data = await res.json(); + return data; +} +async function sign(outTradeNo, nonceStr, timestamp) { + const data = `GET\n/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437\n${timestamp}\n${nonceStr}\n\n` + // 创建签名对象 + const sign = crypto.createSign('RSA-SHA256'); + // 更新签名内容 + sign.update(data); + // 获取存储桶 + const bucket = cloud.storage.bucket('j3k323ol-colorblock-oss'); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const privateKeyPem = await res.Body.transformToString(); + // 使用私钥签名(PKCS1 填充) + const signature = sign.sign({ + key: privateKeyPem, + padding: crypto.constants.RSA_PKCS1_PADDING + }, 'base64'); + // 构建Authorization头 + const authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`; + return authorization; +} \ No newline at end of file diff --git a/server/laf-cloud/functions/test.yaml b/server/laf-cloud/functions/test.yaml new file mode 100644 index 00000000..cc746c37 --- /dev/null +++ b/server/laf-cloud/functions/test.yaml @@ -0,0 +1,6 @@ +name: test +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/userCoin.ts b/server/laf-cloud/functions/userCoin.ts new file mode 100644 index 00000000..c52631c6 --- /dev/null +++ b/server/laf-cloud/functions/userCoin.ts @@ -0,0 +1,47 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + switch (action) { + case 'save': + let coinAmount = Number(ctx.body.coinAmount); + if (!coinAmount&&coinAmount!=0) { + return { code: 0, data: null, msg: "未获取到coinAmount" }; + } + if (coinAmount < 0) { + coinAmount = 0; + } + let ret = await db.collection(dbname).where({ _id: uid }).update({ coinAmount: coinAmount, timestamp:Date.now()}); + return { code: 1, data: { coinAmount: coinAmount, timestamp:Date.now()}, msg: "用户数据更新成功" } + case 'read': + let coinAmount1 = res.data?.coinAmount||0; + let timestamp=res.data?.timestamp||0; + return { code: 1, data: { coinAmount:coinAmount1, timestamp: timestamp}, msg: "成功" } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/userCoin.yaml b/server/laf-cloud/functions/userCoin.yaml new file mode 100644 index 00000000..a88cfc47 --- /dev/null +++ b/server/laf-cloud/functions/userCoin.yaml @@ -0,0 +1,6 @@ +name: userCoin +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/userData.ts b/server/laf-cloud/functions/userData.ts new file mode 100644 index 00000000..85354883 --- /dev/null +++ b/server/laf-cloud/functions/userData.ts @@ -0,0 +1,103 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action=ctx.body.action; + const uid=ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if(!action){ + return { code: 0, data: null, msg: "未获取到action" }; + } + if(!uid){ + return {code:0,data:null,msg:"未获取到uid"}; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + switch (action) { + case 'save': + const userData1 = JSON.parse(ctx.body.userData); + if(!userData1){ + return { code: 0, data: null, msg: "未获取到userData" }; + } + //console.error(userData1) + let data={ + // coinAmount:Number(userData1.coinAmount)||0, + // freezeAmount:Number(userData1.freezeAmount)||0, + // hammerAmount:Number(userData1.hammerAmount)||0, + // level:Number(userData1.level)||0, + // magicAmount:Number(userData1.magicAmount)||0, + // timestamp:Date.now(), + // register_time: Number(userData1.register_time)||Date.now(), + // userPowerTime: Number(userData1.userPowerTime) || 0, + useravatar:userData1.useravatar||Math.floor(Math.random()*4)+"", + username:userData1.username||"user", + useravatarIcon: userData1.useravatarIcon||"0", + } + //console.log(data); + let res2 = await db.collection(dbname).where({ _id: uid }).update(data); + if(res2.ok&&res2.updated==1){ + return { code:1, data: null, msg: "用户数据更新成功" } + }else{ + return { code: 0, data: null, msg: "用户数据更新失败" } + } + case 'read': + let ret = await db.collection(dbname).where({ _id: uid }).getOne(); + let userData=ret.data; + if(ret.data){ + let share = userData.shareLv; + if (share) { + share = JSON.parse(share); + } else { + share = []; + } + let rebornGiftTime = userData.rebornGiftTime || 0; + let rebornGiftCount = userData.rebornGiftCount || 0; + let times = getTodayStartTimestamp(); + if (rebornGiftTime < times) { + rebornGiftCount = 0; + } + let data = { + coinAmount: Number(userData.coinAmount) || 0, + freezeAmount: Number(userData.freezeAmount) || 0, + hammerAmount: Number(userData.hammerAmount) || 0, + level: Number(userData.level) || 0, + magicAmount: Number(userData.magicAmount) || 0, + timestamp: Number(userData.timestamp) || 0, + useravatar: userData.useravatar || "", + username: userData.username || "user", + register_time: Number(userData.register_time) || 0, + monthCardTime: Number(userData.monthCardTime) || 0, + monthCardReward: Number(userData.monthCardReward) || 0, + share: share, + rebornGiftTime: rebornGiftTime, + rebornGiftCount: rebornGiftCount + } + return {code:1,data:data,msg:"获取玩家数据成功"} + }else{ + return {code:0,data:null,msg:"获取玩家数据失败"} + } + //return 1; + default: + return { + code:400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} + +function getTodayStartTimestamp() { + const date = new Date(); + date.setHours(0, 0, 0, 0); + return date.getTime(); +} + diff --git a/server/laf-cloud/functions/userData.yaml b/server/laf-cloud/functions/userData.yaml new file mode 100644 index 00000000..d2f5a62c --- /dev/null +++ b/server/laf-cloud/functions/userData.yaml @@ -0,0 +1,6 @@ +name: userData +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/userHealth.ts b/server/laf-cloud/functions/userHealth.ts new file mode 100644 index 00000000..8289bd15 --- /dev/null +++ b/server/laf-cloud/functions/userHealth.ts @@ -0,0 +1,50 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + + switch (action) { + case 'save': + let healthAmount = Number(ctx.body.healthAmount); + let healthtimestamp = Number(ctx.body.timestamp); + if (!healthAmount) { + return { code: 0, data: null, msg: "未获取到coinAmount" }; + } + if (healthAmount < 0) { + healthAmount = 0; + } + let ret = await db.collection(dbname).where({ _id: uid }).update({ healthAmount: healthAmount, healthtimestamp: healthtimestamp }); + return { code: 1, data: { healthAmount: healthAmount, timestamp: healthtimestamp }, msg: "用户数据更新成功" } + case 'read': + + return { + code: 1, data: { healthAmount: res.data.healthAmount, timestamp: res.data.healthtimestamp}, msg: "成功" } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} diff --git a/server/laf-cloud/functions/userHealth.yaml b/server/laf-cloud/functions/userHealth.yaml new file mode 100644 index 00000000..ec314fb0 --- /dev/null +++ b/server/laf-cloud/functions/userHealth.yaml @@ -0,0 +1,6 @@ +name: userHealth +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/userLevel.ts b/server/laf-cloud/functions/userLevel.ts new file mode 100644 index 00000000..efb6185a --- /dev/null +++ b/server/laf-cloud/functions/userLevel.ts @@ -0,0 +1,278 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +import Utils from "@/Utils"; +import Ip2region from 'ip2region'; +const query = new Ip2region(); +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + const isWuXian = ctx.body.isWuXian; + let db1 = "691093093cf0c4532ba4fd9c"; + if (gameName == "iaa") { + dbname = "usersAd"; + db1 = "691097ac3cf0c4532ba4fd9e"; + } + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res1 = await db.collection(dbname).where({ _id: uid }).getOne(); + const token = ctx.body.token; + if (res1.data && res1.data.token) { + let istoken = Utils.checkToken(token, res1.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" };; + } + } + let address = res1.data?.address; + const ip = ctx.headers['x-forwarded-for'] || ctx.headers['x-real-ip'] || ''; + let address1 = getProvinceByIP(ip); + if (!address || address == "其他") { + address = address1; + } + if (["香港", "澳门", "台湾省"].includes(address)) { + address = "港澳台"; + } + switch (action) { + case 'save': + let levelAmount = Number(ctx.body.levelAmount); + if (!levelAmount) { + return { code: 0, data: null, msg: "未获取到levelAmount" }; + } + let oldlevelAmount = res1.data?.levelAmount || 0; + let oldAdd = res1.data?.addLevel || 0; + let levelTJ = res1.data?.levelTJ || 0; + let count = levelAmount - oldlevelAmount; + if (count > 10 || count < -10) { + count = 1; + return; + } + let add = oldAdd + count; + let isWhite = res1.data?.isWhite; + let careerAmount = res1.data?.careerAmount || 0; + if (isWuXian == "true") { + add += 1; + //console.log("无线增加值:"+(add-oldAdd)); + } else { + //console.log("非无线增加值:"+(add-oldAdd)); + } + add = Math.floor(add); + // if (uid =="686513c8a173b1b78d21f881"){ + // console.error(add); + // console.log(isWhite); + // console.log(typeof isWhite); + // console.log(isWhite==false); + // console.error(levelAmount); + // } + let rankNum = -1; + if (add >= 5 && isWhite == false) { + rankNum = await rank(db1, uid, add, levelAmount, res1, address, levelTJ); + levelTJ = 1; + careerAmount += 1; + } + let film = res1.data.film; + if (!film && film != 0) { + film = res1.data.levelAmount; + } + film += count; + if (uid == "6866ac24a173b1b78d21f8c2") { + console.log("通关:" + levelAmount); + } + // console.error(add); + // console.log(oldAdd); + // console.log(levelAmount); + // console.log(oldlevelAmount); + // console.error("----------------------------------"); + let ret = await db.collection(dbname).where({ _id: uid }).update({ address: address, film: film, careerAmount: careerAmount, levelTJ: levelTJ, levelAmount: levelAmount, addLevel: add, timestamp: Date.now() }); + return { code: 1, data: { IP: ip, address: address, rank: rankNum, levelAmount: levelAmount, film: film, timestamp: Date.now() }, msg: "用户数据更新成功" } + case 'read': + return { + code: 1, data: { levelAmount: res1.data?.levelAmount || 0, film: res1.data?.film ?? res1.data?.levelAmount ?? 0, timestamp: res1.data?.timestamp || 0 }, msg: "成功" + } + default: + return { + code: 400, + message: '无效的操作类型,请使用 save 或 read' + } + } +} +function isSameDayUTC(timestamp1, timestamp2) { + const date1 = new Date(timestamp1); + const date2 = new Date(timestamp2); + + return date1.getUTCFullYear() === date2.getUTCFullYear() && + date1.getUTCMonth() === date2.getUTCMonth() && + date1.getUTCDate() === date2.getUTCDate(); +} +async function rank(db1, uid, add, levelAmount, res1, address, levelTJ) { + let rankInfo = await db.collection("sRank").where({ _id: db1 }).getOne(); + + let total = rankInfo.data.total; + if (total != null) { + total = JSON.parse(total); + } else { + total = {}; + } + if (total["all"]) { + let isHave = false; + for (let i = 0; i < total["all"].length; i++) { + if (total["all"][i].id == uid) { + isHave = true; + total["all"][i].useravatar = res1.data?.useravatar, + total["all"][i].username = res1.data?.username, + total["all"][i].useravatarIcon = res1.data?.useravatarIcon, + total["all"][i].addLevel = add; + total["all"][i].time = Date.now(); + break; + } + } + if (isHave == false) { + let length = total["all"].length; + if (length < 10 || add > total["all"][length - 1].addLevel) { + total["all"].push({ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: levelAmount + }); + } + } + } else { + total["all"] = [{ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: levelAmount + }]; + } + total["all"].sort((a, b) => { + if (a.addLevel !== b.addLevel) { + return b.addLevel - a.addLevel; + } + return a.time - b.time; + }); + if (total["all"].length >= 10) { + total["all"] = total["all"].slice(0, 10); + } + if (total[address]) { + let isHave = false; + for (let i = 0; i < total[address].length; i++) { + if (total[address][i].id == uid) { + isHave = true; + total[address][i].useravatar = res1.data?.useravatar, + total[address][i].username = res1.data?.username, + total[address][i].useravatarIcon = res1.data?.useravatarIcon, + total[address][i].addLevel = add; + total[address][i].time = Date.now(); + break; + } + } + if (isHave == false) { + let length = total[address].length; + if (length < 10 || add > total[address][length - 1].addLevel) { + total[address].push({ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: levelAmount + }); + } + } + } else { + total[address] = [{ + id: uid, + addLevel: add, + time: Date.now(), + username: res1.data?.username, + useravatar: res1.data?.useravatar, + useravatarIcon: res1.data?.useravatarIcon, + levelAmount: levelAmount + }]; + } + total[address].sort((a, b) => { + if (a.addLevel !== b.addLevel) { + return b.addLevel - a.addLevel; + } + return a.time - b.time; + }); + if (total[address].length >= 10) { + total[address] = total[address].slice(0, 10); + } + if (!total["role"]) { + total["role"] = {}; + } + if (levelTJ == 0) { + + if (!total["role"][address]) { + total["role"][address] = 1; + } else { + total["role"][address] += 1; + //console.log(address+"真实:"+total[address].length+"----"+total["role"][address]); + let length = total[address].length; + if (length < 10) { + total["role"][address] = length; + } + //console.error(address + "真实:" + total[address].length + "----" + total["role"][address]) + // const res3 = await db.collection("sRank").where({ name: address }).getOne(); + // let length1=res3.data.totoal.length; + // if(length1<100&&total["role"][address]>length1){ + // total["role"][address]=length1; + // } + //console.error(address+":"+length); + } + } + //console.log(address); + let ret1 = await db.collection("sRank").where({ _id: db1 }).update({ total: JSON.stringify(total) }); + let rank = -1; + for (let i = 0; i < total[address].length; i++) { + if (total[address][i].id == uid) { + rank = i + 1; + break; + } + } + return rank; +} + +/** + * 根据 IP 获取省份名称 + * @param {string} ip - 用户 IP 地址 + * @returns {string} 省份名称(如 "广东省"),未知时返回 "未知" + */ +function getProvinceByIP(ip) { + try { + const result = query.search(ip); + //console.log(result); + if (!result || !result.country.includes('中国')) { + return '其他'; + } + + // ip2region 返回的格式示例:"中国|0|广东省|深圳市|电信" + const parts = result.province; + const provinceCode = parts[2]; // 省份代码在第三部分 + + // 检查是否是已知的省份代码 + // if (PROVCE_CODE_MAP[provinceCode]) { + // return PROVINCE_CODE_MAP[provinceCode]; + // } + + // 如果未映射,返回原始代码(可能是数据库更新后的新代码) + return parts || '其他'; + } catch (error) { + console.error('IP 解析失败:', error); + return '其他'; + } +} diff --git a/server/laf-cloud/functions/userLevel.yaml b/server/laf-cloud/functions/userLevel.yaml new file mode 100644 index 00000000..9d86d5bb --- /dev/null +++ b/server/laf-cloud/functions/userLevel.yaml @@ -0,0 +1,6 @@ +name: userLevel +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/userPower.ts b/server/laf-cloud/functions/userPower.ts new file mode 100644 index 00000000..53ec8c89 --- /dev/null +++ b/server/laf-cloud/functions/userPower.ts @@ -0,0 +1,54 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +import Utils from "@/Utils"; +export default async function (ctx: FunctionContext) { + const action = ctx.body.action; + const uid = ctx.body.uid; + let dbname = "users"; + const gameName = ctx.body.gameName; + if (gameName == "iaa") { + dbname = "usersAd"; + } + if (!action) { + return { code: 0, data: null, msg: "未获取到action" }; + } + + if (!uid) { + return { code: 0, data: null, msg: "未获取到uid" }; + } + let res = await db.collection(dbname).where({ _id: uid }).getOne(); + const token1 = ctx.body.token; + if (res.data && res.data.token) { + let istoken = Utils.checkToken(token1, res.data.token); + if (!istoken) { + return { code: 0, data: null, msg: "token校验失败" }; + } + } + switch (action) { + case 'save': + let userPowerTime = Number(ctx.body.userPowerTime); + if (!userPowerTime) { + return { code: 0, data: null, msg: "未获取到coinAmount" }; + } + // let ret = await db.collection("users").where({ _id: uid }).getOne(); + // let time=ret.data?.userPowerTime||0; + // if(time { + //如果不是JSON,尝试读取文本 + return await res.text().catch(() => 'Empty response'); + }); + return data.prepay_id; + } + return null; +} +async function sign(nonceStr,timestamp,body) { + const data = `POST\n/v3/pay/transactions/jsapi\n${timestamp}\n${nonceStr}\n${JSON.stringify(body)}\n`; + // 创建签名对象 + const sign = crypto.createSign('RSA-SHA256'); + // 更新签名内容 + sign.update(data); + // 获取存储桶 + const bucket = cloud.storage.bucket('j3k323ol-colorblock-oss'); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const privateKeyPem = await res.Body.transformToString(); + // 使用私钥签名(PKCS1 填充) + const signature = sign.sign({ + key: privateKeyPem, + padding: crypto.constants.RSA_PKCS1_PADDING + }, 'base64'); + // 构建Authorization头 + const authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`; + return authorization; +} +/**前端签名*/ +async function frontsign(data) { + // 创建签名对象 + const sign = crypto.createSign('RSA-SHA256'); + // 更新签名内容 + sign.update(data); + // 获取存储桶 + const bucket = cloud.storage.bucket('j3k323ol-colorblock-oss'); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const privateKeyPem = await res.Body.transformToString(); + // 使用私钥签名(PKCS1 填充) + const signature = sign.sign({ + key: privateKeyPem, + padding: crypto.constants.RSA_PKCS1_PADDING + }, 'base64'); + // 构建Authorization头 + return signature +} +const propName={ + gold_1: "金币包1", + gold_2: "金币包2", + gold_3: "金币包3", + gold_4: "金币包4", + gold_5: "金币包5", + gold_6: "金币包6", + unlimited_health_bundle_1:"无限体力组合包1", + unlimited_health_bundle_2:"无限体力组合包2", + unlimited_health_bundle_3:"无限体力组合包3", + freeze_in_game:"局内购买冻结", + hammer_in_game:"局内购买锤子", + wand_in_gamerefill:"局内购买魔棒", + health:"补满体力" +}; + +async function PostMsg(OpenId, msg) { + let token = cloud.shared.get('wxaccess_token'); + if (!token) { + return { code: 0, data: null, msg: "token获取失败" }; + } + const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${token}`; + const data = { + "touser": OpenId, + "msgtype": "text", + "text": + { + "content": msg + } + } + + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: JSON.stringify(data) + }) + return "ok" +} +async function PostImg(OpenId, outTradeNo, name, count, price) { + let token = cloud.shared.get('wxaccess_token'); + if (!token) { + return { code: 0, data: null, msg: "token获取失败" }; + } + const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${token}`; + const nonceStr = crypto.randomBytes(16).toString('hex'); + const timestamp = Math.floor(Date.now() / 1000).toString(); + const total=count*price; + let prepay_id = await getOrder(OpenId,total,outTradeNo,timestamp,nonceStr); + let frontBody={ + "appId": appid, //公众号ID,由商户传入 + "timeStamp": timestamp, //时间戳,自1970年以来的秒数 + "nonceStr": nonceStr, //随机串 + "package": "prepay_id=" + prepay_id + } + const data1 = `${frontBody.appId}\n${frontBody.timeStamp}\n${frontBody.nonceStr}\n${frontBody.package}\n`; + let paySign=await frontsign(data1); + + let user = await db.collection("users").where({ openid: OpenId }).update({iosTime:frontBody.timeStamp}); + let param={ + name:propName[name], + openid:OpenId, + outTradeNo: outTradeNo, + time:timestamp, + quantity:count, + price:price, + body:{ + "appId": frontBody.appId, //公众号ID,由商户传入 + "timeStamp": frontBody.timeStamp, //时间戳,自1970年以来的秒数 + "nonceStr": frontBody.nonceStr, //随机串 + "package": frontBody.package, + "signType": "RSA", //微信签名方式: + "paySign": paySign //微信签名 + } + } + await db.collection("iosOrder").add({ + openid: OpenId, + outTradeNo: outTradeNo, + itemid: name, + itemCount: count, + goodsPrice: Number(price), + time: Date.now(), + type: "ios", + state: 0, + }); + const data = { + "touser": OpenId, + "msgtype": "link", + "link": { + "title": "点我支付", + "description": "金额:" + (total/100).toFixed(2) + "元", + "url": `https://pay.nika4games.com/order2.html#data=${encodeURIComponent(JSON.stringify(param))}`, + "thumb_url": "https://static-host-j3k323ol-colorblock-oss.sealoshzh.site/icon.jpg" + } + } + //console.log(data); + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: JSON.stringify(data) + }) + const data11 = await res.json(); + if (data11.errcode == 40001){ + console.error("未获取到正确token,重新发送请求"); + let res = await getWxToken(); + await PostMsg(OpenId, "点击下方页面进行充值"); + await PostImg(OpenId, outTradeNo, name, count, price); + } + return "ok" +} + + +/**获取微信Token*/ +async function getWxToken() { + const api_url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`; + const ret = await cloud.fetch({ + url: api_url, + method: "get", + }); + if (ret?.data?.access_token) { + cloud.shared.set('wxaccess_token', ret.data.access_token); + return ret.data.access_token; + } else { + //access_token = null; + return null; + } +} diff --git a/server/laf-cloud/functions/wx/KeFuInfo.yaml b/server/laf-cloud/functions/wx/KeFuInfo.yaml new file mode 100644 index 00000000..e2cd7a5c --- /dev/null +++ b/server/laf-cloud/functions/wx/KeFuInfo.yaml @@ -0,0 +1,6 @@ +name: wx/KeFuInfo +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/checkIos.ts b/server/laf-cloud/functions/wx/checkIos.ts new file mode 100644 index 00000000..9501fe0a --- /dev/null +++ b/server/laf-cloud/functions/wx/checkIos.ts @@ -0,0 +1,38 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +export default async function (ctx: FunctionContext) { + let openid=ctx.body.openid; + let time=ctx.body.time; + let outTradeNo = ctx.body.outTradeNo; + if(!openid||!time){ + return false; + } + let user = await db.collection("users").where({ openid:openid}).getOne(); + if(user.data){ + if (user.data.iosTime ==time){ + let res = await db.collection("iosOrder").where({ outTradeNo: outTradeNo }).getOne(); + if (res.data) { + await db.collection("order").add({ + openid: res.data.openid, + outTradeNo: res.data.outTradeNo, + itemid: res.data.itemid, + itemCount: res.data.itemCount, + goodsPrice: res.data.goodsPrice, + time: res.data.time, + type: "ios", + state: 0, + }); + db.collection("iosOrder").where({ outTradeNo: outTradeNo }).remove({ multi: true }); + return true; + } else { + let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + if(res.data){ + console.log("验证成功"); + return true; + } + return false; + } + } + } + return false; +} diff --git a/server/laf-cloud/functions/wx/checkIos.yaml b/server/laf-cloud/functions/wx/checkIos.yaml new file mode 100644 index 00000000..31003a20 --- /dev/null +++ b/server/laf-cloud/functions/wx/checkIos.yaml @@ -0,0 +1,6 @@ +name: wx/checkIos +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/getOrderReward.ts b/server/laf-cloud/functions/wx/getOrderReward.ts new file mode 100644 index 00000000..659d819c --- /dev/null +++ b/server/laf-cloud/functions/wx/getOrderReward.ts @@ -0,0 +1,10 @@ +import cloud from '@lafjs/cloud' +const db = cloud.database(); +export default async function (ctx: FunctionContext) { + let outTradeNo = ctx.body.outTradeNo; + if(!outTradeNo){ + return { code: 0, data:null, msg: "未获取到outTradeNo" }; + } + let res=await db.collection("order").where({ outTradeNo: outTradeNo }).update({ state: 2 }); + return { code:1, data:"ok", msg: "成功" }; +} diff --git a/server/laf-cloud/functions/wx/getOrderReward.yaml b/server/laf-cloud/functions/wx/getOrderReward.yaml new file mode 100644 index 00000000..1d72577b --- /dev/null +++ b/server/laf-cloud/functions/wx/getOrderReward.yaml @@ -0,0 +1,6 @@ +name: wx/getOrderReward +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/getPayInfo.ts b/server/laf-cloud/functions/wx/getPayInfo.ts new file mode 100644 index 00000000..7584a884 --- /dev/null +++ b/server/laf-cloud/functions/wx/getPayInfo.ts @@ -0,0 +1,172 @@ +import cloud from '@lafjs/cloud' +import * as crypto from 'crypto'; +const db = cloud.database(); +const ThinkingData = require('thinkingdata-node'); +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + let outTradeNo = ctx.body.outTradeNo; + let user = await db.collection("users").where({ _id: uid }).getOne(); + if (user.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + if (res.data.state == 1) { + return { + code: 1, data: { + pay_state: 2 + }, msg: "成功" + }; + } else if (res.data.state == 2) { + return { + code: 1, data: { + pay_state: 1, + }, msg: "已领取奖励" + } + } else { + return { + code: 0, data: { + pay_state: 1, + }, msg: "充值未成功" + } + }; + // let time =Date.now(); + // let uid = ctx.body.uid; + // let outTradeNo = ctx.body.outTradeNo; + // let user = await db.collection("users").where({ _id: uid }).getOne(); + // if (user.data == null) { + // return { code: 0, data: null, msg: "未获取到玩家数据" }; + // } + + // let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + // if (res.data.state == 1) { + // return { code: 1, data:{ + // pay_state:2 + // }, msg: "成功" }; + // }else{ + // return {code:1,data:{ + // pay_state:1, + // },msg:"失败"} + // }; + // let session_key = user.data.session_key; + // let token = cloud.shared.get('wxaccess_token'); + // if (!token) { + // return { code: 0, data: null, msg: "token获取失败" }; + // } + // const info = { + // "openid": user.data.openid, + // "offer_id": "1450390549", + // "ts": Math.floor(Date.now() / 1000), + // "zone_id": "1", + // "env": 0, + // "out_trade_no": outTradeNo, + // "biz_id": 2 + // } + // const signature = hmacSha256(session_key, JSON.stringify(info)); + // const key = "4HfZwqLjVkLb6s4TfIxRGKaTxgvvL1rl"; + // let data = '/wxa/game/queryorderinfo' + '&' + JSON.stringify(info); + // const pay_sig = hmacSha256(key, data); + // const url = `https://api.weixin.qq.com/wxa/game/queryorderinfo?access_token=${token}&signature=${signature}&sig_method=hmac_sha256&pay_sig=${pay_sig}`; + // const response = await cloud.fetch(url, { + // url: url, + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json;charset=utf-8' + // }, + // data: JSON.stringify(info) + // }) + // // console.log("查询充值结果"); + // // console.log(response.data); + // let ret = await db.collection("order").where({ "outTradeNo": outTradeNo }).getOne(); + // let payment_name = ""; + // if (ret.data) { + // if(propName[ret.data.itemid]){ + // payment_name = propName[ret.data.itemid]; + // }else{ + // payment_name = ret.data.itemid; + // } + // } + // response.data.payment_name=payment_name; + // response.data.goodsPrice=ret.data.goodsPrice; + // //console.error("充值结果"); + // //console.log(response.data); + // if (response.data.pay_state==2){ + // let res = await db.collection("order").where({ outTradeNo:outTradeNo}).getOne(); + // // console.log(res); + // // console.log + // if(res.data&&res.data.state==0){ + // await db.collection("order").where({ outTradeNo:outTradeNo}).update({ state: 1 }); + // sead(res.data, user.data, getClientIp(ctx)); + // console.error("充值结果:"+(Date.now()-time)); + // } + // } + //return { code: 1, data: response.data, msg: "成功" }; +} +const propName = { + "金币包1": "gold_1", + "金币包2": "gold_2", + "金币包3": "gold_3", + "金币包4": "gold_4", + "金币包5": "gold_5", + "金币包6": "gold_6", + "无限体力组合包1": "unlimited_health_bundle_1", + "无限体力组合包2": "unlimited_health_bundle_2", + "无限体力组合包3": "unlimited_health_bundle_3", + "局内购买冻结": "freeze_in_game", + "局内购买锤子": "hammer_in_game", + "局内购买魔棒": "wand_in_gamerefill", + "补满体力": "health", + "新手礼包": "starter_pack", +}; +function hmacSha256(sessionKey, signData) { + const hmac = crypto.createHmac('sha256', sessionKey); + hmac.update(signData); + return hmac.digest('hex'); +} +const getClientIp = (req) => { + return ( + req.headers['x-forwarded-for']?.split(',')[0]?.trim() || // 代理链中的第一个 IP + req.headers['x-real-ip'] || // Nginx 配置的 real-ip + req.socket.remoteAddress // 原始 IP(可能是代理) + ); +}; +async function sead(order, userInfo, ip) { + console.error("上传数数"); + //db.collection("susu").add({order: order,time: Date.now()}); + if (!userInfo.distinctId) { + console.log("未找到distinctId无法上传数数") + return; + } + let id = '87d18958cea145f29d3265470ecd3486'; + if (userInfo.isDebug != "true") { + id = '121591378fc1423893deb12041413eb3'; + } + let teSDK = ThinkingData.initWithBatchMode(id, 'https://data.nika4fun.com/sync_data', { + dryRun: false, // report data to TE or not + deviceId: "123456789",//设备唯一标识 + }); + let trackEvent = { + accountId: order.openid, + distinctId: userInfo.distinctId,//访客id + event: 'payment', + time: new Date(Date.now() + 8 * 3600000), + ip: ip, + properties: { + order_id: order.outTradeNo, + pay_amount: order.goodsPrice, + payment_name: order.itemid, + payment_num: order.itemCount, + payment_type: "Android", + current_level: userInfo.levelAmount, //当前关卡等级 number + current_health: userInfo.healthAmount, //当前体力值 + tmp_coin: userInfo.coinAmount,//当前金币 + version: "1.6", + user_id: userInfo._id, //用户id + pay_user: userInfo.pay_user, + register_time: userInfo.register_time + }, + callback(e) {if (e) {}} + }; + teSDK.track(trackEvent); + teSDK.close(); + return; +} diff --git a/server/laf-cloud/functions/wx/getPayInfo.yaml b/server/laf-cloud/functions/wx/getPayInfo.yaml new file mode 100644 index 00000000..2cc40098 --- /dev/null +++ b/server/laf-cloud/functions/wx/getPayInfo.yaml @@ -0,0 +1,6 @@ +name: wx/getPayInfo +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/iosPayCall.ts b/server/laf-cloud/functions/wx/iosPayCall.ts new file mode 100644 index 00000000..2e5d02eb --- /dev/null +++ b/server/laf-cloud/functions/wx/iosPayCall.ts @@ -0,0 +1,5 @@ +import cloud from '@lafjs/cloud' + +export default async function (ctx: FunctionContext) { + return { data:'scuess' } +} diff --git a/server/laf-cloud/functions/wx/iosPayCall.yaml b/server/laf-cloud/functions/wx/iosPayCall.yaml new file mode 100644 index 00000000..e9e6fcf8 --- /dev/null +++ b/server/laf-cloud/functions/wx/iosPayCall.yaml @@ -0,0 +1,6 @@ +name: wx/iosPayCall +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/iosgetPayInfo.ts b/server/laf-cloud/functions/wx/iosgetPayInfo.ts new file mode 100644 index 00000000..fd44c302 --- /dev/null +++ b/server/laf-cloud/functions/wx/iosgetPayInfo.ts @@ -0,0 +1,157 @@ +import cloud from '@lafjs/cloud' +import crypto from 'crypto'; +const mchid = "1719895437";//商户id +const serialNo = "2A97ED6ED441E55600EBC8830B9EC889ED41496E";//证书序列号 +const db = cloud.database(); +const ThinkingData = require('thinkingdata-node'); +export default async function (ctx: FunctionContext) { + let outTradeNo = ctx.body.outTradeNo; + if (!outTradeNo) { + return { code: 0, data: null, msg: "未获取到充值订单" }; + } + const uid = ctx.body.uid; + let res = await getOrder(outTradeNo); + let ret = await db.collection("order").where({ "outTradeNo": outTradeNo }).getOne(); + let iosOrder = cloud.shared.get("iosOrder"); // 获取 wsMap + if (!ret.data) { + console.error("ios找不到充值订单:" + outTradeNo); + return { code: 0, data: null, msg: "未获取到充值信息" }; + } + if (ret.data.state == 2) { + console.error("ios已经获取奖励"); + return { code: 0, data: null, msg: "已经获取到奖励" }; + } + let payment_name = ret.data?.itemid; + if (res) { + if (uid) { + let res1 = await db.collection("users").where({ _id: uid }).getOne(); + if (!iosOrder) { + iosOrder = {}; + } else { + //console.log(iosOrder); + } + if (!iosOrder[outTradeNo]) { + sead(ret.data, res1.data, getClientIp(ctx)); + iosOrder[outTradeNo] = 2; + } else { + } + + cloud.shared.set("iosOrder", iosOrder); // 设置 wsMap + switch (payment_name) { + case "month_Card": + const now = Date.now(); + const oldMonthCardTime = Number(res1.data.monthCardTime) || 0; + const monthCardTime = Math.max(oldMonthCardTime, now) + 2592000000; + const monthCardReward = oldMonthCardTime > now ? (Number(res1.data.monthCardReward) || 0) : 0; + await db.collection("users").where({ _id: uid }).update({ monthCardTime: monthCardTime, monthCardReward: monthCardReward, pay_user: true }); + await db.collection("order").where({ outTradeNo: outTradeNo }).update({ monthCardGranted: true }); + break; + case "reborn_Gift": + db.collection("users").where({ _id: uid }).update({ rebornGiftTime: Date.now(), rebornGiftCount: 1, pay_user: true }); + break; + case "starter_pack": + db.collection("users").where({ _id: uid }).update({ starter_packState: 1, pay_user: true }); + break; + default: + db.collection("users").where({ _id: uid }).update({ pay_user: true }); + break; + } + } + await db.collection("order").where({ outTradeNo: outTradeNo }).update({ state: 2 }, { multi: true }); + return { code: 1, data: { goodsPrice: ret.data?.goodsPrice, payment_name: payment_name }, msg: "充值成功" }; + } else { + console.log("充值不成功:" + outTradeNo); + return { code: 0, data: { goodsPrice: ret.data?.goodsPrice, payment_name: payment_name }, msg: "充值失败" }; + } + +} +const getClientIp = (req) => { + return ( + req.headers['x-forwarded-for']?.split(',')[0]?.trim() || // 代理链中的第一个 IP + req.headers['x-real-ip'] || // Nginx 配置的 real-ip + req.socket.remoteAddress // 原始 IP(可能是代理) + ); +}; +/**获取订单*/ +async function getOrder(outTradeNo) { + const url = `https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437`; + const nonceStr = crypto.randomBytes(16).toString('hex'); + const timestamp = Math.floor(Date.now() / 1000).toString(); + let au = await sign(outTradeNo, nonceStr, timestamp); + let res = await fetch(url, { + method: 'GET', // 默认是 GET,可省略 + headers: { + 'Authorization': au, // 认证令牌 + 'Content-Type': 'application/json', // 声明请求体类型(GET 请求通常不需要) + 'Accept': 'application/json', // 声明期望的响应类型 + } + }) + const data = await res.json(); + if (data.trade_state == 'SUCCESS') { + return true; + } else { + return false; + } +} +async function sign(outTradeNo, nonceStr, timestamp) { + const data = `GET\n/v3/pay/transactions/out-trade-no/${outTradeNo}?mchid=1719895437\n${timestamp}\n${nonceStr}\n\n` + // 创建签名对象 + const sign = crypto.createSign('RSA-SHA256'); + // 更新签名内容 + sign.update(data); + // 获取存储桶 + const bucket = cloud.storage.bucket('j3k323ol-colorblock-oss'); + // 获取文件列表 + const res = await bucket.readFile('apiclient_key.pem'); + const privateKeyPem = await res.Body.transformToString(); + // 使用私钥签名(PKCS1 填充) + const signature = sign.sign({ + key: privateKeyPem, + padding: crypto.constants.RSA_PKCS1_PADDING + }, 'base64'); + // 构建Authorization头 + const authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${serialNo}"`; + return authorization; +} +async function sead(order, userInfo, ip) { + if (!userInfo.distinctId) { + return; + } + let id = '87d18958cea145f29d3265470ecd3486'; + if (userInfo.isDebug != "true") { + id = '121591378fc1423893deb12041413eb3'; + } + let teSDK = ThinkingData.initWithBatchMode(id, 'https://data.nika4fun.com/sync_data', { + dryRun: false, // report data to TE or not + deviceId: "123456789",//设备唯一标识 + }); + let trackEvent = { + accountId: order.openid, + distinctId: userInfo.distinctId,//访客id + event: 'payment', + time: new Date(Date.now() + 8 * 3600000), + ip: ip, + properties: { + order_id: order.outTradeNo, + pay_amount: order.goodsPrice, + payment_name: order.itemid, + payment_num: order.itemCount, + payment_type: "ios_old", + current_level: userInfo.levelAmount, //当前关卡等级 number + current_health: userInfo.healthAmount, //当前体力值 + tmp_coin: userInfo.coinAmount,//当前金币 + version: "1.6", + user_id: userInfo._id, //用户id + pay_user: userInfo.pay_user, + register_time: userInfo.register_time + }, + callback(e) { + if (e) { + console.log(e); + } + } + }; + teSDK.track(trackEvent); + teSDK.close(); + return; +} diff --git a/server/laf-cloud/functions/wx/iosgetPayInfo.yaml b/server/laf-cloud/functions/wx/iosgetPayInfo.yaml new file mode 100644 index 00000000..f5580e81 --- /dev/null +++ b/server/laf-cloud/functions/wx/iosgetPayInfo.yaml @@ -0,0 +1,6 @@ +name: wx/iosgetPayInfo +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/orderPaySig.ts b/server/laf-cloud/functions/wx/orderPaySig.ts new file mode 100644 index 00000000..14bc19d0 --- /dev/null +++ b/server/laf-cloud/functions/wx/orderPaySig.ts @@ -0,0 +1,51 @@ +import cloud from '@lafjs/cloud' +import * as crypto from 'crypto'; +const db = cloud.database(); +export default async function (ctx: FunctionContext) { + let uid = ctx.body.uid; + let itemid = ctx.body.itemid; + let itemCount = ctx.body.itemCount; + let itemPrice = ctx.body.itemPrice; + let user = await db.collection("users").where({ _id: uid }).getOne(); + if (user.data == null) { + return { code: 0, data: null, msg: "未获取到玩家数据" }; + } + const outTradeNo = `wcx_` + Math.round(Math.random() * 10 ** 13) + Date.now(); + let signData = JSON.stringify({ + "mode": "goods", + "offerId": "1450390549", + "buyQuantity": Number(itemCount),//购买数量 + "env": 0,//正式环境 + "currencyType": "CNY",//人民币 + "platform": "android", + "zoneId": "1",//分区id + "productId": itemid, + "goodsPrice": Number(itemPrice), + "outTradeNo": outTradeNo, + "attach": "购买道具" + }); + let session_key = user.data.session_key; + const signature = hmacSha256(session_key, signData); + let key = "4HfZwqLjVkLb6s4TfIxRGKaTxgvvL1rl"; + let data = 'requestMidasPaymentGameItem' + '&' + signData; + const paySig = hmacSha256(key, data); + await db.collection("order").add({ + openid: user.data.openid, + outTradeNo: outTradeNo, + itemid: itemid, + itemCount: itemCount, + goodsPrice: Number(itemPrice), + time: Date.now(), + type:"and", + state: 0, + }); + // console.log("申请订单"); + // console.log(outTradeNo); + // 计算哈希值并转换为十六进制字符串 + return { code: 1, data: { paySig: paySig, signature: signature, signData: signData, outTradeNo: outTradeNo }, msg: "成功" } +} +function hmacSha256(sessionKey, signData) { + const hmac = crypto.createHmac('sha256', sessionKey); + hmac.update(signData); + return hmac.digest('hex'); +} \ No newline at end of file diff --git a/server/laf-cloud/functions/wx/orderPaySig.yaml b/server/laf-cloud/functions/wx/orderPaySig.yaml new file mode 100644 index 00000000..e582189b --- /dev/null +++ b/server/laf-cloud/functions/wx/orderPaySig.yaml @@ -0,0 +1,6 @@ +name: wx/orderPaySig +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wx/payCallBack.ts b/server/laf-cloud/functions/wx/payCallBack.ts new file mode 100644 index 00000000..dcdc33be --- /dev/null +++ b/server/laf-cloud/functions/wx/payCallBack.ts @@ -0,0 +1,125 @@ +import cloud from '@lafjs/cloud'; +const db = cloud.database(); +const ThinkingData = require('thinkingdata-node'); +export default async function (ctx: FunctionContext) { + let data = JSON.parse(ctx.body.MiniGame.Payload); + let OutTradeNo = data.OutTradeNo; + let res = await db.collection("order").where({ outTradeNo: OutTradeNo }).getOne(); + if(res.data){ + if(res.data.state!=2){ + await db.collection("order").where({ outTradeNo: OutTradeNo }).update({ state: 1 }); + let user = await db.collection("users").where({ openid: res.data.openid}).getOne(); + sead(res.data, user.data, getClientIp(ctx)); + switch (res.data.itemid) { + case "starter_pack": + db.collection("users").where({ _id:user.data._id}).update({ starter_packState: 1, pay_user: true }); + break; + } + if (!user.data.pay_user) { + db.collection("users").where({ _id:user.data._id }).update({ pay_user: true }); + } + } + } + + // let query={ + // signature: '1c1b5c1c72058436e2f1214e6aeafd258e50acd4', + // timestamp: '1751291889', + // nonce: '3122546041' + // } + /** + * ToUserName: 'gh_1092576a6ce7', + FromUserName: 'orm8I7IA-pMesygmTc5HPufGZHvA', + CreateTime: 1751292233, + MsgType: 'event', + MiniGame: { + Payload: '{"OpenId":"orm8I7BRTjhdQ3Mz9V9cg1jzTUcQ","OutTradeNo":"wcx_92778691620421751292218408","WeChatPayInfo":{"MchOrderNo":"20250630714915544","TransactionId":"4200002678202506307565098431"},"Env":0,"GoodsInfo":{"ProductId":"10011","Quantity":1,"ZoneId":"1","OrigPrice":100,"ActualPrice":100,"Attach":"购买道具","OrderSource":1},"PayChannel":0}', + PayEventSig: 'ecdaf472c239894706e28ab941d2aebf727b5bed45fe4073c645e922b6eefc1a', + IsMock: false + }, + Event: 'minigame_game_pay_goods_deliver_notify' + */ + return { "ErrCode": 0, "ErrMsg": "Success" }; + +} + +async function sead1(outTradeNo) { + let res = await db.collection("order").where({ outTradeNo: outTradeNo }).getOne(); + if (res.data.state == 1) { + return { + code: 1, data: { + pay_state: 2 + }, msg: "成功" + }; + } else if (res.data.state == 2) { + let userInfo = await db.collection("users").where({ openid: res.data.openid }).getOne(); + await sead(outTradeNo,userInfo.data,"") + console.log("1"); + return { + code: 1, data: { + pay_state: 1, + }, msg: "已领取奖励" + } + } else { + return { + code: 1, data: { + pay_state: 1, + }, msg: "充值未成功" + } + }; +} + +const getClientIp = (req) => { + return ( + req.headers['x-forwarded-for']?.split(',')[0]?.trim() || // 代理链中的第一个 IP + req.headers['x-real-ip'] || // Nginx 配置的 real-ip + req.socket.remoteAddress // 原始 IP(可能是代理) + ); +}; + + +async function sead(order, userInfo, ip) { + db.collection("susu").add({ + order:order, + time:Date.now() + }); + if (!userInfo.distinctId) { + console.log("未找到distinctId无法上传数数") + return; + } + let id = '87d18958cea145f29d3265470ecd3486'; + if (userInfo.isDebug != "true") { + id = '121591378fc1423893deb12041413eb3'; + } + let teSDK = ThinkingData.initWithBatchMode(id,'https://data.nika4fun.com/sync_data', { + dryRun: false, // report data to TE or not + deviceId: "123456789",//设备唯一标识 + }); + let trackEvent = { + accountId: order.openid, + distinctId: userInfo.distinctId,//访客id + event: 'payment', + time: new Date(Date.now() + 8 * 3600000), + ip: ip, + properties: { + order_id: order.outTradeNo, + pay_amount: order.goodsPrice, + payment_name: order.itemid, + payment_num: order.itemCount, + payment_type: "Android", + current_level: userInfo.levelAmount, //当前关卡等级 number + current_health: userInfo.healthAmount, //当前体力值 + tmp_coin: userInfo.coinAmount,//当前金币 + version: "1.6", + user_id: userInfo._id, //用户id + pay_user: userInfo.pay_user, + register_time: userInfo.register_time + }, + callback(e) { + if (e) { + } + } + }; + teSDK.track(trackEvent); + teSDK.close(); + return; +} \ No newline at end of file diff --git a/server/laf-cloud/functions/wx/payCallBack.yaml b/server/laf-cloud/functions/wx/payCallBack.yaml new file mode 100644 index 00000000..350ac73d --- /dev/null +++ b/server/laf-cloud/functions/wx/payCallBack.yaml @@ -0,0 +1,6 @@ +name: wx/payCallBack +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/functions/wxPerformanceCall.ts b/server/laf-cloud/functions/wxPerformanceCall.ts new file mode 100644 index 00000000..02972a80 --- /dev/null +++ b/server/laf-cloud/functions/wxPerformanceCall.ts @@ -0,0 +1,6 @@ +import cloud from '@lafjs/cloud' + +export default async function (ctx: FunctionContext) { + console.log('Hello World') + return { data: 'hi, laf' } +} diff --git a/server/laf-cloud/functions/wxPerformanceCall.yaml b/server/laf-cloud/functions/wxPerformanceCall.yaml new file mode 100644 index 00000000..ec27c92d --- /dev/null +++ b/server/laf-cloud/functions/wxPerformanceCall.yaml @@ -0,0 +1,6 @@ +name: wxPerformanceCall +desc: "" +methods: + - GET + - POST +tags: [] diff --git a/server/laf-cloud/tsconfig.json b/server/laf-cloud/tsconfig.json new file mode 100644 index 00000000..6c050871 --- /dev/null +++ b/server/laf-cloud/tsconfig.json @@ -0,0 +1,44 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "allowJs": false, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "baseUrl": ".", + "paths": { + "@/*": [ + "functions/*" + ] + }, + "declaration": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "importHelpers": false, + "module": "Node16", + "declarationMap": true, + // "declarationDir": "types", + "noEmitOnError": false, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist", + "pretty": true, + "removeComments": true, + "stripInternal": true, + "skipDefaultLibCheck": true, + "skipLibCheck": true, + "target": "ES2022", + "alwaysStrict": true, + "lib": [ + "ES2022" + ] + }, + "include": [ + "functions/**/*", + "types/**/*" + ], + "exclude": [ + "tests" + ] +} \ No newline at end of file diff --git a/server/laf-cloud/types/global.d.ts b/server/laf-cloud/types/global.d.ts new file mode 100644 index 00000000..13373181 --- /dev/null +++ b/server/laf-cloud/types/global.d.ts @@ -0,0 +1,1218 @@ +/** + * The input parameters of cloud function calls + */ +declare interface FunctionContext { + __function_name: string + + /** + * This object is parsed from JWT Token Payload + */ + user?: { + [key: string]: any + } + + /** + * Uploaded file, the file object array + */ + files?: File[] + + /** + * HTTP headers + */ + headers?: IncomingHttpHeaders + + /** + * HTTP Query parameter (URL parameter), JSON object + */ + query?: any + + /** + * HTTP Body + */ + body?: any + + /** + * + */ + params?: any + + /** + * HTTP Request ID + */ + requestId?: string + + /** + * HTTP Method + */ + method?: string + + /** + * Express request object + */ + request?: HttpRequest + + /** + * Express response object + */ + response?: HttpResponse + + /** + * WebSocket object + */ + socket?: WebSocket + + [key: string]: any +} + + +// ============ The following is copied from another library and generally does not require direct modification============ + +class IncomingMessage extends Readable { + constructor(socket: Socket); + /** + * The 'message.aborted' property will be 'true' if the request has + * been aborted. + * @since v10.1.0 + * @deprecated Since v17.0.0,v16.12.0 - Check 'message.destroyed' from stream.Readable. + */ + aborted: boolean; + /** + * In case of server request, the HTTP version sent by the client. In the case of + * client response, the HTTP version of the connected-to server. + * Probably either ''1.1'' or ''1.0''. + * + * Also 'message.httpVersionMajor' is the first integer and'message.httpVersionMinor' is the second. + * @since v0.1.1 + */ + httpVersion: string; + httpVersionMajor: number; + httpVersionMinor: number; + /** + * The 'message.complete' property will be 'true' if a complete HTTP message has + * been received and successfully parsed. + * + * This property is particularly useful as a means of determining if a client or + * server fully transmitted a message before a connection was terminated: + * + * '''js + * const req = http.request({ + * host: '127.0.0.1', + * port: 8080, + * method: 'POST' + * }, (res) => { + * res.resume(); + * res.on('end', () => { + * if (!res.complete) + * console.error( + * 'The connection was terminated while the message was still being sent'); + * }); + * }); + * ''' + * @since v0.3.0 + */ + complete: boolean; + /** + * Alias for 'message.socket'. + * @since v0.1.90 + * @deprecated Since v16.0.0 - Use 'socket'. + */ + connection: Socket; + /** + * The 'net.Socket' object associated with the connection. + * + * With HTTPS support, use 'request.socket.getPeerCertificate()' to obtain the + * client's authentication details. + * + * This property is guaranteed to be an instance of the 'net.Socket' class, + * a subclass of 'stream.Duplex', unless the user specified a socket + * type other than 'net.Socket' or internally nulled. + * @since v0.3.0 + */ + socket: Socket; + /** + * The request/response headers object. + * + * Key-value pairs of header names and values. Header names are lower-cased. + * + * '''js + * // Prints something like: + * // + * // { 'user-agent': 'curl/7.22.0', + * // host: '127.0.0.1:8000', + * // accept: '*' } + * console.log(request.getHeaders()); + * ''' + * + * Duplicates in raw headers are handled in the following ways, depending on the + * header name: + * + * * Duplicates of 'age', 'authorization', 'content-length', 'content-type','etag', 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since','last-modified', 'location', + * 'max-forwards', 'proxy-authorization', 'referer','retry-after', 'server', or 'user-agent' are discarded. + * * 'set-cookie' is always an array. Duplicates are added to the array. + * * For duplicate 'cookie' headers, the values are joined together with '; '. + * * For all other headers, the values are joined together with ', '. + * @since v0.1.5 + */ + headers: IncomingHttpHeaders; + /** + * The raw request/response headers list exactly as they were received. + * + * The keys and values are in the same list. It is _not_ a + * list of tuples. So, the even-numbered offsets are key values, and the + * odd-numbered offsets are the associated values. + * + * Header names are not lowercased, and duplicates are not merged. + * + * '''js + * // Prints something like: + * // + * // [ 'user-agent', + * // 'this is invalid because there can be only one', + * // 'User-Agent', + * // 'curl/7.22.0', + * // 'Host', + * // '127.0.0.1:8000', + * // 'ACCEPT', + * // '*' ] + * console.log(request.rawHeaders); + * ''' + * @since v0.11.6 + */ + rawHeaders: string[]; + /** + * The request/response trailers object. Only populated at the ''end'' event. + * @since v0.3.0 + */ + trailers: NodeJS.Dict; + /** + * The raw request/response trailer keys and values exactly as they were + * received. Only populated at the ''end'' event. + * @since v0.11.6 + */ + rawTrailers: string[]; + /** + * Calls 'message.socket.setTimeout(msecs, callback)'. + * @since v0.5.9 + */ + setTimeout(msecs: number, callback?: () => void): this; + /** + * **Only valid for request obtained from {@link Server}.** + * + * The request method as a string. Read only. Examples: ''GET'', ''DELETE''. + * @since v0.1.1 + */ + method?: string | undefined; + /** + * **Only valid for request obtained from {@link Server}.** + * + * Request URL string. This contains only the URL that is present in the actual + * HTTP request. Take the following request: + * + * '''http + * GET /status?name=ryan HTTP/1.1 + * Accept: text/plain + * ''' + * + * To parse the URL into its parts: + * + * '''js + * new URL(request.url, 'http://\${request.getHeaders().host}'); + * ''' + * + * When 'request.url' is ''/status?name=ryan'' and'request.getHeaders().host' is ''localhost:3000'': + * + * '''console + * $ node + * > new URL(request.url, 'http://\${request.getHeaders().host}') + * URL { + * href: 'http://localhost:3000/status?name=ryan', + * origin: 'http://localhost:3000', + * protocol: 'http:', + * username: '', + * password: '', + * host: 'localhost:3000', + * hostname: 'localhost', + * port: '3000', + * pathname: '/status', + * search: '?name=ryan', + * searchParams: URLSearchParams { 'name' => 'ryan' }, + * hash: '' + * } + * ''' + * @since v0.1.90 + */ + url?: string | undefined; + /** + * **Only valid for response obtained from {@link ClientRequest}.** + * + * The 3-digit HTTP response status code. E.G. '404'. + * @since v0.1.1 + */ + statusCode?: number | undefined; + /** + * **Only valid for response obtained from {@link ClientRequest}.** + * + * The HTTP response status message (reason phrase). E.G. 'OK' or 'Internal Server Error'. + * @since v0.11.10 + */ + statusMessage?: string | undefined; + /** + * Calls 'destroy()' on the socket that received the 'IncomingMessage'. If 'error'is provided, an ''error'' event is emitted on the socket and 'error' is passed + * as an argument to any listeners on the event. + * @since v0.3.0 + */ + destroy(error?: Error): this; +} + + +/** +* @see https://expressjs.com/en/api.html#req.params +* +* @example +* app.get('/user/:id', (req, res) => res.send(req.params.id)); // implicitly 'ParamsDictionary' +* app.get(/user\/(.*)/, (req, res) => res.send(req.params[0])); +* app.get('/user/*', (req, res) => res.send(req.params[0])); +*/ +interface HttpRequest extends IncomingMessage { + /** + * Return request header. + * + * The 'Referrer' header field is special-cased, + * both 'Referrer' and 'Referer' are interchangeable. + * + * Examples: + * + * req.get('Content-Type'); + * // => "text/plain" + * + * req.get('content-type'); + * // => "text/plain" + * + * req.get('Something'); + * // => undefined + * + * Aliased as 'req.header()'. + */ + get(name: 'set-cookie'): string[] | undefined; + get(name: string): string | undefined; + + header(name: 'set-cookie'): string[] | undefined; + header(name: string): string | undefined; + + /** + * Check if the given 'type(s)' is acceptable, returning + * the best match when true, otherwise 'undefined', in which + * case you should respond with 406 "Not Acceptable". + * + * The 'type' value may be a single mime type string + * such as "application/json", the extension name + * such as "json", a comma-delimted list such as "json, html, text/plain", + * or an array '["json", "html", "text/plain"]'. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * req.accepts('html'); + * // => "html" + * + * // Accept: text/*, application/json + * req.accepts('html'); + * // => "html" + * req.accepts('text/html'); + * // => "text/html" + * req.accepts('json, text'); + * // => "json" + * req.accepts('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * req.accepts('image/png'); + * req.accepts('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * req.accepts(['html', 'json']); + * req.accepts('html, json'); + * // => "json" + */ + accepts(): string[]; + accepts(type: string): string | false; + accepts(type: string[]): string | false; + accepts(...type: string[]): string | false; + + /** + * Returns the first accepted charset of the specified character sets, + * based on the request's Accept-Charset HTTP header field. + * If none of the specified charsets is accepted, returns false. + * + * For more information, or if you have issues or concerns, see accepts. + */ + acceptsCharsets(): string[]; + acceptsCharsets(charset: string): string | false; + acceptsCharsets(charset: string[]): string | false; + acceptsCharsets(...charset: string[]): string | false; + + /** + * Returns the first accepted encoding of the specified encodings, + * based on the request's Accept-Encoding HTTP header field. + * If none of the specified encodings is accepted, returns false. + * + * For more information, or if you have issues or concerns, see accepts. + */ + acceptsEncodings(): string[]; + acceptsEncodings(encoding: string): string | false; + acceptsEncodings(encoding: string[]): string | false; + acceptsEncodings(...encoding: string[]): string | false; + + /** + * Returns the first accepted language of the specified languages, + * based on the request's Accept-Language HTTP header field. + * If none of the specified languages is accepted, returns false. + * + * For more information, or if you have issues or concerns, see accepts. + */ + acceptsLanguages(): string[]; + acceptsLanguages(lang: string): string | false; + acceptsLanguages(lang: string[]): string | false; + acceptsLanguages(...lang: string[]): string | false; + + /** + * Parse Range header field, capping to the given 'size'. + * + * Unspecified ranges such as "0-" require knowledge of your resource length. In + * the case of a byte range this is of course the total number of bytes. + * If the Range header field is not given 'undefined' is returned. + * If the Range header field is given, return value is a result of range-parser. + * See more ./types/range-parser/index.d.ts + * + * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3" + * should respond with 4 users when available, not 3. + * + */ + range(size: number, options?: RangeParserOptions): RangeParserRanges | RangeParserResult | undefined; + + /** + * Return an array of Accepted media types + * ordered from highest quality to lowest. + */ + accepted: MediaType[]; + + /** + * @deprecated since 4.11 Use either req.params, req.body or req.query, as applicable. + * + * Return the value of param 'name' when present or 'defaultValue'. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks body params, ex: id=12, {"id":12} + * - Checks query string params, ex: ?id=12 + * + * To utilize request bodies, 'req.body' + * should be an object. This can be done by using + * the 'connect.bodyParser()' middleware. + */ + param(name: string, defaultValue?: any): string; + + /** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime 'type'. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * req.is('text/*'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * req.is('application/*'); + * // => true + * + * req.is('html'); + * // => false + */ + is(type: string | string[]): string | false | null; + + /** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + */ + protocol: string; + + /** + * Short-hand for: + * + * req.protocol == 'https' + */ + secure: boolean; + + /** + * Return the remote address, or when + * "trust proxy" is 'true' return + * the upstream addr. + */ + ip: string; + + /** + * When "trust proxy" is 'true', parse + * the "X-Forwarded-For" ip address list. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array '["client", "proxy1", "proxy2"]' + * where "proxy2" is the furthest down-stream. + */ + ips: string[]; + + /** + * Return subdomains as an array. + * + * Subdomains are the dot-separated parts of the host before the main domain of + * the app. By default, the domain of the app is assumed to be the last two + * parts of the host. This can be changed by setting "subdomain offset". + * + * For example, if the domain is "tobi.ferrets.example.com": + * If "subdomain offset" is not set, req.subdomains is '["ferrets", "tobi"]'. + * If "subdomain offset" is 3, req.subdomains is '["tobi"]'. + */ + subdomains: string[]; + + /** + * Short-hand for 'url.parse(req.url).pathname'. + */ + path: string; + + /** + * Parse the "Host" header field hostname. + */ + hostname: string; + + /** + * @deprecated Use hostname instead. + */ + host: string; + + /** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + */ + fresh: boolean; + + /** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + */ + stale: boolean; + + /** + * Check if the request was an _XMLHttpRequest_. + */ + xhr: boolean; + + //body: { username: string; password: string; remember: boolean; title: string; }; + body: ReqBody; + + //cookies: { string; remember: boolean; }; + cookies: any; + + method: string; + + params: P; + + query: ReqQuery; + + route: any; + + signedCookies: any; + + originalUrl: string; + + url: string; + + baseUrl: string; + + app: Application; + + /** + * After middleware.init executed, Request will contain res and next properties + * See: express/lib/middleware/init.js + */ + res?: Response | undefined; + next?: NextFunction | undefined; +} + + +declare class OutgoingMessage extends Writable { + readonly req: IncomingMessage; + chunkedEncoding: boolean; + shouldKeepAlive: boolean; + useChunkedEncodingByDefault: boolean; + sendDate: boolean; + /** + * @deprecated Use 'writableEnded' instead. + */ + // finished: boolean; + /** + * Read-only. 'true' if the headers were sent, otherwise 'false'. + * @since v0.9.3 + */ + readonly headersSent: boolean; + /** + * Aliases of 'outgoingMessage.socket' + * @since v0.3.0 + * @deprecated Since v15.12.0 - Use 'socket' instead. + */ + // readonly connection: Socket | null; + /** + * Reference to the underlying socket. Usually, users will not want to access + * this property. + * + * After calling 'outgoingMessage.end()', this property will be nulled. + * @since v0.3.0 + */ + readonly socket: Socket | null; + constructor(); + /** + * occurs, Same as binding to the 'timeout' event. + * + * Once a socket is associated with the message and is connected,'socket.setTimeout()' will be called with 'msecs' as the first parameter. + * @since v0.9.12 + * @param callback Optional function to be called when a timeout + */ + setTimeout(msecs: number, callback?: () => void): this; + /** + * Sets a single header value for the header object. + * @since v0.4.0 + * @param name Header name + * @param value Header value + */ + setHeader(name: string, value: number | string | ReadonlyArray): this; + /** + * Gets the value of HTTP header with the given name. If such a name doesn't + * exist in message, it will be 'undefined'. + * @since v0.4.0 + * @param name Name of header + */ + getHeader(name: string): number | string | string[] | undefined; + /** + * Returns a shallow copy of the current outgoing headers. Since a shallow + * copy is used, array values may be mutated without additional calls to + * various header-related HTTP module methods. The keys of the returned + * object are the header names and the values are the respective header + * values. All header names are lowercase. + * + * The object returned by the 'outgoingMessage.getHeaders()' method does + * not prototypically inherit from the JavaScript Object. This means that + * typical Object methods such as 'obj.toString()', 'obj.hasOwnProperty()', + * and others are not defined and will not work. + * + * outgoingMessage.setHeader('Foo', 'bar'); + * outgoingMessage.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']); + * + * const headers = outgoingMessage.getHeaders(); + * // headers === { foo: 'bar', 'set-cookie': ['foo=bar', 'bar=baz'] } + * + * @since v8.0.0 + */ + getHeaders(): OutgoingHttpHeaders; + /** + * Returns an array of names of headers of the outgoing outgoingMessage. All + * names are lowercase. + * @since v8.0.0 + */ + getHeaderNames(): string[]; + /** + * Returns 'true' if the header identified by 'name' is currently set in the + * outgoing headers. The header name is case-insensitive. + * + * const hasContentType = outgoingMessage.hasHeader('content-type'); + * + * @since v8.0.0 + */ + hasHeader(name: string): boolean; + /** + * Removes a header that is queued for implicit sending. + * + * outgoingMessage.removeHeader('Content-Encoding'); + * + * @since v0.4.0 + */ + removeHeader(name: string): void; + /** + * Adds HTTP trailers (headers but at the end of the message) to the message. + * + * Trailers are **only** be emitted if the message is chunked encoded. If not, + * the trailer will be silently discarded. + * + * HTTP requires the 'Trailer' header to be sent to emit trailers, + * with a list of header fields in its value, e.g. + * + * message.writeHead(200, { 'Content-Type': 'text/plain', + * 'Trailer': 'Content-MD5' }); + * message.write(fileData); + * message.addTrailers({ 'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667' }); + * message.end(); + * + * Attempting to set a header field name or value that contains invalid characters + * will result in a 'TypeError' being thrown. + * @since v0.3.0 + */ + addTrailers(headers: OutgoingHttpHeaders | ReadonlyArray<[string, string]>): void; + /** + * Compulsorily flushes the message headers + * + * For efficiency reason, Node.js normally buffers the message headers + * until 'outgoingMessage.end()' is called or the first chunk of message data + * is written. It then tries to pack the headers and data into a single TCP + * packet. + * + * It is usually desired (it saves a TCP round-trip), but not when the first + * data is not sent until possibly much later. 'outgoingMessage.flushHeaders()' bypasses the optimization and kickstarts the request. + * @since v1.6.0 + */ + flushHeaders(): void; +} + +declare class ServerResponse extends OutgoingMessage { + statusCode: number; + statusMessage: string; + + constructor(req: IncomingMessage); + + assignSocket(socket: Socket): void; + detachSocket(socket: Socket): void; + // https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53 + // no args in writeContinue callback + writeContinue(callback?: () => void): void; + writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[]): this; + writeHead(statusCode: number, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[]): this; + writeProcessing(): void; +} + +interface HttpResponse extends ServerResponse { + /** + * Set status 'code'. + */ + status(code: StatusCode): this; + + /** + * Set the response HTTP status code to 'statusCode' and send its string representation as the response body. + * @link http://expressjs.com/4x/api.html#res.sendStatus + * + * Examples: + * + * res.sendStatus(200); // equivalent to res.status(200).send('OK') + * res.sendStatus(403); // equivalent to res.status(403).send('Forbidden') + * res.sendStatus(404); // equivalent to res.status(404).send('Not Found') + * res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error') + */ + sendStatus(code: StatusCode): this; + + /** + * Set Link header field with the given 'links'. + * + * Examples: + * + * res.links({ + * next: 'http://api.example.com/users?page=2', + * last: 'http://api.example.com/users?page=5' + * }); + */ + links(links: any): this; + + /** + * Send a response. + * + * Examples: + * + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('

some html

'); + * res.status(404).send('Sorry, cant find that'); + */ + send: Send; + + /** + * Send JSON response. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * res.status(500).json('oh noes!'); + * res.status(404).json('I dont have that'); + */ + json: Send; + + /** + * Send JSON response with JSONP callback support. + * + * Examples: + * + * res.jsonp(null); + * res.jsonp({ user: 'tj' }); + * res.status(500).jsonp('oh noes!'); + * res.status(404).jsonp('I dont have that'); + */ + jsonp: Send; + + /** + * Transfer the file at the given 'path'. + * + * Automatically sets the _Content-Type_ response header field. + * The callback 'fn(err)' is invoked when the transfer is complete + * or when an error occurs. Be sure to check 'res.headersSent' + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + * - 'maxAge' defaulting to 0 (can be string converted by 'ms') + * - 'root' root directory for relative filenames + * - 'headers' object of headers to serve with file + * - 'dotfiles' serve dotfiles, defaulting to false; can be '"allow"' to send them + * + * Other options are passed along to 'send'. + * + * Examples: + * + * The following example illustrates how 'res.sendFile()' may + * be used as an alternative for the 'static()' middleware for + * dynamic situations. The code backing 'res.sendFile()' is actually + * the same code, so HTTP cache support etc is identical. + * + * app.get('/user/:uid/photos/:file', function(req, res){ + * var uid = req.params.uid + * , file = req.params.file; + * + * req.user.mayViewFilesFrom(uid, function(yes){ + * if (yes) { + * res.sendFile('/uploads/' + uid + '/' + file); + * } else { + * res.send(403, 'Sorry! you cant see that.'); + * } + * }); + * }); + * + * @api public + */ + sendFile(path: string, fn?: Errback): void; + sendFile(path: string, options: any, fn?: Errback): void; + + /** + * @deprecated Use sendFile instead. + */ + // sendfile(path: string): void; + /** + * @deprecated Use sendFile instead. + */ + // sendfile(path: string, options: any): void; + /** + * @deprecated Use sendFile instead. + */ + // sendfile(path: string, fn: Errback): void; + /** + * @deprecated Use sendFile instead. + */ + // sendfile(path: string, options: any, fn: Errback): void; + + /** + * Transfer the file at the given 'path' as an attachment. + * + * Optionally providing an alternate attachment 'filename', + * and optional callback 'fn(err)'. The callback is invoked + * when the data transfer is complete, or when an error has + * ocurred. Be sure to check 'res.headersSent' if you plan to respond. + * + * The optional options argument passes through to the underlying + * res.sendFile() call, and takes the exact same parameters. + * + * This method uses 'res.sendfile()'. + */ + download(path: string, fn?: Errback): void; + download(path: string, filename: string, fn?: Errback): void; + download(path: string, filename: string, options: any, fn?: Errback): void; + + /** + * Set _Content-Type_ response header with 'type' through 'mime.lookup()' + * when it does not contain "/", or set the Content-Type to 'type' otherwise. + * + * Examples: + * + * res.type('.html'); + * res.type('html'); + * res.type('json'); + * res.type('application/json'); + * res.type('png'); + */ + contentType(type: string): this; + + /** + * Set _Content-Type_ response header with 'type' through 'mime.lookup()' + * when it does not contain "/", or set the Content-Type to 'type' otherwise. + * + * Examples: + * + * res.type('.html'); + * res.type('html'); + * res.type('json'); + * res.type('application/json'); + * res.type('png'); + */ + type(type: string): this; + + /** + * Respond to the Acceptable formats using an 'obj' + * of mime-type callbacks. + * + * This method uses 'req.accepted', an array of + * acceptable types ordered by their quality values. + * When "Accept" is not present the _first_ callback + * is invoked, otherwise the first match is used. When + * no match is performed the server responds with + * 406 "Not Acceptable". + * + * Content-Type is set for you, however if you choose + * you may alter this within the callback using 'res.type()' + * or 'res.set('Content-Type', ...)'. + * + * res.format({ + * 'text/plain': function(){ + * res.send('hey'); + * }, + * + * 'text/html': function(){ + * res.send('

hey

'); + * }, + * + * 'appliation/json': function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * In addition to canonicalized MIME types you may + * also use extnames mapped to these types: + * + * res.format({ + * text: function(){ + * res.send('hey'); + * }, + * + * html: function(){ + * res.send('

hey

'); + * }, + * + * json: function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * By default Express passes an 'Error' + * with a '.status' of 406 to 'next(err)' + * if a match is not made. If you provide + * a '.default' callback it will be invoked + * instead. + */ + format(obj: any): this; + + /** + * Set _Content-Disposition_ header to _attachment_ with optional 'filename'. + */ + attachment(filename?: string): this; + + /** + * Set header 'field' to 'val', or pass + * an object of header fields. + * + * Examples: + * + * res.set('Foo', ['bar', 'baz']); + * res.set('Accept', 'application/json'); + * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + * + * Aliased as 'res.header()'. + */ + set(field: any): this; + set(field: string, value?: string | string[]): this; + + header(field: any): this; + header(field: string, value?: string | string[]): this; + + // Property indicating if HTTP headers has been sent for the response. + headersSent: boolean; + + /** Get value for header 'field'. */ + get(field: string): string; + + /** Clear cookie 'name'. */ + clearCookie(name: string, options?: any): this; + + /** + * Set cookie 'name' to 'val', with the given 'options'. + * + * Options: + * + * - 'maxAge' max-age in milliseconds, converted to 'expires' + * - 'signed' sign the cookie + * - 'path' defaults to "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + */ + cookie(name: string, val: string, options: CookieOptions): this; + cookie(name: string, val: any, options: CookieOptions): this; + cookie(name: string, val: any): this; + + /** + * Set the location header to 'url'. + * + * The given 'url' can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or "/". + * + * Examples: + * + * res.location('/foo/bar').; + * res.location('http://example.com'); + * res.location('../login'); // /blog/post/1 -> /blog/login + * + * Mounting: + * + * When an application is mounted and 'res.location()' + * is given a path that does _not_ lead with "/" it becomes + * relative to the mount-point. For example if the application + * is mounted at "/blog", the following would become "/blog/login". + * + * res.location('login'); + * + * While the leading slash would result in a location of "/login": + * + * res.location('/login'); + */ + location(url: string): this; + + /** + * Redirect to the given 'url' with optional response 'status' + * defaulting to 302. + * + * The resulting 'url' is determined by 'res.location()', so + * it will play nicely with mounted apps, relative paths, + * '"back"' etc. + * + * Examples: + * + * res.redirect('/foo/bar'); + * res.redirect('http://example.com'); + * res.redirect(301, 'http://example.com'); + * res.redirect('http://example.com', 301); + * res.redirect('../login'); // /blog/post/1 -> /blog/login + */ + redirect(url: string): void; + redirect(status: number, url: string): void; + redirect(url: string, status: number): void; + + /** + * Render 'view' with the given 'options' and optional callback 'fn'. + * When a callback function is given a response will _not_ be made + * automatically, otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - 'cache' boolean hinting to the engine it should cache + * - 'filename' filename of the view being rendered + */ + render(view: string, options?: object, callback?: (err: Error, html: string) => void): void; + render(view: string, callback?: (err: Error, html: string) => void): void; + + locals: Locals; + + charset: string; + + /** + * Adds the field to the Vary response header, if it is not there already. + * Examples: + * + * res.vary('User-Agent').render('docs'); + * + */ + vary(field: string): this; + + // app: Application; + + /** + * Appends the specified value to the HTTP response header field. + * If the header is not already set, it creates the header with the specified value. + * The value parameter can be a string or an array. + * + * Note: calling res.set() after res.append() will reset the previously-set header value. + * + * @since 4.11.0 + */ + append(field: string, value?: string[] | string): this; + + /** + * After middleware.init executed, Response will contain req property + * See: express/lib/middleware/init.js + */ + // req?: Request; +} + + + + +// WebSocket socket. +declare class WebSocket extends EventEmitter { + /** The connection is not yet open. */ + static readonly CONNECTING: 0; + /** The connection is open and ready to communicate. */ + static readonly OPEN: 1; + /** The connection is in the process of closing. */ + static readonly CLOSING: 2; + /** The connection is closed. */ + static readonly CLOSED: 3; + + binaryType: "nodebuffer" | "arraybuffer" | "fragments"; + readonly bufferedAmount: number; + readonly extensions: string; + readonly protocol: string; + /** The current state of the connection */ + readonly readyState: + | typeof WebSocket.CONNECTING + | typeof WebSocket.OPEN + | typeof WebSocket.CLOSING + | typeof WebSocket.CLOSED; + readonly url: string; + + /** The connection is not yet open. */ + readonly CONNECTING: 0; + /** The connection is open and ready to communicate. */ + readonly OPEN: 1; + /** The connection is in the process of closing. */ + readonly CLOSING: 2; + /** The connection is closed. */ + readonly CLOSED: 3; + + onopen: (event: WebSocket.Event) => void; + onerror: (event: WebSocket.ErrorEvent) => void; + onclose: (event: WebSocket.CloseEvent) => void; + onmessage: (event: WebSocket.MessageEvent) => void; + + constructor(address: string | URL, options?: WebSocket.ClientOptions | ClientRequestArgs); + constructor( + address: string | URL, + protocols?: string | string[], + options?: WebSocket.ClientOptions | ClientRequestArgs, + ); + + close(code?: number, data?: string | Buffer): void; + ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void; + pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void; + send(data: any, cb?: (err?: Error) => void): void; + send( + data: any, + options: { mask?: boolean | undefined; binary?: boolean | undefined; compress?: boolean | undefined; fin?: boolean | undefined }, + cb?: (err?: Error) => void, + ): void; + terminate(): void; + + // HTML5 WebSocket events + addEventListener( + method: "message", + cb: (event: WebSocket.MessageEvent) => void, + options?: WebSocket.EventListenerOptions, + ): void; + addEventListener( + method: "close", + cb: (event: WebSocket.CloseEvent) => void, + options?: WebSocket.EventListenerOptions, + ): void; + addEventListener( + method: "error", + cb: (event: WebSocket.ErrorEvent) => void, + options?: WebSocket.EventListenerOptions, + ): void; + addEventListener( + method: "open", + cb: (event: WebSocket.Event) => void, + options?: WebSocket.EventListenerOptions, + ): void; + + removeEventListener(method: "message", cb: (event: WebSocket.MessageEvent) => void): void; + removeEventListener(method: "close", cb: (event: WebSocket.CloseEvent) => void): void; + removeEventListener(method: "error", cb: (event: WebSocket.ErrorEvent) => void): void; + removeEventListener(method: "open", cb: (event: WebSocket.Event) => void): void; + + // Events + on(event: "close", listener: (this: WebSocket, code: number, reason: Buffer) => void): this; + on(event: "error", listener: (this: WebSocket, err: Error) => void): this; + on(event: "upgrade", listener: (this: WebSocket, request: IncomingMessage) => void): this; + on(event: "message", listener: (this: WebSocket, data: WebSocket.RawData, isBinary: boolean) => void): this; + on(event: "open", listener: (this: WebSocket) => void): this; + on(event: "ping" | "pong", listener: (this: WebSocket, data: Buffer) => void): this; + on( + event: "unexpected-response", + listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void, + ): this; + on(event: string | symbol, listener: (this: WebSocket, ...args: any[]) => void): this; + + once(event: "close", listener: (this: WebSocket, code: number, reason: Buffer) => void): this; + once(event: "error", listener: (this: WebSocket, err: Error) => void): this; + once(event: "upgrade", listener: (this: WebSocket, request: IncomingMessage) => void): this; + once(event: "message", listener: (this: WebSocket, data: WebSocket.RawData, isBinary: boolean) => void): this; + once(event: "open", listener: (this: WebSocket) => void): this; + once(event: "ping" | "pong", listener: (this: WebSocket, data: Buffer) => void): this; + once( + event: "unexpected-response", + listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void, + ): this; + once(event: string | symbol, listener: (this: WebSocket, ...args: any[]) => void): this; + + off(event: "close", listener: (this: WebSocket, code: number, reason: Buffer) => void): this; + off(event: "error", listener: (this: WebSocket, err: Error) => void): this; + off(event: "upgrade", listener: (this: WebSocket, request: IncomingMessage) => void): this; + off(event: "message", listener: (this: WebSocket, data: WebSocket.RawData, isBinary: boolean) => void): this; + off(event: "open", listener: (this: WebSocket) => void): this; + off(event: "ping" | "pong", listener: (this: WebSocket, data: Buffer) => void): this; + off( + event: "unexpected-response", + listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void, + ): this; + off(event: string | symbol, listener: (this: WebSocket, ...args: any[]) => void): this; + + addListener(event: "close", listener: (code: number, reason: Buffer) => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "upgrade", listener: (request: IncomingMessage) => void): this; + addListener(event: "message", listener: (data: WebSocket.RawData, isBinary: boolean) => void): this; + addListener(event: "open", listener: () => void): this; + addListener(event: "ping" | "pong", listener: (data: Buffer) => void): this; + addListener( + event: "unexpected-response", + listener: (request: ClientRequest, response: IncomingMessage) => void, + ): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + + removeListener(event: "close", listener: (code: number, reason: Buffer) => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "upgrade", listener: (request: IncomingMessage) => void): this; + removeListener(event: "message", listener: (data: WebSocket.RawData, isBinary: boolean) => void): this; + removeListener(event: "open", listener: () => void): this; + removeListener(event: "ping" | "pong", listener: (data: Buffer) => void): this; + removeListener( + event: "unexpected-response", + listener: (request: ClientRequest, response: IncomingMessage) => void, + ): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; +} \ No newline at end of file