mirror of
https://github.com/cool-team-official/cool-admin-midway.git
synced 2024-11-01 22:20:30 +08:00
新增用户模块
This commit is contained in:
parent
600e2396c4
commit
3f81cf32be
@ -4,8 +4,8 @@
|
||||
"description": "一个项目用COOL就够了",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cool-midway/core": "^6.0.1",
|
||||
"@cool-midway/cloud": "^6.0.0",
|
||||
"@cool-midway/core": "^6.0.1",
|
||||
"@cool-midway/file": "^6.0.0",
|
||||
"@cool-midway/iot": "^6.0.0",
|
||||
"@cool-midway/pay": "^6.0.0",
|
||||
@ -24,6 +24,8 @@
|
||||
"@midwayjs/typeorm": "^3.10.7",
|
||||
"@midwayjs/validate": "^3.10.7",
|
||||
"@midwayjs/view-ejs": "^3.10.7",
|
||||
"axios": "^1.3.5",
|
||||
"@alicloud/pop-core": "^1.7.12",
|
||||
"cache-manager-fs-hash": "^1.0.0",
|
||||
"ipip-ipdb": "^0.6.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
|
40
src/modules/user/config.ts
Normal file
40
src/modules/user/config.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { ModuleConfig } from '@cool-midway/core';
|
||||
|
||||
/**
|
||||
* 模块配置
|
||||
*/
|
||||
export default () => {
|
||||
return {
|
||||
// 模块名称
|
||||
name: '用户模块',
|
||||
// 模块描述
|
||||
description: 'APP、小程序、公众号等用户',
|
||||
// 中间件,只对本模块有效
|
||||
middlewares: [],
|
||||
// 中间件,全局有效
|
||||
globalMiddlewares: [],
|
||||
// 模块加载顺序,默认为0,值越大越优先加载
|
||||
order: 0,
|
||||
// 阿里云短信
|
||||
sms: {
|
||||
signName: '',
|
||||
templateCode: ' ',
|
||||
accessKeyId: '',
|
||||
accessKeySecret: '',
|
||||
// 验证码有效期,单位秒
|
||||
timeout: 60 * 3,
|
||||
},
|
||||
// 微信配置
|
||||
wx: {
|
||||
// 小程序
|
||||
mini: {
|
||||
appid: 'xxx',
|
||||
secret: 'xxx',
|
||||
},
|
||||
mp: {
|
||||
appid: 'xxx',
|
||||
secret: 'xxx',
|
||||
},
|
||||
},
|
||||
} as ModuleConfig;
|
||||
};
|
28
src/modules/user/entity/info.ts
Normal file
28
src/modules/user/entity/info.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { BaseEntity } from '@cool-midway/core';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
@Entity('user_info')
|
||||
export class UserInfoEntity extends BaseEntity {
|
||||
@Index()
|
||||
@Column({ comment: '第三方登录的唯一ID,如:微信、QQ等' })
|
||||
unionid: string;
|
||||
|
||||
@Column({ comment: '头像' })
|
||||
avatarUrl: string;
|
||||
|
||||
@Column({ comment: '昵称' })
|
||||
nickName: string;
|
||||
|
||||
@Index({ unique: true })
|
||||
@Column({ comment: '手机号' })
|
||||
phone: string;
|
||||
|
||||
@Column({ comment: '性别 0-未知 1-男 2-女', default: 0 })
|
||||
gender: number;
|
||||
|
||||
@Column({ comment: '状态 0-正常 1-禁用', default: 0 })
|
||||
status: number;
|
||||
}
|
29
src/modules/user/entity/wx.ts
Normal file
29
src/modules/user/entity/wx.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { BaseEntity } from '@cool-midway/core';
|
||||
import { Column, Entity } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 微信用户
|
||||
*/
|
||||
@Entity('user_wx')
|
||||
export class UserWxEntity extends BaseEntity {
|
||||
@Column({ comment: '头像', nullable: true })
|
||||
avatarUrl: number;
|
||||
|
||||
@Column({ comment: '昵称', nullable: true })
|
||||
nickName: string;
|
||||
|
||||
@Column({ comment: '性别 0-未知 1-男 2-女', default: 0 })
|
||||
gender: number;
|
||||
|
||||
@Column({ comment: '语言' })
|
||||
language: number;
|
||||
|
||||
@Column({ comment: '城市' })
|
||||
city: number;
|
||||
|
||||
@Column({ comment: '省份' })
|
||||
province: number;
|
||||
|
||||
@Column({ comment: '国家' })
|
||||
country: number;
|
||||
}
|
61
src/modules/user/service/sms.ts
Normal file
61
src/modules/user/service/sms.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { Provide, Config, Inject } from '@midwayjs/decorator';
|
||||
import { BaseService } from '@cool-midway/core';
|
||||
import * as _ from 'lodash';
|
||||
import * as Core from '@alicloud/pop-core';
|
||||
import { CacheManager } from '@midwayjs/cache';
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Provide()
|
||||
export class UserSmsService extends BaseService {
|
||||
// 获得模块的配置信息
|
||||
@Config('module.user.sms')
|
||||
config;
|
||||
|
||||
@Inject()
|
||||
cacheManager: CacheManager;
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param phone
|
||||
*/
|
||||
async sendSms(phone) {
|
||||
const TemplateParam = { code: _.random(1000, 9999) };
|
||||
await this.send(phone, TemplateParam);
|
||||
this.cacheManager.set(
|
||||
`sms:${phone}`,
|
||||
TemplateParam.code,
|
||||
this.config.sms.timeout
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
* @param phone
|
||||
* @param templateCode
|
||||
* @param template
|
||||
*/
|
||||
async send(phone, TemplateParam) {
|
||||
const { signName, accessKeyId, accessKeySecret, templateCode } =
|
||||
this.config;
|
||||
const client = new Core({
|
||||
accessKeyId,
|
||||
accessKeySecret,
|
||||
endpoint: 'https://dysmsapi.aliyuncs.com',
|
||||
// endpoint: 'https://cs.cn-hangzhou.aliyuncs.com',
|
||||
apiVersion: '2017-05-25',
|
||||
// apiVersion: '2018-04-18',
|
||||
});
|
||||
const params = {
|
||||
RegionId: 'cn-shanghai',
|
||||
PhoneNumbers: phone,
|
||||
signName,
|
||||
templateCode,
|
||||
TemplateParam: JSON.stringify(TemplateParam),
|
||||
};
|
||||
return await client.request('SendSms', params, {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
}
|
167
src/modules/user/service/wx.ts
Normal file
167
src/modules/user/service/wx.ts
Normal file
@ -0,0 +1,167 @@
|
||||
import { Config, Provide } from '@midwayjs/decorator';
|
||||
import { BaseService, CoolCommException } from '@cool-midway/core';
|
||||
import axios from 'axios';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
@Provide()
|
||||
export class UserWxService extends BaseService {
|
||||
@Config('module.user')
|
||||
config;
|
||||
|
||||
/**
|
||||
* 获得公众号用户信息
|
||||
* @param code
|
||||
*/
|
||||
async mpUserInfo(code) {
|
||||
const token = await this.openOrMpToken(code, this.config.wx.mp);
|
||||
return await this.openOrMpUserInfo(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得微信token 不用code
|
||||
* @param appid
|
||||
* @param secret
|
||||
*/
|
||||
public async getWxToken(type = 'mp') {
|
||||
//@ts-ignore
|
||||
const conf = this.config.wx[type];
|
||||
return await axios.get('https://api.weixin.qq.com/cgi-bin/token', {
|
||||
params: {
|
||||
grant_type: 'client_credential',
|
||||
appid: conf.appid,
|
||||
secret: conf.secret,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户信息
|
||||
* @param token
|
||||
*/
|
||||
async openOrMpUserInfo(token) {
|
||||
return await axios
|
||||
.get('https://api.weixin.qq.com/sns/userinfo', {
|
||||
params: {
|
||||
access_token: token.access_token,
|
||||
openid: token.openid,
|
||||
lang: 'zh_CN',
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
return res.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得token嗯
|
||||
* @param code
|
||||
* @param conf
|
||||
*/
|
||||
async openOrMpToken(code, conf) {
|
||||
const result = await axios.get(
|
||||
'https://api.weixin.qq.com/sns/oauth2/access_token',
|
||||
{
|
||||
params: {
|
||||
appid: conf.appid,
|
||||
secret: conf.secret,
|
||||
code,
|
||||
grant_type: 'authorization_code',
|
||||
},
|
||||
}
|
||||
);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得小程序session
|
||||
* @param code 微信code
|
||||
* @param conf 配置
|
||||
*/
|
||||
async miniSession(code) {
|
||||
const { appid, secret } = this.config.wx.mini;
|
||||
const result = await axios.get(
|
||||
'https://api.weixin.qq.com/sns/jscode2session',
|
||||
{
|
||||
params: {
|
||||
appid,
|
||||
secret,
|
||||
js_code: code,
|
||||
grant_type: 'authorization_code',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得小程序用户信息
|
||||
* @param code
|
||||
* @param encryptedData
|
||||
* @param iv
|
||||
*/
|
||||
async miniUserInfo(code, encryptedData, iv) {
|
||||
const session = await this.miniSession(code);
|
||||
if (session.errcode) {
|
||||
throw new CoolCommException('登录失败,请重试');
|
||||
}
|
||||
const info: any = await this.miniDecryptData(
|
||||
encryptedData,
|
||||
iv,
|
||||
session.session_key
|
||||
);
|
||||
if (info) {
|
||||
delete info['watermark'];
|
||||
return {
|
||||
...info,
|
||||
openid: session['openid'],
|
||||
unionid: session['unionid'],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得小程序手机
|
||||
* @param code
|
||||
* @param encryptedData
|
||||
* @param iv
|
||||
*/
|
||||
async miniPhone(code, encryptedData, iv) {
|
||||
const session = await this.miniSession(code);
|
||||
if (session.errcode) {
|
||||
throw new CoolCommException('获取手机号失败,请刷新重试');
|
||||
}
|
||||
return await this.miniDecryptData(encryptedData, iv, session.session_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序信息解密
|
||||
* @param encryptedData
|
||||
* @param iv
|
||||
* @param sessionKey
|
||||
*/
|
||||
async miniDecryptData(encryptedData, iv, sessionKey) {
|
||||
sessionKey = Buffer.from(sessionKey, 'base64');
|
||||
encryptedData = Buffer.from(encryptedData, 'base64');
|
||||
iv = Buffer.from(iv, 'base64');
|
||||
try {
|
||||
// 解密
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
|
||||
// 设置自动 padding 为 true,删除填充补位
|
||||
decipher.setAutoPadding(true);
|
||||
// @ts-ignore
|
||||
let decoded = decipher.update(encryptedData, 'binary', 'utf8');
|
||||
// @ts-ignore
|
||||
decoded += decipher.final('utf8');
|
||||
// @ts-ignore
|
||||
decoded = JSON.parse(decoded);
|
||||
return decoded;
|
||||
} catch (err) {
|
||||
throw new CoolCommException('获得信息失败');
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user