完善登录

This commit is contained in:
无风 2023-04-14 18:16:10 +08:00
parent 3f81cf32be
commit c4b1de03f6
7 changed files with 298 additions and 11 deletions

View File

@ -36,5 +36,14 @@ export default () => {
secret: 'xxx', secret: 'xxx',
}, },
}, },
// jwt
jwt: {
// token 过期时间,单位秒
expire: 60 * 60 * 2,
// 刷新token 过期时间,单位秒
refreshExpire: 60 * 60 * 24 * 30,
// jwt 秘钥
secret: '093243e6ce8',
},
} as ModuleConfig; } as ModuleConfig;
}; };

View File

@ -0,0 +1,11 @@
import { CoolController, BaseController } from '@cool-midway/core';
import { UserInfoEntity } from '../../entity/info';
/**
*
*/
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: UserInfoEntity,
})
export class AdminUserInfoController extends BaseController {}

View File

@ -0,0 +1,50 @@
import { CoolController, BaseController } from '@cool-midway/core';
import { Body, Get, Inject, Post, Query } from '@midwayjs/core';
import { UserLoginService } from '../../service/login';
import { BaseSysLoginService } from '../../../base/service/sys/login';
/**
*
*/
@CoolController()
export class AppUserLoginController extends BaseController {
@Inject()
userLoginService: UserLoginService;
@Inject()
baseSysLoginService: BaseSysLoginService;
@Post('/mini', { summary: '小程序登录' })
async miniLogin(@Body() body) {
const { code, encryptedData, iv } = body;
return this.ok(await this.userLoginService.mini(code, encryptedData, iv));
}
@Post('/mp', { summary: '公众号登录' })
async mp(@Body('code') code: string) {
return this.ok(await this.userLoginService.mp(code));
}
@Post('/phone', { summary: '手机号登录' })
async phone(@Body('phone') phone: string, @Body('smsCode') smsCode: string) {
return this.ok(await this.userLoginService.phone(phone, smsCode));
}
@Get('/captcha', { summary: '图片验证码' })
async captcha(
@Query('type') type: string,
@Query('width') width: number,
@Query('height') height: number
) {
return this.ok(await this.baseSysLoginService.captcha(type, width, height));
}
@Post('/smsCode', { summary: '验证码' })
async smsCode(
@Body('phone') phone: string,
@Body('captchaId') captchaId: string,
@Body('code') code: string
) {
return this.ok();
}
}

View File

@ -6,23 +6,26 @@ import { Column, Entity, Index } from 'typeorm';
*/ */
@Entity('user_info') @Entity('user_info')
export class UserInfoEntity extends BaseEntity { export class UserInfoEntity extends BaseEntity {
@Index() @Index({ unique: true })
@Column({ comment: '第三方登录唯一ID微信、QQ等' }) @Column({ comment: '登录唯一ID', nullable: true })
unionid: string; unionid: string;
@Column({ comment: '头像' }) @Column({ comment: '头像', nullable: true })
avatarUrl: string; avatarUrl: string;
@Column({ comment: '昵称' }) @Column({ comment: '昵称', nullable: true })
nickName: string; nickName: string;
@Index({ unique: true }) @Index({ unique: true })
@Column({ comment: '手机号' }) @Column({ comment: '手机号', nullable: true })
phone: string; phone: string;
@Column({ comment: '性别 0-未知 1-男 2-女', default: 0 }) @Column({ comment: '性别 0-未知 1-男 2-女', default: 0 })
gender: number; gender: number;
@Column({ comment: '状态 0-正常 1-禁用', default: 0 }) @Column({ comment: '状态 0-禁用 1-正常', default: 1 })
status: number; status: number;
@Column({ comment: '登录方式 0-小程序 1-公众号 2-H5', default: 0 })
loginType: number;
} }

View File

