Files
ZYZ/shared/db/User.ts

338 lines
13 KiB
TypeScript
Raw Permalink 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 { COUNTER, ENCRYPT_KEY, ENCRYPT_IV } from './../consts';
import { CounterModel } from './Counter';
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { genCode, aesEncryptcfb, aesDecryptcfb } from '../pubUtils/util';
import { LoginValidateData37, ChannelInfo } from '../domain/sdk';
import { SearchUserParam } from '../domain/backEndField/search';
const bcrypt = require('bcrypt');
const SALT_WORK_FACTOR = 5
class PlatForm {
@prop({ required: true })
platform: string; // 平台ios, android, web, pc
@prop({ required: true })
unionId: string; // 用户标识
}
/**
* 用户字段接口
*/
@index({ tel: 1 })
@index({ uid: 1 })
@index({ userCode: 1 })
@index({ channelId: 1 })
@index({ token: 1 })
export default class User extends BaseModel {
@prop({ required: true })
uid: number;
@prop({ required: true })
username: string;
@prop({ required: true })
userCode: string; // 用户唯一字符串标识
@prop({ required: true })
token: string;
@prop({ required: true })
channelType: string; // 渠道类型
@prop({ required: true })
channelId: string; // 渠道类型
@prop({ required: true, _id: false })
channelInfo: LoginValidateData37; // 渠道数据
@prop({ required: true, set: (val: string) => aesEncryptcfb(val, ENCRYPT_KEY, ENCRYPT_IV), get: (val: string) => val?aesDecryptcfb(val, ENCRYPT_KEY, ENCRYPT_IV):'' })
tel: string; // 账号
@prop({ required: true, default: '' })
telHash: string;
@prop({ required: true })
password: string;
@prop({ required: true })
salt: string;
// 游客相关
@prop({ required: false, default: false })
isGuest: boolean; // 是否是游客
@prop({ required: false })
guestId: string; // 游客id
@prop({ required: false, default: 0 })
guestTime: number; // 游客体验时间
// 防沉迷相关
@prop({ required: false, default: true })
hasAuthenticated: boolean; // 是否是已认证
@prop({ required: false, default: '1990-01-01' })
birthday: string; // 生日年月
// 暂时不使用内部防沉迷
// @prop({ required: false, default: false })
// hasAuthenticated: boolean; // 是否是已认证
// @prop({ required: false })
// birthday: string; // 生日年月
@prop({ required: false })
pi: string; // 已通过实名认证用户唯一标识
@prop({ required: false, default: 0 })
todayPlayTime: number; // 今日游戏时间
@prop({ required: false })
playTime: number; // 总游戏时间
@prop({ required: false, default: false })
hasSetPw: boolean; // 是否设置过密码
// 最后登录 IP
@prop({ required: false })
ip: string;
@prop({ required: true })
lastLoginTime: Date;
@prop({ required: true })
createTime: Date;
@prop({ required: true })
platform: string;
@prop({ required: true, default: [], type: PlatForm })
platforms: PlatForm[];
@prop({ required: true, type: String, default: [], _id: false })
device: string[];
@prop({ required: true, _id: false })
deviceId: string;
@prop({ required: true, default: 'com.bantu.zyz' })
pkgName: string;
// 服务器类型official, channel, ios, oversea
@prop({ required: true, default: 'official' })
serverType: string;
// 账号是否被屏蔽
@prop({ required: true, default: false })
blocked: boolean;
// 个推CID
@prop({ required: false, default: "" })
getuiCID: string;
public static async createUser(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, guestTime: number = 0, ip: string = '') {
let _tel = aesEncryptcfb(tel, ENCRYPT_KEY, ENCRYPT_IV);
const curTime: Date = new Date();
const uid = await CounterModel.getNewCounter(COUNTER.UID);
const userCode = genCode(8);
const doc = new UserModel();
let update = {};
update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, isGuest, token, lastLoginTime: curTime, guestTime, ip, deviceId });
if (isGuest) update["guestId"] = _tel;
delete update["device"];
delete update["_id"];
const user: UserType = await UserModel.findOneAndUpdate({ tel }, { $setOnInsert: update, $addToSet: { device: deviceId } }, { upsert: true, new: true }).lean({ getters: true });
return user;
}
public static async createUserWithChannel(channelId: string, channelType: string, channelInfo: ChannelInfo, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string = '') {
const curTime: Date = new Date();
const uid = await CounterModel.getNewCounter(COUNTER.UID);
const userCode = genCode(8);
const doc = new UserModel();
let update = {};
update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, token, lastLoginTime: curTime, ip, channelInfo, channelType, deviceId });
delete update["device"];
delete update["_id"];
const user: UserType = await UserModel.findOneAndUpdate({ channelId }, { $setOnInsert: update, $addToSet: { device: deviceId } }, { upsert: true, new: true }).lean({ getters: true });
return user;
}
public static async getLastDeviceGuest(deviceId: string, token: string) {
const user: UserType = await UserModel.findOneAndUpdate({ device: { $elemMatch: { $eq: deviceId } }, isGuest: true }, { token }, { new: true }).sort({ createTime: -1 }).lean({ getters: true });
return user;
}
public static async updateToken(tel: string, token: string, deviceId: string, ip: string) {
const curTime: Date = new Date();
let user = await UserModel.findOneAndUpdate({ tel }, { $set: { token, lastLoginTime: curTime, ip }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true });
return user;
}
public static async updateTokenByChannel(channelId: string, token: string, deviceId: string, ip: string, channelInfo: ChannelInfo) {
const curTime: Date = new Date();
let user = await UserModel.findOneAndUpdate({ channelId }, { $set: { token, lastLoginTime: curTime, ip, channelInfo, deviceId }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true });
return user;
}
public static async updateGetuiCID(tel: string, cid: string) {
let user = await UserModel.findOneAndUpdate({ tel }, { $set: { getuiCID: cid } }, { new: true }).lean({ getters: true });
return user;
}
public static async updateGetuiCIDByChannel(channel: string, cid: string) {
let user = await UserModel.findOneAndUpdate({ channel }, { $set: { getuiCID: cid } }, { new: true }).lean({ getters: true });
return user;
}
public static async createOrUpdate(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string) {
// console.log(tel);
let oldUser: UserType = await UserModel.findOne({ tel }).lean({ getters: true });
if (!oldUser) {
let user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId, 0, ip);
return { isCreate: true, user, deviceId: null};
} else {
let user = await UserModel.updateToken(tel, token, deviceId, ip);
return { isCreate: true, user, deviceId: oldUser.deviceId};
}
}
public static async createOrUpdateChannelUser(channelId: string, channelType: string, channelInfo: ChannelInfo, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, ip: string) {
// console.log(tel);
let oldUser: UserType = await UserModel.findUserByChannel(channelId);
if (!oldUser) {
let user = await UserModel.createUserWithChannel(channelId, channelType, channelInfo, token, platform, pkgName, serverType, deviceId, ip);
return { isCreate: true, user, deviceId: null};
} else {
let user = await UserModel.updateTokenByChannel(channelId, token, deviceId, ip, channelInfo);
return { isCreate: false, user, deviceId: oldUser.deviceId};
}
}
private static async encryptPass(password: string, salt?: string) {
if (!salt) {
salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
}
let npassword = await bcrypt.hash(password, salt);
return { npassword, salt };
}
public static async setPass(uid: number, password: string) {
let r = await this.encryptPass(password);
const user: UserType = await UserModel.findOneAndUpdate({ uid }, { $set: { password: r.npassword, salt: r.salt, hasSetPw: true } }, { new: true }).lean({ getters: true });
return user;
}
public static async bindTel(uid: number, tel: string) {
const user: UserType = await UserModel.findOneAndUpdate({ uid, isGuest: true }, { $set: { tel, isGuest: false } }, { new: true }).lean({ getters: true });
return user;
}
public static async checkPass(tel: string, password: string, token: string, deviceId: string) {
const user: UserType = await UserModel.findOne({ tel }).select('salt deviceId').lean({ getters: true });
if (user) {
const curTime: Date = new Date();
let { salt } = user;
let { npassword } = await this.encryptPass(password, salt);
const checkUser: UserType = await UserModel.findOneAndUpdate({ tel, password: npassword }, { $set: { token, lastLoginTime: curTime, deviceId }, $addToSet: { device: deviceId } }, { new: true }).lean({ getters: true });
return {user: checkUser, deviceId: user.deviceId};
} else {
return {user: null, deviceId: null};
}
}
public static async findUserByToken(token: string) {
const user: UserType = await UserModel.findOne({ token }).select('uid token serverType auth tel userCode pkgName ip channelInfo deviceId').lean({ getters: true });
return user;
}
public static async findUserByChannel(channelId: string) {
const user: UserType = await UserModel.findOne({ channelId }).select('uid token serverType auth tel userCode pkgName ip deviceId channelId channelInfo').lean({ getters: true });
return user;
}
public static async findUserByChannelRegex(channelId: string) {
const user: UserType = await UserModel.findOne({ channelId: { $regex: new RegExp(channelId.toString(), 'i') } }).select('uid token serverType auth tel userCode pkgName ip deviceId').lean({ getters: true });
return user;
}
public static async findTokenByTel(tel: string) {
const user: UserType = await UserModel.findOne({ tel }).lean();
return user ? user.token : null;
}
public static async findTokenByUid(uid: number) {
const user: UserType = await UserModel.findOne({ uid }).lean();
return user ? user.token : null;
}
public static async findUserByTel(tel: string) {
const user: UserType = await UserModel.findOne({ tel }).select('uid tel hasSetPw').lean({ getters: true });
return user;
}
public static async findUserByUid(uid: number) {
const user: UserType = await UserModel.findOne({ uid }).select('uid userCode tel channelInfo channelType ip channelId deviceId').lean({ getters: true });
return user;
}
public static async findUserByUserCode(userCode: string) {
const user: UserType = await UserModel.findOne({ userCode }).lean({ getters: true });
return user;
}
public static async addAuth(uid: number, auth: number) {
const user: UserType = await UserModel.findOneAndUpdate({ uid }, { auth }, { new: true }).select('uid tel').lean({ getters: true });
return user;
}
public static async authentication(uid: number, birthday: string, pi: string) {
const user: UserType = await UserModel.findOneAndUpdate({ uid }, { hasAuthenticated: true, birthday, pi }, { new: true }).lean({ getters: true });
return user;
}
public static async updatePlayTime(userCode: string, isGuest: boolean, todayPlayTime: number, playTime: number) {
const user: UserType = await UserModel.findOneAndUpdate({ userCode }, { $set: { guestTime: isGuest?playTime: 0, todayPlayTime: todayPlayTime, playTime } }, { new: true }).lean({ getters: true });
return user;
}
private static getSearchObj(form: SearchUserParam) {
let searchObj = {};
if(form['uid']) searchObj['uid'] = form.uid;
if(form['tel']) searchObj['tel'] = form.tel;
if(form.channelType && form.channelType != 'normal') searchObj['channelType'] = form.channelType;
return searchObj
}
public static async findByCondition(page: number, pageSize: number, sortField: string = 'updatedAt', sortOrder: string = 'descend', form: SearchUserParam = {}) {
let searchObj = this.getSearchObj(form);
let sort = {};
if(sortField && sortOrder) {
if(sortOrder == 'ascend') {
sort[sortField] = 1;
} else if (sortOrder == 'descend') {
sort[sortField] = -1;
}
}
const result: UserType[] = await UserModel.find(searchObj).limit(pageSize).skip((page - 1) * pageSize).sort(sort).select('-_id -__v -password -salt -token').lean({ getters: true, virtuals: true });
return result;
}
public static async countByCondition(form: SearchUserParam = {}) {
let searchObj = this.getSearchObj(form);
const result = await UserModel.count(searchObj);
return result;
}
}
export const UserModel = getModelForClass(User);
export interface UserType extends Pick<DocumentType<User>, keyof User> { };