Authored by QC-L

添加基础登录逻辑 review by 黄敬囿

  1 +import { LoginService } from './loginService.js'
  2 +
  3 +const login = new LoginService().yohoApi();
  4 +
  5 +export const loginAction = (callBack) => {
  6 + return wx.login({
  7 + success(res) {
  8 + if (res.code) {
  9 + login.checkLogin(res.code).then((data) => {
  10 + // 获取 srd_session
  11 + const loginData = data;
  12 + handleLoginData(loginData);
  13 + callBack(null, data);
  14 + }).catch((error) => {
  15 + callBack(error);
  16 + });
  17 + }
  18 + },
  19 + fail(error) {
  20 + callBack(error);
  21 + }
  22 + })
  23 +};
  24 +
  25 +const handleLoginData = (loginData) => {
  26 + // 上报接口返回的结果,线上debug用
  27 + // 缓存 srd_session
  28 + setStorageWithValueForKey('srd_session', loginData.srd_session);
  29 + // 缓存 openid
  30 + setStorageWithValueForKey('openid', loginData.openid);
  31 + // 如果 unionid 不存在(未使用过任何有货微信产品的全新用户),调用getUnionID函数,再次获取unionID
  32 + if (loginData.unionid) {
  33 + // 对于已经授权过的用户,拿到unionid之后获取一次userinfo更新个人信息
  34 + setStorageWithValueForKey('unionid', loginData.unionid);
  35 + }
  36 +}
  37 +
  38 +/**
  39 + * 判断微信用户是否绑定
  40 + * @param srd_session
  41 + * @param result
  42 + * @returns {Promise.}
  43 + */
  44 +export const decodeUnionId = (srd_session, result) => {
  45 + let res = result.detail;
  46 + // 存储 userInfo
  47 + if (res && res.userInfo) {
  48 + setStorageWithValueForKey('userInfo', res.userInfo);
  49 + } else {
  50 + return;
  51 + }
  52 + const nickName = res.userInfo.nickName;
  53 + const avatarUrl = res.userInfo.avatarUrl;
  54 + if (!srd_session) return;
  55 + return login.getUnionID(srd_session, res.encryptedData, res.iv).then((data) => {
  56 + let union_id;
  57 + // 本地保存 union_id
  58 + if (data.union_id) {
  59 + setStorageWithValueForKey('union_id', data.union_id);
  60 + union_id = data.union_id;
  61 + } else {
  62 + union_id = wx.getStorageSync('union_id');
  63 + }
  64 + if (!union_id) {
  65 + throw new Error('union_id is null');
  66 + }
  67 + // 网络上传头像,union_id,昵称
  68 + login.sendWeChatUserDataWithUnionId(data.union_id, nickName, avatarUrl).catch(error => {});
  69 + return {
  70 + union_id: union_id,
  71 + userInfo: res.userInfo
  72 + };
  73 + });
  74 +};
  75 +
  76 +/**
  77 + * 判断微信用户是否绑定
  78 + * @param unionID
  79 + * @param userInfo
  80 + * @returns {Promise.}
  81 + */
  82 +export const wechatUserIsBind = (union_id, userInfo) => {
  83 + return login.wechatUserIsBind(union_id, userInfo.nickName).then(data => {
  84 + if (!isStringEmpty(data.is_bind) && data.is_bind === "Y") {
  85 + const newUserInfo = {
  86 + is_bind: data.is_bind,
  87 + mobile: data.mobile,
  88 + session_key: data.session_key,
  89 + uid: data.uid,
  90 + ssouid: data.ssouid
  91 + };
  92 + const assignUserInfo = Object.assign(newUserInfo, userInfo);
  93 + setStorageWithValueForKey('userInfo', assignUserInfo);
  94 +
  95 + return Promise.resolve({
  96 + code: 10003,
  97 + data: userInfo,
  98 + message: '微信用户已绑定手机号'
  99 + });
  100 + } else {
  101 + return Promise.resolve({
  102 + code: 10004,
  103 + message: '微信用户未绑定手机号'
  104 + });
  105 + }
  106 + });
  107 +}
  108 +
  109 +const setStorageWithValueForKey = (key, value) => {
  110 + const globalData = getGlobalData();
  111 + wx.setStorage({
  112 + key: key,
  113 + data: value,
  114 + })
  115 + globalData[key] = value;
  116 +}
  117 +
  118 +const getStorageWithValueForKey = (key) => {
  119 + wx.getStorage({
  120 + key: key,
  121 + success: function(res) {
  122 +
  123 + },
  124 + })
  125 +}
  126 +
  127 +const getGlobalData = () => {
  128 + const app = getApp();
  129 + let globalData;
  130 + if (app && app.globalData) {
  131 + // 原生小程序的 globalData 获取方式
  132 + globalData = app && app.globalData;
  133 + } else {
  134 + // Taro 下 globalData 获取方式
  135 + globalData = app && app.props && app.props.globalData;
  136 + }
  137 + return globalData;
  138 +}
  139 +
  140 +const isStringEmpty = (str) => {
  141 + if (str === undefined || str === null || str === '') {
  142 + return true
  143 + } else {
  144 + return false
  145 + }
  146 +}
  1 +import BaseService from '../services/baseService.js';
  2 +
  3 +const WECHAT_SMALLPROGRAM_ONLOGIN = 'wechat.smallProgram.onLogin';
  4 +const WECHAT_SMALLPROGRAM_DECODEUSERINFO = 'wechat.smallProgram.decodeUserInfo';
  5 +const WECHAT_ADDUNIONUPDUSER = 'app.wechat.addUnionUpdUser';
  6 +const WECHAT_PASSPORT_SIGNINBYOPENID = 'app.passport.signinByOpenID';
  7 +
  8 +const WechatPath = '/wechat';
  9 +
  10 +export class LoginService extends BaseService {
  11 + async checkLogin(code) {
  12 + return await this.GET({
  13 + method: WECHAT_SMALLPROGRAM_ONLOGIN,
  14 + jsCode: code,
  15 + miniapp_type: '0' // 暂时为 0
  16 + }, {
  17 + path: WechatPath
  18 + }).then((data) => {
  19 + return data;
  20 + }).catch((error) => {
  21 + throw error;
  22 + })
  23 + }
  24 +
  25 + async getUnionID(srdSession, encryptedData, iv) {
  26 + return await this.GET({
  27 + method: WECHAT_SMALLPROGRAM_DECODEUSERINFO,
  28 + srdSession,
  29 + encryptedData,
  30 + iv
  31 + }, {
  32 + path: WechatPath
  33 + }).then((data) => {
  34 + return data;
  35 + }).catch((error) => {
  36 + throw error;
  37 + });
  38 + }
  39 +
  40 + async sendWeChatUserDataWithUnionId(unionId, nickname, avatarUrl) {
  41 + if (!unionId || !nickname || !avatarUrl || avatarUrl === "images/mine_default_head.png") throw new Error('请检查 unionId 或用户信息是否有误');
  42 + return await this.GET({
  43 + method: WECHAT_ADDUNIONUPDUSER,
  44 + unionId,
  45 + headIco: avatarUrl,
  46 + nickname
  47 + }).then(data => {
  48 + return data;
  49 + }).catch(error => {
  50 + throw error;
  51 + })
  52 + }
  53 +
  54 + async wechatUserIsBind(unionId, nickname) {
  55 + return await this.GET({
  56 + method: WECHAT_PASSPORT_SIGNINBYOPENID,
  57 + openId: unionId,
  58 + nickname
  59 + }).then(data => {
  60 + return data
  61 + }).catch(error => {
  62 + throw error;
  63 + });
  64 + }
  65 +}
