import { MailModel, MailType } from './../db/Mail'; import { DividendModel } from './../db/Dividend'; import { LOT_CODE_LEN, AUCTION_STAGE, AUCTION_TIME, DIVIDEND_CODE_LEN, DIVIDEND_STATUS, LOT_STATUS, MAIL_TYPE, CURRENCY_BY_TYPE, CURRENCY_TYPE, STATUS, DIVIDENDWEEKENDRATE, DIVIDENDMAXRATIO, DIVIDENDPOSITIONMAXRATIO, ROLE_RECEIVE_STATUS, AUCTION_BID_STATUS, DEBUG_MAGIC_WORD } from './../consts'; import { DividendRec, ItemReward } from "../domain/dbGeneral"; import { genCode, resResult } from '../pubUtils/util'; import { LotModel, LotParam } from '../db/Lot'; import { getZeroPointD, getTimeFunD } from '../pubUtils/timeUtil'; import { gameData, getGoodById } from '../pubUtils/data'; import { DividendParam, DividendType } from '../db/Dividend'; import { pushMail } from '../pubUtils/interface'; import { getMailContent } from './mailService'; import { pinus, BackendSession, FrontendOrBackendSession } from 'pinus'; import { participants } from './guildActivityService'; import { Member } from '../domain/battleField/guildActivity'; // ! 获取底价,假数据 export function getBasePrice(gid: number, count: number) { const good = getGoodById(gid); return (good ? good.quality * 100 : 100) * count; } // ! 获取一口价,假数据 export function getMaxPrice(gid: number, count: number) { const good = getGoodById(gid); return (good ? good.quality * 200 : 200) * count; } export function auctionStage() { const curTime = new Date().getTime(); if (curTime < todayGuildBegin().getTime()) return AUCTION_STAGE.DEFAULT; if (curTime < todayWorldBegin().getTime() && curTime > todayGuildBegin().getTime()) return AUCTION_STAGE.GUILD; if (curTime > todayWorldBegin().getTime() && curTime < todayWorldEnd().getTime()) return AUCTION_STAGE.WORLD; if (curTime > todayWorldEnd().getTime()) return AUCTION_STAGE.END; } export async function debugAuctionLots(session: FrontendOrBackendSession, begin: Date) { const serverId = session.get('serverId'); const lots = await LotModel.findWorldLotsByBegin(serverId, begin); return lots; } export async function officialAuctionLots(session: FrontendOrBackendSession, begin: Date) { const serverId = session.get('serverId'); const guildCode = session.get('guildCode'); const stage = auctionStage(); let lots = []; if (stage === AUCTION_STAGE.DEFAULT || stage === AUCTION_STAGE.GUILD) { lots = await LotModel.findGuildLotsByBegin(guildCode, begin); } else if (stage === AUCTION_STAGE.WORLD) { lots = await LotModel.findWorldLotsByBegin(serverId, begin); } return lots; } export function auctionBegin() { return getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN); } export function guildAuctionEnd() { return getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.GUILD_END_HOUR, AUCTION_TIME.GUILD_END_MIN); } export function auctionEnd() { return getTimeFunD().getAfterDayWithHour(0, AUCTION_TIME.WORLD_END_HOUR, AUCTION_TIME.WORLD_END_MIN); } export function todayGuildBegin() { return getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN); } export function yestodayGuildBegin() { return getTimeFunD(getZeroPointD()).getAfterDayWithHour(-1, AUCTION_TIME.GUILD_BEGIN_HOUR, AUCTION_TIME.GUILD_BEGIN_MIN); } export function todayWorldBegin() { return getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.WORLD_BEGIN_HOUR, AUCTION_TIME.WORLD_BEGIN_MIN); } export function todayWorldEnd() { return getTimeFunD(getZeroPointD()).getAfterDayWithHour(0, AUCTION_TIME.WORLD_END_HOUR, AUCTION_TIME.WORLD_END_MIN); } /** * @description 生成拍卖数据 * @export * @param {string} guildCode * @param {number} sourceType * @param {string} sourceCode * @param {number} serverId * @param {ItemReward[]} rewards */ export async function genAuction(guildCode: string, sourceType: number, sourceCode: string, serverId: number, rewards: ItemReward[]) { const begin = auctionBegin(); const guildEnd = guildAuctionEnd(); const end = auctionEnd(); const lotsData: LotParam[] = rewards.map(reward => { const { id, count } = reward; const code = genCode(LOT_CODE_LEN); return { auctionStage: AUCTION_STAGE.DEFAULT, sourceType, sourceCode, serverId, guildCode, code, gid: id, count, begin, end, status: LOT_STATUS.DEFAULT, maxPrice: getMaxPrice(id, count), curPrice: getBasePrice(id, count), } }); const lots = await LotModel.createRecs(lotsData); const dividendCode = genCode(DIVIDEND_CODE_LEN); const dividendData: DividendParam = { guildCode, sourceType, sourceCode, serverId, code: dividendCode, dividends: [], totalPrice: 0, begin, lots: lots.map(lot => { const { code, gid } = lot; return { code, gid, price: 0, time: guildEnd, max: false, count: lot.count } }), }; const dividend = await DividendModel.createDividend(dividendData); return { lots, dividend }; } function posDividend(totalPrice: number, roleRatio: number, totalRatio: number) { const dividend = Math.floor(totalPrice * roleRatio / totalRatio); const maxDividend = Math.floor(totalPrice * DIVIDENDMAXRATIO); return dividend <= maxDividend ? dividend : Math.floor(maxDividend * roleRatio / DIVIDENDPOSITIONMAXRATIO); } function weekendDividend(posNum: number, date: Date) { const day = date.getDay(); return (day === 0 || day === 6) ? Math.floor(posNum * DIVIDENDWEEKENDRATE) : 0; } function dividendRate(data: Member) { return gameData.guildPosition.get(data.job).sellRatio; } function totalDividendRatio(participantsData: Member[]) { const result = participantsData.reduce((sum, data) => { return sum + dividendRate(data); }, 0); return result; } export async function calculateDividend(dividend: DividendType) { if (!dividend) return null; const { code, guildCode, sourceType, sourceCode, lots, totalPrice, status, begin } = dividend; if (status === DIVIDEND_STATUS.SENT) return dividend; const calcuTotalPrice = lots.reduce((acc, lot) => { return acc + lot.price }, 0); if (calcuTotalPrice !== totalPrice) { await DividendModel.updateDividend(code, { totalPrice: calcuTotalPrice }); // 更新 totalPrice } const participantsData = await participants(guildCode, sourceType, sourceCode); const totalRatio = totalDividendRatio(participantsData); const dividends: DividendRec[] = participantsData.map(data => { const { roleId } = data; const posNum = posDividend(calcuTotalPrice, dividendRate(data), totalRatio); const weekendNum = weekendDividend(posNum, begin); return { roleId, posNum, // 职位分红 weekendNum, // 额外分红,周末 total: posNum + weekendNum, // 总分红 status: 0, // 0:未领取,1:已领取 }; }); return await DividendModel.updateDividend(code, { dividends }); } export async function startGuildAuction() { try { console.log('schedule startGuildAuction called:', new Date()); const begin = todayGuildBegin(); await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.GUILD); await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.ING); return true; } catch (e) { console.error('startGuildAuction err: ', e); return false; } } export async function startWorldAuction() { try { console.log('schedule startWorldAuction called:', new Date()); const begin = todayGuildBegin(); await LotModel.setLotSoldByBegin(begin); await LotModel.updateUnSoldLotsStageByBegin(begin, AUCTION_STAGE.WORLD); await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.END); return true; } catch (e) { console.error('startWorldAuction err: ', e); return false; } } export async function stopAuction() { try { console.log('schedule stopAuction called:', new Date()); const begin = todayGuildBegin(); await LotModel.setLotSoldByBegin(begin); await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.END); return true; } catch (e) { console.error('stopAuction err: ', e); return false; } } async function debugDividends() { const begin = yestodayGuildBegin(); const dividends = await DividendModel.findDividendsByBegin(begin); const todayBegin = todayGuildBegin(); const todayDividends = await DividendModel.findDividendsByBegin(todayBegin); return [...dividends, ...todayDividends]; } async function officialDividends() { const begin = yestodayGuildBegin(); const dividends = await DividendModel.findDividendsByBegin(begin); return dividends; } async function updateDebugDividendsStatus(status: number) { const begin = yestodayGuildBegin(); await DividendModel.updateDividendsStatus(begin, status); const todayBegin = todayGuildBegin(); await DividendModel.updateDividendsStatus(todayBegin,status); } async function updateOfficialDividendsStatus(status: number) { const begin = yestodayGuildBegin(); await DividendModel.updateDividendsStatus(begin, status); } export async function sendUngotDividendJob() { try { return await sendUngotDividend(); } catch (e) { console.error('sendUngotDividend err: ', e); return false; } } export function auctionBidStatus(roleId: string, lot: LotParam) { const { curBuyer, status } = lot; return curBuyer !== roleId ? AUCTION_BID_STATUS.RETURN : status === LOT_STATUS.SOLD || status === LOT_STATUS.MAX ? AUCTION_BID_STATUS.SUC : AUCTION_BID_STATUS.LEAD; } export function guildBidStatus(lot: { max: boolean, gid: number, count: number, price: number, dividendStatus: number }) { const { max, gid, count, price: lotPrice, dividendStatus } = lot; return max || lotPrice === getMaxPrice(gid, count) || ((dividendStatus == DIVIDEND_STATUS.END || dividendStatus == DIVIDEND_STATUS.SENT) && lotPrice !== 0) } export async function sendUngotDividend(debug = false) { try { console.log('schedule sendUngotDividend called:', new Date()); const dividends = debug === true ? await debugDividends() : await officialDividends(); const rewards = new Map(); for (let dividend of dividends) { if (!dividend.dividends || dividend.status === DIVIDEND_STATUS.SENT) continue; for (let dividendRec of dividend.dividends) { if (dividendRec.status === ROLE_RECEIVE_STATUS.YES) continue; const sum = rewards.get(dividendRec.roleId) || 0; rewards.set(dividendRec.roleId, sum + dividendRec.total); } } let mails: MailType[] = []; let pushMessage: pushMail[] = []; for (let [roleId, count] of rewards) { // TODO: 邮件类型修改 await getMailContent(roleId, MAIL_TYPE.GUILD_BOSS_REWARD, [JSON.stringify(count)], [{ id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.GOLD), count }], mails, pushMessage); } await MailModel.addMails(mails); pushMessage.forEach(({route, data, uids })=>{ pinus.app.channelService.pushMessageByUids(route, resResult(STATUS.SUCCESS, { mails: data }), uids); }); debug === true ? await updateDebugDividendsStatus(DIVIDEND_STATUS.SENT) : await updateOfficialDividendsStatus(DIVIDEND_STATUS.SENT); return true; } catch (e) { console.error('sendUngotDividend err: ', e); return false; } } /** * 获取拍卖行数据 * @param guildCode * @param session * @param magicWord */ export async function getAuction(guildCode: string, session: FrontendOrBackendSession, magicWord?: string) { const begin = todayGuildBegin(); let lots = magicWord === DEBUG_MAGIC_WORD ? await debugAuctionLots(session, begin) : await officialAuctionLots(session, begin); const dividends = await DividendModel.findGuildDividendsByBegin(guildCode, begin); return { lots, dividends }; }