pigxCloud微服务项目01——服务端——小程序登录

小程序登录

  • 需求说明
  • 小程序登录服务端代码
  • postman访问测试
  • 小程序端登录代码

需求说明

采用pigxCloud微服务架构,在upms服务中增加controller类,用来管理小程序登录。
虽然pigxCloud微服务中,已有“pigx接入小程序使用”模块,如下图所示。但是这是针对已经录入的用户数据,而本人这边需要的是没有录入系统的小程序用户也能登录系统,所以需要改造。
pigxCloud微服务项目01——服务端——小程序登录_第1张图片

小程序登录服务端代码

package com.cxbdapp.msp.admin.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cxbdapp.msp.admin.api.entity.SysSocialDetails;
import com.cxbdapp.msp.admin.api.entity.SysUser;
import com.cxbdapp.msp.admin.mapper.SysSocialDetailsMapper;
import com.cxbdapp.msp.admin.service.SysUserService;
import com.cxbdapp.msp.admin.utils.VoCodeEnum;
import com.cxbdapp.msp.common.core.constant.CacheConstants;
import com.cxbdapp.msp.common.core.constant.SecurityConstants;
import com.cxbdapp.msp.common.core.constant.enums.LoginTypeEnum;
import com.cxbdapp.msp.common.core.util.R;
import com.cxbdapp.msp.common.security.annotation.Inner;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * 小程序用户登录
 *
 * @author zxy
 * @date 2020-10-22 09:30:00
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/patient")
@Api(value = "patient", tags = "小程序用户登录相关 API")
public class SysUserPatientLoginController {

	private final RedisTemplate redisTemplate;
	private final SysSocialDetailsMapper sysSocialDetailsMapper;
	private final SysUserService sysUserService;

	/**
	 * 小程序登录
	 *
	 * @param code      临时登录凭证
	 * @param loginType 1:医生 2:患者
	 * @param rawData   用户非敏感信息
	 * @return token及用户信息
	 */
	@ApiOperation(value = "小程序登录", notes = "小程序登录")
	@Inner(value = false)
	@GetMapping("/login")
	public R login(@RequestParam(value = "code") String code,
				   @RequestParam(value = "loginType") String loginType,
				   @RequestParam(value = "rawData", required = false) String rawData) {
		String openid;
		try {
			// loginType:医生端 LoginTypeEnum.WECHAT.getType()、患者端 LoginTypeEnum.MINI_APP.getType()
			openid = getOpenIdByCode(code, loginType.equals("1") ? LoginTypeEnum.WECHAT.getType() : LoginTypeEnum.MINI_APP.getType());
			if (openid == null) {
				return R.buildResult(null, VoCodeEnum.FAIL.getCode(), "code换openid失败,查询结果为空");
			}
		} catch (Exception e) {
			log.error("code换openid异常", e);
			return R.buildResult(null, VoCodeEnum.FAIL.getCode(), "code换openid异常");
		}

		try {
			SysUser user = sysUserService.getOne(Wrappers.<SysUser>query().lambda()
					.eq(SysUser::getDelFlag, "0")
					.eq(SysUser::getUserType, loginType)
					.eq(SysUser::getMiniOpenid, openid));
			if (user == null) {
				return R.buildResult(openid, VoCodeEnum.USER_NOEXIST.getCode(), VoCodeEnum.USER_NOEXIST.getMsg());
			}
			// 如果rawData不为空,则更新相关用户信息
			setUserFieldProperty(user, rawData);
			// openid登录,并获取token
			String token = getOpenidToken(user.getMiniOpenid());
			if (token == null) {
				return R.buildResult(openid, VoCodeEnum.FAIL.getCode(), "openid登录获取token失败");
			}

			HashMap<String, Object> map = new HashMap<>(4);
			map.put("token", token);
			map.put("userId", user.getUserId());
			map.put("phone", user.getPhone());
			map.put("miniOpenid", user.getMiniOpenid());
			return R.buildResult(map, VoCodeEnum.SUCCESS.getCode(), "小程序登录成功");
		} catch (Exception e) {
			log.error("小程序登录程序异常", e);
			return R.buildResult(null, VoCodeEnum.FAIL.getCode(), "小程序登录程序异常");
		}
	}

