mirror of
https://github.com/xiaoyiweb/YiAi.git
synced 2025-11-19 07:33:45 +08:00
初始化
This commit is contained in:
33
service/src/modules/user/dto/queryAllUser.dto.ts
Normal file
33
service/src/modules/user/dto/queryAllUser.dto.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { IsNotEmpty, MinLength, MaxLength, IsEmail, IsOptional, IsNumber } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class QueryAllUserDto {
|
||||
@ApiProperty({ example: 1, description: '查询页数', required: false })
|
||||
@IsOptional()
|
||||
page: number;
|
||||
|
||||
@ApiProperty({ example: 10, description: '每页数量', required: false })
|
||||
@IsOptional()
|
||||
size: number;
|
||||
|
||||
@ApiProperty({ example: '小九', description: '用户姓名', required: false })
|
||||
@IsOptional()
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ example: 'J_longyan@163.com', description: '用户邮箱', required: false })
|
||||
@IsOptional()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ example: '18888888888', description: '用户手机号码', required: false })
|
||||
@IsOptional()
|
||||
phone: string;
|
||||
|
||||
@ApiProperty({ example: 2, description: '用户状态', required: false })
|
||||
@IsOptional()
|
||||
status: number;
|
||||
|
||||
@ApiProperty({ example: 'super', description: '关键字查询', required: false })
|
||||
@IsOptional()
|
||||
keyword: string;
|
||||
}
|
||||
13
service/src/modules/user/dto/queryInviteRecord.dto.ts
Normal file
13
service/src/modules/user/dto/queryInviteRecord.dto.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { IsNotEmpty, MinLength, MaxLength, IsEmail, IsOptional, IsNumber } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class queryInviteRecordDto {
|
||||
@ApiProperty({ example: 1, description: '查询页数', required: false })
|
||||
@IsOptional()
|
||||
page: number;
|
||||
|
||||
@ApiProperty({ example: 10, description: '每页数量', required: false })
|
||||
@IsOptional()
|
||||
size: number;
|
||||
}
|
||||
9
service/src/modules/user/dto/queryOne.dto.ts
Normal file
9
service/src/modules/user/dto/queryOne.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { IsDefined } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class QueryOneUserDto {
|
||||
@ApiProperty({ example: 1, nullable: true, description: '查询用户的id', required: false })
|
||||
@IsDefined({ message: '用户id是必传参数' })
|
||||
id: number;
|
||||
}
|
||||
9
service/src/modules/user/dto/resetUserPass.dto.ts
Normal file
9
service/src/modules/user/dto/resetUserPass.dto.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { IsDefined } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class ResetUserPassDto {
|
||||
@ApiProperty({ example: 1, nullable: true, description: '用户id', required: false })
|
||||
@IsDefined({ message: '用户id是必传参数' })
|
||||
id: number;
|
||||
}
|
||||
14
service/src/modules/user/dto/retrieve.dto.ts
Normal file
14
service/src/modules/user/dto/retrieve.dto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { IsDefined } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class RetrieveUserDto {
|
||||
@ApiProperty({ example: 100, nullable: true, description: '查询用户的id', required: false })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ example: 'sfas12', nullable: true, description: 'TODO待完善', required: false })
|
||||
secret: string
|
||||
|
||||
@ApiProperty({ example: 100, nullable: true, description: 'TODO待完善', required: false })
|
||||
moreId: number
|
||||
}
|
||||
26
service/src/modules/user/dto/updateUser.dto.ts
Normal file
26
service/src/modules/user/dto/updateUser.dto.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { IsNotEmpty, MinLength, MaxLength, IsEmail, IsOptional } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class UpdateUserDto {
|
||||
@ApiProperty({ example: 'cooper', nullable: true, description: '用户名称', required: false })
|
||||
@MinLength(2, { message: '用户名最低需要大于2位数!' })
|
||||
@MaxLength(12, { message: '用户名不得超过12位!' })
|
||||
@IsNotEmpty({ message: '用户名不能为空!' })
|
||||
@IsOptional()
|
||||
username?: string;
|
||||
|
||||
@ApiProperty({ example: 'https://file.jiangly.com/images/93971628.jpeg', description: '用户头像', required: false })
|
||||
@IsNotEmpty({ message: '用户头像不能为空!' })
|
||||
@IsOptional()
|
||||
avatar?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '我是一台基于深度学习和自然语言处理技术的 AI 机器人,旨在为用户提供高效、精准、个性化的智能服务。',
|
||||
description: '用户签名',
|
||||
required: false,
|
||||
})
|
||||
@IsNotEmpty({ message: '用户签名不能为空!' })
|
||||
@IsOptional()
|
||||
sign?: string;
|
||||
}
|
||||
15
service/src/modules/user/dto/updateUserStatus.dto.ts
Normal file
15
service/src/modules/user/dto/updateUserStatus.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { IsNotEmpty, IsDefined, IsIn } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class UpdateUserStatusDto {
|
||||
@ApiProperty({ example: 2, description: '用户状态', required: false })
|
||||
@IsNotEmpty({ message: '用户状态不能为空!' })
|
||||
@IsDefined({ message: '用户状态是必传参数' })
|
||||
@IsIn([0, 1, 2, 3], { message: '非法参数、用户状态非法!' })
|
||||
status: number;
|
||||
|
||||
@ApiProperty({ example: 1, description: '修改的用户id', required: false })
|
||||
@IsDefined({ message: '用户id是必传参数' })
|
||||
id: number;
|
||||
}
|
||||
21
service/src/modules/user/dto/userRecharge.dto.ts
Normal file
21
service/src/modules/user/dto/userRecharge.dto.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { IsNotEmpty, MinLength, MaxLength, IsEmail, IsOptional, IsNumber, IsDefined } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class UserRechargeDto {
|
||||
@ApiProperty({ example: 1, description: '用户id', required: true })
|
||||
@IsDefined({ message: '用户id是必传参数' })
|
||||
userId: number;
|
||||
|
||||
@ApiProperty({ example: 100, description: '用户对话模型3次数', required: false })
|
||||
@IsOptional()
|
||||
model3Count?: number;
|
||||
|
||||
@ApiProperty({ example: 5, description: '用户对话模型4次数', required: false })
|
||||
@IsOptional()
|
||||
model4Count?: number;
|
||||
|
||||
@ApiProperty({ example: 0, description: '用户MJ额度', required: false })
|
||||
@IsOptional()
|
||||
drawMjCount?: number;
|
||||
}
|
||||
91
service/src/modules/user/user.controller.ts
Normal file
91
service/src/modules/user/user.controller.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { UserService } from './user.service';
|
||||
import { Body, Controller, Get, Post, Query, Req, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { UpdateUserDto } from './dto/updateUser.dto';
|
||||
import { JwtAuthGuard } from '@/common/auth/jwtAuth.guard';
|
||||
import { Request } from 'express';
|
||||
import { AdminAuthGuard } from '@/common/auth/adminAuth.guard';
|
||||
import { UserRechargeDto } from './dto/userRecharge.dto';
|
||||
import { QueryAllUserDto } from './dto/queryAllUser.dto';
|
||||
import { QueryOneUserDto } from './dto/queryOne.dto';
|
||||
import { UpdateUserStatusDto } from './dto/updateUserStatus.dto';
|
||||
import { ResetUserPassDto } from './dto/resetUserPass.dto';
|
||||
import { SuperAuthGuard } from '@/common/auth/superAuth.guard';
|
||||
import { queryInviteRecordDto } from './dto/queryInviteRecord.dto';
|
||||
import { RetrieveUserDto } from './dto/retrieve.dto';
|
||||
|
||||
@Controller('user')
|
||||
@ApiTags('user')
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@Post('update')
|
||||
@ApiOperation({ summary: '更新用户信息' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async update(@Body() body: UpdateUserDto, @Req() req: Request) {
|
||||
return await this.userService.updateInfo(body, req);
|
||||
}
|
||||
|
||||
@Post('genInviteCode')
|
||||
@ApiOperation({ summary: '生成邀请码' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async genInviteCode(@Req() req: Request) {
|
||||
return await this.userService.genInviteCode(req);
|
||||
}
|
||||
|
||||
@Get('inviteRecord')
|
||||
@ApiOperation({ summary: '获取我的邀请记录' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async getInviteRecord(@Req() req: Request, @Query() query: queryInviteRecordDto) {
|
||||
return await this.userService.getInviteRecord(req, query);
|
||||
}
|
||||
|
||||
@Get('inviteLink')
|
||||
@ApiOperation({ summary: '邀请链接被点击' })
|
||||
async inviteLink(@Query('code') code: string) {
|
||||
return await this.userService.inviteLink(code);
|
||||
}
|
||||
|
||||
@Post('recharge')
|
||||
@ApiOperation({ summary: '用户充值' })
|
||||
@UseGuards(SuperAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async userRecharge(@Body() body: UserRechargeDto) {
|
||||
return await this.userService.userRecharge(body);
|
||||
}
|
||||
|
||||
@Get('queryAll')
|
||||
@ApiOperation({ summary: '查询所有用户' })
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async queryAll(@Query() query: QueryAllUserDto, @Req() req: Request) {
|
||||
return await this.userService.queryAll(query, req);
|
||||
}
|
||||
|
||||
@Get('queryOne')
|
||||
@ApiOperation({ summary: '查询单个用户' })
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async queryOne(@Query() params: QueryOneUserDto) {
|
||||
return await this.userService.queryOne(params);
|
||||
}
|
||||
|
||||
@Post('updateStatus')
|
||||
@ApiOperation({ summary: '更新用户状态' })
|
||||
@UseGuards(SuperAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async updateStatus(@Body() body: UpdateUserStatusDto) {
|
||||
return await this.userService.updateStatus(body);
|
||||
}
|
||||
|
||||
@Post('resetUserPass')
|
||||
@ApiOperation({ summary: '重置用户密码' })
|
||||
@UseGuards(SuperAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
async resetUserPass(@Body() body: ResetUserPassDto) {
|
||||
return await this.userService.resetUserPass(body);
|
||||
}
|
||||
}
|
||||
69
service/src/modules/user/user.entity.ts
Normal file
69
service/src/modules/user/user.entity.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Check, Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm';
|
||||
import { BaseEntity } from 'src/common/entity/baseEntity';
|
||||
|
||||
@Entity({ name: 'users' })
|
||||
export class UserEntity extends BaseEntity {
|
||||
@Column({ length: 12, comment: '用户昵称' })
|
||||
username: string;
|
||||
|
||||
@Column({ length: 64, comment: '用户密码', nullable: true })
|
||||
password: string;
|
||||
|
||||
@Column({ default: 0, comment: '用户状态' })
|
||||
status: number;
|
||||
|
||||
@Column({ default: 1, comment: '用户性别' })
|
||||
sex: number;
|
||||
|
||||
@Column({ length: 64, unique: true, comment: '用户邮箱' })
|
||||
email: string;
|
||||
|
||||
@Column({ length: 64, nullable: true, comment: '用户手机号' })
|
||||
phone: string;
|
||||
|
||||
@Column({
|
||||
length: 300,
|
||||
nullable: true,
|
||||
default: 'https://public-1300678944.cos.ap-shanghai.myqcloud.com/ai/7f042f63f.png',
|
||||
comment: '用户头像',
|
||||
})
|
||||
avatar: string;
|
||||
|
||||
@Column({
|
||||
length: 300,
|
||||
nullable: true,
|
||||
default: '我是一台基于深度学习和自然语言处理技术的 AI 机器人,旨在为用户提供高效、精准、个性化的智能服务。',
|
||||
comment: '用户签名',
|
||||
})
|
||||
sign: string;
|
||||
|
||||
@Column({ length: 64, default: '', comment: '注册IP', nullable: true })
|
||||
registerIp: string;
|
||||
|
||||
@Column({ length: 64, default: '', comment: '最后一次登录IP', nullable: true })
|
||||
lastLoginIp: string;
|
||||
|
||||
@Column({ length: 10, default: '', comment: '用户邀请码' })
|
||||
inviteCode: string;
|
||||
|
||||
@Column({ length: 10, default: '', comment: '用户填写的别人的邀请码' })
|
||||
invitedBy: string;
|
||||
|
||||
@Column({ length: 10, default: 'viewer', comment: '用户角色' })
|
||||
role: string;
|
||||
|
||||
@Column({ length: 64, default: '', comment: '微信openId', nullable: true })
|
||||
openId: string;
|
||||
|
||||
@Column({ length: 64, comment: '用户注册来源', nullable: true })
|
||||
client: string;
|
||||
|
||||
@Column({ comment: '用户邀请链接被点击次数', default: 0 })
|
||||
inviteLinkCount: number;
|
||||
|
||||
@Column({ comment: '用户连续签到天数', default: 0 })
|
||||
consecutiveDays: number;
|
||||
|
||||
@Column({ comment: '用户违规记录次数', default: 0 })
|
||||
violationCount: number;
|
||||
}
|
||||
45
service/src/modules/user/user.module.ts
Normal file
45
service/src/modules/user/user.module.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { VerifycationEntity } from '../verification/verifycation.entity';
|
||||
import { VerificationService } from '../verification/verification.service';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UserController } from './user.controller';
|
||||
import { UserService } from './user.service';
|
||||
import { UserEntity } from './user.entity';
|
||||
import { UserBalanceService } from '../userBalance/userBalance.service';
|
||||
import { BalanceEntity } from '../userBalance/balance.entity';
|
||||
import { AccountLogEntity } from '../userBalance/accountLog.entity';
|
||||
import { ConfigEntity } from '../globalConfig/config.entity';
|
||||
import { CramiPackageEntity } from '../crami/cramiPackage.entity';
|
||||
import { WhiteListEntity } from '../chatgpt/whiteList.entity';
|
||||
import { UserBalanceEntity } from '../userBalance/userBalance.entity';
|
||||
import { SalesUsersEntity } from '../sales/salesUsers.entity';
|
||||
import { RedisCacheService } from '../redisCache/redisCache.service';
|
||||
import { FingerprintLogEntity } from '../userBalance/fingerprint.entity';
|
||||
import { ChatLogEntity } from '../chatLog/chatLog.entity';
|
||||
import { ChatGroupEntity } from '../chatGroup/chatGroup.entity';
|
||||
import { MidjourneyEntity } from '../midjourney/midjourney.entity';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
UserEntity,
|
||||
VerifycationEntity,
|
||||
BalanceEntity,
|
||||
AccountLogEntity,
|
||||
ConfigEntity,
|
||||
CramiPackageEntity,
|
||||
WhiteListEntity,
|
||||
UserBalanceEntity,
|
||||
SalesUsersEntity,
|
||||
FingerprintLogEntity,
|
||||
ChatLogEntity,
|
||||
ChatGroupEntity,
|
||||
MidjourneyEntity
|
||||
]),
|
||||
],
|
||||
controllers: [UserController],
|
||||
providers: [UserService, VerificationService, UserBalanceService, RedisCacheService],
|
||||
exports: [UserService],
|
||||
})
|
||||
export class UserModule {}
|
||||
566
service/src/modules/user/user.service.ts
Normal file
566
service/src/modules/user/user.service.ts
Normal file
@@ -0,0 +1,566 @@
|
||||
import { GlobalConfigService } from '../globalConfig/globalConfig.service';
|
||||
import { UserStatusEnum, UserStatusErrMsg } from '../../common/constants/user.constant';
|
||||
import { MailerService } from '@nestjs-modules/mailer';
|
||||
import { VerifycationEntity } from '../verification/verifycation.entity';
|
||||
import { VerificationService } from '../verification/verification.service';
|
||||
import { Injectable, HttpException, HttpStatus, Global } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Connection, UpdateResult, Not, Like, In, MoreThan } from 'typeorm';
|
||||
import { UserRegisterDto } from '../auth/dto/authRegister.dto';
|
||||
import { UserEntity } from './user.entity';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import { UserLoginDto } from '../auth/dto/authLogin.dto';
|
||||
import { VerificationEnum } from '@/common/constants/verification.constant';
|
||||
import { UserBalanceService } from '../userBalance/userBalance.service';
|
||||
import { UpdateUserDto } from './dto/updateUser.dto';
|
||||
import { Request, Response } from 'express';
|
||||
import {
|
||||
createRandomCode,
|
||||
createRandomUid,
|
||||
formatCreateOrUpdateDate,
|
||||
generateRandomString,
|
||||
getClientIp,
|
||||
maskEmail,
|
||||
maskIpAddress,
|
||||
} from '@/common/utils';
|
||||
import { UserRechargeDto } from './dto/userRecharge.dto';
|
||||
import { RechargeType } from '@/common/constants/balance.constant';
|
||||
import { QueryAllUserDto } from './dto/queryAllUser.dto';
|
||||
import { UpdateUserStatusDto } from './dto/updateUserStatus.dto';
|
||||
import { ResetUserPassDto } from './dto/resetUserPass.dto';
|
||||
import { ConfigEntity } from '../globalConfig/config.entity';
|
||||
import { WhiteListEntity } from '../chatgpt/whiteList.entity';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { UserRegisterByPhoneDto } from '../auth/dto/userRegisterByPhone.dto';
|
||||
import { RetrieveUserDto } from './dto/retrieve.dto';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
@InjectRepository(UserEntity)
|
||||
private readonly userEntity: Repository<UserEntity>,
|
||||
@InjectRepository(WhiteListEntity)
|
||||
private readonly whiteListEntity: Repository<WhiteListEntity>,
|
||||
private readonly connection: Connection,
|
||||
private readonly verificationService: VerificationService,
|
||||
private readonly mailerService: MailerService,
|
||||
private readonly userBalanceService: UserBalanceService,
|
||||
private readonly globalConfigService: GlobalConfigService,
|
||||
@InjectRepository(ConfigEntity)
|
||||
private readonly configEntity: Repository<ConfigEntity>,
|
||||
) {}
|
||||
|
||||
/* create and verify */
|
||||
async createUserAndVerifycation(user: UserEntity | UserRegisterDto, req: Request): Promise<UserEntity> {
|
||||
const { username, email, password, invitedBy, client = 0 } = user;
|
||||
if (invitedBy) {
|
||||
const b = await this.userEntity.findOne({ where: { inviteCode: invitedBy } });
|
||||
if (!b) {
|
||||
throw new HttpException('无效的邀请码!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/* 用户是否已经在系统中 */
|
||||
const where = [{ username }, { email }];
|
||||
const u: UserEntity = await this.userEntity.findOne({ where: where });
|
||||
|
||||
if (u && u.status !== UserStatusEnum.PENDING) {
|
||||
throw new HttpException('用户名或者邮箱已被注册!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
const userInput: any = _.cloneDeep(user);
|
||||
const hashedPassword = bcrypt.hashSync(password, 10);
|
||||
const ip = getClientIp(req);
|
||||
userInput.password = hashedPassword;
|
||||
userInput.registerIp = ip;
|
||||
userInput.client = client;
|
||||
|
||||
let n: UserEntity;
|
||||
/* 如果没有注册用户则首次注册记录 如果注册了覆盖发送验证码即可 无需记录用户 */
|
||||
if (!u) {
|
||||
const userDefautlAvatar = await this.globalConfigService.getConfigs(['userDefautlAvatar']);
|
||||
userInput.avatar = userDefautlAvatar;
|
||||
n = await this.userEntity.save(userInput);
|
||||
} else {
|
||||
n = u;
|
||||
}
|
||||
const emailConfigs = await this.configEntity.find({
|
||||
where: {
|
||||
configKey: In([
|
||||
'isVerifyEmail',
|
||||
'registerBaseUrl',
|
||||
'registerVerifyEmailTitle',
|
||||
'registerVerifyEmailDesc',
|
||||
'registerVerifyEmailFrom',
|
||||
'registerVerifyExpir',
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
const configMap: any = emailConfigs.reduce((pre, cur: any) => {
|
||||
pre[cur.configKey] = cur.configVal;
|
||||
return pre;
|
||||
}, {});
|
||||
|
||||
const isVerifyEmail = configMap['isVerifyEmail'] ? Number(configMap['isVerifyEmail']) : 1;
|
||||
if (isVerifyEmail) {
|
||||
const expir = configMap['registerVerifyExpir'] ? Number(configMap['registerVerifyExpir']) : 30 * 60;
|
||||
const v: VerifycationEntity = await this.verificationService.createVerification(n, VerificationEnum.Registration, expir);
|
||||
const { code, email, id } = v;
|
||||
const { registerVerifyEmailFrom } = configMap;
|
||||
console.log('configMap: ', configMap);
|
||||
/* 判断是否开启邮箱验证 */
|
||||
const res = await this.mailerService.sendMail({
|
||||
to: email,
|
||||
subject: `来自${registerVerifyEmailFrom}的账号激活`,
|
||||
template: 'register',
|
||||
context: { baseUrl: configMap['registerBaseUrl'], code, id, ...configMap },
|
||||
});
|
||||
console.log('email response -> : ', res);
|
||||
} else {
|
||||
/* 如果没有邮箱验证则 则直接主动注册验证通过逻辑 */
|
||||
const { username, email, id, invitedBy } = n;
|
||||
await this.updateUserStatus(id, UserStatusEnum.ACTIVE);
|
||||
/* 如果用户填写了 invitedBy 邀请码 查到邀请人信息 */
|
||||
let inviteUser: UserEntity;
|
||||
if (invitedBy) {
|
||||
inviteUser = await this.qureyUserInfoByInviteCode(invitedBy);
|
||||
}
|
||||
await this.userBalanceService.addBalanceToNewUser(id, inviteUser?.id);
|
||||
}
|
||||
return n;
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getSuper() {
|
||||
const user = await this.userEntity.findOne({ where: { role: 'super' } });
|
||||
return user;
|
||||
}
|
||||
|
||||
/* 账号登录验证密码 扫码登录则不用 */
|
||||
async verifyUserCredentials(user): Promise<UserEntity> {
|
||||
const { username, password, uid = 0, phone } = user;
|
||||
let u = null;
|
||||
|
||||
/* 三方登录的 */
|
||||
if (uid > 0) {
|
||||
u = await this.userEntity.findOne({ where: { id: uid } });
|
||||
if (!u) {
|
||||
throw new HttpException('当前账户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (u.password.startsWith('$2a$') || u.password.startsWith('$2b$') || u.password.startsWith('$2y$')) {
|
||||
// 如果是默认
|
||||
if (!bcrypt.compareSync(password, u.password)) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
} else {
|
||||
//如果是md5加密
|
||||
console.log('----,');
|
||||
|
||||
const md5 = crypto.createHash('md5').update(password).digest('hex');
|
||||
console.log('----,', md5);
|
||||
if (md5 !== u.password) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 邮箱登录的 */
|
||||
if (username && password) {
|
||||
const where: any = [{ username }, { email: username }];
|
||||
u = await this.userEntity.findOne({ where: where });
|
||||
if (!u) {
|
||||
throw new HttpException('当前账户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (u.password.startsWith('$2a$') || u.password.startsWith('$2b$') || u.password.startsWith('$2y$')) {
|
||||
// 如果是默认
|
||||
if (!bcrypt.compareSync(password, u.password)) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
} else {
|
||||
//如果是md5加密
|
||||
console.log('----,');
|
||||
|
||||
const md5 = crypto.createHash('md5').update(password).digest('hex');
|
||||
console.log('----,', md5);
|
||||
|
||||
if (md5 !== u.password) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机号登录的 */
|
||||
if (phone && password) {
|
||||
const where: any = [{ phone }];
|
||||
u = await this.userEntity.findOne({ where: where });
|
||||
if (!u) {
|
||||
throw new HttpException('当前账户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (u.password.startsWith('$2a$') || u.password.startsWith('$2b$') || u.password.startsWith('$2y$')) {
|
||||
// 如果是默认
|
||||
if (!bcrypt.compareSync(password, u.password)) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
} else {
|
||||
//如果是md5加密
|
||||
console.log('----,');
|
||||
|
||||
const md5 = crypto.createHash('md5').update(password).digest('hex');
|
||||
console.log('----,', md5);
|
||||
|
||||
if (md5 !== u.password) {
|
||||
throw new HttpException('当前密码错误!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!u) {
|
||||
throw new HttpException('当前账户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (u.status !== UserStatusEnum.ACTIVE) {
|
||||
throw new HttpException(UserStatusErrMsg[u.status], HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
async verifyUserPassword(userId, password) {
|
||||
const u = await this.userEntity.findOne({ where: { id: userId } });
|
||||
if (u.password.startsWith('$2a$') || u.password.startsWith('$2b$') || u.password.startsWith('$2y$')) {
|
||||
//非MD5加密
|
||||
return bcrypt.compareSync(password, u.password);
|
||||
} else {
|
||||
// 如果是md5加密
|
||||
const md5 = crypto.createHash('md5').update(password).digest('hex');
|
||||
console.log('----,', md5);
|
||||
|
||||
return md5 === u.password;
|
||||
}
|
||||
}
|
||||
|
||||
async updateUserStatus(id: number, status: UserStatusEnum) {
|
||||
const u: UpdateResult = await this.userEntity.update({ id }, { status });
|
||||
return u.affected > 0;
|
||||
}
|
||||
|
||||
async getUserStatus(id: number): Promise<number> {
|
||||
const u: UserEntity = await this.userEntity.findOne({ where: { id } });
|
||||
return u.status;
|
||||
}
|
||||
|
||||
async queryUserInfoById(id: number): Promise<UserEntity> {
|
||||
return await this.userEntity.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
async queryOneUserInfo(userId: number): Promise<UserEntity> {
|
||||
return await this.userEntity.findOne({ where: { id: userId } });
|
||||
}
|
||||
|
||||
/* 检查用户状态 */
|
||||
async checkUserStatus(user) {
|
||||
const { id: userId, role } = user;
|
||||
if (role === 'visitor') return true;
|
||||
const u = await this.userEntity.findOne({ where: { id: userId } });
|
||||
if (!u) {
|
||||
throw new HttpException('当前用户信息失效、请重新登录!', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
if (u.status === UserStatusEnum.BLACKLISTED) {
|
||||
throw new HttpException('您的账户已被永久加入黑名单、如有疑问、请联系管理员!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (u.status === UserStatusEnum.LOCKED) {
|
||||
throw new HttpException('您的账户已被封禁、如有疑问、请联系管理员!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/* 获取用户基础信息 */
|
||||
async getUserInfo(userId: number) {
|
||||
const userInfo: any = await this.userEntity.findOne({
|
||||
where: { id: userId },
|
||||
select: ['username', 'avatar', 'role', 'email', 'sign', 'inviteCode', 'openId', 'consecutiveDays'],
|
||||
});
|
||||
if (!userInfo) {
|
||||
throw new HttpException('当前用户信息失效、请重新登录!', HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
userInfo.isBindWx = !!userInfo?.openId;
|
||||
delete userInfo.openId;
|
||||
const userBalance = await this.userBalanceService.queryUserBalance(userId);
|
||||
return { userInfo, userBalance: { ...userBalance } };
|
||||
}
|
||||
|
||||
/* 获取用户信息 */
|
||||
async getUserById(id: number) {
|
||||
return await this.userEntity.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
/* 通过openId获取用户信息 */
|
||||
async getUserOpenId(openId: string) {
|
||||
return await this.userEntity.findOne({ where: { openId } });
|
||||
}
|
||||
|
||||
/* 修改用户信息 */
|
||||
async updateInfo(body: UpdateUserDto, req: Request) {
|
||||
const { id } = req.user;
|
||||
|
||||
const u = await this.userEntity.findOne({ where: { id } });
|
||||
if (!u) {
|
||||
throw new HttpException('当前用户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (body.username && u.username === body.username) {
|
||||
throw new HttpException('没有变更,无需更改!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (body.username) {
|
||||
const u = await this.userEntity.findOne({ where: { username: body.username, id: Not(id) } });
|
||||
if (u) {
|
||||
throw new HttpException('用户名已存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
const r = await this.userEntity.update({ id }, body);
|
||||
if (r.affected <= 0) {
|
||||
throw new HttpException('修改用户信息失败!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return '修改用户信息成功!';
|
||||
}
|
||||
|
||||
/* 修改用户密码 */
|
||||
async updateUserPassword(userId: number, password: string) {
|
||||
const hashedPassword = bcrypt.hashSync(password, 10);
|
||||
const r = await this.userEntity.update({ id: userId }, { password: hashedPassword });
|
||||
if (r.affected <= 0) {
|
||||
throw new HttpException('修改密码失败、请重新试试吧。', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/* 生成用户邀请码 */
|
||||
async genInviteCode(req: Request) {
|
||||
const { id } = req.user;
|
||||
const u = await this.userEntity.findOne({ where: { id } });
|
||||
if (!u || u.inviteCode) {
|
||||
throw new HttpException('已生成过邀请码、请勿重复生成', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const inviteCode = generateRandomString();
|
||||
const user = await this.userEntity.findOne({ where: { inviteCode } });
|
||||
if (user) {
|
||||
throw new HttpException('生成邀请码失败,请重新试一次吧!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const r = await this.userEntity.update({ id }, { inviteCode });
|
||||
if (r.affected <= 0) {
|
||||
throw new HttpException('生成邀请码失败,请重新试一次吧!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return inviteCode;
|
||||
}
|
||||
|
||||
/* 获取我得邀请记录 */
|
||||
async getInviteRecord(req, query) {
|
||||
try {
|
||||
const { id } = req.user;
|
||||
const { page = 1, size = 10 } = query;
|
||||
const u = await this.userEntity.findOne({ where: { id } });
|
||||
const { inviteCode } = u;
|
||||
if (!inviteCode) return [];
|
||||
const [rows, count] = await this.userEntity.findAndCount({
|
||||
where: { inviteCode },
|
||||
order: { id: 'DESC' },
|
||||
select: ['username', 'email', 'createdAt', 'status', 'avatar'],
|
||||
take: size,
|
||||
skip: (page - 1) * size,
|
||||
});
|
||||
formatCreateOrUpdateDate(rows).map((t) => {
|
||||
t.email = maskEmail(t.email);
|
||||
return t;
|
||||
});
|
||||
return { rows, count };
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
throw new HttpException('获取邀请记录失败!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/* 邀请链接被点击 */
|
||||
async inviteLink(code) {
|
||||
const u = await this.userEntity.findOne({ where: { inviteCode: code } });
|
||||
if (!u) return 1;
|
||||
const { inviteLinkCount = 0 } = u;
|
||||
const res = await this.userEntity.update({ inviteCode: code }, { inviteLinkCount: inviteLinkCount + 1 });
|
||||
if (res.affected) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 通过邀请码查询邀请人信息 */
|
||||
async qureyUserInfoByInviteCode(inviteCode: string): Promise<UserEntity> {
|
||||
return await this.userEntity.findOne({ where: { inviteCode } });
|
||||
}
|
||||
|
||||
/* 给用户充值 */
|
||||
async userRecharge(body: UserRechargeDto) {
|
||||
const { userId, model3Count = 0, model4Count = 0, drawMjCount = 0 } = body;
|
||||
await this.userBalanceService.addBalanceToUser(userId, { model3Count, model4Count, drawMjCount });
|
||||
const res = await this.userBalanceService.saveRecordRechargeLog({
|
||||
userId,
|
||||
rechargeType: RechargeType.ADMIN_GIFT,
|
||||
model3Count,
|
||||
model4Count,
|
||||
drawMjCount,
|
||||
extent: '',
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/* 查询所有用户 */
|
||||
async queryAll(query: QueryAllUserDto, req: Request) {
|
||||
const { page = 1, size = 10, username, email, status, keyword, phone } = query;
|
||||
let where = {};
|
||||
username && Object.assign(where, { username: Like(`%${username}%`) });
|
||||
email && Object.assign(where, { email: Like(`%${email}%`) });
|
||||
phone && Object.assign(where, { phone: Like(`%${phone}%`) });
|
||||
status && Object.assign(where, { status });
|
||||
if (keyword) {
|
||||
where = [{ username: Like(`%${keyword}%`) }, { email: Like(`%${keyword}%`) }, { phone: Like(`%${keyword}%`) }];
|
||||
}
|
||||
const [rows, count] = await this.userEntity.findAndCount({
|
||||
skip: (page - 1) * size,
|
||||
where,
|
||||
take: size,
|
||||
order: { createdAt: 'DESC' },
|
||||
cache: true,
|
||||
select: ['username', 'avatar', 'inviteCode', 'role', 'sign', 'status', 'id', 'email', 'createdAt', 'lastLoginIp', 'phone'],
|
||||
});
|
||||
const ids = rows.map((t) => t.id);
|
||||
const data = await this.userBalanceService.queryUserBalanceByIds(ids);
|
||||
rows.forEach((user: any) => (user.balanceInfo = data.find((t) => t.userId === user.id)));
|
||||
req.user.role !== 'super' && rows.forEach((t) => (t.email = maskEmail(t.email)));
|
||||
req.user.role !== 'super' && rows.forEach((t) => (t.lastLoginIp = maskIpAddress(t.lastLoginIp)));
|
||||
req.user.role !== 'super' && rows.forEach((t) => (t.phone = maskIpAddress(t.phone)));
|
||||
return { rows, count };
|
||||
}
|
||||
|
||||
/* 查询单个用户详情 */
|
||||
async queryOne({ id }) {
|
||||
return await this.userEntity.findOne({ where: { id }, select: ['username', 'avatar', 'inviteCode', 'role', 'sign', 'status'] });
|
||||
}
|
||||
|
||||
/* 修改用户状态 */
|
||||
async updateStatus(body: UpdateUserStatusDto) {
|
||||
const { id, status } = body;
|
||||
const n = await this.userEntity.findOne({ where: { id } });
|
||||
if (!n) {
|
||||
throw new HttpException('用户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (n.role === 'super') {
|
||||
throw new HttpException('超级管理员不可被操作!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (n.status === UserStatusEnum.PENDING) {
|
||||
throw new HttpException('未激活用户不可手动变更状态!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (n.role === 'super') {
|
||||
throw new HttpException('超级管理员不可被操作!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (status === UserStatusEnum.PENDING) {
|
||||
throw new HttpException('不可将用户置为未激活状态!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const r = await this.userEntity.update({ id }, { status });
|
||||
if (r.affected <= 0) {
|
||||
throw new HttpException('修改用户状态失败!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return '修改用户状态成功!';
|
||||
}
|
||||
|
||||
/* 重置用户密码 */
|
||||
async resetUserPass(body: ResetUserPassDto) {
|
||||
const { id } = body;
|
||||
const u = await this.userEntity.findOne({ where: { id } });
|
||||
if (!u) {
|
||||
throw new HttpException('用户不存在!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
const defaultPassword = '123456';
|
||||
const hashPassword = bcrypt.hashSync(defaultPassword, 10);
|
||||
const raw = await this.userEntity.update({ id }, { password: hashPassword });
|
||||
if (raw.affected <= 0) {
|
||||
throw new HttpException('重置密码失败!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return `密码重置为[${defaultPassword}]成功!`;
|
||||
}
|
||||
|
||||
/* 记录登录ip */
|
||||
async savaLoginIp(userId: number, ip: string) {
|
||||
return await this.userEntity.update({ id: userId }, { lastLoginIp: ip });
|
||||
}
|
||||
|
||||
/* 通过openId 拿到或创建 */
|
||||
async getUserFromOpenId(openId: string, sceneStr?: string) {
|
||||
const user = await this.userEntity.findOne({ where: { openId } });
|
||||
if (!user) {
|
||||
const inviteCode = sceneStr ? sceneStr.split(':')[1] : '';
|
||||
const inviteUser = await this.qureyUserInfoByInviteCode(inviteCode);
|
||||
const user = await this.createUserFromOpenId(openId, inviteCode);
|
||||
await this.userBalanceService.addBalanceToNewUser(user.id, inviteCode ? inviteUser?.id : null);
|
||||
return user;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/* 通过openId创建一个用户, 传入邀请码 是邀请人的不是自己的 */
|
||||
async createUserFromOpenId(openId: string, invitedBy: string) {
|
||||
const userDefautlAvatar = await this.globalConfigService.getConfigs(['userDefautlAvatar']);
|
||||
const userInfo = {
|
||||
avatar: userDefautlAvatar,
|
||||
username: `用户${createRandomUid()}`,
|
||||
status: UserStatusEnum.ACTIVE,
|
||||
sex: 0,
|
||||
email: `${createRandomUid()}@default.com`,
|
||||
invitedBy,
|
||||
openId,
|
||||
};
|
||||
const user = await this.userEntity.save(userInfo);
|
||||
return user;
|
||||
}
|
||||
|
||||
async bindWx(openId, userId) {
|
||||
try {
|
||||
const user = await this.userEntity.findOne({ where: { id: userId } });
|
||||
if (!user) return { status: false, msg: '当前绑定用户不存在!' };
|
||||
const bindU = await this.userEntity.findOne({ where: { openId } });
|
||||
if (bindU) return { status: false, msg: '该微信已绑定其他账号!' };
|
||||
const res = await this.userEntity.update({ id: userId }, { openId });
|
||||
if (res.affected <= 0) return { status: false, msg: '绑定微信失败、请联系管理员!' };
|
||||
return { status: true, msg: '恭喜您绑定成功、后续可直接扫码登录了!' };
|
||||
} catch (error) {
|
||||
return { status: false, msg: '绑定微信失败、请联系管理员!' };
|
||||
}
|
||||
}
|
||||
|
||||
/* 通过userId获取用户的openId */
|
||||
async getOpenIdByUserId(userId: number) {
|
||||
const user = await this.userEntity.findOne({ where: { id: userId } });
|
||||
return user?.openId;
|
||||
}
|
||||
|
||||
/* 校验手机号注册 */
|
||||
async verifyUserRegisterByPhone(params: UserRegisterByPhoneDto) {
|
||||
const { username, password, phone, phoneCode } = params;
|
||||
const user = await this.userEntity.findOne({ where: [{ username }, { phone }] });
|
||||
if (user && user.username === username) {
|
||||
throw new HttpException('用户名已存在、请更换用户名!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
if (user && user.phone === phone) {
|
||||
throw new HttpException('当前手机号已注册、请勿重复注册!', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/* 创建基础用户 */
|
||||
async createUser(userInfo) {
|
||||
return await this.userEntity.save(userInfo);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user