@ -1,11 +1,19 @@
import { BaseEntity } from '@cool-midway/core'; import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm'; import { Column, Entity, Index } from 'typeorm';
/** /**
* *
*/ */
@Entity('user_wx') @Entity('user_wx')
export class UserWxEntity extends BaseEntity { export class UserWxEntity extends BaseEntity {
@Index()
@Column({ comment: '微信unionid', nullable: true })
unionid: string;
@Index()
@Column({ comment: '微信openid' })
openid: string;
@Column({ comment: '头像', nullable: true }) @Column({ comment: '头像', nullable: true })
avatarUrl: number; avatarUrl: number;
@ -15,15 +23,15 @@ export class UserWxEntity extends BaseEntity {
@Column({ comment: '性别 0-未知 1-男 2-女', default: 0 }) @Column({ comment: '性别 0-未知 1-男 2-女', default: 0 })
gender: number; gender: number;
@Column({ comment: '语言' }) @Column({ comment: '语言', nullable: true })
language: number; language: number;
@Column({ comment: '城市' }) @Column({ comment: '城市', nullable: true })
city: number; city: number;
@Column({ comment: '省份' }) @Column({ comment: '省份', nullable: true })
province: number; province: number;
@Column({ comment: '国家' }) @Column({ comment: '国家', nullable: true })
country: number; country: number;
} }

View File

@ -0,0 +1,192 @@
import { Config, Inject, Provide } from '@midwayjs/decorator';
import { BaseService, CoolCommException } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { UserInfoEntity } from '../entity/info';
import { UserWxService } from './wx';
import * as jwt from 'jsonwebtoken';
import { UserWxEntity } from '../entity/wx';
import { CoolFile } from '@cool-midway/file';
import { BaseSysLoginService } from '../../base/service/sys/login';
import { UserSmsService } from './sms';
/**
*
*/
@Provide()
export class UserLoginService extends BaseService {
@InjectEntityModel(UserInfoEntity)
userInfoEntity: Repository<UserInfoEntity>;
@InjectEntityModel(UserWxEntity)
userWxEntity: Repository<UserWxEntity>;
@Inject()
userWxService: UserWxService;
@Config('module.user.jwt')
jwtConfig;
@Inject()
baseSysLoginService: BaseSysLoginService;
@Inject()
file: CoolFile;
@Inject()
userSmsService: UserSmsService;
/**
*
* @param phone
* @param captchaId
* @param code
*/
async smsCode(phone, captchaId, code) {
// 1、检查图片验证码 2、发送短信验证码
const check = await this.baseSysLoginService.captchaCheck(captchaId, code);
if (!check) {
throw new CoolCommException('图片验证码错误');
}
this.userSmsService.sendSms(phone);
}
/**
*
* @param phone
* @param smsCode
*/
async phone(phone, smsCode) {
// 1、检查短信验证码 2、登录
const check = await this.userSmsService.checkCode(phone, smsCode);
if (check) {
let user: any = await this.userInfoEntity.findOneBy({ phone });
if (!user) {
user = { phone, unionid: phone, loginType: 2 };
await this.userInfoEntity.insert(user);
}
return this.token({ userId: user.id });
}
}
/**
*
* @param code
*/
async mp(code: string) {
let wxUserInfo = await this.userWxService.mpUserInfo(code);
if (wxUserInfo) {
delete wxUserInfo.privilege;
wxUserInfo = await this.saveWxInfo({
openid: wxUserInfo.openid,
unionid: wxUserInfo.unionid,
avatarUrl: wxUserInfo.headimgurl,
nickName: wxUserInfo.nickname,
gender: wxUserInfo.sex,
city: wxUserInfo.city,
province: wxUserInfo.province,
country: wxUserInfo.country,
});
return this.wxLoginToken(wxUserInfo);
} else {
throw new Error('微信登录失败');
}
}
/**
*
* @param wxUserInfo
* @returns
*/
async saveWxInfo(wxUserInfo) {
const find: any = {};
if (wxUserInfo.unionid) {
find.unionid = wxUserInfo.unionid;
}
if (wxUserInfo.openid) {
find.openid = wxUserInfo.openid;
}
let wxInfo: any = await this.userWxEntity.findOneBy(find);
if (wxInfo) {
delete wxUserInfo.avatarUrl;
wxUserInfo.id = wxInfo.id;
} else {
// 微信的链接会失效,需要保存到本地
wxUserInfo.avatarUrl = await this.file.downAndUpload(
wxUserInfo.avatarUrl
);
}
await this.userWxEntity.save(wxUserInfo);
return wxUserInfo;
}
/**
*
* @param code
* @param encryptedData
* @param iv
*/
async mini(code, encryptedData, iv) {
let wxUserInfo = await this.userWxService.miniUserInfo(
code,
encryptedData,
iv
);
if (wxUserInfo) {
// 保存
wxUserInfo = await this.saveWxInfo(wxUserInfo);
return this.wxLoginToken(wxUserInfo);
}
}
/**
* token
* @param wxUserInfo
* @returns
*/
async wxLoginToken(wxUserInfo) {
const unionid = wxUserInfo.unionid ? wxUserInfo.unionid : wxUserInfo.openid;
let userInfo: any = await this.userInfoEntity.findOneBy({ unionid });
if (!userInfo) {
userInfo = {
unionid,
nickName: wxUserInfo.nickName,
avatarUrl: wxUserInfo.avatarUrl,
gender: wxUserInfo.gender,
};
await this.userInfoEntity.insert(userInfo);
return this.token({ userId: userInfo.id });
}
}
/**
* token
* @param info
* @returns
*/
async token(info) {
const { expire, refreshExpire } = this.jwtConfig;
return {
expire,
token: await this.generateToken(info),
refreshExpire,
refreshToken: await this.generateToken(info, true),
};
}
/**
* token
* @param tokenInfo
* @param roleIds
*/
async generateToken(info, isRefresh = false) {
const { expire, refreshExpire, secret } = this.jwtConfig;
const tokenInfo = {
isRefresh,
...info,
};
return jwt.sign(tokenInfo, secret, {
expiresIn: isRefresh ? refreshExpire : expire,
});
}
}

View File

@ -30,6 +30,20 @@ export class UserSmsService extends BaseService {
); );
} }
/**
*
* @param phone
* @param code
* @returns
*/
async checkCode(phone, code) {
const cacheCode = await this.cacheManager.get(`sms:${phone}`);
if (cacheCode === code) {
return true;
}
return false;
}
/** /**
* *
* @param phone * @param phone