好友:初步申请到查看链

This commit is contained in:
luying
2021-02-02 14:16:56 +08:00
parent a9e2b79c79
commit 5df5f6ed93
15 changed files with 582 additions and 16 deletions

View File

@@ -1,8 +1,13 @@
import { Application, BackendSession } from "pinus";
import { resResult, getRandEelm } from "../../../pubUtils/util";
import { STATUS, ROLE } from "../../../consts";
import { RoleModel } from "../../../db/Role";
import Role, { RoleModel, RoleType } from "../../../db/Role";
import { getBeforeDaySeconds } from "../../../pubUtils/timeUtil";
import { FriendApplyModel } from "../../../db/FriendApply";
import { FriendApplyParams, FriendListParam } from "../../../domain/roleField/friend";
import { FriendShipModel, FriendShipType } from "../../../db/FriendShip";
import { FriendRelationModel } from "../../../db/FriendRelation";
import { isRoleOnline } from "../../../services/redisService";
export default function (app: Application) {
return new FriendHandler(app);
@@ -20,10 +25,113 @@ export class FriendHandler {
const day = getBeforeDaySeconds(1);
const { lv } = await RoleModel.findByRoleId(roleId, ROLE.GET_LV);
const allList = await RoleModel.getRecommedList(lv - 5, lv + 5, day);
const allList = await RoleModel.getRecommedList(roleId, lv - 5, lv + 5, day);
let list = getRandEelm(allList, 10);
if(!list.length) list = allList;
return resResult(STATUS.SUCCESS, { list });
}
// 申请
public async applyFriend(msg: { roleIds: string[] }, session: BackendSession) {
let roleId: string = session.get('roleId');
let roleIds = msg.roleIds;
const role = await RoleModel.findByRoleId(roleId, ROLE.GET_ROLE_ID);
for(let hisRoleId of roleIds) {
await FriendApplyModel.createApply(hisRoleId, role);
}
return resResult(STATUS.SUCCESS, {
isSuccess: true
});
}
// 获取申请列表
public async getApplyList(msg: { lastApplyCode: string }, session: BackendSession) {
// TODO 分页
let roleId: string = session.get('roleId');
let list = await FriendApplyModel.getApplyList(roleId);
let result = new Array<FriendApplyParams>();
for(let apply of list) {
let friend = <RoleType>apply.friend;
let param = new FriendApplyParams(apply.applyCode, friend);
result.push(param);
}
return resResult(STATUS.SUCCESS, {
list: result
});
}
// (一键)同意/拒绝申请
public async handleApply(msg: { applyCodeList: string[], isPass: boolean }, session: BackendSession) {
let roleId: string = session.get('roleId');
let { applyCodeList, isPass } = msg;
let role = await RoleModel.findByRoleId(roleId);
let applyList = await FriendApplyModel.getApplyListByCode(applyCodeList);
let result = new Array<FriendApplyParams>();
if(isPass) {
for(let apply of applyList) {
let friend = <RoleType>apply.friend;
// TODO检查我的好友人数上限检查加过好友没有
// TODO检查对方的好友人数上限
// 创建friendShip
let friendShip = await FriendShipModel.createFriendShip([roleId, friend.roleId]);
if(!friendShip) continue;
// 创建或push我的好友relation
let result1 = await FriendRelationModel.addFriend(roleId, friend, friendShip);
// 创建或push他的好友relation
let result2 = await FriendRelationModel.addFriend(friend.roleId, role, friendShip);
}
}
// await FriendApplyModel.deleteApply(applyCodeList);
return resResult(STATUS.SUCCESS, {
list: result
});
}
// 获取好友列表
public async getFriendList(msg: { }, session: BackendSession) {
let roleId: string = session.get('roleId');
let role = await RoleModel.findByRoleId(roleId);
let list = new Array<FriendListParam>();
let myRelation = await FriendRelationModel.findFriendByRole(roleId);
let friendList = myRelation?myRelation.friends: [];
for(let friend of friendList) {
let role = <RoleType>friend.role;
let friendShip = <FriendShipType>friend.friendShip;
if(!role || !friendShip) continue;
let param = new FriendListParam(role, friendShip);
let isOnline = await isRoleOnline(role.roleId);
param.setOnline(isOnline);
let frd = FriendShipModel.getRoleSendAndReceive(roleId, [roleId, role.roleId], friendShip);
if(!frd) continue;
let { sendHeart, receiveHeart, beSentHeart } = frd;
if(sendHeart <= 1) {
param.setCanSend(true);
}
if(beSentHeart >= 1 && receiveHeart < beSentHeart) {
param.setCanReceive(true);
}
list.push(param);
}
let { friendCnt = 0, blockCnt = 0 } = role;
return resResult(STATUS.SUCCESS, {
todayReceiveCnt: 0,
todaySendCnt: 0,
list,
friendCnt, blockCnt
});
}
}

