소스 검색

mp:增加【自动回复】的后端新增、修改、删除接口

YunaiV 2 년 전
부모
커밋
6fc0b3fc54

+ 6 - 0
yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java

@@ -55,4 +55,10 @@ public interface ErrorCodeConstants {
     ErrorCode MENU_SAVE_FAIL = new ErrorCode(1006008000, "创建菜单失败,原因:{}");
     ErrorCode MENU_DELETE_FAIL = new ErrorCode(1006008001, "删除菜单失败,原因:{}");
 
+    // ========== 公众号自动回复 1006009000============
+    ErrorCode AUTO_REPLY_NOT_EXISTS = new ErrorCode(1006009000, "自动回复不存在");
+    ErrorCode AUTO_REPLY_ADD_SUBSCRIBE_FAIL_EXISTS = new ErrorCode(1006009001, "操作失败,原因:已存在关注时的回复");
+    ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1006009002, "操作失败,原因:已存在该消息类型的回复");
+    ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1006009003, "操作失败,原因:已关在该关键字的回复");
+
 }

+ 27 - 4
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.java

@@ -2,7 +2,9 @@ package cn.iocoder.yudao.module.mp.controller.admin.message;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyCreateReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyRespVO;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyUpdateReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO;
 import cn.iocoder.yudao.module.mp.convert.message.MpAutoReplyConvert;
 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
@@ -12,10 +14,7 @@ import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
@@ -48,4 +47,28 @@ public class MpAutoReplyController {
         return success(MpAutoReplyConvert.INSTANCE.convert(autoReply));
     }
 
+    @PostMapping("/create")
+    @ApiOperation("创建公众号自动回复")
+    @PreAuthorize("@ss.hasPermission('mp:auto-reply:create')")
+    public CommonResult<Long> createAutoReply(@Valid @RequestBody MpAutoReplyCreateReqVO createReqVO) {
+        return success(mpAutoReplyService.createAutoReply(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation("更新公众号自动回复")
+    @PreAuthorize("@ss.hasPermission('mp:auto-reply:update')")
+    public CommonResult<Boolean> updateAutoReply(@Valid @RequestBody MpAutoReplyUpdateReqVO updateReqVO) {
+        mpAutoReplyService.updateAutoReply(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @ApiOperation("删除公众号自动回复")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
+    @PreAuthorize("@ss.hasPermission('mp:auto-reply:delete')")
+    public CommonResult<Boolean> deleteAutoReply(@RequestParam("id") Long id) {
+        mpAutoReplyService.deleteAutoReply(id);
+        return success(true);
+    }
+
 }

+ 0 - 4
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/autoreply/MpAutoReplyCreateReqVO.java

@@ -18,8 +18,4 @@ public class MpAutoReplyCreateReqVO extends MpAutoReplyBaseVO {
     @NotNull(message = "微信公众号 ID不能为空")
     private Long accountId;
 
-    @ApiModelProperty(value = "回复类型", required = true, example = "1", notes = "参见 MpAutoReplyTypeEnum 枚举")
-    @NotNull(message = "回复类型不能为空")
-    private Integer type;
-
 }

+ 5 - 0
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpAutoReplyConvert.java

@@ -1,7 +1,9 @@
 package cn.iocoder.yudao.module.mp.convert.message;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyCreateReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyRespVO;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyUpdateReqVO;
 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
 import org.mapstruct.Mapper;
@@ -29,4 +31,7 @@ public interface MpAutoReplyConvert {
 
     MpAutoReplyRespVO convert(MpAutoReplyDO bean);
 
+    MpAutoReplyDO convert(MpAutoReplyCreateReqVO bean);
+
+    MpAutoReplyDO convert(MpAutoReplyUpdateReqVO bean);
 }

+ 18 - 0
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/message/MpAutoReplyMapper.java

@@ -49,4 +49,22 @@ public interface MpAutoReplyMapper extends BaseMapperX<MpAutoReplyDO> {
                 .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.SUBSCRIBE.getType()));
     }
 
+    default MpAutoReplyDO selectByAccountIdAndSubscribe(Long accountId) {
+        return selectOne(MpAutoReplyDO::getAccountId, accountId,
+                MpAutoReplyDO::getType, MpAutoReplyTypeEnum.SUBSCRIBE.getType());
+    }
+
+    default MpAutoReplyDO selectByAccountIdAndMessage(Long accountId, String requestMessageType) {
+        return selectOne(new LambdaQueryWrapperX<MpAutoReplyDO>()
+                .eq(MpAutoReplyDO::getAccountId, accountId)
+                .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType())
+                .eq(MpAutoReplyDO::getRequestMessageType, requestMessageType));
+    }
+
+    default MpAutoReplyDO selectByAccountIdAndKeyword(Long accountId, String requestKeyword) {
+        return selectOne(new LambdaQueryWrapperX<MpAutoReplyDO>()
+                .eq(MpAutoReplyDO::getAccountId, accountId)
+                .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.KEYWORD.getType())
+                .eq(MpAutoReplyDO::getRequestKeyword, requestKeyword));
+    }
 }

