login.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. 'use strict';
  2. const assert = require('assert');
  3. const _ = require('lodash');
  4. const { ObjectId } = require('mongoose').Types;
  5. const { CrudService } = require('naf-framework-mongoose/lib/service');
  6. const { BusinessError, ErrorCode } = require('naf-core').Error;
  7. const jwt = require('jsonwebtoken');
  8. const uuid = require('uuid');
  9. class LoginService extends CrudService {
  10. constructor(ctx) {
  11. super(ctx, 'login');
  12. this.uModel = this.ctx.model.User;
  13. this.stuModel = this.ctx.model.Student;
  14. this.tModel = this.ctx.model.Teacher;
  15. this.schModel = this.ctx.model.School;
  16. this.hModel = this.ctx.model.Headteacher;
  17. }
  18. async login(data) {
  19. const { mobile, passwd } = data;
  20. assert(mobile, 'mobile不能为空');
  21. // assert(/^\d{11}$/i.test(mobile), 'mobile无效');
  22. assert(passwd, 'passwd不能为空');
  23. const res = await this.uModel.findOne({ mobile }, '+passwd');
  24. if (!res) {
  25. throw new BusinessError(ErrorCode.USER_NOT_EXIST);
  26. }
  27. // 验证密码
  28. console.log(res.passwd.secret);
  29. console.log(passwd);
  30. if (res.passwd.secret !== passwd) {
  31. throw new BusinessError(ErrorCode.BAD_PASSWORD);
  32. }
  33. return await this.createJwt(res);
  34. }
  35. // 创建登录Token
  36. async createJwt({ _id, name, mobile, openid, type, uid, status }) {
  37. const { secret, expiresIn = '1d', issuer = type } = this.config.jwt;
  38. const subject = mobile;
  39. let _userid = '';
  40. let res = {};
  41. if (type === '0') {
  42. _userid = _id.toString();
  43. res = { userid: _userid, name, type, id: _id, status };
  44. } else if (type === '1') {
  45. _userid = uid.toString();
  46. const result = await this.hModel.findById(_userid);
  47. res = { userid: _userid, name, type, id: _id, status };
  48. } else if (type === '2') {
  49. _userid = uid.toString();
  50. const result = await this.schModel.findById(_userid);
  51. res = { userid: _userid, code: result.code, name, type, id: _id, status };
  52. } else if (type === '3') {
  53. _userid = uid.toString();
  54. const result = await this.tModel.findById(_userid);
  55. res = { userid: _userid, schid: result.schid, schname: result.schname, name, type, id: _id, status };
  56. } else if (type === '4') {
  57. _userid = uid.toString();
  58. const result = await this.stuModel.findById(_userid);
  59. res = { userid: _userid, schid: result.schoolid, schname: result.school_name, termid: result.termid, batchid: result.batchid, classid: result.classid, bedroomid: result.bedroomid, bedroom: result.bedroom, job: result.job, name, type, id: _id, status };
  60. }
  61. const token = await jwt.sign(res, secret, { expiresIn, issuer, subject });
  62. return token;
  63. }
  64. // 微信校验mq接口
  65. async wxcheck(data) {
  66. const { qrcode, openid } = data;
  67. assert(qrcode, 'qrcode不能为空');
  68. assert(openid, 'openid不能为空');
  69. // TODO: 发布扫码成功消息
  70. const { mq } = this.ctx;
  71. const ex = 'qrcode.login';
  72. const parm = {
  73. durable: true,
  74. headers: {
  75. openid,
  76. } };
  77. if (mq) {
  78. await mq.topic(ex, qrcode, 'scaned', parm);
  79. }
  80. }
  81. // 微信登录接口
  82. async wxlogin(data) {
  83. const { qrcode, openid } = data;
  84. assert(qrcode, 'qrcode不能为空');
  85. assert(openid, 'openid不能为空');
  86. const res = await this.uModel.findOne({ openid });
  87. if (!res) {
  88. throw new BusinessError(ErrorCode.USER_NOT_EXIST);
  89. }
  90. const result = await this.createJwt(res);
  91. return result;
  92. }
  93. async userbind(data) {
  94. const { qrcode, openid } = data;
  95. assert(qrcode, 'qrcode不能为空');
  96. assert(openid, 'openid不能为空');
  97. const key = `naf:qrcode:login:${qrcode}`;
  98. const status = await this.app.redis.get(key);
  99. if (!status) {
  100. throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
  101. }
  102. if (status !== 'pending') {
  103. throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码状态无效');
  104. }
  105. const res = await this.uModel.findOne({ openid });
  106. if (!res) {
  107. throw new BusinessError(ErrorCode.USER_NOT_EXIST);
  108. }
  109. const result = await this.createJwt(res);
  110. // TODO: 修改二维码状态,登录凭证保存到redis
  111. await this.app.redis.set(key, `scaned:${result}`, 'EX', 600);
  112. // TODO: 发布扫码成功消息
  113. const { mq } = this.ctx;
  114. const ex = 'qrcode.login';
  115. if (mq) {
  116. await mq.topic(ex, qrcode, 'scaned', { durable: true });
  117. }
  118. }
  119. /**
  120. * 创建二维码
  121. * 随机生成二维码,并保存在Redis中,状态初始为pending
  122. * 状态描述:
  123. * pending - 等待扫码
  124. * consumed - 使用二维码登录完成
  125. * ${jwt.token} - Jwt登录凭证
  126. */
  127. async createQrcode() {
  128. const qrcode = uuid();
  129. const key = `naf:qrcode:login:${qrcode}`;
  130. await this.app.redis.set(key, 'pending', 'EX', 600);
  131. return qrcode;
  132. }
  133. // 使用二维码换取登录凭证
  134. async qrcodeLogin(qrcode) {
  135. assert(qrcode, 'qrcode不能为空');
  136. const key = `naf:qrcode:login:${qrcode}`;
  137. const val = await this.app.redis.get(key);
  138. if (!val) {
  139. throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
  140. }
  141. const [ status, token ] = val.split(':', 2);
  142. if (status !== 'scaned' || !token) {
  143. throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码状态无效');
  144. }
  145. // TODO: 修改二维码状态
  146. await this.app.redis.set(key, 'consumed', 'EX', 600);
  147. return { token };
  148. }
  149. // 检查二维码状态
  150. async checkQrcode(qrcode) {
  151. assert(qrcode, 'qrcode不能为空');
  152. const key = `naf:qrcode:login:${qrcode}`;
  153. const val = await this.app.redis.get(key);
  154. if (!val) {
  155. throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
  156. }
  157. const [ status ] = val.split(':', 2);
  158. return { status };
  159. }
  160. }
  161. module.exports = LoginService;