	/**
	 * 临时登录凭证code换小程序openid
	 *
	 * @param code 临时登录凭证
	 * @param type 医生端还是患者端
	 * @return openid
	 */
	private String getOpenIdByCode(String code, String type) {
		SysSocialDetails condition = new SysSocialDetails();
		condition.setType(type);
		SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition));

		String url = String.format(SecurityConstants.MINI_APP_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), socialDetails.getAppSecret(), code);
		String body = HttpUtil.get(url);
		if (StrUtil.isEmpty(body)) {
			return null;
		}
		log.info("微信小程序响应报文:{}", body);

		JSONObject sessionKeyOpenId = JSONUtil.parseObj(body);
		String openid = (String) sessionKeyOpenId.get("openid");
		return openid;
	}

	/**
	 * 将用户非敏感信息set到用户类属性中
	 *
	 * @param user    用户类
	 * @param rawData 用户非敏感信息
	 */
	private void setUserFieldProperty(SysUser user, String rawData) {
		if (rawData == null || "".equals(rawData) || "null".equals(rawData)) {
			return;
		}
		JSONObject rawDataJson = JSONUtil.parseObj(rawData);
		String nickName = (String) rawDataJson.get("nickName"); // 用户昵称
		String avatarUrl = (String) rawDataJson.get("avatarUrl"); // 用户头像图片的 URL
		String gender = rawDataJson.get("gender") + ""; // 用户性别 0未知	1男性 2女性
		String country = (String) rawDataJson.get("country"); // 用户所在国家
		String province = (String) rawDataJson.get("province"); // 用户所在省份
		String city = (String) rawDataJson.get("city"); // 用户所在城市
		String language = (String) rawDataJson.get("language"); // 显示 country,province,city 所用的语言:en英文 zh_CN简体中文 zh_TW繁体中文

		if ("1".equals(user.getUserType())) {
			// 医生端 插入数据库表 ......
		} else {
			// 患者端 插入数据库表 ......
		}
	}

	/**
	 * openid登录,并获取token
	 *
	 * @param miniOpenid 小程序openid
	 * @return token
	 */
	private String getOpenidToken(String miniOpenid) {
		String token;
		try {
			String requestUrl = "http://msp-auth:3000/mobile/token/social?grant_type=mobil&mobile=MINI@" + miniOpenid;
			String result = HttpRequest.post(requestUrl)
					.header("Authorization", "Basic cGlnOnBpZw==")
					.execute().body();
			log.info("响应报文:{}", result);
			JSONObject jsonObject = JSONUtil.parseObj(result);
			token = (String) jsonObject.get("access_token");
			return token;

		} catch (Exception e) {
			log.error("获取token错误", e);
		}
		return null;
	}

	/**
	 * 绑定手机号码
	 *
	 * @param loginType 1:医生 2:患者
	 * @param mobile    手机号码
	 * @param smsCode   手机验证码
	 * @param openid    小程序openid
	 * @return R
	 */
	@ApiOperation(value = "绑定手机号码", notes = "绑定手机号码")
	@Inner(value = false)
	@GetMapping("/bindMobile")
	public R bindMobile(@RequestParam(value = "loginType") String loginType,
						@RequestParam(value = "mobile") String mobile,
						@RequestParam(value = "smsCode") String smsCode,
						@RequestParam(value = "openid") String openid) {
		try {
			log.info("Start check smsCode");
			String check = checkCode(mobile, smsCode);
			if (check != null) {
				return R.buildResult(null, VoCodeEnum.SMSCODE_CHECK_FAIL.getCode(), check);
			}

			SysUser syncUser = sysUserService.getOne(Wrappers.<SysUser>query().lambda()
					.eq(SysUser::getDelFlag, "0")
					.eq(SysUser::getUserType, loginType)
					.eq(SysUser::getPhone, mobile));
			if (syncUser != null) {
				syncUser.setMiniOpenid(openid);
				sysUserService.updateById(syncUser);
				return R.buildResult(null, VoCodeEnum.SUCCESS.getCode(), "绑定手机号码成功,请登录");
			}
			return R.buildResult(null, VoCodeEnum.PUBLIC_ACCOUNT_REGISTER.getCode(), "用户不存在!");
		} catch (Exception e) {
			log.error("绑定手机号码程序异常", e);
			return R.buildResult(null, VoCodeEnum.FAIL.getCode(), "绑定手机号码程序异常, 请稍后尝试或联系管理员!");
		}
	}

	/**
	 * 校验验证码
	 *
	 * @param mobile 手机号
	 * @param code   验证码
	 */
	private String checkCode(String mobile, String code) {
		String str = null;
		if (StrUtil.isBlank(code)) {
			str = "验证码不能为空";
		}

		String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile;
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		if (!redisTemplate.hasKey(key)) {
			str = "手机号或验证码错误";
		}

		Object codeObj = redisTemplate.opsForValue().get(key);
		if (codeObj == null) {
			str = "验证码已过期";
		}

		String saveCode = codeObj != null ? codeObj.toString() : null;
		if (StrUtil.isBlank(saveCode)) {
			redisTemplate.delete(key);
			str = "验证码已过期";
		}

		if (!StrUtil.equals(saveCode, code)) {
			redisTemplate.delete(key);
			str = "手机号或验证码错误";
		}

		redisTemplate.delete(key);
		return str;
	}

}

postman访问测试

测试成功返回结果:

{
    "code": 200,
    "msg": "小程序登录成功",
    "data": {
        "miniOpenid": "oqiSD4hAeiNqBfEMnZxl2eFev6OI",
        "userId": 12,
        "phone": "...........",
        "token": "ffb59af6-f8b5-4970-9ffe-3bd57f8e7ea8"
    }
}

pigxCloud微服务项目01——服务端——小程序登录_第2张图片

小程序端登录代码

拿到token后,小程序端登录带上token即可正常访问:

wx.login({
  success(res){
    wx.request({
      url: url,
      method: 'post',
      header: {
        'Authorization': 'Bearer '+token
      },
      success(r) {
        console.log(r)
      }
    })
  }
})

你可能感兴趣的:(微服务,小程序,cloud,微服务,腾讯云,小程序)