View File

@@ -29,6 +29,9 @@ import { SchoolModel } from '@db/School';
import { CeAttrNumber } from '@db/generalField';
import { smsModel } from '@db/Sms';
import { isString } from 'underscore';
import { FriendShipModel } from '@db/FriendShip';
import { FriendApplyModel } from '@db/FriendApply';
import { FriendRelationModel } from '@db/FriendRelation';
/**
* Test Service
@@ -161,6 +164,9 @@ export default class GMUsers extends Service {
await PvpDefenseModel.deleteAccount(roleId);
await ItemModel.deleteAccount(roleId);
await SchoolModel.deleteAccount(roleId);
await FriendShipModel.deleteAccount(roleId);
await FriendApplyModel.deleteAccount(roleId);
await FriendRelationModel.deleteAccount(roleId);
return ctx.service.utils.resResult(STATUS.SUCCESS);
}

34
package-lock.json generated
View File

@@ -138,6 +138,15 @@
"get-intrinsic": "^1.0.2"
}
},
"chinese-random-name": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/chinese-random-name/-/chinese-random-name-1.0.0.tgz",
"integrity": "sha512-0ZJ0LhV4O9vqas4IgQB7IDqqSovpSoHlvZo3FWXShKBfs6BS7l4uRQVqin54D4KNrG81G7cZ7p6YIpn93fo2wQ==",
"requires": {
"flatten": "^1.0.2",
"random-to": "0.0.2"
}
},
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@@ -234,6 +243,11 @@
"is-symbol": "^1.0.2"
}
},
"flatten": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
"integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg=="
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
@@ -477,6 +491,21 @@
"sliced": "1.0.1"
}
},
"mongoose-lean-getters": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/mongoose-lean-getters/-/mongoose-lean-getters-0.1.2.tgz",
"integrity": "sha512-OIgQP4POwsWE3oItEIohbvb0BvqW2SV248WU6Hh7mlEX1isPTCrfayZUgVoBkd9L/q42FTza+NZ4qOXtJSzEOQ==",
"requires": {
"mpath": "0.5.x"
},
"dependencies": {
"mpath": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.2.tgz",
"integrity": "sha512-NOeCoW6AYc3hLi30npe7uzbD9b4FQZKH40YKABUCCvaKKL5agj6YzvHoNx8jQpDMNPgIa5bvSZQbQpWBAVD0Kw=="
}
}
},
"mongoose-lean-virtuals": {
"version": "0.7.6",
"resolved": "https://registry.npmjs.org/mongoose-lean-virtuals/-/mongoose-lean-virtuals-0.7.6.tgz",
@@ -685,6 +714,11 @@
"resolved": "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz",
"integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I="
},
"random-to": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/random-to/-/random-to-0.0.2.tgz",
"integrity": "sha1-RoO58lfPuWSqPXsyPgCKIMboWnY="
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",

View File

@@ -1,8 +1,10 @@
export enum ROLE {
// 玩家列表显示基础数据
SHOW_SIMPLE = 'roleId roleName ce headHid sHid lv title job quitTime',
SHOW_SIMPLE = 'roleId roleName ce headHid sHid lv title job quitTime vLv guildName',
HANDLE_APPLY = 'roleId friendCnt lv',
GET_LV = 'lv'
GET_LV = 'lv',
GET_ROLE_ID = 'lv'
};

View File

@@ -324,6 +324,8 @@ export const FILENAME = {
DIC_ARMY_DEVELOPMENTCONSUME: 'dic_army_developmentConsume',
DIC_ARMY_BOSS_RANK_REWARD: 'dic_army_bossrankReward',
DIC_ARMY_DONATE_BOX_REWARD: 'dic_army_donateBoxReward',
DIC_ROLE_FRIEND: 'dic_zyz_friends',
DIC_ROLE_FRIEND_LEVEL: 'dic_zyz_closelevel'
}
export const WAR_RELATE_TABLES = [

70
shared/db/FriendApply.ts Normal file
View File

@@ -0,0 +1,70 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typegoose/typegoose';
import Role, { RoleType } from './Role';
import { genCode } from '../pubUtils/util';
import { ROLE } from '../consts';
/**
* 好友系统关系表
*/
@index({ roleId: 1, createdAt: -1 })
export default class FriendApply extends BaseModel {
@prop({ required: true, default: '' })
applyCode: string; // 唯一code
@prop({ required: true, default: '' })
roleId: string; // 操作玩家本人id
@prop({ required: true, default: '' })
frdRoleId: string; // 好友id
@prop({ required: true, ref: 'Role', type: mongoose.Schema.Types.ObjectId })
friend: Ref<Role>;
// 创建申请
public static async createApply(roleId: string, friend: RoleType) {
const applyCode = genCode(10);
const result: FriendApplyType = await FriendApplyModel.findOneAndUpdate({ roleId, frdRoleId: friend.roleId },
{
$set: { roleId, frdRoleId: friend.roleId, friend: friend._id },
$setOnInsert: { applyCode }
}, { upsert: true, new: true }).lean();
return result;
}
// 获取列表
public static async getApplyList(roleId: string) {
const list: FriendApplyType[] = await FriendApplyModel.find({ roleId }, { _id: 0 })
// .select(select)
.populate('friend', ROLE.SHOW_SIMPLE, 'Role')
.lean({ getters: true });
return list;
}
// 根据applyCode获得申请
public static async getApplyListByCode(applyCodeList: string[]) {
const list: FriendApplyType[] = await FriendApplyModel.find({ applyCode: { $in: applyCodeList } }, { _id: 0 })
// .select(select)
.populate('friend', ROLE.SHOW_SIMPLE, 'Role')
.lean({ getters: true });
return list;
}
// 删除申请
public static async deleteApply(applyCodeList: string[]) {
const result = await FriendApplyModel.deleteMany({ applyCode: { $in: applyCodeList } });
return result;
}
public static async deleteAccount(roleId: string) {
let result = await FriendApplyModel.deleteMany({ $or: [{ roleId: roleId }, { frdRoleId: roleId }] });
return result;
}
}
export const FriendApplyModel = getModelForClass(FriendApply);
export interface FriendApplyType extends Pick<DocumentType<FriendApply>, keyof FriendApply> { };

