Bladeren bron

mp:实现 tag 模块的后端接口

YunaiV 2 jaren geleden
bovenliggende
commit
d1cc9dde0c

+ 8 - 1
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, "用户不存在");
 

+ 1 - 1
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}}

+ 39 - 0
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}}

+ 9 - 0
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<Boolean> syncTag(@RequestParam("accountId") Long accountId) {
+        mpTagService.syncTag(accountId);
+        return success(true);
+    }
+
 }

+ 4 - 3
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;
 

+ 13 - 3
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<MpTagRespVO> convertPage(PageResult<MpTagDO> 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);
+
 }

+ 7 - 0
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

+ 6 - 0
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<MpTagDO> {
 
@@ -17,4 +19,8 @@ public interface MpTagMapper extends BaseMapperX<MpTagDO> {
                 .orderByDesc(MpTagDO::getId));
     }
 
+    default List<MpTagDO> selectListByAccountId(Long accountId) {
+        return selectList(MpTagDO::getAccountId, accountId);
+    }
+
 }

+ 17 - 0
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 接口
  *
@@ -52,6 +55,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;
+    }
+
+    /**
      * 从缓存中,获得公众号账号
      *
      * @param appId 微信公众号 appId

+ 7 - 0
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<MpTagDO> getTagPage(MpTagPageReqVO pageReqVO);
 
+    /**
+     * 同步公众号标签
+     *
+     * @param accountId 公众号账号的编号
+     */
+    void syncTag(Long accountId);
+
 }

+ 104 - 5
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 实现类
@@ -28,28 +43,112 @@ public class MpTagServiceImpl implements MpTagService {
     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<MpTagDO> 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<WxUserTag> wxTags;
+        try {
+            wxTags = mpService.getUserTagService().tagGet();
+        } catch (WxErrorException e) {
+            throw new RuntimeException(e);
+        }
+
+        // 第二步,合并更新回自己的数据库;由于标签只有 100 个,所以直接 for 循环操作
+        Map<Long, MpTagDO> 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));
+        }
     }
 
 }