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
aab2c47898
commit
4aed6f02b1
@ -1,3 +0,0 @@
|
||||
核心包单独项目
|
||||
|
||||
请转到:https://github.com/cool-team-official/cool-admin-midway-packages 查看
|
@ -21,19 +21,6 @@ export default () => {
|
||||
// 验证码有效期,单位秒
|
||||
timeout: 60 * 3,
|
||||
},
|
||||
// 微信配置
|
||||
wx: {
|
||||
// 小程序
|
||||
mini: {
|
||||
appid: '',
|
||||
secret: '',
|
||||
},
|
||||
// 公众号
|
||||
mp: {
|
||||
appid: '',
|
||||
secret: '',
|
||||
},
|
||||
},
|
||||
// jwt
|
||||
jwt: {
|
||||
// token 过期时间,单位秒
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { CoolController, BaseController } from '@cool-midway/core';
|
||||
import { UserAddressEntity } from '../../entity/address';
|
||||
import { UserAddressService } from '../../service/address';
|
||||
|
||||
/**
|
||||
* 用户-地址
|
||||
@ -7,5 +8,6 @@ import { UserAddressEntity } from '../../entity/address';
|
||||
@CoolController({
|
||||
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
|
||||
entity: UserAddressEntity,
|
||||
service: UserAddressService,
|
||||
})
|
||||
export class AdminUserAddressesController extends BaseController {}
|
||||
|
39
src/modules/user/controller/app/address.ts
Normal file
39
src/modules/user/controller/app/address.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Get, Inject, Provide } from '@midwayjs/decorator';
|
||||
import { CoolController, BaseController } from '@cool-midway/core';
|
||||
import { UserAddressEntity } from '../../entity/address';
|
||||
import { UserAddressService } from '../../service/address';
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
@Provide()
|
||||
@CoolController({
|
||||
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
|
||||
entity: UserAddressEntity,
|
||||
service: UserAddressService,
|
||||
insertParam: ctx => {
|
||||
return {
|
||||
userId: ctx.user.id,
|
||||
};
|
||||
},
|
||||
pageQueryOp: {
|
||||
where: async ctx => {
|
||||
return [['userId =:userId', { userId: ctx.user.id }]];
|
||||
},
|
||||
addOrderBy: {
|
||||
isDefault: 'DESC',
|
||||
},
|
||||
},
|
||||
})
|
||||
export class AppUserAddressController extends BaseController {
|
||||
@Inject()
|
||||
userAddressService: UserAddressService;
|
||||
|
||||
@Inject()
|
||||
ctx;
|
||||
|
||||
@Get('/default', { summary: '默认地址' })
|
||||
async default() {
|
||||
return this.ok(await this.userAddressService.default(this.ctx.user.id));
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import {
|
||||
TagTypes,
|
||||
CoolTag,
|
||||
} from '@cool-midway/core';
|
||||
import { Get, Inject, Query } from '@midwayjs/core';
|
||||
import { Body, Inject, Post } from '@midwayjs/core';
|
||||
import { UserWxService } from '../../service/wx';
|
||||
|
||||
/**
|
||||
@ -18,9 +18,8 @@ export class UserCommController extends BaseController {
|
||||
userWxService: UserWxService;
|
||||
|
||||
@CoolTag(TagTypes.IGNORE_TOKEN)
|
||||
@Get('/wxMpConfig', { summary: '获取微信公众号配置' })
|
||||
public async getWxMpConfig(@Query() url: string) {
|
||||
const a = await this.userWxService.getWxMpConfig(url);
|
||||
return this.ok(a);
|
||||
@Post('/wxMpConfig', { summary: '获取微信公众号配置' })
|
||||
public async getWxMpConfig(@Body('url') url: string) {
|
||||
return this.ok(await this.userWxService.getWxMpConfig(url));
|
||||
}
|
||||
}
|
||||
|
@ -43,4 +43,23 @@ export class AppUserInfoController extends BaseController {
|
||||
await this.userInfoService.logoff(this.ctx.user.id);
|
||||
return this.ok();
|
||||
}
|
||||
|
||||
@Post('/bindPhone', { summary: '绑定手机号' })
|
||||
async bindPhone(@Body('phone') phone: string, @Body('code') code: string) {
|
||||
await this.userInfoService.bindPhone(this.ctx.user.id, phone, code);
|
||||
return this.ok();
|
||||
}
|
||||
|
||||
@Post('/miniPhone', { summary: '绑定小程序手机号' })
|
||||
async miniPhone(@Body() body) {
|
||||
const { code, encryptedData, iv } = body;
|
||||
return this.ok(
|
||||
await this.userInfoService.miniPhone(
|
||||
this.ctx.user.id,
|
||||
code,
|
||||
encryptedData,
|
||||
iv
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,12 @@ export class AppUserLoginController extends BaseController {
|
||||
return this.ok(await this.userLoginService.mp(code));
|
||||
}
|
||||
|
||||
@CoolTag(TagTypes.IGNORE_TOKEN)
|
||||
@Post('/wxApp', { summary: '微信APP授权登录' })
|
||||
async app(@Body('code') code: string) {
|
||||
return this.ok(await this.userLoginService.wxApp(code));
|
||||
}
|
||||
|
||||
@CoolTag(TagTypes.IGNORE_TOKEN)
|
||||
@Post('/phone', { summary: '手机号登录' })
|
||||
async phone(@Body('phone') phone: string, @Body('smsCode') smsCode: string) {
|
||||
|
63
src/modules/user/service/address.ts
Normal file
63
src/modules/user/service/address.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Init, Inject, Provide } from '@midwayjs/decorator';
|
||||
import { BaseService } from '@cool-midway/core';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { UserAddressEntity } from '../entity/address';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
@Provide()
|
||||
export class UserAddressService extends BaseService {
|
||||
@InjectEntityModel(UserAddressEntity)
|
||||
userAddressEntity: Repository<UserAddressEntity>;
|
||||
|
||||
@Inject()
|
||||
ctx;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
await super.init();
|
||||
this.setEntity(this.userAddressEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表信息
|
||||
*/
|
||||
async list() {
|
||||
return this.userAddressEntity
|
||||
.createQueryBuilder()
|
||||
.where('userId = :userId ', { userId: this.ctx.user.id })
|
||||
.addOrderBy('isDefault', 'DESC')
|
||||
.getMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改之后
|
||||
* @param data
|
||||
* @param type
|
||||
*/
|
||||
async modifyAfter(data: any, type: 'add' | 'update' | 'delete') {
|
||||
if (type == 'add' || type == 'update') {
|
||||
if (data.isDefault) {
|
||||
await this.userAddressEntity
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({ isDefault: false })
|
||||
.where('userId = :userId ', { userId: this.ctx.user.id })
|
||||
.andWhere('id != :id', { id: data.id })
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认地址
|
||||
*/
|
||||
async default(userId) {
|
||||
return await this.userAddressEntity.findOneBy({
|
||||
userId: Equal(userId),
|
||||
isDefault: true,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import { Inject, Provide } from '@midwayjs/decorator';
|
||||
import { BaseService, CoolCommException } from '@cool-midway/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { UserInfoEntity } from '../entity/info';
|
||||
import { v1 as uuid } from 'uuid';
|
||||
import { UserSmsService } from './sms';
|
||||
import * as md5 from 'md5';
|
||||
import { PluginService } from '../../plugin/service/info';
|
||||
import { UserWxService } from './wx';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
@ -22,13 +23,29 @@ export class UserInfoService extends BaseService {
|
||||
@Inject()
|
||||
userSmsService: UserSmsService;
|
||||
|
||||
@Inject()
|
||||
userWxService: UserWxService;
|
||||
|
||||
/**
|
||||
* 绑定小程序手机号
|
||||
* @param userId
|
||||
* @param code
|
||||
* @param encryptedData
|
||||
* @param iv
|
||||
*/
|
||||
async miniPhone(userId: number, code: any, encryptedData: any, iv: any) {
|
||||
const phone = await this.userWxService.miniPhone(code, encryptedData, iv);
|
||||
await this.userInfoEntity.update({ id: Equal(userId) }, { phone });
|
||||
return phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
async person(id) {
|
||||
return await this.userInfoEntity.findOneBy({ id });
|
||||
return await this.userInfoEntity.findOneBy({ id: Equal(id) });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,8 +72,9 @@ export class UserInfoService extends BaseService {
|
||||
* @returns
|
||||
*/
|
||||
async updatePerson(id, param) {
|
||||
const info = await this.person(id);
|
||||
if (!info) throw new CoolCommException('用户不存在');
|
||||
try {
|
||||
const info = await this.person(id);
|
||||
// 修改了头像要重新处理
|
||||
if (param.avatarUrl && info.avatarUrl != param.avatarUrl) {
|
||||
const file = await this.pluginService.getInstance('upload');
|
||||
@ -65,6 +83,8 @@ export class UserInfoService extends BaseService {
|
||||
uuid() + '.png'
|
||||
);
|
||||
}
|
||||
} catch (err) {}
|
||||
try {
|
||||
return await this.userInfoEntity.update({ id }, param);
|
||||
} catch (err) {
|
||||
throw new CoolCommException('更新失败,参数错误或者手机号已存在');
|
||||
@ -85,4 +105,18 @@ export class UserInfoService extends BaseService {
|
||||
}
|
||||
await this.userInfoEntity.update(user.id, { password: md5(password) });
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号
|
||||
* @param userId
|
||||
* @param phone
|
||||
* @param code
|
||||
*/
|
||||
async bindPhone(userId, phone, code) {
|
||||
const check = await this.userSmsService.checkCode(phone, code);
|
||||
if (!check) {
|
||||
throw new CoolCommException('验证码错误');
|
||||
}
|
||||
await this.userInfoEntity.update({ id: userId }, { phone });
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Config, Inject, Provide } from '@midwayjs/decorator';
|
||||
import { BaseService, CoolCommException } from '@cool-midway/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { UserInfoEntity } from '../entity/info';
|
||||
import { UserWxService } from './wx';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
@ -62,7 +62,9 @@ export class UserLoginService extends BaseService {
|
||||
// 1、检查短信验证码 2、登录
|
||||
const check = await this.userSmsService.checkCode(phone, smsCode);
|
||||
if (check) {
|
||||
let user: any = await this.userInfoEntity.findOneBy({ phone });
|
||||
let user: any = await this.userInfoEntity.findOneBy({
|
||||
phone: Equal(phone),
|
||||
});
|
||||
if (!user) {
|
||||
user = {
|
||||
phone,
|
||||
@ -105,6 +107,33 @@ export class UserLoginService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信APP授权登录
|
||||
* @param code
|
||||
*/
|
||||
async wxApp(code: string) {
|
||||
let wxUserInfo = await this.userWxService.appUserInfo(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,
|
||||
},
|
||||
1
|
||||
);
|
||||
return this.wxLoginToken(wxUserInfo);
|
||||
} else {
|
||||
throw new Error('微信登录失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存微信信息
|
||||
* @param wxUserInfo
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { Config, Provide } from '@midwayjs/decorator';
|
||||
import { Config, Init, Inject, Provide } from '@midwayjs/decorator';
|
||||
import { BaseService, CoolCache, CoolCommException } from '@cool-midway/core';
|
||||
import axios from 'axios';
|
||||
import * as crypto from 'crypto';
|
||||
import { v1 as uuid } from 'uuid';
|
||||
import * as moment from 'moment';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Equal, Repository } from 'typeorm';
|
||||
import { UserInfoEntity } from '../entity/info';
|
||||
import { UserWxEntity } from '../entity/wx';
|
||||
import { PluginService } from '../../plugin/service/info';
|
||||
|
||||
/**
|
||||
* 微信
|
||||
@ -13,6 +18,66 @@ export class UserWxService extends BaseService {
|
||||
@Config('module.user')
|
||||
config;
|
||||
|
||||
@InjectEntityModel(UserInfoEntity)
|
||||
userInfoEntity: Repository<UserInfoEntity>;
|
||||
|
||||
@InjectEntityModel(UserWxEntity)
|
||||
userWxEntity: Repository<UserWxEntity>;
|
||||
|
||||
@Inject()
|
||||
pluginService: PluginService;
|
||||
|
||||
/**
|
||||
* 获得小程序实例
|
||||
* @returns
|
||||
*/
|
||||
async getMiniApp() {
|
||||
const wxPlugin = await this.pluginService.getInstance('wx');
|
||||
return wxPlugin.MiniApp();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得公众号实例
|
||||
* @returns
|
||||
*/
|
||||
async getOfficialAccount() {
|
||||
const wxPlugin = await this.pluginService.getInstance('wx');
|
||||
return wxPlugin.OfficialAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得App实例
|
||||
* @returns
|
||||
*/
|
||||
async getOpenPlatform() {
|
||||
const wxPlugin = await this.pluginService.getInstance('wx');
|
||||
return wxPlugin.OpenPlatform();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户的openId
|
||||
* @param userId
|
||||
* @param type 0-小程序 1-公众号 2-App
|
||||
*/
|
||||
async getOpenid(userId: number, type = 0) {
|
||||
const user = await this.userInfoEntity.findOneBy({
|
||||
id: Equal(userId),
|
||||
status: 1,
|
||||
});
|
||||
if (!user) {
|
||||
throw new CoolCommException('用户不存在或已被禁用');
|
||||
}
|
||||
const wx = await this.userWxEntity
|
||||
.createQueryBuilder('a')
|
||||
.where('a.type = :type', { type })
|
||||
.andWhere('(a.unionid = :unionid or a.openid =:openid )', {
|
||||
unionid: user.unionid,
|
||||
openid: user.unionid,
|
||||
})
|
||||
.getOne();
|
||||
return wx ? wx.openid : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得微信配置
|
||||
* @param appId
|
||||
@ -30,7 +95,9 @@ export class UserWxService extends BaseService {
|
||||
},
|
||||
}
|
||||
);
|
||||
const { appid } = this.config.wx.mp;
|
||||
|
||||
const account = (await this.getOfficialAccount()).getAccount();
|
||||
const appid = account.getAppId();
|
||||
// 返回结果集
|
||||
const result = {
|
||||
timestamp: parseInt(moment().valueOf() / 1000 + ''),
|
||||
@ -57,7 +124,16 @@ export class UserWxService extends BaseService {
|
||||
* @param code
|
||||
*/
|
||||
async mpUserInfo(code) {
|
||||
const token = await this.openOrMpToken(code, this.config.wx.mp);
|
||||
const token = await this.openOrMpToken(code, 'mp');
|
||||
return await this.openOrMpUserInfo(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得app用户信息
|
||||
* @param code
|
||||
*/
|
||||
async appUserInfo(code) {
|
||||
const token = await this.openOrMpToken(code, 'open');
|
||||
return await this.openOrMpUserInfo(token);
|
||||
}
|
||||
|
||||
@ -66,18 +142,14 @@ export class UserWxService extends BaseService {
|
||||
* @param appid
|
||||
* @param secret
|
||||
*/
|
||||
@CoolCache(3600)
|
||||
public async getWxToken(type = 'mp') {
|
||||
//@ts-ignore
|
||||
const conf = this.config.wx[type];
|
||||
const result = await axios.get('https://api.weixin.qq.com/cgi-bin/token', {
|
||||
params: {
|
||||
grant_type: 'client_credential',
|
||||
appid: conf.appid,
|
||||
secret: conf.secret,
|
||||
},
|
||||
});
|
||||
return result.data;
|
||||
let app;
|
||||
if (type == 'mp') {
|
||||
app = await this.getOfficialAccount();
|
||||
} else {
|
||||
app = await this.getOpenPlatform();
|
||||
}
|
||||
return await app.getAccessToken().getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,15 +173,19 @@ export class UserWxService extends BaseService {
|
||||
/**
|
||||
* 获得token嗯
|
||||
* @param code
|
||||
* @param conf
|
||||
* @param type
|
||||
*/
|
||||
async openOrMpToken(code, conf) {
|
||||
async openOrMpToken(code, type = 'mp') {
|
||||
const account =
|
||||
type == 'mp'
|
||||
? (await this.getOfficialAccount()).getAccount()
|
||||
: (await this.getMiniApp()).getAccount();
|
||||
const result = await axios.get(
|
||||
'https://api.weixin.qq.com/sns/oauth2/access_token',
|
||||
{
|
||||
params: {
|
||||
appid: conf.appid,
|
||||
secret: conf.secret,
|
||||
appid: account.getAppId(),
|
||||
secret: account.getSecret(),
|
||||
code,
|
||||
grant_type: 'authorization_code',
|
||||
},
|
||||
@ -124,20 +200,10 @@ export class UserWxService extends BaseService {
|
||||
* @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;
|
||||
const app = await this.getMiniApp();
|
||||
const utils = app.getUtils();
|
||||
const result = await utils.codeToSession(code);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,7 +244,12 @@ export class UserWxService extends BaseService {
|
||||
if (session.errcode) {
|
||||
throw new CoolCommException('获取手机号失败,请刷新重试');
|
||||
}
|
||||
return await this.miniDecryptData(encryptedData, iv, session.session_key);
|
||||
const result = await this.miniDecryptData(
|
||||
encryptedData,
|
||||
iv,
|
||||
session.session_key
|
||||
);
|
||||
return result.phoneNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,23 +259,8 @@ export class UserWxService extends BaseService {
|
||||
* @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('获得信息失败');
|
||||
}
|
||||
const app = await this.getMiniApp();
|
||||
const utils = app.getUtils();
|
||||
return await utils.decryptSession(sessionKey, iv, encryptedData);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user