Commit d9b386b0 authored by xuepeng.chang's avatar xuepeng.chang

feat:微信解绑功能

parent 9a37979b
...@@ -7,6 +7,8 @@ public class UserConstant { ...@@ -7,6 +7,8 @@ public class UserConstant {
public static final String USER_FREEZE_ERROR = "账号异常,已冻结。"; public static final String USER_FREEZE_ERROR = "账号异常,已冻结。";
public static final String USER_WECHAT_BIND_ERROR ="该手机对应账户对应账户已绑定其他微信";
public static final Integer defaultTenantId = 560761; public static final Integer defaultTenantId = 560761;
public static final String defaultTenantIdString = "560761"; public static final String defaultTenantIdString = "560761";
......
...@@ -7,6 +7,7 @@ import cn.quantgroup.xyqb.controller.IBaseController; ...@@ -7,6 +7,7 @@ import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.controller.middleoffice.login.ILoginModule; import cn.quantgroup.xyqb.controller.middleoffice.login.ILoginModule;
import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo; import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo;
import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry; import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry;
import cn.quantgroup.xyqb.entity.middleoffice.UnbindParam;
import cn.quantgroup.xyqb.exception.DataException; import cn.quantgroup.xyqb.exception.DataException;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.WechatConfigBean; import cn.quantgroup.xyqb.model.WechatConfigBean;
...@@ -73,6 +74,21 @@ public class AppletController implements IBaseController { ...@@ -73,6 +74,21 @@ public class AppletController implements IBaseController {
.buildSuccessResultGeneric(loginVo); .buildSuccessResultGeneric(loginVo);
} }
@PostMapping("/unbindWechat")
public JsonResult<Boolean> unbindWechat(@Validated @RequestBody UnbindParam unbindParam) {
log.info("/middle_office/applet/unbindWechat:{}",JSON.toJSONString(unbindParam));
if (!containsAppName(unbindParam.getAppName())) {
throw new DataException("appName不合法");
}
if(StringUtils.isEmpty(unbindParam.getTenantId())){
unbindParam.setTenantId(getTenantId());
}
boolean unbind = iAppletService.unbind(unbindParam);
return JsonResult.buildSuccessResultGeneric(unbind);
}
/** /**
* @return * @return
*/ */
......
package cn.quantgroup.xyqb.entity;
import cn.quantgroup.xyqb.entity.converter.EncryptConverter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "wechat_bind_log")
@Data
public class WechatBindLog extends BaseEntity implements Serializable {
private static final long serialVersionUID = -1L;
/** 用户id*/
@Column(name = "user_id")
private Long userId;
/** 微信用户唯一标识*/
@Column(name = "open_id")
private String openId;
/** 应用名称*/
@Column(name = "app_name")
private String appName;
/** 用户在微信平台的唯一标识*/
@Column(name = "union_id")
private String unionId;
/** 加密手机号*/
@Column(name = "encrypted_phone_no")
@Convert(converter = EncryptConverter.class)
private String encryptedPhoneNo;
/** 绑定解绑类型 1:绑定 2:解绑*/
@Column(name = "bind_type")
private Integer bindType;
/** 微信应用id*/
@Column(name = "app_id")
private String appId;
/** 租户id*/
@Column(name = "tenant_id")
private Integer tenantId;
public WechatBindLog(Long userId, String openId, String appName, String appId, Integer tenantId,String unionId,Integer bindType) {
this.userId = userId;
this.openId = openId;
this.appName = appName;
this.appId = appId;
this.tenantId = tenantId;
this.unionId=unionId;
this.bindType = bindType;
}
}
package cn.quantgroup.xyqb.entity.middleoffice;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UnbindParam implements Serializable {
private static final long serialVersionUID = -1L;
/**
* 应用标识
*/
@NotNull(message = "appName 不能为空")
private String appName;
/**
* 应用标识
*/
@NotNull(message = "appId 不能为空")
private String appId;
/**
* 应用对应的渠道号
*/
@NotNull(message = "channelId 不能为空")
private Long channelId;
@NotNull(message = "phone 不能为空")
private String phone;
@NotNull(message = "userId 不能为空")
private Long userId;
/**
* 租户ID
*/
private Integer tenantId;
/** 微信小程序的渠道号*/
private String wechatChannelId;
}
...@@ -30,6 +30,7 @@ public class WechatPhoneNoUpdateEventListener implements ApplicationListener<Pho ...@@ -30,6 +30,7 @@ public class WechatPhoneNoUpdateEventListener implements ApplicationListener<Pho
} }
userInfo.setPhoneNo(user.getPhoneNo()); userInfo.setPhoneNo(user.getPhoneNo());
userInfo.setEncryptedPhoneNo(user.getPhoneNo());
wechatService.saveWechatUserInfo(userInfo); wechatService.saveWechatUserInfo(userInfo);
} }
} }
...@@ -50,7 +50,9 @@ public enum BizExceptionEnum { ...@@ -50,7 +50,9 @@ public enum BizExceptionEnum {
FAIL_JI_GUANG_VALIDATE("2024","极光验证未通过"), FAIL_JI_GUANG_VALIDATE("2024","极光验证未通过"),
//通用记录 //通用记录
ERROR_PARAM("4000","参数错误"), ERROR_PARAM("4000","参数错误"),
INVALID_SMS_CODE("4001","验证码失效,请重新获取"); INVALID_SMS_CODE("4001","验证码失效,请重新获取"),
USER_WECHAT_BIND_ERROR("3001","该手机对应账户对应账户已绑定其他微信");
private final String businessCode; private final String businessCode;
private final String msg; private final String msg;
......
...@@ -18,6 +18,10 @@ import static org.springframework.transaction.annotation.Propagation.MANDATORY; ...@@ -18,6 +18,10 @@ import static org.springframework.transaction.annotation.Propagation.MANDATORY;
public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Long> { public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Long> {
WechatUserInfo findByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName, String appId, Integer tenantId); WechatUserInfo findByOpenIdAndAppNameAndAppIdAndTenantId(String openId, String appName, String appId, Integer tenantId);
List<WechatUserInfo> findByPhoneNoAndAppNameAndAppIdAndTenantId(String phoneNo, String appName,String appId, Integer tenantId);
List<WechatUserInfo> findByUserIdAndAppNameAndAppIdAndTenantId(Long userId, String appName,String appId, Integer tenantId);
/** /**
* 兼容历史逻辑,老的代码可能只通过appName进行支付宝、百度等登录 * 兼容历史逻辑,老的代码可能只通过appName进行支付宝、百度等登录
...@@ -99,6 +103,9 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -99,6 +103,9 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Transactional @Transactional
void deleteByUserIdAndTenantId(Long userId, Integer tenantId); void deleteByUserIdAndTenantId(Long userId, Integer tenantId);
@Transactional
void deleteById(Long id);
/** /**
* 通过userId查询相关绑定微信记录 * 通过userId查询相关绑定微信记录
*/ */
......
package cn.quantgroup.xyqb.repository;
import cn.quantgroup.xyqb.entity.WechatBindLog;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface IWechatBindLogRepository extends JpaRepository<WechatBindLog, Long> {
}
...@@ -2,6 +2,7 @@ package cn.quantgroup.xyqb.service.middleoffice.applet; ...@@ -2,6 +2,7 @@ package cn.quantgroup.xyqb.service.middleoffice.applet;
import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo; import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo;
import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry; import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry;
import cn.quantgroup.xyqb.entity.middleoffice.UnbindParam;
/** /**
* @author :dongjianhua * @author :dongjianhua
...@@ -13,4 +14,6 @@ import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry; ...@@ -13,4 +14,6 @@ import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry;
public interface IAppletService { public interface IAppletService {
Long relevance(AppletParamEntry appletParamEntry); Long relevance(AppletParamEntry appletParamEntry);
LoginVo login(String appName, String openId, Integer tenantId, String utmSource, String unionId,String appId); LoginVo login(String appName, String openId, Integer tenantId, String utmSource, String unionId,String appId);
boolean unbind(UnbindParam unbindParam);
} }
...@@ -5,26 +5,39 @@ import cn.quantgroup.xyqb.constant.UserConstant; ...@@ -5,26 +5,39 @@ import cn.quantgroup.xyqb.constant.UserConstant;
import cn.quantgroup.xyqb.controller.middleoffice.login.ILoginModule; import cn.quantgroup.xyqb.controller.middleoffice.login.ILoginModule;
import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo; import cn.quantgroup.xyqb.controller.middleoffice.login.LoginVo;
import cn.quantgroup.xyqb.entity.User; import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.WechatBindLog;
import cn.quantgroup.xyqb.entity.WechatUserInfo; import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry; import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry;
import cn.quantgroup.xyqb.entity.middleoffice.UnbindParam;
import cn.quantgroup.xyqb.exception.AppletException; import cn.quantgroup.xyqb.exception.AppletException;
import cn.quantgroup.xyqb.exception.BizException; import cn.quantgroup.xyqb.exception.BizException;
import cn.quantgroup.xyqb.exception.BizExceptionEnum; import cn.quantgroup.xyqb.exception.BizExceptionEnum;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.repository.IWeChatUserRepository; import cn.quantgroup.xyqb.repository.IWeChatUserRepository;
import cn.quantgroup.xyqb.repository.IWechatBindLogRepository;
import cn.quantgroup.xyqb.service.middleoffice.applet.IAppletService; import cn.quantgroup.xyqb.service.middleoffice.applet.IAppletService;
import cn.quantgroup.xyqb.service.register.IUserRegisterService; import cn.quantgroup.xyqb.service.register.IUserRegisterService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.session.impl.SessionServiceImpl;
import cn.quantgroup.xyqb.service.user.IUserService; import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.util.StringUtils; import cn.quantgroup.xyqb.util.StringUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
import static cn.quantgroup.xyqb.constant.UserConstant.USER_FREEZE_ERROR; import static cn.quantgroup.xyqb.constant.UserConstant.USER_FREEZE_ERROR;
/** /**
* @author :dongjianhua * @author :dongjianhua
* @date :Created in 2020/5/27 17:27 * @date :Created in 2020/5/27 17:27
...@@ -38,25 +51,28 @@ public class AppletServiceImpl implements IAppletService { ...@@ -38,25 +51,28 @@ public class AppletServiceImpl implements IAppletService {
private final IWeChatUserRepository iWeChatUserRepository; private final IWeChatUserRepository iWeChatUserRepository;
private final IUserRegisterService iUserRegisterService; private final IUserRegisterService iUserRegisterService;
private final IUserService userService; private final IUserService userService;
private ILoginModule loginModule; private ILoginModule loginModule;
private final WechatConfiguration wechatConfiguration; private final WechatConfiguration wechatConfiguration;
private final IWechatBindLogRepository wechatBindLogRepository;
private final ISessionService sessionService;
@Autowired @Autowired
public AppletServiceImpl(IWeChatUserRepository iWeChatUserRepository, public AppletServiceImpl(IWeChatUserRepository iWeChatUserRepository,
IUserRegisterService iUserRegisterService, IUserRegisterService iUserRegisterService,
IUserService userService, IUserService userService,
ILoginModule loginModule, WechatConfiguration wechatConfiguration) { ILoginModule loginModule, WechatConfiguration wechatConfiguration,
IWechatBindLogRepository wechatBindLogRepository, ISessionService sessionService) {
this.iWeChatUserRepository = iWeChatUserRepository; this.iWeChatUserRepository = iWeChatUserRepository;
this.iUserRegisterService = iUserRegisterService; this.iUserRegisterService = iUserRegisterService;
this.userService = userService; this.userService = userService;
this.loginModule = loginModule; this.loginModule = loginModule;
this.wechatConfiguration = wechatConfiguration; this.wechatConfiguration = wechatConfiguration;
this.wechatBindLogRepository = wechatBindLogRepository;
this.sessionService = sessionService;
} }
@Override @Override
...@@ -68,6 +84,25 @@ public class AppletServiceImpl implements IAppletService { ...@@ -68,6 +84,25 @@ public class AppletServiceImpl implements IAppletService {
throw new BizException(BizExceptionEnum.ERROR_MATCHING_WECHAT_APP_ID); throw new BizException(BizExceptionEnum.ERROR_MATCHING_WECHAT_APP_ID);
} }
appletParamEntry.setAppId(appId); appletParamEntry.setAppId(appId);
// 校验当前用户的手机号有没有绑定过openId
List<WechatUserInfo> wechatUserInfos = iWeChatUserRepository.findByPhoneNoAndAppNameAndAppIdAndTenantId(appletParamEntry.getMobile(),
appletParamEntry.getAppName(), appletParamEntry.getAppId(), appletParamEntry.getTenantId());
if(wechatUserInfos.size()>1){
log.info("微信关联失败[service]:phoneNo:{},openId:{},appName:{}", appletParamEntry.getMobile(), appletParamEntry.getOpenId(),appletParamEntry.getAppName());
throw new AppletException(BizExceptionEnum.USER_WECHAT_BIND_ERROR.getMsg(), BizExceptionEnum.USER_WECHAT_BIND_ERROR.getBusinessCode());
}
if(wechatUserInfos.size() ==1){
WechatUserInfo wechatUserInfoOne = wechatUserInfos.get(0);
if(Objects.equals(wechatUserInfoOne.getOpenId(),appletParamEntry.getOpenId())){
log.info("微信关联成功:重复关联:跳过:[service]:userId:{},phoneNo:{},openId:{}", wechatUserInfoOne.getUserId(), wechatUserInfoOne.getPhoneNo(), wechatUserInfoOne.getOpenId());
return wechatUserInfoOne.getUserId();
}else{
log.info("微信关联失败[service]:phoneNo:{},openId:{},appName:{}", appletParamEntry.getMobile(), appletParamEntry.getOpenId(),appletParamEntry.getAppName());
throw new AppletException(BizExceptionEnum.USER_WECHAT_BIND_ERROR.getMsg(), BizExceptionEnum.USER_WECHAT_BIND_ERROR.getBusinessCode());
}
}
WechatUserInfo wechatUserInfo = iWeChatUserRepository.findByOpenIdAndAppNameAndAppIdAndTenantId(appletParamEntry.getOpenId(), appletParamEntry.getAppName(), appletParamEntry.getAppId(), appletParamEntry.getTenantId()); WechatUserInfo wechatUserInfo = iWeChatUserRepository.findByOpenIdAndAppNameAndAppIdAndTenantId(appletParamEntry.getOpenId(), appletParamEntry.getAppName(), appletParamEntry.getAppId(), appletParamEntry.getTenantId());
//这个接口先不考虑更换手机号的情况 //这个接口先不考虑更换手机号的情况
if (appletParamEntry.getTenantId() == null) { if (appletParamEntry.getTenantId() == null) {
...@@ -107,6 +142,10 @@ public class AppletServiceImpl implements IAppletService { ...@@ -107,6 +142,10 @@ public class AppletServiceImpl implements IAppletService {
//如果存在就更新在微信表里 //如果存在就更新在微信表里
//iWeChatUserRepository.findByOpenIdAndAppNameAndTenantId() //iWeChatUserRepository.findByOpenIdAndAppNameAndTenantId()
iWeChatUserRepository.save(wechatUserInfo); iWeChatUserRepository.save(wechatUserInfo);
// 记录微信绑定记录
WechatBindLog wechatBindLog = new WechatBindLog(wechatUserInfo.getUserId(), wechatUserInfo.getOpenId(), wechatUserInfo.getAppName(),
appId, wechatUserInfo.getTenantId(), wechatUserInfo.getUnionId(),1 );
wechatBindLogRepository.save(wechatBindLog);
return wechatUserInfo.getUserId(); return wechatUserInfo.getUserId();
} }
...@@ -147,5 +186,36 @@ public class AppletServiceImpl implements IAppletService { ...@@ -147,5 +186,36 @@ public class AppletServiceImpl implements IAppletService {
utmSource == null ? "" : utmSource, user.getId(), tenantId); utmSource == null ? "" : utmSource, user.getId(), tenantId);
} }
@Override
@Transactional(rollbackFor = Exception.class)
public boolean unbind(UnbindParam unbindParam) {
// 获取当前用户绑定了对应的微信小程序
List<WechatUserInfo> wechatUserInfoList = iWeChatUserRepository.findByUserIdAndAppNameAndAppIdAndTenantId(unbindParam.getUserId(), unbindParam.getAppName(),
unbindParam.getAppId(), unbindParam.getTenantId());
if(CollectionUtils.isEmpty(wechatUserInfoList)){
log.info("用户没有微信绑定记录,appName:{} ,phone:{},userId:{}", unbindParam.getAppName(), unbindParam.getPhone(),unbindParam.getUserId());
return true;
}
// 删除微信关联记录,并记录解绑记录
for (WechatUserInfo wechatUserInfo : wechatUserInfoList) {
WechatBindLog wechatBindLog = new WechatBindLog(wechatUserInfo.getUserId(), wechatUserInfo.getOpenId(), wechatUserInfo.getAppName(),
wechatUserInfo.getAppId(), wechatUserInfo.getTenantId(), wechatUserInfo.getUnionId(),2);
log.info("删除微信绑定记录,wechatUserInfo:{}", JSON.toJSONString(wechatUserInfo));
iWeChatUserRepository.deleteById(wechatUserInfo.getId());
wechatBindLogRepository.save(wechatBindLog);
}
// 删除微信关联记录后,清除微信渠道的登陆token
if(org.apache.commons.lang3.StringUtils.isNotEmpty(unbindParam.getWechatChannelId())){
List<String> wechatChannelIds = Arrays.stream(unbindParam.getWechatChannelId().split(",")).collect(Collectors.toList());
for (String wechatChannelId : wechatChannelIds) {
LoginProperties loginProperties = new LoginProperties(1, Long.valueOf(wechatChannelId), unbindParam.getTenantId());
sessionService.deleteWechatSession(unbindParam.getUserId(),loginProperties,unbindParam.getTenantId());
log.info("微信解绑成功,清除微信的登陆token,userId:{},wechatChannelId:{}", unbindParam.getUserId(),wechatChannelId);
}
}
log.info("微信解绑成功,unbindParam:{} ", JSON.toJSONString(unbindParam));
return true;
}
} }
...@@ -36,6 +36,9 @@ public interface ISessionService { ...@@ -36,6 +36,9 @@ public interface ISessionService {
void kdspDeleteSession(Long userId, LoginProperties loginProperties,Integer tenantId); void kdspDeleteSession(Long userId, LoginProperties loginProperties,Integer tenantId);
void deleteWechatSession(Long userId, LoginProperties loginProperties,Integer tenantId);
/** /**
* 更新session * 更新session
* 用户信息存在,更新session中的最后访问时间,重新写入缓存. * 用户信息存在,更新session中的最后访问时间,重新写入缓存.
......
...@@ -147,7 +147,7 @@ public class SessionServiceImpl implements ISessionService { ...@@ -147,7 +147,7 @@ public class SessionServiceImpl implements ISessionService {
* @param properties baitiao/xyqb/vcc ... + 用户注册来源 * @param properties baitiao/xyqb/vcc ... + 用户注册来源
* @return redisKey. 用来标识这个渠道的用户 Session 是否存在 * @return redisKey. 用来标识这个渠道的用户 Session 是否存在
*/ */
private String generateLoginPropertiesKey(Long userId, LoginProperties properties, Integer tenantId) { public static String generateLoginPropertiesKey(Long userId, LoginProperties properties, Integer tenantId) {
if (ObjectUtils.isEmpty(properties.getTenantId())) { if (ObjectUtils.isEmpty(properties.getTenantId())) {
return Constants.Session.USER_SESSION_ID_CACHE + ":" + userId + ":" + properties.getMerchantName() + ":" + properties.getCreatedFrom(); return Constants.Session.USER_SESSION_ID_CACHE + ":" + userId + ":" + properties.getMerchantName() + ":" + properties.getCreatedFrom();
} else if (properties.getTenantId().equals(0) || TenantUtil.TENANT_DEFAULT.equals(properties.getTenantId())) { } else if (properties.getTenantId().equals(0) || TenantUtil.TENANT_DEFAULT.equals(properties.getTenantId())) {
...@@ -431,6 +431,15 @@ public class SessionServiceImpl implements ISessionService { ...@@ -431,6 +431,15 @@ public class SessionServiceImpl implements ISessionService {
} }
} }
@Override
public void deleteWechatSession(Long userId, LoginProperties loginProperties, Integer tenantId) {
String key = generateLoginPropertiesKey(userId, loginProperties, tenantId);
String token = stringRedisTemplate.opsForValue().get(key);
log.info("deleteWechatSession,通过用户获取token结果,token:{} , userId: {},loginProperties:{},tenantId:{}",
token, userId,JSON.toJSONString(loginProperties),tenantId);
deleteSession(token, tenantId);
}
/** /**
* 获取用户的会话缓存Set的Redis-Key * 获取用户的会话缓存Set的Redis-Key
......
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