diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/config/properties/WxConfig.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/config/properties/WxConfig.java index 119a7d0..0c78530 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/config/properties/WxConfig.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/config/properties/WxConfig.java @@ -27,6 +27,11 @@ public class WxConfig { private String accessTokenUrl; + private String obtainWxOfficialAccountUserUrl; + + private String ObtainUserInfoByOpenidUrl; + + public String getLoginUrl() { return loginUrl; @@ -51,4 +56,20 @@ public class WxConfig { public void setAccessTokenUrl(String accessTokenUrl) { this.accessTokenUrl = accessTokenUrl; } + + public String getObtainWxOfficialAccountUserUrl() { + return obtainWxOfficialAccountUserUrl; + } + + public void setObtainWxOfficialAccountUserUrl(String obtainWxOfficialAccountUserUrl) { + this.obtainWxOfficialAccountUserUrl = obtainWxOfficialAccountUserUrl; + } + + public String getObtainUserInfoByOpenidUrl() { + return ObtainUserInfoByOpenidUrl; + } + + public void setObtainUserInfoByOpenidUrl(String obtainUserInfoByOpenidUrl) { + ObtainUserInfoByOpenidUrl = obtainUserInfoByOpenidUrl; + } } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/UserMemberController.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/UserMemberController.java index c21f86d..45713fc 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/UserMemberController.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/UserMemberController.java @@ -1,12 +1,17 @@ package com.flossom.miniProgram.controller; +import com.flossom.common.core.utils.StringUtils; import com.flossom.common.core.web.controller.BaseController; import com.flossom.common.core.web.domain.AjaxResult; import com.flossom.miniProgram.domain.vo.UserMemberUpdateVo; import com.flossom.miniProgram.service.IWxUserMemberService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + @RestController @RequestMapping("/user") public class UserMemberController extends BaseController { @@ -23,6 +28,10 @@ public class UserMemberController extends BaseController { */ @PostMapping("/login") public AjaxResult login(@RequestParam(value = "code") String code) throws Exception { + if (StringUtils.isBlank(code)) { + logger.error("登录code不能为空"); + return AjaxResult.error("登录失败"); + } return AjaxResult.success(wxUserMemberService.login(code)); } @@ -36,6 +45,10 @@ public class UserMemberController extends BaseController { */ @PostMapping("/upgradeMember") public AjaxResult upgradeMember(@RequestParam(value = "code") String code) throws Exception { + if (StringUtils.isBlank(code)) { + logger.error("获取手机号code不能为空"); + return AjaxResult.error("获取手机号失败"); + } return AjaxResult.success("获取成功", wxUserMemberService.upgradeMember(code)); } @@ -45,7 +58,7 @@ public class UserMemberController extends BaseController { * @return */ @PostMapping("/updateUser") - public AjaxResult updateUser(@RequestBody UserMemberUpdateVo userMemberUpdateVo) { + public AjaxResult updateUser(@RequestBody @Validated UserMemberUpdateVo userMemberUpdateVo) { wxUserMemberService.updateUser(userMemberUpdateVo); return AjaxResult.success(); } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxNoRemindController.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxNoRemindController.java index cdf36ad..7ca861e 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxNoRemindController.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxNoRemindController.java @@ -40,4 +40,14 @@ public class WxNoRemindController extends BaseController { return AjaxResult.success(); } + /** + * 是否关注微信公众号 + * + * @return + */ + @GetMapping("/isAttentionOfficialAccount") + public R isAttentionOfficialAccount() throws Exception { + return R.ok(wxNoRemindService.isAttentionOfficialAccount()); + } + } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserDetailRet.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserDetailRet.java new file mode 100644 index 0000000..160f1b7 --- /dev/null +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserDetailRet.java @@ -0,0 +1,167 @@ +package com.flossom.miniProgram.domain.vo; + +import java.util.List; + +/** + * 分页获取 微信公众号 的关注者用户详情 + */ +public class OfficialAccountUserDetailRet { + + private List user_info_list; + + private Integer errcode; + + private String errmsg; + + public OfficialAccountUserDetailRet() { + } + + public OfficialAccountUserDetailRet(List user_info_list, Integer errcode, String errmsg) { + this.user_info_list = user_info_list; + this.errcode = errcode; + this.errmsg = errmsg; + } + + public List getUser_info_list() { + return user_info_list; + } + + public void setUser_info_list(List user_info_list) { + this.user_info_list = user_info_list; + } + + public Integer getErrcode() { + return errcode; + } + + public void setErrcode(Integer errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + class userInfoList { + private Integer subscribe; + private String openid; + private String language; + private String subscribe_time; + private String unionid; + private String remark; + private Integer groupid; + private List tagid_list; + private String subscribe_scene; + private String qr_scene; + private String qr_scene_str; + + public userInfoList() { + } + + public userInfoList(Integer subscribe, String openid, String language, String subscribe_time, String unionid, String remark, Integer groupid, List tagid_list, String subscribe_scene, String qr_scene, String qr_scene_str) { + this.subscribe = subscribe; + this.openid = openid; + this.language = language; + this.subscribe_time = subscribe_time; + this.unionid = unionid; + this.remark = remark; + this.groupid = groupid; + this.tagid_list = tagid_list; + this.subscribe_scene = subscribe_scene; + this.qr_scene = qr_scene; + this.qr_scene_str = qr_scene_str; + } + + public Integer getSubscribe() { + return subscribe; + } + + public void setSubscribe(Integer subscribe) { + this.subscribe = subscribe; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getSubscribe_time() { + return subscribe_time; + } + + public void setSubscribe_time(String subscribe_time) { + this.subscribe_time = subscribe_time; + } + + public String getUnionid() { + return unionid; + } + + public void setUnionid(String unionid) { + this.unionid = unionid; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Integer getGroupid() { + return groupid; + } + + public void setGroupid(Integer groupid) { + this.groupid = groupid; + } + + public List getTagid_list() { + return tagid_list; + } + + public void setTagid_list(List tagid_list) { + this.tagid_list = tagid_list; + } + + public String getSubscribe_scene() { + return subscribe_scene; + } + + public void setSubscribe_scene(String subscribe_scene) { + this.subscribe_scene = subscribe_scene; + } + + public String getQr_scene() { + return qr_scene; + } + + public void setQr_scene(String qr_scene) { + this.qr_scene = qr_scene; + } + + public String getQr_scene_str() { + return qr_scene_str; + } + + public void setQr_scene_str(String qr_scene_str) { + this.qr_scene_str = qr_scene_str; + } + } +} diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserReq.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserReq.java new file mode 100644 index 0000000..af567af --- /dev/null +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserReq.java @@ -0,0 +1,32 @@ +package com.flossom.miniProgram.domain.vo; + +/** + * 获取 微信公众号关注者详情 请求对象 + */ +public class OfficialAccountUserReq { + private String openid; + private String lang = "zh_CN"; + + public OfficialAccountUserReq() { + } + + public OfficialAccountUserReq(String openid) { + this.openid = openid; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getLang() { + return lang; + } + + public void setLang(String lang) { + this.lang = lang; + } +} diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserRet.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserRet.java new file mode 100644 index 0000000..c3c4905 --- /dev/null +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/OfficialAccountUserRet.java @@ -0,0 +1,100 @@ +package com.flossom.miniProgram.domain.vo; + +import java.util.List; + +/** + * 分页获取 微信公众号 的关注者 + */ +public class OfficialAccountUserRet { + + private Integer total; + private Integer count; + private data data; + private String next_openid; + private Integer errcode; + private String errmsg; + + public OfficialAccountUserRet() { + } + + public OfficialAccountUserRet(Integer total, Integer count, OfficialAccountUserRet.data data, String next_openid, Integer errcode, String errmsg) { + this.total = total; + this.count = count; + this.data = data; + this.next_openid = next_openid; + this.errcode = errcode; + this.errmsg = errmsg; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public OfficialAccountUserRet.data getData() { + return data; + } + + public void setData(OfficialAccountUserRet.data data) { + this.data = data; + } + + public String getNext_openid() { + return next_openid; + } + + public void setNext_openid(String next_openid) { + this.next_openid = next_openid; + } + + public Integer getErrcode() { + return errcode; + } + + public void setErrcode(Integer errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + public List getOpenidList() { + return getData().getOpenid(); + } + + class data { + private List openid; + + public data() { + } + + public data(List openid) { + this.openid = openid; + } + + public List getOpenid() { + return openid; + } + + public void setOpenid(List openid) { + this.openid = openid; + } + } + +} diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/UserMemberUpdateVo.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/UserMemberUpdateVo.java index 7c2efd1..c084660 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/UserMemberUpdateVo.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/domain/vo/UserMemberUpdateVo.java @@ -1,8 +1,10 @@ package com.flossom.miniProgram.domain.vo; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.flossom.common.core.annotation.Excel; +import org.hibernate.validator.constraints.Length; +import org.springframework.format.annotation.DateTimeFormat; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import java.util.Date; public class UserMemberUpdateVo { @@ -10,6 +12,8 @@ public class UserMemberUpdateVo { /** * 昵称 */ + @NotNull(message = "昵称不能为空") + @Size(min = 1, max = 20, message = "昵称长度需要控制在20位以内") private String nickname; /** @@ -20,22 +24,85 @@ public class UserMemberUpdateVo { /** * 省 */ - private Integer province; + @NotNull(message = "请选择省") + private Integer provinceId; /** * 市 */ - private Integer city; + @NotNull(message = "请选择市") + private Integer cityId; /** * 区 */ - private Integer area; + @NotNull(message = "请选择区") + private Integer areaId; /** * 生日 */ - @JsonFormat(pattern = "yyyy-MM-dd") + @NotNull(message = "请选择生日日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday; + public UserMemberUpdateVo() { + } + + public UserMemberUpdateVo(String nickname, String headimg, Integer provinceId, Integer cityId, Integer areaId, Date birthday) { + this.nickname = nickname; + this.headimg = headimg; + this.provinceId = provinceId; + this.cityId = cityId; + this.areaId = areaId; + this.birthday = birthday; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public String getHeadimg() { + return headimg; + } + + public void setHeadimg(String headimg) { + this.headimg = headimg; + } + + public Integer getProvinceId() { + return provinceId; + } + + public void setProvinceId(Integer provinceId) { + this.provinceId = provinceId; + } + + public Integer getCityId() { + return cityId; + } + + public void setCityId(Integer cityId) { + this.cityId = cityId; + } + + public Integer getAreaId() { + return areaId; + } + + public void setAreaId(Integer areaId) { + this.areaId = areaId; + } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxNoRemindService.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxNoRemindService.java index 27679af..7c540b7 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxNoRemindService.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxNoRemindService.java @@ -1,6 +1,7 @@ package com.flossom.miniProgram.service; import com.flossom.common.core.domain.entity.WxNoRemindRecord; +import com.sun.org.apache.xpath.internal.operations.Bool; public interface IWxNoRemindService { @@ -8,4 +9,6 @@ public interface IWxNoRemindService { void closeOfficialAccount(); + Boolean isAttentionOfficialAccount() throws Exception; + } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxNoRemindServiceImpl.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxNoRemindServiceImpl.java index ecd24fd..21585cb 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxNoRemindServiceImpl.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxNoRemindServiceImpl.java @@ -7,6 +7,7 @@ import com.flossom.common.core.utils.DateUtils; import com.flossom.common.core.web.domain.AjaxResult; import com.flossom.common.security.utils.SecurityUtils; import com.flossom.miniProgram.service.IWxNoRemindService; +import com.flossom.miniProgram.utils.MiniProgramUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -28,4 +29,9 @@ public class WxNoRemindServiceImpl implements IWxNoRemindService { wxNoRemindRecord.setCreateTime(DateUtils.getNowDate()); wxNoRemindRecordMapper.insertWxNoRemindRecord(wxNoRemindRecord); } + + @Override + public Boolean isAttentionOfficialAccount() throws Exception { + return MiniProgramUtils.isAttentionOfficialAccount(SecurityUtils.getLoginUser().getWxUserMember().getUnionid()); + } } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxUserMemberServiceImpl.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxUserMemberServiceImpl.java index 1ee250c..ebfc039 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxUserMemberServiceImpl.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxUserMemberServiceImpl.java @@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -100,27 +101,31 @@ public class WxUserMemberServiceImpl implements IWxUserMemberService { @Override public String upgradeMember(String code) throws Exception { - String accessToken = MiniProgramUtils.getAccessToken(SecurityUtils.getLoginUser().getWxUserMember().getOpenid()); - if (StringUtils.isBlank(accessToken)) { - throw new ServiceException("获取用户手机号码失败"); - } - // 获取手机号 - String result = MiniProgramUtils.getPhone(code, accessToken); + String result = MiniProgramUtils.getPhone(code); logger.info("请求微信服务器获取手机号码返回结果:{}", result); WxCode2PhoneRet wxCode2PhoneRet = JSON.parseObject(result, WxCode2PhoneRet.class); if (wxCode2PhoneRet.getErrcode() != 0) { logger.error("获取用户手机号码失败: {}", wxCode2PhoneRet.getErrmsg()); throw new ServiceException("获取用户手机号码失败"); } - wxUserMemberMapper.upgradeMember(wxCode2PhoneRet.getPhoneInfo().getPhoneNumber(), SecurityUtils.getWxUserId().intValue()); + wxUserMemberMapper.upgradeMember(wxCode2PhoneRet.getPhoneInfo().getPhoneNumber(), SecurityUtils.getLoginUser().getWxUserMember().getId().intValue()); return wxCode2PhoneRet.getPhoneInfo().getPhoneNumber(); } @Override public void updateUser(UserMemberUpdateVo userMemberUpdateVo) { - WxUserMember wxUserMember = new WxUserMember(); + // 完善用户信息, 判断是否标记完善信息 + WxUserMember wxUserMember = SecurityUtils.getLoginUser().getWxUserMember(); + if (wxUserMember.getIsCompleteInformation() == null || + wxUserMember.getIsCompleteInformation() == 0) { + // 修改完善 + wxUserMember.setIsCompleteInformation(1); + // TODO: 首次完善,增加加分 + + } BeanUtils.copyProperties(userMemberUpdateVo, wxUserMember); + wxUserMember.setUpdateTime(DateUtils.getNowDate()); wxUserMemberMapper.updateWxUserMember(wxUserMember); } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/utils/MiniProgramUtils.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/utils/MiniProgramUtils.java index c8f70ad..b78f7c7 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/utils/MiniProgramUtils.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/utils/MiniProgramUtils.java @@ -3,19 +3,26 @@ package com.flossom.miniProgram.utils; import com.alibaba.fastjson.JSON; import com.flossom.common.core.constant.CacheConstants; import com.flossom.common.core.domain.entity.WxParameterSetting; +import com.flossom.common.core.exception.ServiceException; import com.flossom.common.core.mapper.WxParameterSettingMapper; import com.flossom.common.core.utils.StringUtils; import com.flossom.common.redis.service.RedisService; import com.flossom.miniProgram.config.properties.WxConfig; +import com.flossom.miniProgram.domain.vo.OfficialAccountUserDetailRet; +import com.flossom.miniProgram.domain.vo.OfficialAccountUserReq; +import com.flossom.miniProgram.domain.vo.OfficialAccountUserRet; import com.flossom.miniProgram.domain.vo.WxAccessTokenRet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * 小程序工具类 @@ -94,29 +101,18 @@ public class MiniProgramUtils { } /** - * @param code - * @return

- * errcode number 错误码 - * errmsg string 错误信息 - * phone_info object 用户手机号信息 - * 属性 类型 说明 - * phoneNumber string 用户绑定的手机号(国外手机号会有区号) - * purePhoneNumber string 没有区号的手机号 - * countryCode string 区号 + * 获取 acceess_token + * + * @return + * @throws Exception */ - public static String getPhone(String code, String accessToken) throws Exception { - Map params = new HashMap(); - params.put("code", code); - return HttpClientUtils.postParameters(wxConfig.getObtainPhoneUrl() + "?access_token=" + accessToken, JSON.toJSONString(params)); - } - - public static String getAccessToken(String openid) throws Exception { + public static String getAccessToken() throws Exception { /** * TODO: 将 access_token 保存在 redis,官方定义说 access_token 的有效期暂定为 2个小时,使用注意事项: * 1、从 redis 获取 access_token,存在则未过期,不存在则过期了,重新获取 access_token */ - String accessToken = redisService.getCacheObject(CacheConstants.WX_ACCESS_TOKEN_CACHE + openid); + String accessToken = redisService.getCacheObject(CacheConstants.WX_ACCESS_TOKEN_CACHE); if (StringUtils.isBlank(accessToken)) { // 获取 access_token Map params = new HashMap(); @@ -132,10 +128,138 @@ public class MiniProgramUtils { return null; } // 保存缓存 - redisService.setCacheObject(CacheConstants.WX_ACCESS_TOKEN_CACHE + openid, + redisService.setCacheObject(CacheConstants.WX_ACCESS_TOKEN_CACHE, wxAccessTokenRet.getAccess_token(), CacheConstants.WX_ACCESS_TOKEN_EXPIRATION, TimeUnit.MINUTES); return wxAccessTokenRet.getAccess_token(); } return accessToken; } + + /** + * @param code + * @return

+ * errcode number 错误码 + * errmsg string 错误信息 + * phone_info object 用户手机号信息 + * 属性 类型 说明 + * phoneNumber string 用户绑定的手机号(国外手机号会有区号) + * purePhoneNumber string 没有区号的手机号 + * countryCode string 区号 + */ + public static String getPhone(String code) throws Exception { + String accessToken = getAccessToken(); + if (StringUtils.isBlank(accessToken)) { + throw new ServiceException("获取用户手机号码失败"); + } + + Map params = new HashMap(); + params.put("code", code); + return HttpClientUtils.postParameters(wxConfig.getObtainPhoneUrl() + "?access_token=" + accessToken, JSON.toJSONString(params)); + } + + /** + * 分页 获取关注微信公众号的用户列表 + * TODO: 一次拉取调用最多拉取10000个关注者的OpenID + * + * @param nextOpenid 下一个用户openid + * @return + * @throws Exception + */ + public static OfficialAccountUserRet obtainWxOfficialAccountUser(String nextOpenid) throws Exception { + String accessToken = getAccessToken(); + if (StringUtils.isBlank(accessToken)) { + logger.error("获取关注微信公众号用户列表失败,获取accessToken失败!"); + throw new ServiceException("操作失败"); + } + + Map params = new HashMap(); + params.put("access_token", accessToken); + if (StringUtils.isNotBlank(nextOpenid)) { + params.put("next_openid", nextOpenid); + } + String result = HttpClientUtils.getParameters(wxConfig.getObtainWxOfficialAccountUserUrl(), params); + logger.info("请求微信服务器获取关注微信公众号的用户列表:{}", result); + OfficialAccountUserRet officialAccountUserRet = JSON.parseObject(result, OfficialAccountUserRet.class); + if (officialAccountUserRet.getErrcode() != 0) { + logger.error("获取 关注微信公众号的用户列表 失败: {}", officialAccountUserRet.getErrmsg()); + throw new ServiceException("操作失败"); + } + return officialAccountUserRet; + } + + /** + * 获取 关注微信公众号用户的 unionid + * TODO: officialAccountUserReqList 最多支持 100 条 + */ + public static OfficialAccountUserDetailRet ObtainUserInfoByOpenid(List officialAccountUserReqList) throws Exception { + String accessToken = getAccessToken(); + if (StringUtils.isBlank(accessToken)) { + logger.error("获取关注微信公众号用户的unionid,获取accessToken失败!"); + throw new ServiceException("操作失败"); + } + + Map params = new HashMap(); + params.put("user_list", JSON.toJSONString(officialAccountUserReqList)); + String result = HttpClientUtils.postParameters(wxConfig.getObtainUserInfoByOpenidUrl() + "access_token=" + accessToken, params); + logger.info("请求微信服务器获获取 关注微信公众号用户的 unionid列表:{}", result); + OfficialAccountUserDetailRet officialAccountUserDetailRet = JSON.parseObject(result, OfficialAccountUserDetailRet.class); + if (officialAccountUserDetailRet.getErrcode() != 0) { + logger.error("获取 关注微信公众号的用户列表 失败: {}", officialAccountUserDetailRet.getErrmsg()); + throw new ServiceException("操作失败"); + } + return officialAccountUserDetailRet; + } + + /** + * 是否关注微信公众号 + * + * @return + */ + public static Boolean isAttentionOfficialAccount(String unionId) throws Exception { + String nextOpenid = null; + Integer currentTotal = 0; + while (true) { + // 1、获取关注微信公众号的用户列表 + OfficialAccountUserRet officialAccountUserRet = obtainWxOfficialAccountUser(null); + if (officialAccountUserRet.getErrcode() != 0) { + logger.error("获取微信用户列表失败: {}", officialAccountUserRet.getErrmsg()); + throw new ServiceException("获取微信用户列表失败"); + } + if (officialAccountUserRet.getCount() == null || officialAccountUserRet.getCount() == 0) { + return false; + } + currentTotal += officialAccountUserRet.getCount(); + nextOpenid = officialAccountUserRet.getNext_openid(); + List openidList = officialAccountUserRet.getOpenidList(); + logger.info("微信公众号关注列表:{}", openidList); + + // 2、获取关注微信公众号的unionid + int pageSize = 100; + int page = (int) Math.ceil(officialAccountUserRet.getCount() / pageSize); + if (page > 0) { + List pageOpenidList = officialAccountUserRet.getOpenidList(); + for (int pageNo = 1; pageNo <= page; pageNo++) { + // 构建分页集合 + List pageList = pageOpenidList.stream().skip((pageNo - 1) * 100).limit(pageSize).collect(Collectors.toList()); + // 获取 unionid 集合 + List officialAccountUserReqList = new ArrayList<>(); + for (String openid : pageList) { + OfficialAccountUserReq officialAccountUserReq = new OfficialAccountUserReq(openid); + officialAccountUserReqList.add(officialAccountUserReq); + } + OfficialAccountUserDetailRet officialAccountUserDetailRet = ObtainUserInfoByOpenid(officialAccountUserReqList); + + // 3、判断当前unionid是否在关注者列表 + return officialAccountUserDetailRet.getUser_info_list().contains(unionId); + } + } + // 4、 查完全部,未找到退出 + if (StringUtils.isBlank(nextOpenid) || officialAccountUserRet.getTotal() == currentTotal) { + break; + } + } + return false; + } + + }