From d1cc9dde0c22e0192941c3739748c46c6d3d342c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 8 Jan 2023 11:29:44 +0800 Subject: [PATCH] =?UTF-8?q?mp=EF=BC=9A=E5=AE=9E=E7=8E=B0=20tag=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=9A=84=E5=90=8E=E7=AB=AF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/mp/enums/ErrorCodeConstants.java | 9 +- .../admin/menu/MpMenuController.http | 2 +- .../controller/admin/tag/MpTagController.http | 39 +++++++ .../controller/admin/tag/MpTagController.java | 9 ++ .../controller/admin/tag/vo/MpTagRespVO.java | 7 +- .../module/mp/convert/tag/MpTagConvert.java | 16 ++- .../module/mp/dal/dataobject/tag/MpTagDO.java | 7 ++ .../module/mp/dal/mysql/tag/MpTagMapper.java | 6 + .../mp/service/account/MpAccountService.java | 17 +++ .../module/mp/service/tag/MpTagService.java | 7 ++ .../mp/service/tag/MpTagServiceImpl.java | 109 +++++++++++++++++- 11 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http diff --git a/yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java b/yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java index 471d3b2ea..61e777f58 100644 --- a/yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java +++ b/yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java @@ -14,12 +14,19 @@ public interface ErrorCodeConstants { ErrorCode ACCOUNT_GENERATE_QR_CODE_FAIL = new ErrorCode(1006000001, "生成公众号二维码失败,原因:{}"); ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1006000001, "清空公众号的 API 配额失败,原因:{}"); - // ========== 公众号账号 1006001000============ + // ========== 公众号统计 1006001000============ ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1006001000, "获取用户增减数据失败,原因:{}"); ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1006001001, "获得用户累计数据失败,原因:{}"); ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1006001002, "获得消息发送概况数据失败,原因:{}"); ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1006001003, "获得接口分析数据失败,原因:{}"); + // ========== 公众号标签 1006002000============ + ErrorCode TAG_NOT_EXISTS = new ErrorCode(1006002000, "标签不存在"); + ErrorCode TAG_CREATE_FAIL = new ErrorCode(1006002001, "创建标签失败,原因:{}"); + ErrorCode TAG_UPDATE_FAIL = new ErrorCode(1006002001, "更新标签失败,原因:{}"); + ErrorCode TAG_DELETE_FAIL = new ErrorCode(1006002001, "删除标签失败,原因:{}"); + ErrorCode TAG_GET_FAIL = new ErrorCode(1006002001, "获得标签失败,原因:{}"); + // TODO 要处理下 ErrorCode COMMON_NOT_EXISTS = new ErrorCode(1006001002, "用户不存在"); diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http index 8124ad831..95388b3d9 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http @@ -1,4 +1,4 @@ -### 请求 /login 接口 => 成功 +### 请求 /mp/menu/save 接口 => 成功 POST {{baseUrl}}/mp/menu/save Content-Type: application/json Authorization: Bearer {{token}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http new file mode 100644 index 000000000..fe79105ba --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http @@ -0,0 +1,39 @@ +### 请求 /mp/tag/create 接口 => 成功 +POST {{baseUrl}}/mp/tag/create +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "accountId": "1", + "name": "测试" +} + +### 请求 /mp/tag/update 接口 => 成功 +PUT {{baseUrl}}/mp/tag/update +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "id": "3", + "name": "测试标签啦" +} + +### 请求 /mp/tag/delete 接口 => 成功 +DELETE {{baseUrl}}/mp/tag/delete?id=3 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 请求 /mp/tag/page 接口 => 成功 +GET {{baseUrl}}/mp/tag/page?accountId=1&pageNo=1&pageSize=10 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +### 请求 /mp/tag/sync 接口 => 成功 +POST {{baseUrl}}/mp/tag/sync?accountId=1 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.java index 03e96fb60..8b70a4169 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.java @@ -62,4 +62,13 @@ public class MpTagController { return success(MpTagConvert.INSTANCE.convertPage(pageResult)); } + @PostMapping("/sync") + @ApiOperation("同步公众标签") + @ApiImplicitParam(name = "id", value = "公众号账号的编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('mp:tag:sync')") + public CommonResult syncTag(@RequestParam("accountId") Long accountId) { + mpTagService.syncTag(accountId); + return success(true); + } + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/vo/MpTagRespVO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/vo/MpTagRespVO.java index 47b3c4720..cae0dab16 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/vo/MpTagRespVO.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/vo/MpTagRespVO.java @@ -6,7 +6,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import javax.validation.constraints.NotNull; import java.util.Date; @ApiModel("管理后台 - 公众号标签 Response VO") @@ -15,10 +14,12 @@ import java.util.Date; @ToString(callSuper = true) public class MpTagRespVO extends MpTagBaseVO { - @ApiModelProperty(value = "编号", required = true) - @NotNull(message = "编号不能为空") + @ApiModelProperty(value = "编号", required = true, example = "1024") private Long id; + @ApiModelProperty(value = "此标签下粉丝数量", required = true, example = "0") + private Integer count; + @ApiModelProperty(value = "创建时间", required = true) private Date createTime; diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/tag/MpTagConvert.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/tag/MpTagConvert.java index f8121003d..64969c83d 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/tag/MpTagConvert.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/tag/MpTagConvert.java @@ -1,12 +1,14 @@ package cn.iocoder.yudao.module.mp.convert.tag; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagCreateReqVO; import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagRespVO; import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagUpdateReqVO; +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import cn.iocoder.yudao.module.mp.dal.dataobject.tag.MpTagDO; import me.chanjar.weixin.mp.bean.tag.WxUserTag; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; import java.util.List; @@ -16,8 +18,6 @@ public interface MpTagConvert { MpTagConvert INSTANCE = Mappers.getMapper(MpTagConvert.class); - WxUserTag convert(MpTagCreateReqVO bean); - WxUserTag convert(MpTagUpdateReqVO bean); MpTagRespVO convert(WxUserTag bean); @@ -26,4 +26,14 @@ public interface MpTagConvert { PageResult convertPage(PageResult page); + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(source = "tag.id", target = "tagId"), + @Mapping(source = "tag.name", target = "name"), + @Mapping(source = "tag.count", target = "count"), + @Mapping(source = "account.id", target = "accountId"), + @Mapping(source = "account.appId", target = "appId"), + }) + MpTagDO convert(WxUserTag tag, MpAccountDO account); + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/tag/MpTagDO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/tag/MpTagDO.java index bd975c4a4..ca0a8b724 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/tag/MpTagDO.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/tag/MpTagDO.java @@ -5,6 +5,7 @@ import lombok.*; import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import me.chanjar.weixin.mp.bean.tag.WxUserTag; /** * 公众号标签 DO @@ -31,6 +32,12 @@ public class MpTagDO extends BaseDO { * 标签名 */ private String name; + /** + * 此标签下粉丝数 + * + * 冗余:{@link WxUserTag#getCount()} 字段,需要管理员点击【同步】后,更新该字段 + */ + private Integer count; /** * 微信公众号 ID diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/tag/MpTagMapper.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/tag/MpTagMapper.java index 1162fbe9f..760f53aa1 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/tag/MpTagMapper.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/tag/MpTagMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagPageReqVO; import cn.iocoder.yudao.module.mp.dal.dataobject.tag.MpTagDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface MpTagMapper extends BaseMapperX { @@ -17,4 +19,8 @@ public interface MpTagMapper extends BaseMapperX { .orderByDesc(MpTagDO::getId)); } + default List selectListByAccountId(Long accountId) { + return selectList(MpTagDO::getAccountId, accountId); + } + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountService.java index bbcff2d2b..e122c6a98 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountService.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountService.java @@ -9,6 +9,9 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import javax.validation.Valid; import java.util.List; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.ACCOUNT_NOT_EXISTS; + /** * 公众号账号 Service 接口 * @@ -51,6 +54,20 @@ public interface MpAccountService { */ MpAccountDO getAccount(Long id); + /** + * 获得公众号账号。若不存在,则抛出业务异常 + * + * @param id 编号 + * @return 公众号账号 + */ + default MpAccountDO getRequiredAccount(Long id) { + MpAccountDO account = getAccount(id); + if (account == null) { + throw exception(ACCOUNT_NOT_EXISTS); + } + return account; + } + /** * 从缓存中,获得公众号账号 * diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagService.java index 0d441379d..11422468d 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagService.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagService.java @@ -45,4 +45,11 @@ public interface MpTagService { */ PageResult getTagPage(MpTagPageReqVO pageReqVO); + /** + * 同步公众号标签 + * + * @param accountId 公众号账号的编号 + */ + void syncTag(Long accountId); + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagServiceImpl.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagServiceImpl.java index b30a52d61..88d13a847 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagServiceImpl.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/tag/MpTagServiceImpl.java @@ -1,18 +1,33 @@ package cn.iocoder.yudao.module.mp.service.tag; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagCreateReqVO; import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagPageReqVO; import cn.iocoder.yudao.module.mp.controller.admin.tag.vo.MpTagUpdateReqVO; +import cn.iocoder.yudao.module.mp.convert.tag.MpTagConvert; +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import cn.iocoder.yudao.module.mp.dal.dataobject.tag.MpTagDO; import cn.iocoder.yudao.module.mp.dal.mysql.tag.MpTagMapper; import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; +import cn.iocoder.yudao.module.mp.service.account.MpAccountService; import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.tag.WxUserTag; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*; /** * 公众号标签 Service 实现类 @@ -27,29 +42,113 @@ public class MpTagServiceImpl implements MpTagService { @Resource private MpTagMapper mpTagMapper; + @Resource + private MpAccountService mpAccountService; + @Resource @Lazy // 延迟加载,为了解决延迟加载 private MpServiceFactory mpServiceFactory; @Override public Long createTag(MpTagCreateReqVO createReqVO) { -// return wxMpService.getUserTagService().tagCreate(createReqVO.getName()); - return null; + // 获得公众号账号 + MpAccountDO account = mpAccountService.getRequiredAccount(createReqVO.getAccountId()); + + // 第一步,新增标签到公众号平台。标签名的唯一,交给公众号平台 + WxMpService mpService = mpServiceFactory.getRequiredMpService(createReqVO.getAccountId()); + WxUserTag wxTag; + try { + wxTag = mpService.getUserTagService().tagCreate(createReqVO.getName()); + } catch (WxErrorException e) { + throw exception(TAG_CREATE_FAIL, e.getError().getErrorMsg()); + } + + // 第二步,新增标签到数据库 + MpTagDO tag = MpTagConvert.INSTANCE.convert(wxTag, account); + mpTagMapper.insert(tag); + return tag.getId(); } @Override public void updateTag(MpTagUpdateReqVO updateReqVO) { -// return wxMpService.getUserTagService().tagUpdate(updateReqVO.getId(), updateReqVO.getName()); + // 校验标签存在 + MpTagDO tag = validateTagExists(updateReqVO.getId()); + + // 第一步,更新标签到公众号平台。标签名的唯一,交给公众号平台 + WxMpService mpService = mpServiceFactory.getRequiredMpService(tag.getAccountId()); + try { + mpService.getUserTagService().tagUpdate(tag.getTagId(), updateReqVO.getName()); + } catch (WxErrorException e) { + throw exception(TAG_UPDATE_FAIL, e.getError().getErrorMsg()); + } + + // 第二步,更新标签到数据库 + mpTagMapper.updateById(new MpTagDO().setId(tag.getId()).setName(updateReqVO.getName())); } @Override public void deleteTag(Long id) { -// return wxMpService.getUserTagService().tagDelete(id); + // 校验标签存在 + MpTagDO tag = validateTagExists(id); + + // 第一步,删除标签到公众号平台。 + WxMpService mpService = mpServiceFactory.getRequiredMpService(tag.getAccountId()); + try { + mpService.getUserTagService().tagDelete(tag.getTagId()); + } catch (WxErrorException e) { + throw exception(TAG_DELETE_FAIL, e.getError().getErrorMsg()); + } + + // 第二步,删除标签到数据库 + mpTagMapper.deleteById(tag.getId()); + } + + private MpTagDO validateTagExists(Long id) { + MpTagDO tag = mpTagMapper.selectById(id); + if (tag == null) { + throw exception(TAG_NOT_EXISTS); + } + return tag; } @Override public PageResult getTagPage(MpTagPageReqVO pageReqVO) { - return null; + return mpTagMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void syncTag(Long accountId) { + MpAccountDO account = mpAccountService.getRequiredAccount(accountId); + + // 第一步,从公众号平台获取最新的标签列表 + WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId); + List wxTags; + try { + wxTags = mpService.getUserTagService().tagGet(); + } catch (WxErrorException e) { + throw new RuntimeException(e); + } + + // 第二步,合并更新回自己的数据库;由于标签只有 100 个,所以直接 for 循环操作 + Map tagMap = convertMap(mpTagMapper.selectListByAccountId(accountId), + MpTagDO::getTagId); + wxTags.forEach(wxTag -> { + MpTagDO tag = tagMap.remove(wxTag.getId()); + // 情况一,不存在,新增 + if (tag == null) { + tag = MpTagConvert.INSTANCE.convert(wxTag, account); + mpTagMapper.insert(tag); + return; + } + // 情况二,存在,则更新 + mpTagMapper.updateById(new MpTagDO().setId(tag.getId()) + .setName(wxTag.getName()).setCount(wxTag.getCount())); + }); + // 情况三,部分标签已经不存在了,删除 + if (CollUtil.isNotEmpty(tagMap)) { + mpTagMapper.deleteBatchIds(convertList(tagMap.values(), MpTagDO::getId)); + } } }