mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 04:39:00 +08:00
feat(wx-account-fans): 添加粉丝导出功能及数据脱敏处理
- 新增 WxAccountFansExcelVO 类用于粉丝数据的 Excel 导出 - 在 WxAccountFansController 中实现导出接口,支持按条件筛选粉丝 - 更新 WxAccountFansServiceImpl 中的粉丝更新逻辑,确保数据同步至微信 - 对粉丝的 OpenID 进行脱敏处理,确保隐私安全 (cherry picked from commit a277413d3d7681407daca46bbb635f57a032b25f)
This commit is contained in:
1 parent
e63dc8678e
commit
cb08f11a5d
5 files changed
+353
-241
No files matched your search
@@ -18,6 +18,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pigx-common-excel</artifactId>
|
||||
</dependency>
|
||||
<!--注册中心客户端-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
|
||||
+57
-5
@@ -16,7 +16,21 @@
|
||||
*/
|
||||
package com.pig4cloud.pigx.mp.controller;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
|
||||
import com.pig4cloud.pigx.common.sensitive.util.DesensitizedUtils;
|
||||
import com.pig4cloud.pigx.mp.entity.vo.WxAccountFansExcelVO;
|
||||
import java.util.List;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
|
||||
@@ -26,7 +40,7 @@ import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 微信公众号粉丝管理
|
||||
* 微信公众号粉丝管理,委托服务层维护粉丝及微信状态,并提供按列表条件筛选的脱敏 Excel 导出。
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2019-03-26 22:08:08
|
||||
@@ -34,10 +48,43 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/wx-account-fans")
|
||||
@Tag(name = "公众号粉丝管理")
|
||||
public class WxAccountFansController {
|
||||
|
||||
private final WxAccountFansService wxAccountFansService;
|
||||
|
||||
/**
|
||||
* 导出当前租户内符合筛选条件的全部粉丝,不受分页参数限制。 OpenID 始终按密钥规则脱敏,仅转换导出副本,不修改数据库实体。
|
||||
* @param query 查询条件;公众号 AppID 为空时不限定公众号,昵称为空时不限定昵称
|
||||
* @return Excel 行数据;无匹配记录时返回空列表,由 ResponseExcel 输出表头
|
||||
*/
|
||||
@Operation(summary = "导出公众号粉丝", description = "按公众号和昵称筛选全部记录,OpenID 脱敏导出")
|
||||
@ResponseExcel(name = "fans")
|
||||
@GetMapping("/export")
|
||||
@HasPermission("mp_wxaccountfans_sync")
|
||||
public List<WxAccountFansExcelVO> export(WxAccountFans query) {
|
||||
return wxAccountFansService
|
||||
.list(Wrappers.<WxAccountFans>lambdaQuery()
|
||||
.eq(StrUtil.isNotBlank(query.getWxAccountAppid()), WxAccountFans::getWxAccountAppid,
|
||||
query.getWxAccountAppid())
|
||||
.like(StrUtil.isNotBlank(query.getNickname()), WxAccountFans::getNickname, query.getNickname()))
|
||||
.stream()
|
||||
.map(fan -> {
|
||||
WxAccountFansExcelVO row = new WxAccountFansExcelVO();
|
||||
BeanUtil.copyProperties(fan, row, "openid", "subscribeStatus", "subscribeTime", "isBlack", "tagIds");
|
||||
// Excel 不经过 Jackson 的 @Sensitive 序列化,单独对导出副本脱敏。
|
||||
row.setOpenid(DesensitizedUtils.key(fan.getOpenid()));
|
||||
row.setSubscribeStatus(YesNoEnum.YES.getCode().equals(fan.getSubscribeStatus()) ? "已关注" : "未关注");
|
||||
row.setSubscribeTime(fan.getSubscribeTime() == null ? StrUtil.EMPTY
|
||||
: LocalDateTimeUtil.format(fan.getSubscribeTime(), DatePattern.NORM_DATETIME_PATTERN));
|
||||
row.setIsBlack(CommonConstants.FAIL.equals(fan.getIsBlack()) ? YesNoEnum.YES.getDesc()
|
||||
: YesNoEnum.NO.getDesc());
|
||||
row.setTagIds(ArrayUtil.join(fan.getTagIds(), StrUtil.COMMA));
|
||||
return row;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param page 分页对象
|
||||
@@ -60,15 +107,20 @@ public class WxAccountFansController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改微信公众号粉丝
|
||||
* @param wxAccountFans 微信公众号粉丝
|
||||
* @return R
|
||||
* 修改粉丝备注及标签,通过 ID 恢复数据库中的真实 OpenID 和公众号 AppID 后调用微信。
|
||||
* @param wxAccountFans 待更新数据,须包含粉丝 ID;回显 OpenID 可为脱敏值
|
||||
* @return 更新成功返回成功响应;记录不存在返回失败响应,客户端应保留编辑状态并提示刷新
|
||||
* @throws Exception 数据库或微信调用异常,交由统一异常处理器转换为失败响应
|
||||
*/
|
||||
@Operation(summary = "修改公众号粉丝")
|
||||
@SysLog("修改微信公众号粉丝")
|
||||
@PutMapping
|
||||
@HasPermission("mp_wxaccountfans_edit")
|
||||
public R updateById(@RequestBody WxAccountFans wxAccountFans) {
|
||||
return R.ok(wxAccountFansService.updateFans(wxAccountFans));
|
||||
if (!Boolean.TRUE.equals(wxAccountFansService.updateFans(wxAccountFans))) {
|
||||
return R.failed("粉丝记录不存在,请刷新后重试");
|
||||
}
|
||||
return R.ok(Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
@@ -19,6 +19,8 @@ package com.pig4cloud.pigx.mp.entity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.pig4cloud.pigx.common.core.util.TenantTable;
|
||||
import com.pig4cloud.pigx.common.sensitive.annotation.Sensitive;
|
||||
import com.pig4cloud.pigx.common.sensitive.core.SensitiveTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -46,6 +48,7 @@ public class WxAccountFans extends Model<WxAccountFans> {
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
@Sensitive(type = SensitiveTypeEnum.KEY)
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.pig4cloud.pigx.mp.entity.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.apache.fesod.sheet.annotation.write.style.ColumnWidth;
|
||||
|
||||
/**
|
||||
* 公众号粉丝 Excel 行,由导出接口从实体转换;状态和日期为展示文本,OpenID 为脱敏副本。 该对象仅用于文件输出,不接受写入请求,不参与粉丝身份识别。
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(24)
|
||||
public class WxAccountFansExcelVO {
|
||||
|
||||
/** 用户标识,固定六位掩码,仅保留末三位;null 时单元格为空。 */
|
||||
@ExcelProperty("OpenID")
|
||||
private String openid;
|
||||
|
||||
/** 关注状态展示文本:已关注或未关注。 */
|
||||
@ExcelProperty("关注状态")
|
||||
private String subscribeStatus;
|
||||
|
||||
/** 关注时间,格式 yyyy-MM-dd HH:mm:ss;未记录时为空。 */
|
||||
@ExcelProperty("关注时间")
|
||||
private String subscribeTime;
|
||||
|
||||
/** 粉丝昵称,可为空。 */
|
||||
@ExcelProperty("昵称")
|
||||
private String nickname;
|
||||
|
||||
/** 微信返回的语言代码,可为空。 */
|
||||
@ExcelProperty("语言")
|
||||
private String language;
|
||||
|
||||
/** 黑名单状态展示文本:是或否。 */
|
||||
@ExcelProperty("黑名单")
|
||||
private String isBlack;
|
||||
|
||||
/** 标签 ID 的逗号分隔文本,无标签时为空。 */
|
||||
@ExcelProperty("标签ID")
|
||||
private String tagIds;
|
||||
|
||||
/** 粉丝备注,可为空。 */
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
/** 粉丝所属公众号名称,可为空。 */
|
||||
@ExcelProperty("公众号名称")
|
||||
private String wxAccountName;
|
||||
|
||||
}
|
||||
+239
-236
@@ -69,273 +69,276 @@ import java.util.List;
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class WxAccountFansServiceImpl extends ServiceImpl<WxAccountFansMapper, WxAccountFans>
|
||||
implements WxAccountFansService {
|
||||
implements WxAccountFansService {
|
||||
|
||||
private static final int SIZE = 100;
|
||||
private static final int SIZE = 100;
|
||||
|
||||
private final WxAccountTagMapper wxAccountTagMapper;
|
||||
private final WxAccountTagMapper wxAccountTagMapper;
|
||||
|
||||
private final WxAccountMapper wxAccountMapper;
|
||||
private final WxAccountMapper wxAccountMapper;
|
||||
|
||||
/**
|
||||
* 保存粉丝
|
||||
*
|
||||
* @param wxMpUser wx mp 用户
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WxAccountFans saveFans(WxMpUser wxMpUser) {
|
||||
// 删除历史关注保存的信息
|
||||
baseMapper.delete(Wrappers.<WxAccountFans>lambdaQuery().eq(WxAccountFans::getOpenid, wxMpUser.getOpenId()));
|
||||
/**
|
||||
* 保存粉丝
|
||||
* @param wxMpUser wx mp 用户
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WxAccountFans saveFans(WxMpUser wxMpUser) {
|
||||
// 删除历史关注保存的信息
|
||||
baseMapper.delete(Wrappers.<WxAccountFans>lambdaQuery().eq(WxAccountFans::getOpenid, wxMpUser.getOpenId()));
|
||||
|
||||
WxAccountFans wxAccountFans = new WxAccountFans();
|
||||
wxAccountFans.setOpenid(wxMpUser.getOpenId());
|
||||
wxAccountFans.setSubscribeStatus(String.valueOf(BooleanUtil.toInt(wxMpUser.getSubscribe())));
|
||||
wxAccountFans.setSubscribeTime(LocalDateTime
|
||||
.ofInstant(Instant.ofEpochMilli(wxMpUser.getSubscribeTime() * 1000L), ZoneId.systemDefault()));
|
||||
WxAccountFans wxAccountFans = new WxAccountFans();
|
||||
wxAccountFans.setOpenid(wxMpUser.getOpenId());
|
||||
wxAccountFans.setSubscribeStatus(String.valueOf(BooleanUtil.toInt(wxMpUser.getSubscribe())));
|
||||
wxAccountFans.setSubscribeTime(LocalDateTime
|
||||
.ofInstant(Instant.ofEpochMilli(wxMpUser.getSubscribeTime() * 1000L), ZoneId.systemDefault()));
|
||||
|
||||
// 随机生成一个昵称,方便平台内部使用
|
||||
String generatedName = ChineseNameGenerator.getInstance().generate();
|
||||
wxAccountFans.setNickname(generatedName);
|
||||
wxAccountFans.setLanguage(wxMpUser.getLanguage());
|
||||
wxAccountFans.setHeadimgUrl(wxMpUser.getHeadImgUrl());
|
||||
wxAccountFans.setRemark(wxMpUser.getRemark());
|
||||
wxAccountFans.setTagIds(wxAccountFans.getTagIds());
|
||||
// 随机生成一个昵称,方便平台内部使用
|
||||
String generatedName = ChineseNameGenerator.getInstance().generate();
|
||||
wxAccountFans.setNickname(generatedName);
|
||||
wxAccountFans.setLanguage(wxMpUser.getLanguage());
|
||||
wxAccountFans.setHeadimgUrl(wxMpUser.getHeadImgUrl());
|
||||
wxAccountFans.setRemark(wxMpUser.getRemark());
|
||||
wxAccountFans.setTagIds(wxAccountFans.getTagIds());
|
||||
|
||||
WxAccount wxAccount = wxAccountMapper
|
||||
.selectOne(Wrappers.<WxAccount>lambdaQuery().eq(WxAccount::getAppid, WxMpContextHolder.getAppId()));
|
||||
wxAccountFans.setWxAccountId(wxAccount.getId());
|
||||
wxAccountFans.setWxAccountAppid(wxAccount.getAppid());
|
||||
wxAccountFans.setWxAccountName(wxAccount.getName());
|
||||
return wxAccountFans;
|
||||
}
|
||||
WxAccount wxAccount = wxAccountMapper
|
||||
.selectOne(Wrappers.<WxAccount>lambdaQuery().eq(WxAccount::getAppid, WxMpContextHolder.getAppId()));
|
||||
wxAccountFans.setWxAccountId(wxAccount.getId());
|
||||
wxAccountFans.setWxAccountAppid(wxAccount.getAppid());
|
||||
wxAccountFans.setWxAccountName(wxAccount.getName());
|
||||
return wxAccountFans;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询粉丝
|
||||
*
|
||||
* @param page 粉丝
|
||||
* @param wxAccountFans 查询条件
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public IPage getFansWithTagPage(Page page, WxAccountFans wxAccountFans) {
|
||||
// 查询当前公众号的标签
|
||||
List<WxAccountTag> wxAccountTags = wxAccountTagMapper.selectList(Wrappers.emptyWrapper());
|
||||
/**
|
||||
* 分页查询粉丝
|
||||
* @param page 粉丝
|
||||
* @param wxAccountFans 查询条件
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public IPage getFansWithTagPage(Page page, WxAccountFans wxAccountFans) {
|
||||
// 查询当前公众号的标签
|
||||
List<WxAccountTag> wxAccountTags = wxAccountTagMapper.selectList(Wrappers.emptyWrapper());
|
||||
|
||||
// 翻页查询粉丝
|
||||
LambdaQueryWrapper<WxAccountFans> wrapper = Wrappers.<WxAccountFans>lambdaQuery()
|
||||
.eq(StrUtil.isNotBlank(wxAccountFans.getWxAccountAppid()), WxAccountFans::getWxAccountAppid,
|
||||
wxAccountFans.getWxAccountAppid())
|
||||
.like(StrUtil.isNotBlank(wxAccountFans.getNickname()), WxAccountFans::getNickname,
|
||||
wxAccountFans.getNickname());
|
||||
// 翻页查询粉丝
|
||||
LambdaQueryWrapper<WxAccountFans> wrapper = Wrappers.<WxAccountFans>lambdaQuery()
|
||||
.eq(StrUtil.isNotBlank(wxAccountFans.getWxAccountAppid()), WxAccountFans::getWxAccountAppid,
|
||||
wxAccountFans.getWxAccountAppid())
|
||||
.like(StrUtil.isNotBlank(wxAccountFans.getNickname()), WxAccountFans::getNickname,
|
||||
wxAccountFans.getNickname());
|
||||
|
||||
IPage<WxAccountFans> fansPage = baseMapper.selectPage(page, wrapper);
|
||||
IPage<WxAccountFans> fansPage = baseMapper.selectPage(page, wrapper);
|
||||
|
||||
List<WxAccountFansVo> voList = fansPage.getRecords().stream().map(fans -> {//
|
||||
WxAccountFansVo vo = new WxAccountFansVo();
|
||||
BeanUtils.copyProperties(fans, vo);
|
||||
// 赋值 TAG_NAME
|
||||
if (ArrayUtil.isNotEmpty(fans.getTagIds())) {
|
||||
List<WxAccountTag> tagList = wxAccountTags.stream()
|
||||
.filter(tag -> tag.getWxAccountAppid().endsWith(fans.getWxAccountAppid()) //
|
||||
&& ArrayUtil.contains(fans.getTagIds(), tag.getTagId()))
|
||||
.toList();
|
||||
vo.setTagList(tagList);
|
||||
}
|
||||
return vo;
|
||||
}).toList();
|
||||
List<WxAccountFansVo> voList = fansPage.getRecords().stream().map(fans -> {//
|
||||
WxAccountFansVo vo = new WxAccountFansVo();
|
||||
BeanUtils.copyProperties(fans, vo);
|
||||
// 赋值 TAG_NAME
|
||||
if (ArrayUtil.isNotEmpty(fans.getTagIds())) {
|
||||
List<WxAccountTag> tagList = wxAccountTags.stream()
|
||||
.filter(tag -> tag.getWxAccountAppid().endsWith(fans.getWxAccountAppid()) //
|
||||
&& ArrayUtil.contains(fans.getTagIds(), tag.getTagId()))
|
||||
.toList();
|
||||
vo.setTagList(tagList);
|
||||
}
|
||||
return vo;
|
||||
}).toList();
|
||||
|
||||
page.setRecords(voList);
|
||||
page.setTotal(fansPage.getTotal());
|
||||
return page;
|
||||
}
|
||||
page.setRecords(voList);
|
||||
page.setTotal(fansPage.getTotal());
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新粉丝信息
|
||||
*
|
||||
* @param wxAccountFans 信息
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateFans(WxAccountFans wxAccountFans) {
|
||||
baseMapper.updateById(wxAccountFans);
|
||||
/**
|
||||
* 更新粉丝数据并同步微信备注、标签;数据库中的 OpenID 和公众号 AppID 覆盖回显身份字段。
|
||||
* 非空备注才同步至微信,非空标签数组先移除原标签再添加新标签,沿用现有更新行为。
|
||||
* @param wxAccountFans 待更新粉丝,不能为 null,须包含有效 ID;OpenID 可为脱敏回显值
|
||||
* @return 记录存在且更新完成返回 true;记录不存在返回 false,不写库也不调用微信
|
||||
* @throws Exception 数据库或微信 API 调用失败时向上抛出,数据库事务回滚
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateFans(WxAccountFans wxAccountFans) {
|
||||
WxAccountFans stored = baseMapper.selectById(wxAccountFans.getId());
|
||||
if (stored == null) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
// OpenID 回显经过脱敏,持久化与微信操作必须使用数据库中的真实身份。
|
||||
wxAccountFans.setOpenid(stored.getOpenid());
|
||||
wxAccountFans.setWxAccountAppid(stored.getWxAccountAppid());
|
||||
baseMapper.updateById(wxAccountFans);
|
||||
|
||||
// 获取操作微信接口类
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(wxAccountFans.getWxAccountAppid());
|
||||
// 获取操作微信接口类
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(wxAccountFans.getWxAccountAppid());
|
||||
|
||||
if (StrUtil.isNotBlank(wxAccountFans.getRemark())) {
|
||||
WxMpUserService wxMpUserService = wxMpService.getUserService();
|
||||
wxMpUserService.userUpdateRemark(wxAccountFans.getOpenid(), wxAccountFans.getRemark());
|
||||
if (StrUtil.isNotBlank(wxAccountFans.getRemark())) {
|
||||
WxMpUserService wxMpUserService = wxMpService.getUserService();
|
||||
wxMpUserService.userUpdateRemark(wxAccountFans.getOpenid(), wxAccountFans.getRemark());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户标签
|
||||
WxMpUserTagService userTagService = wxMpService.getUserTagService();
|
||||
if (ArrayUtil.isNotEmpty(wxAccountFans.getTagIds())) {
|
||||
// 移除原有标签
|
||||
List<Long> oldTag = userTagService.userTagList(wxAccountFans.getOpenid());
|
||||
for (Long tagId : oldTag) {
|
||||
userTagService.batchUntagging(tagId, new String[]{wxAccountFans.getOpenid()});
|
||||
}
|
||||
// 更新用户标签
|
||||
WxMpUserTagService userTagService = wxMpService.getUserTagService();
|
||||
if (ArrayUtil.isNotEmpty(wxAccountFans.getTagIds())) {
|
||||
// 移除原有标签
|
||||
List<Long> oldTag = userTagService.userTagList(wxAccountFans.getOpenid());
|
||||
for (Long tagId : oldTag) {
|
||||
userTagService.batchUntagging(tagId, new String[] { wxAccountFans.getOpenid() });
|
||||
}
|
||||
|
||||
// 添加新标签
|
||||
for (Long tagId : wxAccountFans.getTagIds()) {
|
||||
// 添加新标签
|
||||
for (Long tagId : wxAccountFans.getTagIds()) {
|
||||
|
||||
userTagService.batchTagging(tagId, new String[]{wxAccountFans.getOpenid()});
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
userTagService.batchTagging(tagId, new String[] { wxAccountFans.getOpenid() });
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Boolean unblack(Long[] ids, String appId) {
|
||||
List<WxAccountFans> wxAccountFans = this.listByIds(CollUtil.toList(ids));
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserBlacklistService blackListService = wxMpService.getBlackListService();
|
||||
List<String> collect = wxAccountFans.stream().map(WxAccountFans::getOpenid).toList();
|
||||
blackListService.pullFromBlacklist(collect);
|
||||
// 更新数据
|
||||
List<WxAccountFans> fansList = wxAccountFans.stream().peek(item -> {
|
||||
item.setIsBlack(CommonConstants.SUCCESS);
|
||||
}).toList();
|
||||
this.updateBatchById(fansList);
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Boolean unblack(Long[] ids, String appId) {
|
||||
List<WxAccountFans> wxAccountFans = this.listByIds(CollUtil.toList(ids));
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserBlacklistService blackListService = wxMpService.getBlackListService();
|
||||
List<String> collect = wxAccountFans.stream().map(WxAccountFans::getOpenid).toList();
|
||||
blackListService.pullFromBlacklist(collect);
|
||||
// 更新数据
|
||||
List<WxAccountFans> fansList = wxAccountFans.stream().peek(item -> {
|
||||
item.setIsBlack(CommonConstants.SUCCESS);
|
||||
}).toList();
|
||||
this.updateBatchById(fansList);
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Boolean black(Long[] ids, String appId) {
|
||||
List<WxAccountFans> wxAccountFans = this.listByIds(CollUtil.toList(ids));
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserBlacklistService blackListService = wxMpService.getBlackListService();
|
||||
List<String> collect = wxAccountFans.stream().map(WxAccountFans::getOpenid).toList();
|
||||
blackListService.pushToBlacklist(collect);
|
||||
// 更新数据
|
||||
List<WxAccountFans> fansList = wxAccountFans.stream().peek(item -> {
|
||||
item.setIsBlack(CommonConstants.FAIL);
|
||||
}).toList();
|
||||
this.updateBatchById(fansList);
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Boolean black(Long[] ids, String appId) {
|
||||
List<WxAccountFans> wxAccountFans = this.listByIds(CollUtil.toList(ids));
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserBlacklistService blackListService = wxMpService.getBlackListService();
|
||||
List<String> collect = wxAccountFans.stream().map(WxAccountFans::getOpenid).toList();
|
||||
blackListService.pushToBlacklist(collect);
|
||||
// 更新数据
|
||||
List<WxAccountFans> fansList = wxAccountFans.stream().peek(item -> {
|
||||
item.setIsBlack(CommonConstants.FAIL);
|
||||
}).toList();
|
||||
this.updateBatchById(fansList);
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公众号粉丝,生产建议异步
|
||||
*
|
||||
* @param appId
|
||||
* @return
|
||||
*/
|
||||
@Async
|
||||
@Override
|
||||
public void syncAccountFans(String appId) {
|
||||
WxAccount wxAccount = wxAccountMapper
|
||||
.selectOne(Wrappers.<WxAccount>query().lambda().eq(WxAccount::getAppid, appId));
|
||||
/**
|
||||
* 获取公众号粉丝,生产建议异步
|
||||
* @param appId
|
||||
* @return
|
||||
*/
|
||||
@Async
|
||||
@Override
|
||||
public void syncAccountFans(String appId) {
|
||||
WxAccount wxAccount = wxAccountMapper
|
||||
.selectOne(Wrappers.<WxAccount>query().lambda().eq(WxAccount::getAppid, appId));
|
||||
|
||||
// 获取操作微信接口类
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserService wxMpUserService = wxMpService.getUserService();
|
||||
// 根据公众号查询已同步用户openid 查询最新的一条 (注意不能按订阅时间排序)
|
||||
String finalNextOpenId = queryNextOpenId(appId);
|
||||
TenantBroker.runAs(() -> WxMpInitConfigRunner.getTenants().get(appId),
|
||||
(id) -> fetchUser(finalNextOpenId, wxAccount, wxMpUserService));
|
||||
// 获取操作微信接口类
|
||||
WxMpService wxMpService = WxMpInitConfigRunner.getMpServices().get(appId);
|
||||
WxMpUserService wxMpUserService = wxMpService.getUserService();
|
||||
// 根据公众号查询已同步用户openid 查询最新的一条 (注意不能按订阅时间排序)
|
||||
String finalNextOpenId = queryNextOpenId(appId);
|
||||
TenantBroker.runAs(() -> WxMpInitConfigRunner.getTenants().get(appId),
|
||||
(id) -> fetchUser(finalNextOpenId, wxAccount, wxMpUserService));
|
||||
|
||||
log.info("公众号 {} 粉丝同步完成", wxAccount.getName());
|
||||
}
|
||||
log.info("公众号 {} 粉丝同步完成", wxAccount.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信用户
|
||||
*
|
||||
* @param nextOpenid 下一组开始的openid
|
||||
* @param wxAccount 公众号信息
|
||||
* @param wxMpUserService mp操作类
|
||||
*/
|
||||
private void fetchUser(String nextOpenid, WxAccount wxAccount, WxMpUserService wxMpUserService) {
|
||||
try {
|
||||
WxMpUserList wxMpUserList = wxMpUserService.userList(nextOpenid);
|
||||
// openId 分组 每组 100个 openid
|
||||
List<List<String>> openIdsList = CollUtil.split(wxMpUserList.getOpenids(), SIZE)
|
||||
.stream()
|
||||
.filter(CollUtil::isNotEmpty)
|
||||
.toList();
|
||||
// 处理每个分组. 调用查询用户信息
|
||||
for (List<String> openIdList : openIdsList) {
|
||||
log.info("开始批量获取用户信息 {}", openIdList);
|
||||
List<WxAccountFans> wxAccountFansList = new ArrayList<>();
|
||||
wxMpUserService.userInfoList(openIdList).forEach(wxMpUser -> {
|
||||
WxAccountFans wxAccountFans = buildDbUser(wxAccount, wxMpUser);
|
||||
wxAccountFansList.add(wxAccountFans);
|
||||
});
|
||||
this.saveOrUpdateBatch(wxAccountFansList);
|
||||
log.info("批量插入用户信息完成 {}", openIdList);
|
||||
}
|
||||
// 如果nextOpenId 不为空,则继续获取
|
||||
if (StrUtil.isNotBlank(wxMpUserList.getNextOpenid())) {
|
||||
fetchUser(wxMpUserList.getNextOpenid(), wxAccount, wxMpUserService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("同步微信公众号 {} 粉丝异常 {}", wxAccount.getName(), e.getMessage());
|
||||
// 失败重试逻辑
|
||||
String finalNextOpenId = queryNextOpenId(wxAccount.getAppid());
|
||||
fetchUser(finalNextOpenId, wxAccount, wxMpUserService);
|
||||
}
|
||||
/**
|
||||
* 获取微信用户
|
||||
* @param nextOpenid 下一组开始的openid
|
||||
* @param wxAccount 公众号信息
|
||||
* @param wxMpUserService mp操作类
|
||||
*/
|
||||
private void fetchUser(String nextOpenid, WxAccount wxAccount, WxMpUserService wxMpUserService) {
|
||||
try {
|
||||
WxMpUserList wxMpUserList = wxMpUserService.userList(nextOpenid);
|
||||
// openId 分组 每组 100个 openid
|
||||
List<List<String>> openIdsList = CollUtil.split(wxMpUserList.getOpenids(), SIZE)
|
||||
.stream()
|
||||
.filter(CollUtil::isNotEmpty)
|
||||
.toList();
|
||||
// 处理每个分组. 调用查询用户信息
|
||||
for (List<String> openIdList : openIdsList) {
|
||||
log.info("开始批量获取用户信息 {}", openIdList);
|
||||
List<WxAccountFans> wxAccountFansList = new ArrayList<>();
|
||||
wxMpUserService.userInfoList(openIdList).forEach(wxMpUser -> {
|
||||
WxAccountFans wxAccountFans = buildDbUser(wxAccount, wxMpUser);
|
||||
wxAccountFansList.add(wxAccountFans);
|
||||
});
|
||||
this.saveOrUpdateBatch(wxAccountFansList);
|
||||
log.info("批量插入用户信息完成 {}", openIdList);
|
||||
}
|
||||
// 如果nextOpenId 不为空,则继续获取
|
||||
if (StrUtil.isNotBlank(wxMpUserList.getNextOpenid())) {
|
||||
fetchUser(wxMpUserList.getNextOpenid(), wxAccount, wxMpUserService);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("同步微信公众号 {} 粉丝异常 {}", wxAccount.getName(), e.getMessage());
|
||||
// 失败重试逻辑
|
||||
String finalNextOpenId = queryNextOpenId(wxAccount.getAppid());
|
||||
fetchUser(finalNextOpenId, wxAccount, wxMpUserService);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建数据库存取对象
|
||||
*
|
||||
* @param wxAccount 公众号信息
|
||||
* @param wxMpUser 用户微信信息
|
||||
*/
|
||||
private WxAccountFans buildDbUser(WxAccount wxAccount, WxMpUser wxMpUser) {
|
||||
WxAccountFans wxAccountFans = new WxAccountFans();
|
||||
wxAccountFans.setOpenid(wxMpUser.getOpenId());
|
||||
wxAccountFans.setSubscribeStatus(String.valueOf(BooleanUtil.toInt(wxMpUser.getSubscribe())));
|
||||
/**
|
||||
* 构建数据库存取对象
|
||||
* @param wxAccount 公众号信息
|
||||
* @param wxMpUser 用户微信信息
|
||||
*/
|
||||
private WxAccountFans buildDbUser(WxAccount wxAccount, WxMpUser wxMpUser) {
|
||||
WxAccountFans wxAccountFans = new WxAccountFans();
|
||||
wxAccountFans.setOpenid(wxMpUser.getOpenId());
|
||||
wxAccountFans.setSubscribeStatus(String.valueOf(BooleanUtil.toInt(wxMpUser.getSubscribe())));
|
||||
|
||||
// 2020-11-25 部分用户订阅时间为空,跳过此字段处理
|
||||
if (ObjectUtil.isNotEmpty(wxMpUser.getSubscribeTime())) {
|
||||
wxAccountFans.setSubscribeTime(LocalDateTime
|
||||
.ofInstant(Instant.ofEpochMilli(wxMpUser.getSubscribeTime() * 1000L), ZoneId.systemDefault()));
|
||||
}
|
||||
// 2020-11-25 部分用户订阅时间为空,跳过此字段处理
|
||||
if (ObjectUtil.isNotEmpty(wxMpUser.getSubscribeTime())) {
|
||||
wxAccountFans.setSubscribeTime(LocalDateTime
|
||||
.ofInstant(Instant.ofEpochMilli(wxMpUser.getSubscribeTime() * 1000L), ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
// 随机生成一个昵称,方便平台内部使用
|
||||
String generatedName = ChineseNameGenerator.getInstance().generate();
|
||||
wxAccountFans.setNickname(generatedName);
|
||||
wxAccountFans.setLanguage(wxMpUser.getLanguage());
|
||||
wxAccountFans.setHeadimgUrl(wxMpUser.getHeadImgUrl());
|
||||
wxAccountFans.setRemark(wxMpUser.getRemark());
|
||||
wxAccountFans.setTagIds(wxAccountFans.getTagIds());
|
||||
wxAccountFans.setWxAccountId(wxAccount.getId());
|
||||
wxAccountFans.setWxAccountAppid(wxAccount.getAppid());
|
||||
wxAccountFans.setWxAccountName(wxAccount.getName());
|
||||
wxAccountFans.setIsBlack(CommonConstants.SUCCESS);
|
||||
return wxAccountFans;
|
||||
}
|
||||
// 随机生成一个昵称,方便平台内部使用
|
||||
String generatedName = ChineseNameGenerator.getInstance().generate();
|
||||
wxAccountFans.setNickname(generatedName);
|
||||
wxAccountFans.setLanguage(wxMpUser.getLanguage());
|
||||
wxAccountFans.setHeadimgUrl(wxMpUser.getHeadImgUrl());
|
||||
wxAccountFans.setRemark(wxMpUser.getRemark());
|
||||
wxAccountFans.setTagIds(wxAccountFans.getTagIds());
|
||||
wxAccountFans.setWxAccountId(wxAccount.getId());
|
||||
wxAccountFans.setWxAccountAppid(wxAccount.getAppid());
|
||||
wxAccountFans.setWxAccountName(wxAccount.getName());
|
||||
wxAccountFans.setIsBlack(CommonConstants.SUCCESS);
|
||||
return wxAccountFans;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前公众号最后插入的openId
|
||||
*
|
||||
* @param appId 公众号标识
|
||||
* @return openid / null
|
||||
*/
|
||||
private String queryNextOpenId(String appId) {
|
||||
Page<WxAccountFans> queryPage = new Page<>(0, 1);
|
||||
Page<WxAccountFans> fansPage = baseMapper.selectPage(queryPage,
|
||||
Wrappers.<WxAccountFans>query()
|
||||
.lambda()
|
||||
.eq(WxAccountFans::getWxAccountAppid, appId)
|
||||
.orderByDesc(WxAccountFans::getCreateTime));
|
||||
String nextOpenId = null;
|
||||
if (fansPage.getTotal() > 0) {
|
||||
nextOpenId = fansPage.getRecords().get(0).getOpenid();
|
||||
}
|
||||
return nextOpenId;
|
||||
}
|
||||
/**
|
||||
* 查询当前公众号最后插入的openId
|
||||
* @param appId 公众号标识
|
||||
* @return openid / null
|
||||
*/
|
||||
private String queryNextOpenId(String appId) {
|
||||
Page<WxAccountFans> queryPage = new Page<>(0, 1);
|
||||
Page<WxAccountFans> fansPage = baseMapper.selectPage(queryPage,
|
||||
Wrappers.<WxAccountFans>query()
|
||||
.lambda()
|
||||
.eq(WxAccountFans::getWxAccountAppid, appId)
|
||||
.orderByDesc(WxAccountFans::getCreateTime));
|
||||
String nextOpenId = null;
|
||||
if (fansPage.getTotal() > 0) {
|
||||
nextOpenId = fansPage.getRecords().get(0).getOpenid();
|
||||
}
|
||||
return nextOpenId;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user