Commit b6f136eb authored by shangtx's avatar shangtx

chore: 小程序登录

parent 239f7354
package com.onsiteservice.miniapp.controller.weixn;
import cn.binarywang.wx.miniapp.api.WxMaService;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.onsiteservice.common.annotation.user.CurrentUserId;
import com.onsiteservice.core.result.Result;
import com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO;
import com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO;
import com.onsiteservice.miniapp.service.weixin.WeiXinService;
import com.onsiteservice.miniapp.service.weixin.dto.MiniQrCodeDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static com.onsiteservice.core.result.ResultGenerator.success;
/**
* 微信相关Controller
*/
@ApiSupport(order = 2)
@Api(tags = "微信小程序")
@RestController
@RequestMapping("/weixin")
@Validated
@Slf4j
public class WeiXinController {
@Resource
private WxMaService wxMaService;
@Resource
private WeiXinService weiXinService;
/**
* 微信登录
*/
@ApiOperation(value = "微信登录", notes = "作者: 潘维吉")
@PostMapping("/login")
public Result login(@RequestBody @Validated LoginDTO loginDTO) {
return weiXinService.userLogin(loginDTO);
}
/**
* 获取微信用户绑定手机号信息
*/
@ApiOperation(value = "获取微信手机号", notes = "作者: 潘维吉")
@PostMapping("/phone")
public Result getPhone(@RequestBody @Validated WxEncryptDTO wxEncryptDTO, @CurrentUserId Long userId) {
return success(weiXinService.getPhone(wxEncryptDTO, userId), "获取用户绑定手机号信息成功");
}
/**
* 获取微信小程序码
* 文档: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html
*/
@ApiOperation(value = "获取微信小程序码", notes = "作者: 潘维吉")
@PostMapping("/qr-code")
public Result getQrCode(@RequestBody @NonNull @Validated MiniQrCodeDTO miniQrCodeDTO) {
return weiXinService.getQrCode(miniQrCodeDTO);
}
}
package com.onsiteservice.miniapp.controller.weixn.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
/**
* @author 潘维吉
* @date 2019-3-20 13:36
* 微信登录参数
* DTO 数据传输对象类 Bean是跟数据库有关的实体类,而DTO是与数据库无关的数据,只是为了数据显示时创建的中间层用于数据展示的
* DTO通常用于不同层(UI层、服务层或者域模型层)直接的数据传输,以隔离不同层,降低层间耦合
* DTO类来继承entity实体类,在DTO类里放一些业务字段,并提供get、set方法
* 当我们在业务逻辑层或者交互层用到一些数据库中不存在的字段时,我们就需要在DTO类里放这些字段
* 这些字段的意义就相当于一些经处理过的数据库字段,实质意义就是方便数据交互,提高效率
*/
@ApiModel("微信登录参数")
@Getter
@Setter
@ToString
public class LoginDTO {
@ApiModelProperty(value = "微信登录code", required = true)
@NotBlank(message = "微信code不能为空")
private String code;
/* @ApiModelProperty(value = "微信登录用户信息")
@NotNull(message = "用户信息userInfo不能为null")
private Map userInfo;*/
@ApiModelProperty(value = "服务端获取数据 不可泄露给前端 存在风险", hidden = true)
private String sessionKey;
private String encryptedData;
private String iv;
private String signature;
private String rawData;
}
package com.onsiteservice.miniapp.controller.weixn.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
/**
* @author 潘维吉
* @date 2019-3-20 13:36
* 微信解密参数
*/
@ApiModel(description = "微信解密参数")
@Getter
@Setter
@ToString
public class WxEncryptDTO {
@ApiModelProperty(value = "微信登录code")
@NotBlank(message = "微信code不能为空")
private String code;
@ApiModelProperty(value = "服务端数据 不可泄露给前端 存在风险", hidden = true)
private String sessionKey;
private String encryptedData;
private String iv;
}
package com.onsiteservice.miniapp.service.user;
import com.onsiteservice.dao.common.AbstractMapper;
import com.onsiteservice.entity.user.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService extends AbstractMapper<User> {
}
package com.onsiteservice.miniapp.service.weixin;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.google.common.collect.ImmutableMap;
import com.onsiteservice.core.exception.ServiceException;
import com.onsiteservice.core.result.Result;
import com.onsiteservice.core.security.jwt.JwtManager;
import com.onsiteservice.entity.user.User;
import com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO;
import com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO;
import com.onsiteservice.miniapp.service.user.UserService;
import com.onsiteservice.miniapp.service.weixin.dto.MiniQrCodeDTO;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;
import static com.onsiteservice.core.result.ResultGenerator.fail;
import static com.onsiteservice.core.result.ResultGenerator.success;
/**
* @author 潘维吉
* @date 2021-09-14 08:33
* 微信服务类
*/
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class WeiXinService {
@Resource
private WxMaService wxMaService;
@Resource
private UserService userService;
/**
* 用户信息解密 登录或注册一体
*/
public Result userLogin(LoginDTO loginDTO) {
try {
WxMaJscode2SessionResult sessionResult = null;
try {
// 获取openid、unionid、session_key等
sessionResult = wxMaService.getUserService().getSessionInfo(loginDTO.getCode());
} catch (WxErrorException e) {
throw new ServiceException("微信小程序登录失败:" + e.getMessage());
}
String unionId = sessionResult.getUnionid(); // unionId需要先在微信开放平台绑定平台
String openId = sessionResult.getOpenid();
String sessionKey = sessionResult.getSessionKey();
// 用户信息校验 wx.login重复登录 SessionKey可能失效
if (!wxMaService.getUserService().checkUserInfo(
sessionKey, loginDTO.getRawData(), loginDTO.getSignature())) {
return fail("微信用户信息校验失败");
}
if (StringUtils.isBlank(unionId)) {
return fail("unionId无法获取,登录失败");
}
// 解密用户信息
WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(
sessionKey, loginDTO.getEncryptedData(), loginDTO.getIv());
String nickName = userInfo.getNickName();
String avatarUrl = userInfo.getAvatarUrl();
// 是否存在用户
Optional<User> hasUser = Optional.ofNullable(userService.selectOneByProperty("unionId", unionId));
if (hasUser.isPresent()) {
User user = hasUser.get();
user.setNickName(nickName);
user.setAvatar(avatarUrl);
userService.updateByPrimaryKeySelective(user);
// 关联导入IM账号
return toLogin(user, user.getId(), "登录成功");
} else {
// 新增用户
User user = User.builder()
.userName(nickName)
.nickName(nickName)
.unionId(unionId)
.openId(openId)
.avatar(avatarUrl)
.sex(Integer.parseInt(userInfo.getGender()))
.build();
userService.insertSelective(user);
return toLogin(user, user.getId(), "注册成功");
}
} catch (Exception e) {
log.error("登录失败:" + e.getMessage());
throw new ServiceException("登录失败");
}
}
/**
* 登录 通用代码重构封装
*/
private Result toLogin(User user, Long userId, String msg) {
//生成一个token,保存用户登录状态
String jwtToken = JwtManager.createToken(Map.of(JwtManager.USER_ID, userId));
Map<String, Object> map = ImmutableMap.of("token", jwtToken,
"userId", userId, "userInfo", user);
return success(map, msg);
}
/**
* 获取微信用户绑定手机号信息
*/
public WxMaPhoneNumberInfo getPhone(WxEncryptDTO wxEncryptDTO, Long userId) {
WxMaJscode2SessionResult json = null;
try {
json = wxMaService.getUserService().getSessionInfo(wxEncryptDTO.getCode());
} catch (WxErrorException e) {
throw new ServiceException("微信小程序异常:" + e.getMessage());
}
wxEncryptDTO.setSessionKey(json.getSessionKey());
// 解密手机号
try {
WxMaPhoneNumberInfo phoneInfo = wxMaService.getUserService().getPhoneNoInfo(
wxEncryptDTO.getSessionKey(), wxEncryptDTO.getEncryptedData(), wxEncryptDTO.getIv());
updatePhone(userId, phoneInfo.getPhoneNumber());
return phoneInfo;
} catch (Exception e) {
log.error("获取微信手机号失败:" + e.getMessage());
throw new ServiceException("获取微信手机号失败,请重试");
}
}
/**
* 获取微信小程序码
*/
public Result getQrCode(MiniQrCodeDTO miniQrCodeDTO) {
try {
// 获取base64数据
byte[] qrCode = wxMaService.getQrcodeService().createWxaCodeUnlimitBytes(miniQrCodeDTO.getScene(),
miniQrCodeDTO.getPath(), miniQrCodeDTO.getWidth(), false, new WxMaCodeLineColor("0", "0", "0"), false);
return success(qrCode, "获取微信小程序码成功");
} catch (Exception e) {
throw new ServiceException("获取微信小程序码失败");
}
}
/**
* 更新用户手机号
*/
private void updatePhone(Long userId, String phoneNumber) {
// 更新用户手机号
User user = userService.selectByPrimaryKey(userId);
user.setPhone(phoneNumber);
userService.updateByPrimaryKeySelective(user);
}
}
package com.onsiteservice.miniapp.service.weixin.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author 潘维吉
* @date 2021/10/9 17:09
* @email 406798106@qq.com
* @description
*/
@ApiModel("微信生成小程序码参数")
@Getter
@Setter
@ToString
public class MiniQrCodeDTO implements Serializable {
@ApiModelProperty(value = "场景(编码的路径参数 不加?分号)", required = true)
@NotBlank(message = "场景不能为空")
private String scene;
@ApiModelProperty(value = "已经发布的小程序存在的页面路径(最前面不要填加 /, 不能携带参数, 不填默认跳主页面)")
private String path;
@ApiModelProperty(value = "二维码的宽度(单位 px, 最小 280px, 最大 1280px 默认430px)")
private Integer width;
}
......@@ -2,8 +2,8 @@
spring:
profiles:
include:
- property-sales-ruchen-common-dev
- common-dev
logging:
level:
com.property.sales.app.mapper: debug
com.onsiteservice.miniapp.mapper: debug
......@@ -2,7 +2,7 @@
spring:
profiles:
include:
- property-sales-ruchen-common-prod
- common-prod
logging:
config: classpath:logback-spring-prod.xml
# 联调环境配置
spring:
profiles:
include:
- property-sales-ruchen-common-sit
#logging:
# config: classpath:logback-spring-sit.xml
# 测试环境配置
spring:
profiles:
include:
- property-sales-ruchen-common-test
# 所有环境通用的配置
server:
port: 8080
port: 9261
# 所有自定义项目工程配置值 在project下配置
project:
......@@ -18,7 +18,7 @@ project:
spring:
profiles:
include:
- onsite-service-common
- common
application:
name: onsite-service-mini-app
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment