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就够了",
|
"description": "一个项目用COOL就够了",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cool-midway/core": "^6.0.1",
|
|
||||||
"@cool-midway/cloud": "^6.0.0",
|
"@cool-midway/cloud": "^6.0.0",
|
||||||
|
"@cool-midway/core": "^6.0.1",
|
||||||
"@cool-midway/file": "^6.0.0",
|
"@cool-midway/file": "^6.0.0",
|
||||||
"@cool-midway/iot": "^6.0.0",
|
"@cool-midway/iot": "^6.0.0",
|
||||||
"@cool-midway/pay": "^6.0.0",
|
"@cool-midway/pay": "^6.0.0",
|
||||||
@ -24,6 +24,8 @@
|
|||||||
"@midwayjs/typeorm": "^3.10.7",
|
"@midwayjs/typeorm": "^3.10.7",
|
||||||
"@midwayjs/validate": "^3.10.7",
|
"@midwayjs/validate": "^3.10.7",
|
||||||
"@midwayjs/view-ejs": "^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",
|
"cache-manager-fs-hash": "^1.0.0",
|
||||||
"ipip-ipdb": "^0.6.0",
|
"ipip-ipdb": "^0.6.0",
|
||||||
"jsonwebtoken": "^9.0.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