Commit ac5d090d authored by 李健华's avatar 李健华

OneID一体化

parent 6626a7ce
...@@ -411,6 +411,11 @@ ...@@ -411,6 +411,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies> </dependencies>
......
package cn.quantgroup.xyqb.controller.external;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.accessable.IpValidator;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaFiniteValidator;
import cn.quantgroup.xyqb.aspect.captcha.LoginInterceptor;
import cn.quantgroup.xyqb.aspect.limit.PasswordFreeAccessValidator;
import cn.quantgroup.xyqb.aspect.lock.PasswordErrorFiniteValidator;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.entity.Merchant;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.UserAttached;
import cn.quantgroup.xyqb.entity.UserDetail;
import cn.quantgroup.xyqb.exception.UserNotExistException;
import cn.quantgroup.xyqb.exception.VerificationCodeErrorException;
import cn.quantgroup.xyqb.model.*;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue;
import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse;
import cn.quantgroup.xyqb.service.http.IHttpService;
import cn.quantgroup.xyqb.service.merchant.IMerchantService;
import cn.quantgroup.xyqb.service.register.IUserRegisterService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.service.user.*;
import cn.quantgroup.xyqb.service.wechat.IWechatFollowService;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.IpUtil;
import cn.quantgroup.xyqb.util.PasswordUtil;
import cn.quantgroup.xyqb.util.TenantUtil;
import cn.quantgroup.xyqb.util.ValidationUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.util.*;
import static cn.quantgroup.xyqb.Constants.VERIFICATION_CODE_FINITE_COUNT_NEW;
/**
* 微信公众号与企业微信关注情况
*
*/
@Slf4j
@RestController
@RequestMapping("/wechatFollow")
public class WechatFollowController implements IBaseController {
@Autowired
private IWechatFollowService wechatFollowService;
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("/wechatFollowTask")
public JsonResult wechatFollowTask() {
wechatFollowService.executeTask();
return null;
}
}
...@@ -64,12 +64,12 @@ public class AppletController { ...@@ -64,12 +64,12 @@ public class AppletController {
*/ */
@Validated @Validated
@PostMapping("/login") @PostMapping("/login")
public JsonResult login(@RequestParam String appName, @RequestParam String openId, @RequestParam(required = false) Integer tenantId, String utmSource, @RequestParam(required = false) Integer appNo) { public JsonResult login(@RequestParam String appName, @RequestParam String openId, @RequestParam(required = false) Integer tenantId, String utmSource, @RequestParam(required = false) Integer appNo, @RequestParam(required = false) String unionId) {
if (!containsAppName(appName)) { if (!containsAppName(appName)) {
throw new DataException("appName不合法"); throw new DataException("appName不合法");
} }
LoginVo login = iAppletService.login(appName, openId, tenantId, utmSource); LoginVo login = iAppletService.login(appName, openId, tenantId, utmSource, unionId);
return JsonResult.buildSuccessResultGeneric(login); return JsonResult.buildSuccessResultGeneric(login);
} }
......
package cn.quantgroup.xyqb.entity;
import cn.quantgroup.xyqb.util.EmojiUtil;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* 微信公众号和企业微信关注情况表
*/
@Entity
@Table(name = "wechat_info_relation")
@Data
public class WechatInfoRelation extends BaseEntity implements Serializable {
private static final long serialVersionUID = 8208660828881784475L;
@Column(name = "user_id")
private Long userId;
@Column(name = "open_id")
private String openId;
@Column(name = "union_id")
private String unionId;
@Column(name = "is_follow_wechat")
private Integer isFollowWechat;
@Column(name = "is_follow_enterprise_wechat")
private Integer isFollowEnterpriseWechat;
@Column(name = "is_followed_wechat")
private Integer isFollowedWechat;
@Column(name = "is_followed_enterprise_wechat")
private Integer isFollowedEnterpriseWechat;
}
package cn.quantgroup.xyqb.model.webchat;
import lombok.Data;
import javax.persistence.Column;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Data
public class WechatUserListResponse implements Serializable {
private static final long serialVersionUID = -1L;
private Integer total;
private Integer count;
private Map<String, List<String>> data;
private String next_openid;
private Integer errcode;
private String errmsg;
}
package cn.quantgroup.xyqb.repository;
import cn.quantgroup.xyqb.entity.WechatInfoRelation;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
public interface IWeChatInfoRelationRepository extends JpaRepository<WechatInfoRelation, Long> {
@Transactional(rollbackFor = Exception.class)
@Modifying
@Query(value = "update wechat_info_relation as wir set wir.is_follow_wechat = 1 where wir.open_id IN (?1)", nativeQuery = true)
void updateIsFollowWechatInOpenIdList(List<String> openIdList);
}
...@@ -72,4 +72,17 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon ...@@ -72,4 +72,17 @@ public interface IWeChatUserRepository extends JpaRepository<WechatUserInfo, Lon
@Modifying @Modifying
@Query(value = "update wechat_userinfo set user_id=null,phone_no='*' where (open_id=?1 or user_id=?2) and app_name=?3", nativeQuery = true) @Query(value = "update wechat_userinfo set user_id=null,phone_no='*' where (open_id=?1 or user_id=?2) and app_name=?3", nativeQuery = true)
int dissociateUser(String openId, Long userId, String appName); int dissociateUser(String openId, Long userId, String appName);
/**
* 更新unionId
* @param userId
* @param appName
* @param unionId
* @return
*/
@Transactional(propagation = MANDATORY, rollbackFor = Exception.class)
@Modifying
@Query(value = "update wechat_userinfo set union_id=?3 where user_id=?1 and app_name=?2", nativeQuery = true)
int updateUserUnionId(Long userId, String appName, String unionId);
} }
...@@ -12,5 +12,5 @@ import cn.quantgroup.xyqb.entity.middleoffice.AppletParamEntry; ...@@ -12,5 +12,5 @@ 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); LoginVo login(String appName, String openId, Integer tenantId, String utmSource, String unionId);
} }
...@@ -104,7 +104,7 @@ public class AppletServiceImpl implements IAppletService { ...@@ -104,7 +104,7 @@ public class AppletServiceImpl implements IAppletService {
@Override @Override
public LoginVo login(String appName, String openId, Integer tenantId, String utmSource) { public LoginVo login(String appName, String openId, Integer tenantId, String utmSource, String unionId) {
if (TenantUtil.validationTenantIdIsNullOrZero(tenantId)) { if (TenantUtil.validationTenantIdIsNullOrZero(tenantId)) {
tenantId = TenantUtil.TENANT_DEFAULT; tenantId = TenantUtil.TENANT_DEFAULT;
} }
...@@ -128,6 +128,10 @@ public class AppletServiceImpl implements IAppletService { ...@@ -128,6 +128,10 @@ public class AppletServiceImpl implements IAppletService {
} }
iOauthLoginInfoService.addLoginInfo(user, tenantId); iOauthLoginInfoService.addLoginInfo(user, tenantId);
if (!unionId.equals(wechatUserInfo.getUnionId())) {
iWeChatUserRepository.updateUserUnionId(wechatUserInfo.getUserId(), appName, unionId);
}
} else { } else {
// 通过租户id和openId查询是否有关联 // 通过租户id和openId查询是否有关联
Long userId = tenantService.getTenantCustomerInfoByOpenId(openId, tenantId); Long userId = tenantService.getTenantCustomerInfoByOpenId(openId, tenantId);
...@@ -142,6 +146,7 @@ public class AppletServiceImpl implements IAppletService { ...@@ -142,6 +146,7 @@ public class AppletServiceImpl implements IAppletService {
} }
} }
LoginVo loginVo = loginModule.loginByUserId(user.getRegisteredFrom(), LoginVo loginVo = loginModule.loginByUserId(user.getRegisteredFrom(),
utmSource == null ? "" : utmSource, user.getId(), tenantId); utmSource == null ? "" : utmSource, user.getId(), tenantId);
return loginVo; return loginVo;
......
package cn.quantgroup.xyqb.service.wechat;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse;
public interface IWechatFollowService {
AccessTokenResponse getToken();
JsonResult executeWechatFollowStatus(String nextOpenId);
void executeTask();
}
package cn.quantgroup.xyqb.service.wechat.impl;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.event.WechatBindEvent;
import cn.quantgroup.xyqb.exception.WechatRelateUserException;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse;
import cn.quantgroup.xyqb.model.webchat.WechatEventMsg;
import cn.quantgroup.xyqb.model.webchat.WechatUserListResponse;
import cn.quantgroup.xyqb.repository.IWeChatInfoRelationRepository;
import cn.quantgroup.xyqb.repository.IWeChatUserRepository;
import cn.quantgroup.xyqb.service.http.IHttpService;
import cn.quantgroup.xyqb.service.wechat.IWechatFollowService;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.util.ValidationUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* Created by Miraculous on 2017/1/19.
*/
@Slf4j
@Service
public class WechatFollowServiceImpl implements IWechatFollowService {
private static final String WECHAT_FOLLOW_TOKEN_KEY_PREFIX = "wechatFollow:token:";
@Value("${wechat.appid}")
private String appId;
@Value("${wechat.secret}")
private String secret;
private String accessTokenUrl;
private String userListUrl;
private String qyAccessTokenUrl;
private String departmentUserListUrl;
@Resource
private IHttpService httpService;
@Resource
private IWeChatInfoRelationRepository weChatInfoRelationRepository;
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
private void init() {
// 公众号
accessTokenUrl = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, secret);
userListUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s";
// 企业微信
qyAccessTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wwd6e3a8c6bc4f3763&corpsecret=5SvwrDrf0tapQZnKLcmDJ9nprQ7zEard_mzXCqNm06Y";
// 成员列表
departmentUserListUrl = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=%s&department_id=%s&fetch_child=1";
//
}
@Override
public AccessTokenResponse getToken() {
try {
String response = httpService.get(accessTokenUrl);
if (StringUtils.isEmpty(response)) {
return null;
}
AccessTokenResponse accessTokenResponse = JSONObject.parseObject(response,
AccessTokenResponse.class);
if (accessTokenResponse == null) {
return null;
}
accessTokenResponse.setInitialTime(System.currentTimeMillis() - Constants.MILLIS_OF_TEN_SECOND);
redisTemplate.opsForValue().set(WECHAT_FOLLOW_TOKEN_KEY_PREFIX, JSONObject.toJSONString(accessTokenResponse), accessTokenResponse.getExpiresIn() + Constants.THOUSAND_SECOND, TimeUnit.SECONDS);
return accessTokenResponse;
} catch (Exception ex) {
return null;
}
}
@Override
public JsonResult executeWechatFollowStatus(String nextOpenId) {
AccessTokenResponse token = getToken();
String userListUrlFormat = String.format(userListUrl, token.getAccessToken(), nextOpenId);
try {
String response = httpService.get(userListUrlFormat);
if (response != null) {
WechatUserListResponse wechatUserListResponse = JSONObject.parseObject(response, WechatUserListResponse.class);
if (wechatUserListResponse.getErrcode() == null) {
nextOpenId = wechatUserListResponse.getNext_openid();
List<String> opendIdList = wechatUserListResponse.getData().get("openid");
if (!opendIdList.isEmpty()) {
// 更新指定OpenId 关注状态
weChatInfoRelationRepository.updateIsFollowWechatInOpenIdList(opendIdList);
}
if (nextOpenId != "") {
executeWechatFollowStatus(nextOpenId);
}
return JsonResult.buildSuccessResult("", opendIdList);
} else {
log.error("excuateWechatFollowStatus:errResponse--{}", wechatUserListResponse);
return null;
}
}
} catch (Exception ex) {
log.error("excuateWechatFollowStatus--{}", ex.getMessage());
return null;
}
return null;
}
@Override
public void executeTask() {
// 初始化任务数据
initTask();
// 微信公众号关注数据更新
// executeWechatFollowStatus("");
// 企业微信关注数据更新
executeEnterpriseFollowStatus("");
}
private void executeEnterpriseFollowStatus(String s) {
}
private void initTask() {
try {
// 获取最大微信微信息ID
log.info("微信同步公众号新增数据开始--");
String querySql = "SELECT MAX(`wechat_userinfo_id`) as maxid FROM wechat_info_relation";
Integer maxId = jdbcTemplate.queryForObject(querySql, Integer.class);
System.out.println(maxId);
if (maxId == null) {
maxId = 0;
}
// 同步新增微信公众号数据(app_name 为 xyqb 并且有open_id和union_id)
String sql = String.format("INSERT INTO wechat_info_relation(`wechat_userinfo_id`, `user_id`, `open_id`, `union_id`) SELECT id, user_id, open_id, open_id from wechat_userinfo where id>%s and `open_id` is not null", maxId);
jdbcTemplate.update(sql);
log.info("微信同步公众号新增数据完成--");
// 修改当前所有关注状态为 0 表示未关注
log.info("修改微信关注状态开始--");
String updateSql = "UPDATE wechat_info_relation set is_follow_wechat=0, is_follow_enterprise_wechat=0";
jdbcTemplate.update(updateSql);
log.info("修改微信关注状态完成--");
} catch (Exception e) {
log.error("微信公众号与企业微信关注情况任务失败---{}", e.getMessage());
}
}
}
package cn.quantgroup.xyqb.xxlJob;
import cn.quantgroup.xyqb.service.wechat.IWechatFollowService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@JobHandler(value = "WechatFollowStatusJobHandler")
public class WechatFollowStatusJobHandler extends IJobHandler {
@Autowired
private IWechatFollowService wechatFollowService;
@Override
public ReturnT<String> execute(String s) throws Exception {
wechatFollowService.executeTask();
return ReturnT.SUCCESS;
}
}
package cn.quantgroup.xyqb.xxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
@Configuration
@Slf4j
public class XxlJobConfig {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
// 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
// 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxlJobSpringExecutor.setAppName(appName);
//xxlJobSpringExecutor.setAppName("xxl-job-executor-vcc-analysis");
// 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxlJobSpringExecutor.setIp(ip);
// 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxlJobSpringExecutor.setPort(port);
// 执行器通讯TOKEN [选填]:非空时启用;调度中心和执行器进行安全性校验,双方AccessToken匹配才允许通讯
xxlJobSpringExecutor.setAccessToken(accessToken);
// 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxlJobSpringExecutor.setLogPath(logPath);
// 执行器日志保存天数 [选填] :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
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