Files
ZYZ/game-server/app/services/auctionService.ts
2022-04-08 20:39:01 +08:00

515 lines
21 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, DIVIDENDMAXRATIO, DIVIDENDPOSITIONMAXRATIO, ROLE_RECEIVE_STATUS, AUCTION_BID_STATUS, DEBUG_MAGIC_WORD, AUCTION_SOURCE, TA_EVENT, getAuctionSourceTypeName, PUSH_ROUTE } from './../consts';
import { DividendRec, ItemReward } from "../domain/dbGeneral";
import { genCode, getRandSingleEelm } from '../pubUtils/util';
import { LotModel, LotParam, LotType } from '../db/Lot';
import { getCurDay, getTimeFunD, getTimeFunM, nowSeconds } from '../pubUtils/timeUtil';
import { gameData, getGoodById } from '../pubUtils/data';
import { DividendParam, DividendType } from '../db/Dividend';
import { sendMailByContent } from './mailService';
import { FrontendOrBackendSession, pinus } from 'pinus';
import { participants } from './guildActivity/guildActivityService';
import { Member } from '../domain/battleField/guildActivity';
import * as dicParam from '../pubUtils/dicParam';
import { RewardInter } from '../pubUtils/interface';
import { reportTAEvent } from './sdkService';
import { getAllServers } from './redisService';
import { sendMessageToGuildWithSuc, sendMessageToServer, sendMessageToServerWithSuc } from './pushService';
// ! 获取底价,假数据
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 async function auctionStage() {
const curTime = await getCurrentTimeWithSetDay();
if (curTime < (await todayGuildBegin()).getTime()) return AUCTION_STAGE.DEFAULT;
if (curTime < (await todayWorldBegin()).getTime() && curTime > (await todayGuildBegin()).getTime()) return AUCTION_STAGE.GUILD;
if (curTime > (await todayWorldBegin()).getTime() && curTime < (await todayWorldEnd()).getTime()) return AUCTION_STAGE.WORLD;
if (curTime > (await 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 = await auctionStage();
let lots: LotType[] = [];
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 await treatLotsTime(lots);
}
export async function treatLotsTime(lots: LotType[]) {
let result: LotType[] = []
for(let lot of lots) {
result.push(await treatSingleLotTime(lot));
}
return result;
}
export async function treatSingleLotTime(lot: LotType) {
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let current = await getCurrentTime();
if(Math.floor(current/1000) != nowSeconds()) {
lot.begin.setHours(20, 20, 0, 0);
lot.end.setHours(22, 0, 0, 0);
return {...lot, begin: lot.begin, end: lot.end}
}
}
return lot
}
// 拍卖行开始时间 今天20:20
export async function auctionBegin() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 军团拍卖行结束时间 20:30
export async function guildAuctionEnd() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_CLOSE);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 拍卖行结束时间 22:00
export async function auctionEnd() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.WORLD_CLOSE);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 今天军团拍卖行开始时间 20:20
export async function todayGuildBegin() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
async function getCurrentTime() {
let now = Date.now();
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let guilds = pinus.app.getServersByType('guild');
let guild = getRandSingleEelm(guilds);
now = await pinus.app.rpc.guild.guildActivityRemote.getCurrentTime.toServer(guild.id);
}
return now
}
async function getCurrentTimeWithSetDay() {
let now = new Date();
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let guilds = pinus.app.getServersByType('guild');
let guild = getRandSingleEelm(guilds);
let currentTime = await pinus.app.rpc.guild.guildActivityRemote.getCurrentTime.toServer(guild.id);
now.setDate(new Date(currentTime).getDate());
}
return now.getTime();
}
// 明天军团拍卖行开始时间 20:20
export async function tomorrowGuildBegin() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
// console.log(hour, minute)
return <Date>getTimeFunD(now).getAfterDayAndSetHour(1, hour, minute);
}
// 明天军团拍卖行开始时间 20:20
export async function tomorrowGuildEnd() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_CLOSE);
// console.log(hour, minute)
return <Date>getTimeFunD(now).getAfterDayAndSetHour(1, hour, minute);
}
// 昨天军团拍卖行开始时间 20:20
export async function yestodayGuildBegin() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(-1, hour, minute);
}
// 今天世界拍卖行开始时间 20:40
export async function todayWorldBegin() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.WORLD_OPEN);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 今天世界拍卖行结束时间 22:00
export async function todayWorldEnd() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.WORLD_CLOSE);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 拍卖行预览时间 20:00
export async function guildAuctionPreview() {
let now = await getCurrentTime();
let { hour, minute } = gameData.auctionTime.get(AUCTION_TIME.GUILD_PREVIEW);
return <Date>getTimeFunD(now).getAfterDayAndSetHour(0, hour, minute);
}
// 演武台。8点以前是今天的8点以后是明天的
export async function getBossAuctionBegin() {
if((await getCurrentTimeWithSetDay()) < (await guildAuctionPreview()).getTime()) {
return await todayGuildBegin();
} else {
return await tomorrowGuildBegin();
}
}
// 演武台。8点以前是今天的8点以后是明天的
export async function getBossAuctionEnd() {
if((await getCurrentTimeWithSetDay()) < (await guildAuctionPreview()).getTime()) {
return await guildAuctionEnd();
} else {
return await tomorrowGuildEnd();
}
}
/**
* @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: { goods: RewardInter, basePrice: number, maxPrice: number, sort: number}[]) {
let begin = await auctionBegin();
let end = await auctionEnd();
if(sourceType == AUCTION_SOURCE.BOSS) { // 军团boss本
begin = await getBossAuctionBegin();
end = await getBossAuctionEnd();
}
const guildEnd = await guildAuctionEnd();
const lotsData: LotParam[] = rewards.map(({ goods, basePrice, maxPrice, sort }) => {
const { id, count } = goods;
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, curPrice: basePrice, prePrice: 0, sort
}
});
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, curPrice } = lot;
return { code, gid, price: curPrice, time: guildEnd, max: false, count: lot.count }
}),
};
const dividend = await DividendModel.createDividend(dividendData);
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.AUCTION_ADD, { lots });
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.DIVIDEND_ADD, { dividends: [dividend] });
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 * dicParam.GUILD_AUCTION.DIVIDEND_WEEKEND_RATE) : 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) * dicParam.GUILD_AUCTION.DIVIDEND_RATE;
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 = await todayGuildBegin();
let lots = await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.GUILD);
let dividends = await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.ING);
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let day = getCurDay();
let time = <number>getTimeFunM().getTimeWithWeek(day, 20, 20, 0);
pinus.app.rpc.guild.guildActivityRemote.setCurrentTime.broadcast(time);
await pushCurrentTime(time);
}
await pushAuctionUpdate(lots, dividends);
return true;
} catch (e) {
console.error('startGuildAuction err: ', e);
return false;
}
}
export async function sendLotsRewardToMlail(lots: LotType[]) {
let lotByRole = new Map<string, (RewardInter&{price: number})[]>();
for(let lot of lots) {
if(!lotByRole.has(lot.curBuyer)) {
lotByRole.set(lot.curBuyer, []);
}
lotByRole.get(lot.curBuyer).push({ id: lot.gid, count: lot.count, price: lot.curPrice });
}
for(let [roleId, goods ] of lotByRole) {
await sendMailByContent(MAIL_TYPE.AUTION_REWARD, roleId, { goods });
for(let { id, count, price } of goods) {
let dicGoods = gameData.goods.get(id);
reportTAEvent(roleId, TA_EVENT.AUCTION_ITEM_GET, { item_name: dicGoods?.name, item_count: count, deel_price: price });
}
}
}
export async function startWorldAuction() {
try {
console.log('schedule startWorldAuction called:', new Date());
const begin = await todayGuildBegin();
let lots = await LotModel.setLotSoldByBegin(begin); // 正在竞拍的拍品
await sendLotsRewardToMlail(lots);
lots = await LotModel.updateUnSoldLotsStageByBegin(begin, AUCTION_STAGE.WORLD);
let dividends = await DividendModel.updateDividendsStatus(begin, DIVIDEND_STATUS.END);
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let day = getCurDay();
let time = <number>getTimeFunM().getTimeWithWeek(day, 20, 40, 0);
pinus.app.rpc.guild.guildActivityRemote.setCurrentTime.broadcast(time);
await pushCurrentTime(time);
}
await pushAuctionUpdate(lots, dividends);
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 = await todayGuildBegin();
let lots = await LotModel.setLotSoldByBegin(begin); // 正在竞拍的拍品
await sendLotsRewardToMlail(lots);
lots = await LotModel.updateLotsStageByBegin(begin, AUCTION_STAGE.END);
if(dicParam.SERVER_DEBUG_MODE.CURRENT_TIME == 1) {
let day = getCurDay();
let time = <number>getTimeFunM().getTimeWithWeek(day, 22, 0, 0);
pinus.app.rpc.guild.guildActivityRemote.setCurrentTime.broadcast(time);
await pushCurrentTime(time);
}
await pushAuctionUpdate(lots, []);
return true;
} catch (e) {
console.error('stopAuction err: ', e);
return false;
}
}
// debug函数修改客户端当前时间
export async function pushCurrentTime(time: number) {
let serverlists = await getAllServers()
for(let serverId of serverlists) {
await sendMessageToServer(serverId, PUSH_ROUTE.PUSH_CURRENT_TIME, { time });
}
}
async function debugDividends() {
const begin = await yestodayGuildBegin();
const dividends = await DividendModel.findDividendsByBegin(begin);
const todayBegin = await todayGuildBegin();
const todayDividends = await DividendModel.findDividendsByBegin(todayBegin);
return [...dividends, ...todayDividends];
}
async function officialDividends() {
const begin = await yestodayGuildBegin();
const dividends = await DividendModel.findDividendsByBegin(begin);
return dividends;
}
async function updateDebugDividendsStatus(status: number) {
const begin = await yestodayGuildBegin();
await DividendModel.updateDividendsStatus(begin, status);
const todayBegin = await todayGuildBegin();
await DividendModel.updateDividendsStatus(todayBegin,status);
}
async function updateOfficialDividendsStatus(status: number) {
const begin = await 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 || ((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<string, number>();
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);
reportTAEvent(dividendRec.roleId, TA_EVENT.AUCTION_DIVIDEND, { type: getAuctionSourceTypeName(dividend.sourceType), count: dividendRec.total })
}
}
for (let [roleId, count] of rewards) {
// TODO: 邮件类型修改
await sendMailByContent(MAIL_TYPE.GUILD_DIVIDEND, roleId, {
params: [JSON.stringify(count)],
goods: [{ id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.GOLD), count }]
});
}
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 = await todayGuildBegin();
let lots = magicWord === DEBUG_MAGIC_WORD ? await debugAuctionLots(session, begin) : await officialAuctionLots(session, begin);
let dividends: DividendType[] = [];
if(guildCode) {
dividends = await DividendModel.findGuildDividendsByBegin(guildCode, begin);
}
return { lots, dividends };
}
export async function pushAuctionOver(lot: LotType) {
if(lot.auctionStage == AUCTION_STAGE.GUILD) {
await sendMessageToGuildWithSuc(lot.guildCode, PUSH_ROUTE.AUCTION_OVER, { lot });
} else if (lot.auctionStage == AUCTION_STAGE.WORLD) {
await sendMessageToServerWithSuc(lot.serverId, PUSH_ROUTE.AUCTION_OVER, { lot });
}
}
export async function pushAuctionUpdate(lots: LotType[], dividends: DividendType[]) {
const stage = await auctionStage();
if(stage === AUCTION_STAGE.DEFAULT || stage === AUCTION_STAGE.GUILD) {
let lotsResult = new Map<string, LotType[]>();
for(let lot of lots) {
if(!lotsResult.has(lot.guildCode)) {
lotsResult.set(lot.guildCode, []);
}
lotsResult.get(lot.guildCode).push(lot);
}
for(let [guildCode, lots] of lotsResult) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.AUCTION_UPDATE, { lots });
}
} else {
let lotsResult = new Map<number, LotType[]>();
for(let lot of lots) {
if(!lotsResult.has(lot.serverId)) {
lotsResult.set(lot.serverId, []);
}
lotsResult.get(lot.serverId).push(lot);
}
for(let [serverId, lots] of lotsResult) {
await sendMessageToServerWithSuc(serverId, PUSH_ROUTE.AUCTION_UPDATE, { lots });
}
}
let dividendsResult = new Map<string, DividendType[]>();
for(let dividend of dividends) {
if(!dividendsResult.has(dividend.guildCode)) {
dividendsResult.set(dividend.guildCode, []);
}
dividendsResult.get(dividend.guildCode).push(dividend);
}
for(let [guildCode, dividends] of dividendsResult) {
await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.DIVIDEND_UPDATE, { dividends })
}
}