View File

@@ -1,7 +1,8 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typegoose/typegoose';
import Role from './Role';
import FriendShip from './FriendShip';
import Role, { RoleType } from './Role';
import FriendShip, { FriendShipType } from './FriendShip';
import { ROLE } from '../consts';
class Relation {
@prop({ required: true, default: '' })
@@ -26,9 +27,36 @@ export default class FriendRelation extends BaseModel {
@prop({ required: true, default: [], type: Relation, _id: false })
blacklist: Relation[]; // 黑名单列表
// 创建
public static async addFriend(roleId: string, friend: RoleType, friendShip: FriendShipType) {
const result: FriendRelationType = await FriendRelationModel.findOneAndUpdate(
{ roleId }, {
$setOnInsert: { blacklist: [] },
$push: { friends: { roleId: friend.roleId, role: friend._id, friendShip: friendShip._id } }
}, { upsert: true, new: true }).lean();
return result;
}
// 获取列表
public static async findFriendByRole(roleId: string) {
const result: FriendRelationType = await FriendRelationModel.findOne({ roleId })
.populate('friends.role', ROLE.SHOW_SIMPLE, 'Role')
.populate('friends.friendShip', null, 'FriendShip')
.lean({ getters: true, virtuals: true });
return result;
}
public static async deleteAccount(roleId: string) {
let result = await FriendRelationModel.deleteMany({ roleId });
await FriendRelationModel.updateMany({}, { $pull: { friends: { roleId }, blacklist: { roleId } }});
return result;
}
}
export const FriendRelationModel = getModelForClass(FriendRelation);
export interface FriendRelationType extends Pick<DocumentType<FriendRelation>, keyof FriendRelation>{};
export interface FriendRelationType extends Pick<DocumentType<FriendRelation>, keyof FriendRelation> { };

View File

@@ -1,5 +1,6 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { getFriendLvByExp } from '../pubUtils/data';
/**
* 好友直接的亲密值
@@ -28,15 +29,63 @@ export default class FriendShip extends BaseModel {
refTime: Date;
@prop({ required: true, default: 0 })
frdValue: number; // 两人之间的亲密值
friendValue: number; // 两人之间的亲密值
public get frdLv() { // 亲密等级
return this.frdValue;
public get friendLv() { // 亲密等级
return getFriendLvByExp(this.friendValue);
}
private static getRoleIds(roleIds: string[]) {
roleIds.sort();
return {
roleIdA: roleIds[0],
roleIdB: roleIds[1],
roleIds: roleIds.join('|')
}
}
public static getRoleSendAndReceive(myRoleId: string, roleIds: string[], friendShip: FriendShipType) {
let { roleIdA, roleIdB } = this.getRoleIds(roleIds);
if (roleIdA == myRoleId) {
return {
sendHeart: friendShip.sendHeartCntA,
beSentHeart: friendShip.sendHeartCntB,
recieveHeart: friendShip.receiveHeartCntA,
beReceivedHeart: friendShip.receiveHeartCntB
}
} else if (roleIdB == myRoleId) {
return {
sendHeart: friendShip.sendHeartCntB,
beSentHeart: friendShip.sendHeartCntA,
receiveHeart: friendShip.receiveHeartCntB,
beReceivedHeart: friendShip.receiveHeartCntA
}
} else {
return false
}
}
// 创建
public static async createFriendShip(roleIdList: string[]) {
if (roleIdList.length != 2) return false;
let { roleIdA, roleIdB, roleIds } = this.getRoleIds(roleIdList);
console.log(roleIdA, roleIdB, roleIds)
const doc = new FriendShipModel();
const update = Object.assign(doc.toJSON(), { roleIdA, roleIdB, roleIds });
const result: FriendShipType = await FriendShipModel.findOneAndUpdate({ roleIds }, update, { upsert: true, new: true }).lean();
return result;
}
public static async deleteAccount(roleId: string) {
let result = await FriendShipModel.deleteMany({ $or: [{ roleIdA: roleId }, { roleIdB: roleId }] });
return result;
}
}
export const FriendShipModel = getModelForClass(FriendShip);
export interface FriendShipType extends Pick<DocumentType<FriendShip>, keyof FriendShip>{};
export interface FriendShipType extends Pick<DocumentType<FriendShip>, keyof FriendShip> { };

View File

@@ -437,7 +437,7 @@ export default class Role extends BaseModel {
// 获取未加入公会且登录时间在三天内的人
public static async getInviteList(time: number, serverId: number) {
const result = await RoleModel.find({ quitTime: { $gt: time }, hasGuild: false, serverId })
const result = await RoleModel.find({ loginTime: { $gt: time }, hasGuild: false, serverId })
.select('roleId roleName ce headHid sHid lv title job quitTime')
.sort({quitTime: -1, lv: -1, ce: -1})
.limit(100).lean({getters: true});
@@ -445,8 +445,8 @@ export default class Role extends BaseModel {
}
// 获取好友推荐列表
public static async getRecommedList(minLv: number, maxLv: number, time: number) {
const result = await RoleModel.find({ quitTime: { $gt: time }, lv: { $gte: minLv, $lte: maxLv } }, { _id: 0 })
public static async getRecommedList(roleId: string, minLv: number, maxLv: number, time: number) {
const result = await RoleModel.find({ loginTime: { $gt: time }, lv: { $gte: minLv, $lte: maxLv }, roleId: { $ne: roleId } }, { _id: 0 })
.select(ROLE.SHOW_SIMPLE)
.sort({quitTime: -1, lv: -1, ce: -1})
.limit(100).lean({ getters: true });

View File

@@ -0,0 +1,61 @@
import { RoleType } from "../../db/Role";
import { FriendShipType } from "../../db/FriendShip";
export class FriendParams {
roleId: string;
roleName: string;
lv: number;
headHid: number;
sHid: number;
ce: number;
title: number;
constructor(role: RoleType) {
this.roleId = role.roleId;
this.roleName = role.roleName;
this.lv = role.lv;
this.headHid = role.headHid;
this.sHid = role.sHid;
this.ce = role.ce;
this.title = role.title;
}
}
export class FriendApplyParams extends FriendParams {
applyCode: string;
constructor(applyCode: string, role: RoleType) {
super(role);
this.applyCode = applyCode;
}
}
export class FriendListParam extends FriendParams {
guildName: string = "";
isOnline: boolean = false;
quitTime: number = 0;
friendValue: number = 0;
friendLv: number = 1;
canReceive: boolean = false;
canSend: boolean = false;
constructor(role: RoleType, friendship: FriendShipType) {
super(role);
if(role.guildName) this.guildName = role.guildName;
this.quitTime = role.quitTime;
this.friendLv = friendship.friendLv;
this.friendValue = friendship.friendValue;
}
setOnline(isOnline: boolean) {
this.isOnline = isOnline;
}
setCanReceive(canReceive: boolean) {
this.canReceive = canReceive;
}
setCanSend(canSend: boolean) {
this.canSend = canSend;
}
}

View File

@@ -56,6 +56,9 @@ import { RewardInter } from "./interface";
import { dicArmyDevelopConsume } from '../pubUtils/dictionary/DicArmyDevelopConsume';
import { dicArmyBossRank } from '../pubUtils/dictionary/DicArmyBossRank';
import { dicArmyDonate } from '../pubUtils/dictionary/DicArmyDonateBoxReward';
import { dicRoleFriend } from "./dictionary/DicRoleFriend";
import { dicRoleFriendLv } from "./dictionary/DicRoleFriendLv";
export const gameData = {
blurprtCompose: dicBlueprtCompose,
blueprtPossibility: dicBlueprtPossibility,
@@ -134,6 +137,8 @@ export const gameData = {
armyDevelopConsume: dicArmyDevelopConsume,
armyBossRank: dicArmyBossRank,
armyDonateBox: dicArmyDonate,
roleFriend: dicRoleFriend,
roleFriendLv: dicRoleFriendLv,
};
// 在此提供一些原先在gamedata中提供的方法以便更方便获取gameData数据
@@ -398,3 +403,12 @@ export function getArmyDonateBoxBaseById(id: number) {
export function getArmyWishPoolBaseByLv(lv: number) {
return gameData.armyWishPool.get(lv);
}
export function getFriendLvByExp(exp: number) {
let resultLv = 1;
for(let [lv, {value}] of gameData.roleFriendLv) {
resultLv = lv;
if(exp < value) break;
}
return resultLv;
}

View File

@@ -0,0 +1,30 @@
// 好友表
import { readJsonFile } from '../util'
import { FILENAME } from '../../consts'
export interface DicRoleFriend {
// id
readonly id: number;
// 主公等级
readonly lv: number;
// 好友数量
readonly frdCnt: number;
// 每日可收取好友情谊点上限
readonly receiveMax: number;
// 每日可赠送好友情谊点上限
readonly sendMax: number;
}
const str = readJsonFile(FILENAME.DIC_ROLE_FRIEND);
let arr = JSON.parse(str);
export const dicRoleFriend = new Map<number, DicRoleFriend>();
arr.forEach(o => {
dicRoleFriend.set(o.lv, o);
});
arr = undefined;

View File

@@ -0,0 +1,28 @@
// 亲密度等级表
import { readJsonFile } from '../util'
import { FILENAME } from '../../consts'
export interface DicRoleFriendLv {
// id
readonly id: number;
// 亲密度等级
readonly lv: number;
// 亲密度值
readonly value: number;
// 寻宝加成
readonly comBattleAdd: number;
}
const str = readJsonFile(FILENAME.DIC_ROLE_FRIEND_LEVEL);
let arr = JSON.parse(str);
export const dicRoleFriendLv = new Map<number, DicRoleFriendLv>();
arr.forEach(o => {
dicRoleFriendLv.set(o.lv, o);
});
arr = undefined;

View File

@@ -0,0 +1,62 @@
[
{
"id": 1,
"lv": 1,
"value": 50,
"comBattleAdd": 2
},
{
"id": 2,
"lv": 2,
"value": 100,
"comBattleAdd": 4
},
{
"id": 3,
"lv": 3,
"value": 160,
"comBattleAdd": 6
},
{
"id": 4,
"lv": 4,
"value": 230,
"comBattleAdd": 8
},
{
"id": 5,
"lv": 5,
"value": 310,
"comBattleAdd": 10
},
{
"id": 6,
"lv": 6,
"value": 400,
"comBattleAdd": 13
},
{
"id": 7,
"lv": 7,
"value": 500,
"comBattleAdd": 16
},
{
"id": 8,
"lv": 8,
"value": 610,
"comBattleAdd": 19
},
{
"id": 9,
"lv": 9,
"value": 730,
"comBattleAdd": 22
},
{
"id": 10,
"lv": 10,
"value": 860,
"comBattleAdd": 25
}
]

View File

@@ -0,0 +1,72 @@
[
{
"id": 1,
"lv": 20,
"frdCnt": 30,
"receiveMax": 30,
"sendMax": 30
},
{
"id": 2,
"lv": 30,
"frdCnt": 40,
"receiveMax": 35,
"sendMax": 35
},
{
"id": 3,
"lv": 40,
"frdCnt": 50,
"receiveMax": 40,
"sendMax": 40
},
{
"id": 4,
"lv": 50,
"frdCnt": 60,
"receiveMax": 45,
"sendMax": 45
},
{
"id": 5,
"lv": 55,
"frdCnt": 70,
"receiveMax": 45,
"sendMax": 45
},
{
"id": 6,
"lv": 60,
"frdCnt": 80,
"receiveMax": 50,
"sendMax": 50
},
{
"id": 7,
"lv": 65,
"frdCnt": 90,
"receiveMax": 50,
"sendMax": 50
},
{
"id": 8,
"lv": 70,
"frdCnt": 100,
"receiveMax": 60,
"sendMax": 60
},
{
"id": 9,
"lv": 75,
"frdCnt": 120,
"receiveMax": 60,
"sendMax": 60
},
{
"id": 10,
"lv": 80,
"frdCnt": 150,
"receiveMax": 70,
"sendMax": 70
}
]