utils_localDatabase.js

const fs = require('fs');
const path = require('path');
const axios = require('axios');
const moment = require('moment-timezone');

function getFilePath(fileName) {
    if (!fileName) throw Error('getFilePath Error => missing fileName');
    const osType = process.platform;
    if (osType === 'win32') {
        var rootPath = path.join(__dirname, '..', '/');
        var fullPath = path.join(rootPath, `/media/${fileName}.json`);
    } else if (osType === 'linux') {
        var fullPath = `/media/${fileName}.json`
    } else {
        console.log(`当前系统类型为 ${osType}`);
        throw Error('getFilePath Error => unknown osType');
    }
    return fullPath
}
/**
 * 执行 GraphQL 查询。
 * @async
 * @param {string} queryStr - GraphQL 查询字符串。
 * @param {Object} variables - GraphQL 查询变量。
 * @param {string} [operate=null] - GraphQL 操作名称。
 * @param {string} [auth=null] - 授权令牌。
 * @returns {Promise<Object>} - GraphQL 查询结果。
 * @description
 * gql数据库
 */
const myGQL = async (queryStr, variables, operate = null, auth = null) => {
    let res = null
    await axios({
        url: 'https://kvu5jiwt70.execute-api.cn-northwest-1.amazonaws.com.cn/graphql',
        method: 'post',
        headers: {
            'accept': 'application/json',
            'content-type': 'application/json',
            "x-api-key": "172df9f6-2241-4f60-a4bb-b54847d5cdb0",
            "authorization": auth ? "Bearer " + auth : ""
        },
        data: {
            query: queryStr,
            operationName: operate,
            variables: variables
        },
        timeout: 60000
    }).then((v) => {
        if (v.data.data) res = v.data.data
        else res = v.data
    }).catch((err) => {
        console.log(err.response.data)
        console.log('gql请求报错', err.message)
    });
    return res
}

/**
 * 添加或更新数据库内容。
 * @async
 * @param {string} fileName - 文件名。
 * @param {string} type - 操作类型('add', 'delete', 'set')。
 * @param {string} dataType - 数据类型。
 * @param {Object} newData - 新数据或更新数据。
 * @description
 * 添加数据库内容
 */
const dataControler = async (fileName, type, dataType, newData) => {
    let fullPath = getFilePath(fileName);
    let dataList = require(fullPath);
    let oldData = dataList[dataType]
    switch (type) {
        case 'add':
            if (dataType === "dailyFollow") dataList[dataType] = oldData + 1
            else dataList[dataType] = oldData.concat(newData)
            break;
        case 'delete':
            {
                let keys = Object.keys(newData);
                console.log('对象大小', keys.length);
                let ind;
                if (keys.length > 1) ind = oldData.findIndex(v => (v[keys[0]] === newData[keys[0]]) && (v[keys[1]] === newData[keys[1]]));
                else ind = oldData.findIndex(v => v[keys[0]] === newData[keys[0]]);

                if (ind > -1) dataList[dataType].splice(ind, 1);
                else console.log('未找到该数据,请检查参数', ind);

                break;
            }
        case 'set':
            if (dataType === "dailyFollow") dataList[dataType] = newData
            else {
                let key = newData.key
                let ind = oldData.findIndex(v => v[key] === newData[key]);
                delete newData.key
                dataList[dataType][ind] = Object.assign({}, oldData[ind], newData);;
            }
            break;
        default:
            break;
    }
    fs.writeFileSync(fullPath, JSON.stringify(dataList) + '\n');
}
/**
 * 查询数据库内容。
 * @async
 * @param {string} fileName - 文件名。
 * @param {string} dataType - 数据类型。
 * @param {Object} query - 查询条件。
 * @returns {Promise<Object>} - 查询结果。
 * @description
 * 查询数据库内容
 */
const dataGetter = async (fileName, dataType, query) => {
    let fullPath = getFilePath(fileName);
    let dataList = require(fullPath);
    let oldData = dataList[dataType]
    if (dataType === "dailyFollow") return oldData
    else {
        if (!query.key2) {
            let res = oldData.find(v => v[query.key] === query.value);
            return res
        }
        else {
            let res = oldData.find(v => (v[query.key] === query.value) && (v[query.key2] === query.value2));
            return res
        }
    }
}

/**
 * 检查文件是否存在,如果不存在则创建文件,并初始化数据结构。
 * @async
 * @param {string} fileName - 文件名。
 * @description
 * 检查文件
 */
const checkFile = async (fileName) => {
    const fullPath = getFilePath(fileName);
    try {
        let data = require(fullPath);
        if (!data.userList) {
            console.log('没有userList,创建userList');
            data.userList = [];
            fs.writeFileSync(fullPath, JSON.stringify(data) + '\n');
        }
        if (!data.videoList) {
            console.log('没有videoList,创建videoList');
            data.videoList = [];
            fs.writeFileSync(fullPath, JSON.stringify(data) + '\n');
        }
        if (data.dailyFollow === undefined) {
            console.log('没有dailyFollow,创建dailyFollow');
            data.dailyFollow = 0;
            fs.writeFileSync(fullPath, JSON.stringify(data) + '\n');
        }

    }
    catch {
        console.log('没有data文件,创建文件');
        fs.writeFileSync(fullPath, JSON.stringify({
            "userList": [],
            "videoList": [],
            "dailyFollow": 0
        }) + '\n');
    }
}

/**
 * 创建或更新用户信息。
 * @async
 * @param {string} platformName - 平台名称。
 * @param {Object} userInfo - 用户信息。
 * @param {boolean} [follow=false] - 是否关注用户。
 * @description
 * 创建或新增用户
 */
const createUser = async (platformName, userInfo, follow) => {
    userInfo.platform = platformName
    userInfo.product = 'palpalcloud'
    if (follow) userInfo.follow = follow
    if (typeof userInfo.otherInfo !== 'string') {
        let str = ''
        for (let i = 0; i < userInfo.otherInfo.length; i++) {
            if (i < userInfo.otherInfo.length - 1) str += userInfo.otherInfo[i] + '###';
            else str += userInfo.otherInfo[i];
        }
        userInfo.otherInfo = str
    }
    delete userInfo.id

    let update = await myGQL(`
    mutation updateUser(
        $name: String!
        $platform: String!
        $product: String!
        ${userInfo.data ? "$data: String!" : ""}
        ${userInfo.intro ? "$intro: String!" : ""}
        ${userInfo.works ? "$works: String!" : ""}
        ${userInfo.otherInfo ? "$otherInfo: String!" : ""}
        ${follow ? "$follow: String!" : ""}
      ) {
        updateUser(
          name: $name
          platform: $platform
          product: $product
          ${userInfo.data ? "data: $data" : ""}
          ${userInfo.intro ? "intro: $intro" : ""}
          ${userInfo.works ? "works: $works" : ""}
          ${userInfo.otherInfo ? "otherInfo: $otherInfo" : ""}
          ${follow ? "follow: $follow" : ""}
        )
      }
    `, userInfo, 'updateUser')

    // console.log(update);
}

/**
 * 记录已访问过的视频信息。
 * @async
 * @param {string} fileName - 文件名。
 * @param {Object} videoInfo - 视频信息。
 * @returns {Promise<string>} - 返回视频检查结果,可能是 'over', 'haveVideo' 或其他错误信息。
 * @description
 * 记录已访问过的视频
 */
const visitedVideo = async (fileName, videoInfo) => {
    let video = await myGQL(`
    mutation checkVideo($name: String!, $platform: String!, $product: String!) {
        checkVideo(name: $name, platform: $platform, product: $product)
    }
    `, {
        name: videoInfo.title + '###' + videoInfo.user,
        platform: fileName,
        product: "palpalcloud"
    }, 'checkVideo')

    if (video?.checkVideo === 'Video Created') {
        return 'over'
    }
    else if (video?.checkVideo === 'Video Exists') {
        return 'haveVideo'
    }
    else {
        console.log('查询video结果报错', video);
    }
}