+ 26 - 0
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyService.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.mp.service.message;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyCreateReqVO;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyUpdateReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO;
 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
@@ -29,6 +31,29 @@ public interface MpAutoReplyService {
      */
     MpAutoReplyDO getAutoReply(Long id);
 
+
+    /**
+     * 创建公众号自动回复
+     *
+     * @param createReqVO 创建请求
+     * @return 自动回复的编号
+     */
+    Long createAutoReply(MpAutoReplyCreateReqVO createReqVO);
+
+    /**
+     * 更新公众号自动回复
+     *
+     * @param updateReqVO 更新请求
+     */
+    void updateAutoReply(MpAutoReplyUpdateReqVO updateReqVO);
+
+    /**
+     * 删除公众号自动回复
+     *
+     * @param id 自动回复的编号
+     */
+    void deleteAutoReply(Long id);
+
     /**
      * 当收到消息时,自动回复
      *
@@ -46,4 +71,5 @@ public interface MpAutoReplyService {
      * @return 回复的消息
      */
     WxMpXmlOutMessage replyForSubscribe(String appId, WxMpXmlMessage wxMessage);
+
 }

+ 102 - 0
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyServiceImpl.java

@@ -2,13 +2,18 @@ package cn.iocoder.yudao.module.mp.service.message;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyCreateReqVO;
+import cn.iocoder.yudao.module.mp.controller.admin.message.vo.autoreply.MpAutoReplyUpdateReqVO;
 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO;
 import cn.iocoder.yudao.module.mp.convert.message.MpAutoReplyConvert;
 import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
 import cn.iocoder.yudao.module.mp.dal.mysql.message.MpAutoReplyMapper;
 import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyTypeEnum;
+import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils;
 import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
 import me.chanjar.weixin.common.api.WxConsts;