@@ -4,7 +4,7 @@ import trimObject from './utils/trimObject.js' @@ -4,7 +4,7 @@ import trimObject from './utils/trimObject.js'
4 import queryString from './utils/query-string/query-string.js' 4 import queryString from './utils/query-string/query-string.js'
5 const crypto = require('./utils/cryptojs/cryptojs.js'); 5 const crypto = require('./utils/cryptojs/cryptojs.js');
6 import MD5 from './utils/md5/md5.js' 6 import MD5 from './utils/md5/md5.js'
7 - 7 +import udid from './utils/udid.js'
8 8
9 const request = (methods = 'GET') => { 9 const request = (methods = 'GET') => {
10 return async (options = {}) => { 10 return async (options = {}) => {
@@ -46,7 +46,7 @@ const handleParam = (params) => { @@ -46,7 +46,7 @@ const handleParam = (params) => {
46 let globalData = getGlobalData(); 46 let globalData = getGlobalData();
47 getUid(params); 47 getUid(params);
48 getUdid(params); 48 getUdid(params);
49 - const session_key = globalData && globalData.sessionKey ? globalData.sessionKey : ''; 49 + const session_key = globalData && globalData.userInfo && globalData.userInfo.session_key ? globalData.userInfo.session_key : '';
50 const publicParams = _publicParams(); 50 const publicParams = _publicParams();
51 let newParams = { 51 let newParams = {
52 ...params, 52 ...params,
@@ -59,16 +59,7 @@ const handleParam = (params) => { @@ -59,16 +59,7 @@ const handleParam = (params) => {
59 } 59 }
60 60
61 const getUdid = (params) => { 61 const getUdid = (params) => {
62 - const globalData = getGlobalData();  
63 - if (params && !params.hasOwnProperty('udid')) {  
64 - let udid = globalData.udid ? globalData.udid : "";  
65 - if (udid.length === 0) {  
66 - udid = getYHStorageSync('udid', 'request');  
67 - }  
68 - if (udid.length !== 0) {  
69 - params.udid = udid;  
70 - }  
71 - } 62 + params.udid = udid.get();
72 } 63 }
73 64
74 const getUid = (params) => { 65 const getUid = (params) => {
@@ -130,6 +121,7 @@ const handleHeader = (options, newParams) => { @@ -130,6 +121,7 @@ const handleHeader = (options, newParams) => {
130 const session_key = newParams.session_key ? newParams.session_key : ''; 121 const session_key = newParams.session_key ? newParams.session_key : '';
131 let header = { 122 let header = {
132 'x-yoho-verify': resultString, 123 'x-yoho-verify': resultString,
  124 + 'Cookie': 'JSESSIONID=' + session_key,
133 'Cookies': 'JSESSIONID=' + session_key, 125 'Cookies': 'JSESSIONID=' + session_key,
134 } 126 }
135 return header; 127 return header;
@@ -159,7 +151,7 @@ const sendRequest = (resolve, reject, options) => { @@ -159,7 +151,7 @@ const sendRequest = (resolve, reject, options) => {
159 const statusCode = res.statusCode; 151 const statusCode = res.statusCode;
160 const errMsg = res.errMsg; 152 const errMsg = res.errMsg;
161 const data = res.data; 153 const data = res.data;
162 - if (data && data.code == 200 && data.data) { 154 + if (data && (data.code == 200 || data.code == 301) && data.data) {
163 resolve(data.data); 155 resolve(data.data);
164 } else { 156 } else {
165 const code = res.statusCode; 157 const code = res.statusCode;
  1 +import uuid from './uuid';
  2 +export default {
  3 + get() {
  4 + let udid = wx.getStorageSync('udid');
  5 +
  6 + if (udid) {
  7 + return udid;
  8 + }
  9 +
  10 + udid = uuid().replace(/-/g, '');
  11 + wx.setStorageSync('udid', udid);
  12 +
  13 + return udid;
  14 + }
  15 +};
  1 +// github: https://github.com/kelektiv/node-uuid
  2 +// update: https://wzrd.in/standalone/uuid%2Fv4@latest
  3 +!function(r){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=r();else if("function"==typeof define&&define.amd)define([],r);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.uuidv4=r()}}(function(){return function r(e,n,t){function o(f,u){if(!n[f]){if(!e[f]){var a="function"==typeof require&&require;if(!u&&a)return a(f,!0);if(i)return i(f,!0);var d=new Error("Cannot find module '"+f+"'");throw d.code="MODULE_NOT_FOUND",d}var p=n[f]={exports:{}};e[f][0].call(p.exports,function(r){var n=e[f][1][r];return o(n?n:r)},p,p.exports,r,e,n,t)}return n[f].exports}for(var i="function"==typeof require&&require,f=0;f<t.length;f++)o(t[f]);return o}({1:[function(r,e,n){function t(r,e){var n=e||0,t=o;return t[r[n++]]+t[r[n++]]+t[r[n++]]+t[r[n++]]+"-"+t[r[n++]]+t[r[n++]]+"-"+t[r[n++]]+t[r[n++]]+"-"+t[r[n++]]+t[r[n++]]+"-"+t[r[n++]]+t[r[n++]]+t[r[n++]]+t[r[n++]]+t[r[n++]]+t[r[n++]]}for(var o=[],i=0;i<256;++i)o[i]=(i+256).toString(16).substr(1);e.exports=t},{}],2:[function(r,e,n){var t="undefined"!=typeof crypto&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&msCrypto.getRandomValues.bind(msCrypto);if(t){var o=new Uint8Array(16);e.exports=function(){return t(o),o}}else{var i=new Array(16);e.exports=function(){for(var r,e=0;e<16;e++)0===(3&e)&&(r=4294967296*Math.random()),i[e]=r>>>((3&e)<<3)&255;return i}}},{}],3:[function(r,e,n){function t(r,e,n){var t=e&&n||0;"string"==typeof r&&(e="binary"===r?new Array(16):null,r=null),r=r||{};var f=r.random||(r.rng||o)();if(f[6]=15&f[6]|64,f[8]=63&f[8]|128,e)for(var u=0;u<16;++u)e[t+u]=f[u];return e||i(f)}var o=r("./lib/rng"),i=r("./lib/bytesToUuid");e.exports=t},{"./lib/bytesToUuid":1,"./lib/rng":2}]},{},[3])(3)});
@@ -6,6 +6,7 @@ import searchImg from '../../static/images/search.png'; @@ -6,6 +6,7 @@ import searchImg from '../../static/images/search.png';
6 import {common as commonModel} from '../../models'; 6 import {common as commonModel} from '../../models';
7 import './index.scss'; 7 import './index.scss';
8 import { connect } from '@tarojs/redux'; 8 import { connect } from '@tarojs/redux';
  9 +import { loginAction, decodeUnionId, wechatUserIsBind } from '../../libs/login/login.js'
9 10
10 11
11 @connect(({ filterMenu }) => ({ 12 @connect(({ filterMenu }) => ({
@@ -116,6 +117,18 @@ export default class Index extends Component { @@ -116,6 +117,18 @@ export default class Index extends Component {
116 }) 117 })
117 } 118 }
118 119
  120 + login(e) {
  121 + loginAction((error, loginData) => {
  122 + if (error) return;
  123 + decodeUnionId(loginData.srd_session, e).then(data => {
  124 + console.log(data);
  125 + wechatUserIsBind(data.union_id, data.userInfo).then(message => {
  126 + console.log(message);
  127 + });
  128 + });
  129 + })
  130 + }
  131 +
119 render() { 132 render() {
120 let {tabs, productList} = this.state; 133 let {tabs, productList} = this.state;
121 let {filterMenu} = this.props; 134 let {filterMenu} = this.props;
@@ -133,6 +146,7 @@ export default class Index extends Component { @@ -133,6 +146,7 @@ export default class Index extends Component {
133 <View className="header-nav"> 146 <View className="header-nav">
134 <Text className="page-label">飞碟好物</Text> 147 <Text className="page-label">飞碟好物</Text>
135 <Text onClick={this.gotoNative}>跳转原生页面</Text> 148 <Text onClick={this.gotoNative}>跳转原生页面</Text>
  149 + <Button openType="getUserInfo" onGetUserInfo={this.login}>登录</Button>
136 <Navigator url="/pages/search/index" className="search" hover-class="none"><Image className="search-btn" src={searchImg}></Image></Navigator> 150 <Navigator url="/pages/search/index" className="search" hover-class="none"><Image className="search-btn" src={searchImg}></Image></Navigator>
137 </View> 151 </View>
138 152