package com.epmet.service.impl; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; import com.alibaba.fastjson.JSON; import com.epmet.common.token.constant.LoginConstant; import com.epmet.commons.tools.exception.EpmetErrorCode; import com.epmet.commons.tools.exception.RenException; import com.epmet.commons.tools.security.dto.TokenDto; import com.epmet.commons.tools.security.password.PasswordUtils; import com.epmet.commons.tools.utils.CpUserDetailRedis; import com.epmet.commons.tools.utils.DateUtils; import com.epmet.commons.tools.utils.Result; import com.epmet.commons.tools.validator.ValidatorUtils; import com.epmet.dto.UserDTO; import com.epmet.dto.UserWechatDTO; import com.epmet.dto.form.*; import com.epmet.dto.result.PasswordLoginUserInfoResultDTO; import com.epmet.dto.result.UserTokenResultDTO; import com.epmet.feign.EpmetUserFeignClient; import com.epmet.feign.OperAccessOpenFeignClient; import com.epmet.jwt.JwtTokenProperties; import com.epmet.jwt.JwtTokenUtils; import com.epmet.redis.CustomerAppWxServiceUtil; import com.epmet.service.CaptchaService; import com.epmet.service.LoginService; import com.epmet.utils.WxMaServiceUtils; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** * @Description * @Author yinzuomei * @Date 2020/3/14 20:31 */ @Slf4j @Service public class LoginServiceImpl implements LoginService { private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class); @Autowired private EpmetUserFeignClient epmetUserFeignClient; @Autowired private WxMaServiceUtils wxMaServiceUtils; @Autowired private JwtTokenUtils jwtTokenUtils; @Autowired private JwtTokenProperties jwtTokenProperties; @Autowired private CpUserDetailRedis cpUserDetailRedis; @Autowired private CaptchaService captchaService; @Autowired private OperAccessOpenFeignClient operAccessOpenFeignClient; /** * 居民端微信小程序登录 * * @param formDTO * @return com.epmet.commons.tools.utils.Result * @author yinzuomei * @since 2020/3/14 19:34 */ @Override public Result loginByWxCode(LoginByWxCodeFormDTO formDTO) { if(!(LoginConstant.APP_RESI.equals(formDTO.getApp())&&LoginConstant.CLIENT_WXMP.equals(formDTO.getClient()))){ logger.error("当前接口只适用于居民端微信小程序登录"); throw new RenException("参数错误"); } //1、根据wxCode获取微信信息 WxMaJscode2SessionResult wxMaJscode2SessionResult = this.getWxMaUser(formDTO.getApp(),formDTO.getWxCode(),formDTO.getAppId()); logger.info("openId=[" + wxMaJscode2SessionResult.getOpenid() + "]unionId=[" + wxMaJscode2SessionResult.getUnionid() + "]"); //2、根据openId查询数据库,没有则直接插入一条记录 String userId = this.getUserId(formDTO, wxMaJscode2SessionResult); if (StringUtils.isNotBlank(userId)) { //3、封装token且存到redis String token=this.generateToken(formDTO,userId); this.saveTokenDto(formDTO,userId,wxMaJscode2SessionResult,token); UserTokenResultDTO userTokenResultDTO = new UserTokenResultDTO(); userTokenResultDTO.setToken(token); return new Result().ok(userTokenResultDTO); }else{ logger.error("登录失败userId为空"); throw new RenException("登录失败"); } } /** * 解析微信code获取小程序用户信息 * * @param app * @param wxCode * @return cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult * @author yinzuomei * @date 2020/3/14 20:16 */ @Override public WxMaJscode2SessionResult getWxMaUser(String app,String wxCode,String appId) { WxMaJscode2SessionResult wxMaJscode2SessionResult = null; try { if (StringUtils.isNotBlank(appId)){ WxMaService wxMaService = CustomerAppWxServiceUtil.getWxMaService(appId); if (wxMaService == null){ throw new RenException("解析微信用户信息失败"); } wxMaJscode2SessionResult = wxMaService.jsCode2SessionInfo(wxCode); }else{ if (LoginConstant.APP_GOV.equals(app)) { wxMaJscode2SessionResult = wxMaServiceUtils.govWxMaService().jsCode2SessionInfo(wxCode); } else if (LoginConstant.APP_OPER.equals(app)) { wxMaJscode2SessionResult = wxMaServiceUtils.operWxMaService().jsCode2SessionInfo(wxCode); } else if (LoginConstant.APP_RESI.equals(app)) { wxMaJscode2SessionResult = wxMaServiceUtils.resiWxMaService().jsCode2SessionInfo(wxCode); } } } catch (WxErrorException e) { log.error("->[getMaOpenId]::error[{}]", "解析微信code失败",e); } if (null == wxMaJscode2SessionResult) { log.warn(String.format("解析微信用户信息失败,app[%s],wxCode[%s],result:[%S]",app,wxCode, JSON.toJSONString(wxMaJscode2SessionResult))); throw new RenException("解析微信用户信息失败"); } else if (StringUtils.isBlank(wxMaJscode2SessionResult.getOpenid())) { log.warn(String.format("获取微信openid失败,app[%s],wxCode[%s]",app,wxCode)); throw new RenException("获取微信openid失败"); } return wxMaJscode2SessionResult; } @Override public String getResiWxPhone(ResiWxPhoneFormDTO formDTO) { String phone=""; try { ValidatorUtils.validateEntity(formDTO, ResiWxPhoneFormDTO.AddUserInternalGroup.class); WxMaService wxMaService = null; if (StringUtils.isNotBlank(formDTO.getAppId())){ wxMaService = CustomerAppWxServiceUtil.getWxMaService(formDTO.getAppId()); }else{ wxMaService = wxMaServiceUtils.resiWxMaService(); } WxMaJscode2SessionResult wxMaJscode2SessionResult = wxMaService.jsCode2SessionInfo(formDTO.getWxCode()); WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(wxMaJscode2SessionResult.getSessionKey(), formDTO.getEncryptedData(), formDTO.getIv()); if (null != phoneNoInfo) { phone= phoneNoInfo.getPurePhoneNumber(); } } catch (WxErrorException e) { log.error("method exception", e); log.error(String.format("获取用户微信绑定的手机号接口异常%s",e.getMessage())); } catch(Exception e){ log.error("method exception", e); log.error(String.format("获取用户微信绑定的手机号接口异常%s",e.getMessage())); } return phone; } /** * 根据openId查询用户id * * @param formDTO * @param wxMaJscode2SessionResult * @return java.lang.String * @author yinzuomei * @since 2020/3/14 19:34 */ private String getUserId(LoginByWxCodeFormDTO formDTO, WxMaJscode2SessionResult wxMaJscode2SessionResult) { WxLoginUserInfoFormDTO wxLoginUserInfoFormDTO = new WxLoginUserInfoFormDTO(); wxLoginUserInfoFormDTO.setApp(formDTO.getApp()); wxLoginUserInfoFormDTO.setOpenId(wxMaJscode2SessionResult.getOpenid()); //1、先根据app、client、openId查询 Result userResult = epmetUserFeignClient.selecWxLoginUserInfo(wxLoginUserInfoFormDTO); String userId = ""; if (!userResult.success()) { logger.error("根据openId、app获取用户信息失败" + userResult.getMsg()); throw new RenException("获取用户信息失败" + userResult.getMsg()); } if(null!=userResult.getData()&&StringUtils.isNotBlank(userResult.getData().getId())){ userId = userResult.getData().getId(); } //2、如果已经存在userId,则更新微信信息 if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(formDTO.getEncryptedData()) && StringUtils.isNotBlank(formDTO.getIv())) { this.updateWxInfO(userId,formDTO,wxMaJscode2SessionResult); } //3、数据库不存在此用户则创建此用户 if (StringUtils.isBlank(userId)) { userId = createUser(formDTO, wxMaJscode2SessionResult); } return userId; } /** * @return com.epmet.commons.tools.utils.Result * @param userId * @param wxMaJscode2SessionResult * @Author yinzuomei * @Description 获取用户微信基本信息更新到本地 * @Date 2020/3/20 19:51 **/ private Result updateWxInfO(String userId, LoginByWxCodeFormDTO formDTO, WxMaJscode2SessionResult wxMaJscode2SessionResult) { WxMaUserInfo wxMaUserInfo = wxMaServiceUtils.resiWxMaService().getUserService() .getUserInfo(wxMaJscode2SessionResult.getSessionKey(), formDTO.getEncryptedData(), formDTO.getIv()); UserWechatDTO userWechatDTO = this.packageCustomerUserDTO(wxMaUserInfo); userWechatDTO.setUserId(userId); Result updateUserDtoResult=epmetUserFeignClient.saveOrUpdateUserWechatDTO(userWechatDTO); return new Result(); } /** * @param formDTO * @param wxMaJscode2SessionResult * @return java.lang.String * @Author yinzuomei * @Description 陌生人首次授权,创建用户信息 * @Date 2020/3/20 19:42 **/ private String createUser(LoginByWxCodeFormDTO formDTO, WxMaJscode2SessionResult wxMaJscode2SessionResult) { String userId = ""; //查询customer_user UserWechatDTO userWechatDTO = new UserWechatDTO(); if (StringUtils.isNotBlank(formDTO.getIv()) && StringUtils.isNotBlank(formDTO.getEncryptedData())) { WxMaUserInfo wxMaUserInfo = wxMaServiceUtils.resiWxMaService().getUserService() .getUserInfo(wxMaJscode2SessionResult.getSessionKey(), formDTO.getEncryptedData(), formDTO.getIv()); userWechatDTO = this.packageCustomerUserDTO(wxMaUserInfo); } else { userWechatDTO.setWxOpenId(wxMaJscode2SessionResult.getOpenid()); userWechatDTO.setUnionId(wxMaJscode2SessionResult.getUnionid()); } Result saveUserWechatResult = epmetUserFeignClient.saveOrUpdateUserWechatDTO(userWechatDTO); if (!saveUserWechatResult.success()||null==saveUserWechatResult.getData()) { throw new RenException("创建用户失败" + saveUserWechatResult.getMsg()); } userId = saveUserWechatResult.getData().getId(); return userId; } /** * @param wxMaUserInfo * @return com.epmet.dto.UserWechatDTO * @Author yinzuomei * @Description 微信信息封装为customer_user记录 * @Date 2020/3/17 18:22 **/ private UserWechatDTO packageCustomerUserDTO(WxMaUserInfo wxMaUserInfo) { UserWechatDTO userWechatDTO = new UserWechatDTO(); userWechatDTO.setCity(wxMaUserInfo.getCity()); userWechatDTO.setWxOpenId(wxMaUserInfo.getOpenId()); userWechatDTO.setUnionId(wxMaUserInfo.getUnionId()); userWechatDTO.setNickname(wxMaUserInfo.getNickName()); userWechatDTO.setCountry(wxMaUserInfo.getCountry()); userWechatDTO.setHeadImgUrl(wxMaUserInfo.getAvatarUrl()); userWechatDTO.setProvince(wxMaUserInfo.getProvince()); userWechatDTO.setSex(Integer.valueOf(wxMaUserInfo.getGender())); return userWechatDTO; } /** * 手机号+密码登录接口 * * @param formDTO * @return com.epmet.commons.tools.utils.Result * @author yinzuomei * @since 2020/3/14 19:34 */ @Override public Result loginByPassword(LoginByPassWordFormDTO formDTO) { if(!(LoginConstant.APP_OPER.equals(formDTO.getApp())&&LoginConstant.CLIENT_WEB.equals(formDTO.getClient()))){ logger.error("当前接口只适用于运营端管理后台"); throw new RenException("当前接口只适用于运营端管理后台"); } //1、验证码是否正确 boolean flag = captchaService.validate(formDTO.getUuid(), formDTO.getCaptcha()); if (!flag) { logger.warn(String.format("用户%s登录,验证码输入错误", formDTO.getPhone())); //2020-05-21去除验证码校验 //return new Result().error(EpmetErrorCode.ERR10019.getCode()); } //2、账号是否存在 //获取用户信息 PasswordLoginUserInfoFormDTO passwordLoginUserInfoFormDTO = new PasswordLoginUserInfoFormDTO(); passwordLoginUserInfoFormDTO.setApp(formDTO.getApp()); passwordLoginUserInfoFormDTO.setPhone(formDTO.getPhone()); Result userInfoResult = epmetUserFeignClient.selectLoginUserInfoByPassword(passwordLoginUserInfoFormDTO); if (!userInfoResult.success() || null == userInfoResult.getData()) { // logger.error("根据手机号查询运营人员信息失败,返回10003账号不存在"); throw new RenException(EpmetErrorCode.ERR10003.getCode()); } //3、密码是否正确 //密码错误 if (!PasswordUtils.matches(formDTO.getPassword(), userInfoResult.getData().getPassWord())) { throw new RenException(EpmetErrorCode.ERR10004.getCode()); } //4、生成token返回,且将TokenDto存到redis UserTokenResultDTO userTokenResultDTO = new UserTokenResultDTO(); userTokenResultDTO.setToken(this.packagingUserToken(formDTO, userInfoResult.getData().getUserId())); return new Result().ok(userTokenResultDTO); } /** * 封装用户token值 * * @param formDTO * @param userId * @return java.lang.String * @author yinzuomei * @since 2020/3/14 19:34 */ private String packagingUserToken(LoginByPassWordFormDTO formDTO, String userId) { // 生成token Map map = new HashMap<>(); map.put("app", formDTO.getApp()); map.put("client", formDTO.getClient()); map.put("userId", userId); String token = jwtTokenUtils.createToken(map); logger.info("app:"+formDTO.getApp()+";client:"+formDTO.getClient()+";userId:"+userId+";生成token["+token+"]"); int expire = jwtTokenProperties.getExpire(); TokenDto tokenDto = new TokenDto(); tokenDto.setApp(formDTO.getApp()); tokenDto.setClient(formDTO.getClient()); tokenDto.setUserId(userId); tokenDto.setToken(token); tokenDto.setUpdateTime(System.currentTimeMillis()); tokenDto.setExpireTime(jwtTokenUtils.getExpiration(token).getTime()); cpUserDetailRedis.set(tokenDto, expire); logger.info("截止时间:"+ DateUtils.format(jwtTokenUtils.getExpiration(token),"yyyy-MM-dd HH:mm:ss")); return token; } @Override public Result logoutByToken(TokenDto tokenDto) { //记录登出日志 //删除redis if (null == tokenDto) { logger.error("运营端用户退出系统错误:账号不存在,接口继续执行返回成功"); return new Result(); } cpUserDetailRedis.logout(tokenDto.getApp(), tokenDto.getClient(), tokenDto.getUserId()); //web端清空菜单栏和权限 Result operAccessResult = operAccessOpenFeignClient.clearOperUserAccess(); if (operAccessResult.success()) { logger.info(String.format("运营人员%s退出成功,清空菜单和权限redis成功", tokenDto.getUserId())); } else { logger.error(String.format("运营人员%s退出成功,清空菜单和权限redis异常", tokenDto.getUserId())); } return new Result(); } /** * @Description 生成token * @Date 2020/4/18 23:04 **/ private String generateToken(LoginCommonFormDTO formDTO,String userId){ Map map = new HashMap<>(); map.put("app", formDTO.getApp()); map.put("client", formDTO.getClient()); map.put("userId", userId); String token = jwtTokenUtils.createToken(map); logger.info("app:"+formDTO.getApp()+";client:"+formDTO.getClient()+";userId:"+userId+";生成token["+token+"]"); return token; } /** * @Description 生成token * @Date 2020/4/18 23:04 **/ private String saveTokenDto(LoginCommonFormDTO formDTO, String userId, WxMaJscode2SessionResult wxMaJscode2SessionResult, String token) { int expire = jwtTokenProperties.getExpire(); TokenDto tokenDto = new TokenDto(); tokenDto.setApp(formDTO.getApp()); tokenDto.setClient(formDTO.getClient()); tokenDto.setUserId(userId); tokenDto.setOpenId(wxMaJscode2SessionResult.getOpenid()); tokenDto.setSessionKey(wxMaJscode2SessionResult.getSessionKey()); tokenDto.setUnionId(wxMaJscode2SessionResult.getUnionid()); tokenDto.setToken(token); tokenDto.setUpdateTime(System.currentTimeMillis()); tokenDto.setExpireTime(jwtTokenUtils.getExpiration(token).getTime()); cpUserDetailRedis.set(tokenDto, expire); logger.info("截止时间:"+ DateUtils.format(jwtTokenUtils.getExpiration(token),"yyyy-MM-dd HH:mm:ss")); return token; } }