@@ -19,8 +24,12 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import javax.validation.Validator;
 import java.util.List;
 
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
+
 /**
  * 公众号的自动回复 Service 实现类
  *
@@ -36,6 +45,9 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
     @Lazy // 延迟加载,避免循环依赖
     private MpAccountService mpAccountService;
 
+    @Resource
+    private Validator validator;
+
     @Resource
     private MpAutoReplyMapper mpAutoReplyMapper;
 
@@ -49,6 +61,96 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
         return mpAutoReplyMapper.selectById(id);
     }
 
+    @Override
+    public Long createAutoReply(MpAutoReplyCreateReqVO createReqVO) {
+        // 第一步,校验数据
+        if (createReqVO.getResponseMessageType() != null) {
+            MpUtils.validateMessage(validator, createReqVO.getResponseMessageType(), createReqVO);
+        }
+        validateAutoReplyConflict(null, createReqVO.getAccountId(), createReqVO.getType(),
+                createReqVO.getRequestKeyword(), createReqVO.getRequestMessageType());
+
+        // 第二步,插入数据
+        MpAccountDO account = mpAccountService.getRequiredAccount(createReqVO.getAccountId());
+        MpAutoReplyDO autoReply = MpAutoReplyConvert.INSTANCE.convert(createReqVO)
+                .setAppId(account.getAppId());
+        mpAutoReplyMapper.insert(autoReply);
+        return autoReply.getId();
+    }
+
+    @Override
+    public void updateAutoReply(MpAutoReplyUpdateReqVO updateReqVO) {
+        // 第一步,校验数据
+        if (updateReqVO.getResponseMessageType() != null) {
+            MpUtils.validateMessage(validator, updateReqVO.getResponseMessageType(), updateReqVO);
+        }
+        MpAutoReplyDO autoReply = validateAutoReplyExists(updateReqVO.getId());
+        validateAutoReplyConflict(updateReqVO.getId(), autoReply.getAccountId(), updateReqVO.getType(),
+                updateReqVO.getRequestKeyword(), updateReqVO.getRequestMessageType());
+
+        // 第二步,更新数据
+        MpAutoReplyDO updateObj = MpAutoReplyConvert.INSTANCE.convert(updateReqVO)
+                .setAccountId(null).setAppId(null); // 避免前端传递,更新着两个字段
+        mpAutoReplyMapper.updateById(updateObj);
+    }
+
+    /**
+     * 校验自动回复是否冲突
+     *
+     * 不同的 type,会有不同的逻辑:
+     * 1. type = SUBSCRIBE 时,不允许有其他的自动回复
+     * 2. type = MESSAGE 时,校验 requestMessageType 已经存在自动回复
+     * 3. type = KEYWORD 时,校验 keyword 已经存在自动回复
+     *
+     * @param id 自动回复编号
+     * @param accountId 公众号账号的编号
+     * @param type 类型
+     * @param requestKeyword 请求关键词
+     * @param requestMessageType 请求消息类型
+     */
+    private void validateAutoReplyConflict(Long id, Long accountId, Integer type,
+                                           String requestKeyword, String requestMessageType) {
+        // 获得已经存在的自动回复
+        MpAutoReplyDO autoReply = null;
+        ErrorCode errorCode = null;
+        if (MpAutoReplyTypeEnum.SUBSCRIBE.getType().equals(type)) {
+            autoReply = mpAutoReplyMapper.selectByAccountIdAndSubscribe(accountId);
+            errorCode = AUTO_REPLY_ADD_SUBSCRIBE_FAIL_EXISTS;
+        } else if (MpAutoReplyTypeEnum.MESSAGE.getType().equals(type)) {
+            autoReply = mpAutoReplyMapper.selectByAccountIdAndMessage(accountId, requestMessageType);
+            errorCode = AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS;
+        } else if (MpAutoReplyTypeEnum.KEYWORD.getType().equals(type)) {
+            autoReply = mpAutoReplyMapper.selectByAccountIdAndKeyword(accountId, requestKeyword);
+            errorCode = AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS;
+        }
+        if (autoReply == null) {
+            return;
+        }
+
+        // 存在冲突,抛出业务异常
+        if (id == null // 情况一,新增(id == null),存在记录,说明冲突
+            || ObjUtil.notEqual(id, autoReply.getId())) { // 情况二,修改(id != null),id 不匹配,说明冲突
+            throw exception(errorCode);
+        }
+    }
+
+    @Override
+    public void deleteAutoReply(Long id) {
+        // 校验用户存在
+        validateAutoReplyExists(id);
+
+        // 删除自动回复
+        mpAutoReplyMapper.deleteById(id);
+    }
+
+    private MpAutoReplyDO validateAutoReplyExists(Long id) {
+        MpAutoReplyDO autoReply = mpAutoReplyMapper.selectById(id);
+        if (autoReply == null) {
+            throw exception(AUTO_REPLY_NOT_EXISTS);
+        }
+        return autoReply;
+    }
+
     @Override
     public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) {
         // 第一步,匹配自动回复

+ 26 - 24
yudao-ui-admin/src/views/mp/autoReply/index.vue

@@ -96,33 +96,35 @@ SOFTWARE.
       </el-tab-pane>
       <el-tab-pane name="3">
         <span slot="label"><i class="el-icon-news"></i> 关键词回复</span>
-        <el-table v-loading="loading" :data="list">
-          <el-table-column label="关键词" align="center" prop="requestKeyword"/>
-          <el-table-column label="匹配类型" align="center" prop="requestMatch">
-            <template v-slot="scope">
-              <dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
-            </template>
-          </el-table-column>
-          <el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>
-          <el-table-column label="创建时间" align="center" prop="createTime" width="180">
-            <template slot-scope="scope">
-              <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
-                         v-hasPermi="['mp:auto-reply:update']">修改
-              </el-button>
-              <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
-                         v-hasPermi="['mp:auto-reply:delete']">删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
       </el-tab-pane>
     </el-tabs>
 
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="关键词" align="center" prop="requestKeyword"/>
+      <el-table-column label="匹配类型" align="center" prop="requestMatch">
+        <template v-slot="scope">
+          <dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
+                     v-hasPermi="['mp:auto-reply:update']">修改
+          </el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
+                     v-hasPermi="['mp:auto-reply:delete']">删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
     <!-- 添加或修改自动回复的对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">