/**
 * 查询数据库中的特定内容。
 * @async
 * @param {string} fileName - 数据文件的名称。
 * @param {string} param - 数据类型参数。
 * @param {Object} info - 查询信息。
 * @returns {Promise<Object>} - 查询结果。
 * @description
 * 查询内容
 */
const getInfo = async (fileName, param, info) => {
    let result = await dataGetter(fileName, param, info);
    return result
}

/**
 * 清除每日关注数。
 * @async
 * @param {string} fileName - 数据文件的名称。
 * @returns {Promise<Object>} - 操作结果,主键为 "main"。
 * @description
 * 清除每日
 */
const clearDailyFollow = async (fileName) => {
    try {
        console.log('~~~清除每日关注~~~');
        await dataControler(fileName, 'set', 'dailyFollow', 0)
        return {
            "main": "over"
        }
    }
    catch (err) {
        console.log(err);
        return {
            "main": "error"
        }
    }

}

/**
 * 批量增加每日关注数。
 * @async
 * @param {string} fileName - 数据文件的名称。
 * @param {number} num - 要增加的数值。
 * @description
 * 批量加每日
 */
const addDailyFollow = async (fileName, num) => {
    try {
        let result = await dataGetter(fileName, 'dailyFollow');
        await dataControler(fileName, 'set', 'dailyFollow', result + num);
    }
    catch (err) {
        console.log('增加每日关注报错', err.message);
    }
}

function checkIfOneDayPassed(lastRunTime) {
    const currentTime = new Date().getTime();
    const oneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
    if (currentTime - lastRunTime >= oneDay) {
        console.log('~~~已经过去了一天~~~');
        return true
    }
    else return false
}

async function getTimeAndWait(hour, minute1, minute2, wait = 150) {
    // 获取当前时间的时间戳 
    const timestamp = Date.now();
    // 使用moment-timezone将时间戳转换为UTC+8时区的时间 
    const dateUtcPlus8 = moment(timestamp).tz('Asia/Shanghai');
    const hours = dateUtcPlus8.hour();
    const minutes = dateUtcPlus8.minute();

    if (hours === hour && minutes >= minute1 && minutes <= minute2) {
        let hoursStr = hours < 10 ? '0' + hours : hours;
        let minutesStr = minutes < 10 ? '0' + minutes : minutes;
        console.log(`当前时间: ${hoursStr}:${minutesStr}`, `预计等待: ${wait}分`);

        for (let i = 0; i < wait; i++) {
            await new Promise(resolve => setTimeout(resolve, 60 * 1000));
            if (i % 2 === 0) console.log(`等待中... 第${i + 1}分钟`);
        }
        console.log('等待结束');
    }
}

const autoClearDailyFollow = async (fileName) => {
    const fullPath = getFilePath(`${fileName}_timer`);
    try {
        let data = require(fullPath);
        if (!data.lastSaveTime) {
            data.lastSaveTime = new Date().getTime();
            fs.writeFileSync(fullPath, JSON.stringify(data) + '\n');
        }
        else {
            let oneDayPass = checkIfOneDayPassed(data.lastSaveTime)
            if (oneDayPass) {
                await clearDailyFollow(fileName)
                data.lastSaveTime = new Date().getTime();
                fs.writeFileSync(fullPath, JSON.stringify(data) + '\n');
            }
        }
    }
    catch {
        console.log('没有时间检测文件,创建文件');
        await clearDailyFollow(fileName)
        fs.writeFileSync(fullPath, JSON.stringify({
            lastSaveTime: new Date().getTime()
        }) + '\n');
    }
}

module.exports = {
    checkFile: checkFile,
    createUser: createUser,
    visitedVideo: visitedVideo,
    getInfo: getInfo,
    clearDailyFollow: clearDailyFollow,
    addDailyFollow: addDailyFollow,
    autoClearDailyFollow: autoClearDailyFollow
}