2023-03-04 14:30:13 +08:00
|
|
|
|
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
|
|
|
|
|
import fetch from 'node-fetch'
|
|
|
|
|
import moment from 'moment'
|
|
|
|
|
import lodash from 'lodash'
|
|
|
|
|
import fs from 'fs'
|
|
|
|
|
import base from './base.js'
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync('./data/payLog/')) {
|
|
|
|
|
fs.mkdirSync('./data/payLog/')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class PayData {
|
|
|
|
|
constructor (authKey = '') {
|
|
|
|
|
this.#authkey = encodeURIComponent(authKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#genShinId = ''
|
|
|
|
|
#oringinData = []
|
|
|
|
|
#authkey = ''
|
|
|
|
|
|
|
|
|
|
/** 获取原始支付数据 */
|
|
|
|
|
async getOringinalData (id = '') {
|
|
|
|
|
let res = await fetch(this.getUrl() + id, this.headers)
|
|
|
|
|
let ret = await res.json()
|
2023-07-11 00:21:36 +08:00
|
|
|
|
let check = this.checkResult(ret)
|
|
|
|
|
if (check?.errorMsg) return check
|
|
|
|
|
let list = ret?.data?.list
|
|
|
|
|
if (!Array.isArray(list)) {
|
|
|
|
|
console.error(ret)
|
|
|
|
|
return { errorMsg: '获取失败,错误码:' + ret?.retcode }
|
2023-03-04 14:30:13 +08:00
|
|
|
|
}
|
|
|
|
|
if (list.length === 20) {
|
|
|
|
|
this.#oringinData.push(...list)
|
|
|
|
|
await this.getOringinalData(list[19].id)
|
|
|
|
|
return true
|
|
|
|
|
} else {
|
|
|
|
|
this.#oringinData.push(...list)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 获取大月卡数据 */
|
|
|
|
|
async getPrimogemLog (id = '') {
|
|
|
|
|
let res = await fetch(this.getUrl('getPrimogemLog') + id, this.headers)
|
|
|
|
|
let ret = await res.json()
|
2023-07-11 00:21:36 +08:00
|
|
|
|
let check = this.checkResult(ret)
|
|
|
|
|
if (check?.errorMsg) return check
|
|
|
|
|
let list = ret?.data?.list
|
|
|
|
|
if (!Array.isArray(list)) {
|
|
|
|
|
console.error(ret)
|
|
|
|
|
return { errorMsg: '获取失败,错误码:' + ret?.retcode }
|
|
|
|
|
}
|
2023-03-04 14:30:13 +08:00
|
|
|
|
if (list.length === 20) {
|
|
|
|
|
list.forEach(v => {
|
|
|
|
|
if (v.add_num === '680') this.#oringinData.push(v)
|
|
|
|
|
})
|
|
|
|
|
await this.getPrimogemLog(list[19].id)
|
|
|
|
|
return true
|
|
|
|
|
} else {
|
|
|
|
|
list.forEach(v => {
|
|
|
|
|
if (v.add_num === '680') this.#oringinData.push(v)
|
|
|
|
|
})
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-11 00:21:36 +08:00
|
|
|
|
async getUserInfo() {
|
|
|
|
|
let res = await fetch(this.getUrl('getUserInfo'), this.headers)
|
|
|
|
|
let ret = await res.json()
|
|
|
|
|
let check = this.checkResult(ret)
|
|
|
|
|
if (check?.errorMsg) return check
|
|
|
|
|
let data = ret?.data
|
|
|
|
|
if (data?.uid) {
|
|
|
|
|
return data
|
|
|
|
|
}
|
|
|
|
|
return {errorMsg: '获取失败,可能是链接已过期或不正确'}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkResult(ret) {
|
|
|
|
|
if (ret?.retcode === -101 || ret?.retcode === -100) {
|
|
|
|
|
return ret.retcode === -101 ? {errorMsg: '您的链接过期,请重新获取'} : {errorMsg: '链接不正确,请重新获取'}
|
|
|
|
|
}
|
|
|
|
|
if (/unknown auth appid/.test(ret?.message)) {
|
|
|
|
|
return {errorMsg: '抽卡或其他链接现已无法获取充值记录,请发送客服页面的链接!'}
|
|
|
|
|
}
|
|
|
|
|
return {errorMsg: ''}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-04 14:30:13 +08:00
|
|
|
|
/** 对原始数据进行筛选,组合 */
|
|
|
|
|
async filtrateData () {
|
2023-07-11 00:21:36 +08:00
|
|
|
|
// 由于新接口不返回uid了,所以先查询出用户信息
|
|
|
|
|
const userInfo = await this.getUserInfo()
|
|
|
|
|
if (userInfo?.errorMsg) return userInfo
|
|
|
|
|
this.#genShinId = userInfo.uid
|
2023-03-04 14:30:13 +08:00
|
|
|
|
// 获取数据
|
|
|
|
|
let isSucceed = await this.getOringinalData()
|
|
|
|
|
// 判断数据是否获取成功
|
|
|
|
|
if (isSucceed?.errorMsg) return isSucceed
|
|
|
|
|
await this.getPrimogemLog()
|
2023-07-11 00:21:36 +08:00
|
|
|
|
// 判断零氪党的情况
|
|
|
|
|
if (this.#oringinData.length === 0) {
|
|
|
|
|
return {errorMsg: '未获取到您的任何充值数据'}
|
2023-03-04 14:30:13 +08:00
|
|
|
|
}
|
|
|
|
|
// 将原始数据按id排序
|
|
|
|
|
this.#oringinData = this.#oringinData.sort((a, b) => {
|
|
|
|
|
let val1 = Number(a.id)
|
|
|
|
|
let val2 = Number(b.id)
|
|
|
|
|
if (val2 > val1) {
|
|
|
|
|
return -1
|
|
|
|
|
} else {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// 单双倍结晶数
|
|
|
|
|
const price = [680, 300, 8080, 3880, 2240, 1090, 330, 60]
|
|
|
|
|
const doublePrice = [0, 0, 12960, 6560, 3960, 1960, 600, 120]
|
|
|
|
|
let month = 0
|
|
|
|
|
let sum = 0
|
|
|
|
|
let i = -1
|
|
|
|
|
let listIndex = 0
|
|
|
|
|
let list = []
|
|
|
|
|
for (let index = 0; index < this.#oringinData.length; index++) {
|
|
|
|
|
// 如果小于零则返回
|
|
|
|
|
let num = Number(this.#oringinData[index].add_num)
|
|
|
|
|
if (num < 0) continue
|
|
|
|
|
// 获取月份
|
2023-07-11 00:21:36 +08:00
|
|
|
|
// let thisMonth = ++moment(this.#oringinData[index].time).toArray()[1]
|
|
|
|
|
// 新接口改名为:datetime
|
|
|
|
|
let thisMonth = ++moment(this.#oringinData[index].datetime).toArray()[1]
|
2023-03-04 14:30:13 +08:00
|
|
|
|
if (thisMonth !== month) {
|
|
|
|
|
i++
|
|
|
|
|
month = thisMonth
|
|
|
|
|
list[listIndex++] = {
|
|
|
|
|
month: thisMonth + '月',
|
|
|
|
|
payNum: [0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
|
}
|
|
|
|
|
} else if (!i) {
|
|
|
|
|
list[i] = {
|
|
|
|
|
month: thisMonth + '月',
|
|
|
|
|
payNum: [0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (let index = 0; index < 8; index++) {
|
|
|
|
|
if (num === price[index] || num === doublePrice[index]) {
|
|
|
|
|
list[i].payNum[index]++
|
|
|
|
|
if (num !== 680) sum += num
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
uid: this.#genShinId,
|
|
|
|
|
crystal: sum,
|
|
|
|
|
monthData: list
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
|
headers: {
|
|
|
|
|
accept: 'application/json, text/plain, */*',
|
|
|
|
|
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
|
|
|
|
'sec-fetch-dest': 'empty',
|
|
|
|
|
'sec-fetch-mode': 'cors',
|
|
|
|
|
'sec-fetch-site': 'same-site'
|
|
|
|
|
},
|
|
|
|
|
referrer: 'https://webstatic.mihoyo.com/',
|
|
|
|
|
referrerPolicy: 'strict-origin-when-cross-origin',
|
|
|
|
|
method: 'GET',
|
|
|
|
|
mode: 'cors',
|
|
|
|
|
credentials: 'include'
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-11 00:21:36 +08:00
|
|
|
|
/**
|
|
|
|
|
* 获取url
|
|
|
|
|
* @param api 原石 getPrimogemLog // 结晶 getCrystalLog // 获取用户信息 getUserInfo
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
getUrl(api = 'getCrystalLog') {
|
|
|
|
|
const baseUrl = 'https://hk4e-api.mihoyo.com/common/hk4e_self_help_query/User'
|
|
|
|
|
const isUserInfo = api === 'getUserInfo', isCrystalLog = api === 'getCrystalLog'
|
|
|
|
|
const url = isUserInfo ? '/GetUserInfo' : isCrystalLog ? '/GetCrystalLog' : '/GetPrimogemLog'
|
|
|
|
|
let params = ''
|
|
|
|
|
params += '?selfquery_type=1'
|
|
|
|
|
params += '&sign_type=2'
|
|
|
|
|
params += '&auth_appid=csc'
|
|
|
|
|
params += '&authkey_ver=1'
|
|
|
|
|
params += '&game_biz=hk4e_cn'
|
|
|
|
|
params += '&win_direction=portrait'
|
|
|
|
|
params += '&bbs_auth_required=true'
|
|
|
|
|
params += '&bbs_game_role_required=hk4e_cn'
|
|
|
|
|
params += '&app_client=bbs'
|
|
|
|
|
params += '&lang=zh-cn'
|
|
|
|
|
params += '&csc_authkey_required=true'
|
|
|
|
|
params += '&authkey=' + this.#authkey
|
|
|
|
|
params += '&page_id=1'
|
|
|
|
|
// 此条件限定只查询获取的
|
|
|
|
|
if (!isUserInfo) {
|
|
|
|
|
params += '&add_type=produce'
|
|
|
|
|
params += '&size=20'
|
|
|
|
|
// endId,外部拼接
|
|
|
|
|
params += '&end_id='
|
|
|
|
|
}
|
|
|
|
|
return baseUrl + url + params
|
|
|
|
|
// 老API:
|
|
|
|
|
// let type = api === 'getCrystalLog' ? 3 : 1
|
|
|
|
|
// return `https://hk4e-api.mihoyo.com/ysulog/api/${api}?selfquery_type=${type}&lang=zh-cn&sign_type=2&auth_appid=csc&authkey_ver=1&authkey=${this.#authkey}&game_biz=hk4e_cn&app_client=bbs&type=${type}&size=20&end_id=`
|
2023-03-04 14:30:13 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class HtmlData extends base {
|
|
|
|
|
/**
|
|
|
|
|
* @param data 数据
|
|
|
|
|
* @param data.monthData 月份数据
|
|
|
|
|
* @param data.crystal 总结晶数
|
|
|
|
|
*/
|
|
|
|
|
constructor (data = {}) {
|
|
|
|
|
super()
|
|
|
|
|
this.monthData = data.monthData
|
|
|
|
|
this.crystal = data.crystal
|
|
|
|
|
this.uid = data.uid
|
|
|
|
|
this.model = 'payLog'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crystal = 0
|
|
|
|
|
uid = ''
|
|
|
|
|
monthData = []
|
|
|
|
|
|
|
|
|
|
// 价格
|
|
|
|
|
price = [68, 30, 648, 328, 198, 98, 30, 6]
|
|
|
|
|
|
|
|
|
|
/** 柱形图数据 */
|
|
|
|
|
getBarData () {
|
|
|
|
|
return this.monthData.map(v => {
|
|
|
|
|
return {
|
|
|
|
|
type: v.month,
|
|
|
|
|
sales: v.payNum.reduce((sum, val, index) => sum + val * this.price[index], 0)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 顶部数据 */
|
|
|
|
|
getTopData (crystal = 0) {
|
|
|
|
|
const maxMonth = this.maxcConsumption()
|
|
|
|
|
const sum = this.sumConsumption()
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
title: '总消费',
|
|
|
|
|
value: '¥' + this.getBarData().reduce((sum, val) => sum + val.sales, 0)
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '总结晶',
|
|
|
|
|
value: this.crystal
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '消费最多',
|
|
|
|
|
value: maxMonth.type
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: maxMonth.type + '消费',
|
|
|
|
|
value: '¥' + maxMonth.sales
|
|
|
|
|
},
|
|
|
|
|
...sum
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 饼图数据 */
|
|
|
|
|
getPieData () {
|
|
|
|
|
const data = this.sumConsumption()
|
|
|
|
|
let pieData = []
|
|
|
|
|
data.forEach((val, index) => {
|
|
|
|
|
let value = val.value * this.price[index]
|
|
|
|
|
if (value) {
|
|
|
|
|
pieData.push({
|
|
|
|
|
value,
|
|
|
|
|
name: val.title
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return pieData
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 消费最多月 */
|
|
|
|
|
maxcConsumption () {
|
|
|
|
|
return this.getBarData().sort((a, b) => {
|
|
|
|
|
if (a.sales < b.sales) {
|
|
|
|
|
return 1
|
|
|
|
|
} else {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
})[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 每种消费的总数 */
|
|
|
|
|
sumConsumption () {
|
|
|
|
|
let sum = {
|
|
|
|
|
小月卡: 0,
|
|
|
|
|
大月卡: 0,
|
|
|
|
|
648: 0,
|
|
|
|
|
328: 0,
|
|
|
|
|
198: 0,
|
|
|
|
|
98: 0,
|
|
|
|
|
30: 0,
|
|
|
|
|
6: 0
|
|
|
|
|
}
|
|
|
|
|
// 循环sum,按照月份统计各个充值的类别总数
|
|
|
|
|
let k = Object.keys(sum).reverse()
|
|
|
|
|
this.monthData.forEach(val => {
|
|
|
|
|
val.payNum.forEach((v, i) => {
|
|
|
|
|
sum[k[i]] += v
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
// 返回title,value对象'648':123456
|
|
|
|
|
let value = Object.values(sum).reverse()
|
|
|
|
|
return k.map((val, index) => {
|
|
|
|
|
return {
|
|
|
|
|
title: val,
|
|
|
|
|
value: value[index]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function renderImg (data) {
|
|
|
|
|
const htmlData = new HtmlData(data)
|
|
|
|
|
const imgDatas = {
|
|
|
|
|
...htmlData.screenData,
|
|
|
|
|
topData: htmlData.getTopData(),
|
|
|
|
|
barData: JSON.stringify(htmlData.getBarData()),
|
|
|
|
|
pieData: JSON.stringify(htmlData.getPieData()),
|
|
|
|
|
saveId: htmlData.uid,
|
|
|
|
|
uid: htmlData.uid
|
|
|
|
|
}
|
|
|
|
|
let img = await puppeteer.screenshot('payLog', imgDatas)
|
|
|
|
|
return img
|
|
|
|
|
}
|