Commit 2173cf0364254fb620df464c162085563d4306fe

Authored by YunaiV
1 parent c0b95025

mp:menu 前端接入菜单的保存、清空操作

yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java
@@ -52,6 +52,7 @@ public interface ErrorCodeConstants { @@ -52,6 +52,7 @@ public interface ErrorCodeConstants {
52 ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}"); 52 ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}");
53 53
54 // ========== 公众号菜单 1006008000============ 54 // ========== 公众号菜单 1006008000============
55 - ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006008000, "菜单不存在"); 55 + ErrorCode MENU_SAVE_FAIL = new ErrorCode(1006008000, "创建菜单失败,原因:{}");
  56 + ErrorCode MENU_DELETE_FAIL = new ErrorCode(1006008001, "删除菜单失败,原因:{}");
56 57
57 } 58 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http
@@ -6,24 +6,24 @@ tenant-id: {{adminTenentId}} @@ -6,24 +6,24 @@ tenant-id: {{adminTenentId}}
6 6
7 { 7 {
8 "accountId": "1", 8 "accountId": "1",
9 - "buttons": [ 9 + "menus": [
10 { 10 {
11 "type":"click", 11 "type":"click",
12 "name":"今日歌曲", 12 "name":"今日歌曲",
13 - "key":"V1001_TODAY_MUSIC" 13 + "menuKey":"V1001_TODAY_MUSIC"
14 }, 14 },
15 { 15 {
16 "name":"搜索", 16 "name":"搜索",
17 "type":"view", 17 "type":"view",
18 - "url":"http://www.soso.com/" 18 + "url":"https://www.soso.com/"
19 }, 19 },
20 { 20 {
21 "name": "父按钮", 21 "name": "父按钮",
22 - "subButtons": [ 22 + "children": [
23 { 23 {
24 "type":"click", 24 "type":"click",
25 "name":"归去来兮", 25 "name":"归去来兮",
26 - "key":"MUSIC" 26 + "menuKey":"MUSIC"
27 }, 27 },
28 { 28 {
29 "name":"不说", 29 "name":"不说",
@@ -33,6 +33,17 @@ tenant-id: {{adminTenentId}} @@ -33,6 +33,17 @@ tenant-id: {{adminTenentId}}
33 }] 33 }]
34 } 34 }
35 35
  36 +### 请求 /mp/menu/save 接口 => 成功(清空)
  37 +POST {{baseUrl}}/mp/menu/save
  38 +Content-Type: application/json
  39 +Authorization: Bearer {{token}}
  40 +tenant-id: {{adminTenentId}}
  41 +
  42 +{
  43 + "accountId": "1",
  44 + "menus": []
  45 +}
  46 +
36 ### 请求 /mp/menu/list 接口 => 成功 47 ### 请求 /mp/menu/list 接口 => 成功
37 GET {{baseUrl}}/mp/menu/list?accountId=1 48 GET {{baseUrl}}/mp/menu/list?accountId=1
38 Authorization: Bearer {{token}} 49 Authorization: Bearer {{token}}
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.java
@@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*; @@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
15 15
16 import javax.annotation.Resource; 16 import javax.annotation.Resource;
17 import javax.validation.Valid; 17 import javax.validation.Valid;
18 -  
19 import java.util.List; 18 import java.util.List;
20 19
21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -32,28 +31,20 @@ public class MpMenuController { @@ -32,28 +31,20 @@ public class MpMenuController {
32 @PostMapping("/save") 31 @PostMapping("/save")
33 @ApiOperation("保存公众号菜单") 32 @ApiOperation("保存公众号菜单")
34 @PreAuthorize("@ss.hasPermission('mp:menu:save')") 33 @PreAuthorize("@ss.hasPermission('mp:menu:save')")
35 - public CommonResult<Long> saveMenu(@Valid @RequestBody MpMenuSaveReqVO createReqVO) {  
36 - return success(mpMenuService.saveMenu(createReqVO)); 34 + public CommonResult<Boolean> saveMenu(@Valid @RequestBody MpMenuSaveReqVO createReqVO) {
  35 + mpMenuService.saveMenu(createReqVO);
  36 + return success(true);
37 } 37 }
38 38
39 @DeleteMapping("/delete") 39 @DeleteMapping("/delete")
40 @ApiOperation("删除公众号菜单") 40 @ApiOperation("删除公众号菜单")
41 - @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) 41 + @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true, example = "10", dataTypeClass = Long.class)
42 @PreAuthorize("@ss.hasPermission('mp:menu:delete')") 42 @PreAuthorize("@ss.hasPermission('mp:menu:delete')")
43 - public CommonResult<Boolean> deleteMenu(@RequestParam("id") Long id) {  
44 - mpMenuService.deleteMenu(id); 43 + public CommonResult<Boolean> deleteMenu(@RequestParam("accountId") Long accountId) {
  44 + mpMenuService.deleteMenuByAccountId(accountId);
45 return success(true); 45 return success(true);
46 } 46 }
47 47
48 - @GetMapping("/get")  
49 - @ApiOperation("获得公众号菜单")  
50 - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)  
51 - @PreAuthorize("@ss.hasPermission('mp:menu:query')")  
52 - public CommonResult<MpMenuRespVO> getMenu(@RequestParam("id") Long id) {  
53 - MpMenuDO menu = mpMenuService.getMenu(id);  
54 - return success(MpMenuConvert.INSTANCE.convert(menu));  
55 - }  
56 -  
57 @GetMapping("/list") 48 @GetMapping("/list")
58 @ApiOperation("获得公众号菜单列表") 49 @ApiOperation("获得公众号菜单列表")
59 @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true, example = "10", dataTypeClass = Long.class) 50 @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true, example = "10", dataTypeClass = Long.class)
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuBaseVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
2 2
3 -import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;  
4 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; 3 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
5 -import io.swagger.annotations.ApiModelProperty;  
6 import lombok.Data; 4 import lombok.Data;
7 import me.chanjar.weixin.common.api.WxConsts; 5 import me.chanjar.weixin.common.api.WxConsts;
8 6
9 -import javax.validation.constraints.NotNull;  
10 import java.util.List; 7 import java.util.List;
11 8
12 // TODO 芋艿:完善 swagger 注解 9 // TODO 芋艿:完善 swagger 注解
@@ -17,17 +14,6 @@ import java.util.List; @@ -17,17 +14,6 @@ import java.util.List;
17 @Data 14 @Data
18 public class MpMenuBaseVO { 15 public class MpMenuBaseVO {
19 16
20 - @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048")  
21 - @NotNull(message = "公众号账号的编号不能为空")  
22 - private Long accountId;  
23 -  
24 - /**  
25 - * 微信公众号 appid  
26 - *  
27 - * 冗余 {@link MpAccountDO#getAppId()}  
28 - */  
29 - private String appId;  
30 -  
31 /** 17 /**
32 * 菜单名称 18 * 菜单名称
33 */ 19 */
@@ -42,10 +28,6 @@ public class MpMenuBaseVO { @@ -42,10 +28,6 @@ public class MpMenuBaseVO {
42 * 父菜单编号 28 * 父菜单编号
43 */ 29 */
44 private Long parentId; 30 private Long parentId;
45 - /**  
46 - * 排序  
47 - */  
48 - private Integer sort;  
49 31
50 // ========== 按钮操作 ========== 32 // ========== 按钮操作 ==========
51 33
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuRespVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
2 2
3 -import lombok.*; 3 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
4 9
5 -import java.util.*;  
6 -  
7 -import io.swagger.annotations.*; 10 +import javax.validation.constraints.NotNull;
  11 +import java.util.Date;
8 12
  13 +// TODO swagger 文档
9 @ApiModel("管理后台 - 微信菜单 Response VO") 14 @ApiModel("管理后台 - 微信菜单 Response VO")
10 @Data 15 @Data
11 @EqualsAndHashCode(callSuper = true) 16 @EqualsAndHashCode(callSuper = true)
@@ -15,6 +20,17 @@ public class MpMenuRespVO extends MpMenuBaseVO { @@ -15,6 +20,17 @@ public class MpMenuRespVO extends MpMenuBaseVO {
15 @ApiModelProperty(value = "主键", required = true) 20 @ApiModelProperty(value = "主键", required = true)
16 private Long id; 21 private Long id;
17 22
  23 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048")
  24 + @NotNull(message = "公众号账号的编号不能为空")
  25 + private Long accountId;
  26 +
  27 + /**
  28 + * 微信公众号 appid
  29 + *
  30 + * 冗余 {@link MpAccountDO#getAppId()}
  31 + */
  32 + private String appId;
  33 +
18 @ApiModelProperty(value = "创建时间", required = true) 34 @ApiModelProperty(value = "创建时间", required = true)
19 private Date createTime; 35 private Date createTime;
20 36
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuSaveReqVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
2 2
3 -import lombok.*;  
4 -import io.swagger.annotations.*;  
5 -import me.chanjar.weixin.common.bean.menu.WxMenuButton; 3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
6 6
  7 +import javax.validation.Valid;
  8 +import javax.validation.constraints.NotEmpty;
7 import javax.validation.constraints.NotNull; 9 import javax.validation.constraints.NotNull;
8 import java.util.List; 10 import java.util.List;
9 11
10 -@ApiModel("管理后台 - 微信菜单保存 Request VO") 12 +// TODO 芋艿:swagger 文档
  13 +@ApiModel("管理后台 - 公众号菜单保存 Request VO")
11 @Data 14 @Data
12 -@EqualsAndHashCode(callSuper = true)  
13 -@ToString(callSuper = true)  
14 -public class MpMenuSaveReqVO extends MpMenuBaseVO { 15 +public class MpMenuSaveReqVO {
15 16
16 - @NotNull(message = "按钮不能为空")  
17 - private List<WxMenuButton> buttons; 17 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048")
  18 + @NotNull(message = "公众号账号的编号不能为空")
  19 + private Long accountId;
  20 +
  21 + @NotEmpty(message = "菜单不能为空")
  22 + @Valid
  23 + private List<Menu> menus;
  24 +
  25 + @Data
  26 + public static class Menu extends MpMenuBaseVO {
  27 +
  28 + private List<Menu> children;
  29 +
  30 + }
18 31
19 } 32 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/menu/MpMenuConvert.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuRespVO; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuRespVO;
4 import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO; 4 import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
5 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 5 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
6 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; 6 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
  7 +import me.chanjar.weixin.common.bean.menu.WxMenuButton;
7 import org.mapstruct.Mapper; 8 import org.mapstruct.Mapper;
8 import org.mapstruct.Mapping; 9 import org.mapstruct.Mapping;
9 import org.mapstruct.Mappings; 10 import org.mapstruct.Mappings;
@@ -16,8 +17,6 @@ public interface MpMenuConvert { @@ -16,8 +17,6 @@ public interface MpMenuConvert {
16 17
17 MpMenuConvert INSTANCE = Mappers.getMapper(MpMenuConvert.class); 18 MpMenuConvert INSTANCE = Mappers.getMapper(MpMenuConvert.class);
18 19
19 - MpMenuDO convert(MpMenuSaveReqVO bean);  
20 -  
21 MpMenuRespVO convert(MpMenuDO bean); 20 MpMenuRespVO convert(MpMenuDO bean);
22 21
23 List<MpMenuRespVO> convertList(List<MpMenuDO> list); 22 List<MpMenuRespVO> convertList(List<MpMenuDO> list);
@@ -27,11 +26,20 @@ public interface MpMenuConvert { @@ -27,11 +26,20 @@ public interface MpMenuConvert {
27 @Mapping(source = "menu.replyMessageType", target = "type"), 26 @Mapping(source = "menu.replyMessageType", target = "type"),
28 @Mapping(source = "menu.replyContent", target = "content"), 27 @Mapping(source = "menu.replyContent", target = "content"),
29 @Mapping(source = "menu.replyMediaId", target = "mediaId"), 28 @Mapping(source = "menu.replyMediaId", target = "mediaId"),
30 - @Mapping(source = "menu.replyMediaUrl", target = "mediaUrl"),  
31 @Mapping(source = "menu.replyTitle", target = "title"), 29 @Mapping(source = "menu.replyTitle", target = "title"),
32 @Mapping(source = "menu.replyDescription", target = "description"), 30 @Mapping(source = "menu.replyDescription", target = "description"),
33 @Mapping(source = "menu.replyArticles", target = "articles"), 31 @Mapping(source = "menu.replyArticles", target = "articles"),
34 }) 32 })
35 MpMessageSendOutReqBO convert(String openid, MpMenuDO menu); 33 MpMessageSendOutReqBO convert(String openid, MpMenuDO menu);
36 34
  35 + List<WxMenuButton> convert(List<MpMenuSaveReqVO.Menu> list);
  36 +
  37 + @Mappings({
  38 + @Mapping(source = "menuKey", target = "key"),
  39 + @Mapping(source = "children", target = "subButtons"),
  40 + })
  41 + WxMenuButton convert(MpMenuSaveReqVO.Menu bean);
  42 +
  43 + MpMenuDO convert02(MpMenuSaveReqVO.Menu menu);
  44 +
37 } 45 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/menu/MpMenuDO.java
@@ -64,10 +64,6 @@ public class MpMenuDO extends BaseDO { @@ -64,10 +64,6 @@ public class MpMenuDO extends BaseDO {
64 * 父菜单编号 64 * 父菜单编号
65 */ 65 */
66 private Long parentId; 66 private Long parentId;
67 - /**  
68 - * 排序  
69 - */  
70 - private Integer sort;  
71 67
72 // ========== 按钮操作 ========== 68 // ========== 按钮操作 ==========
73 69
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/menu/MpMenuMapper.java
1 package cn.iocoder.yudao.module.mp.dal.mysql.menu; 1 package cn.iocoder.yudao.module.mp.dal.mysql.menu;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
4 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 5 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
5 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
6 7
@@ -18,4 +19,7 @@ public interface MpMenuMapper extends BaseMapperX&lt;MpMenuDO&gt; { @@ -18,4 +19,7 @@ public interface MpMenuMapper extends BaseMapperX&lt;MpMenuDO&gt; {
18 return selectList(MpMenuDO::getAccountId, accountId); 19 return selectList(MpMenuDO::getAccountId, accountId);
19 } 20 }
20 21
  22 + default void deleteByAccountId(Long accountId) {
  23 + delete(new LambdaQueryWrapperX<MpMenuDO>().eq(MpMenuDO::getAccountId, accountId));
  24 + }
21 } 25 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/menu/MpMenuService.java
@@ -18,24 +18,15 @@ public interface MpMenuService { @@ -18,24 +18,15 @@ public interface MpMenuService {
18 * 保存公众号菜单 18 * 保存公众号菜单
19 * 19 *
20 * @param createReqVO 创建信息 20 * @param createReqVO 创建信息
21 - * @return 编号  
22 */ 21 */
23 - Long saveMenu(@Valid MpMenuSaveReqVO createReqVO); 22 + void saveMenu(@Valid MpMenuSaveReqVO createReqVO);
24 23
25 /** 24 /**
26 * 删除公众号菜单 25 * 删除公众号菜单
27 * 26 *
28 - * @param id 编号 27 + * @param accountId 公众号账号的编号
29 */ 28 */
30 - void deleteMenu(Long id);  
31 -  
32 - /**  
33 - * 获得公众号菜单  
34 - *  
35 - * @param id 编号  
36 - * @return 公众号菜单  
37 - */  
38 - MpMenuDO getMenu(Long id); 29 + void deleteMenuByAccountId(Long accountId);
39 30
40 /** 31 /**
41 * 用户点击菜单按钮时,回复对应的消息 32 * 用户点击菜单按钮时,回复对应的消息
@@ -50,7 +41,7 @@ public interface MpMenuService { @@ -50,7 +41,7 @@ public interface MpMenuService {
50 /** 41 /**
51 * 获得公众号菜单列表 42 * 获得公众号菜单列表
52 * 43 *
53 - * @param accountId 公众号编号 44 + * @param accountId 公众号账号的编号
54 * @return 公众号菜单列表 45 * @return 公众号菜单列表
55 */ 46 */
56 List<MpMenuDO> getMenuListByAccountId(Long accountId); 47 List<MpMenuDO> getMenuListByAccountId(Long accountId);
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/menu/MpMenuServiceImpl.java
1 package cn.iocoder.yudao.module.mp.service.menu; 1 package cn.iocoder.yudao.module.mp.service.menu;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO; 5 import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
5 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert; 6 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert;
  7 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
6 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 8 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
7 import cn.iocoder.yudao.module.mp.dal.mysql.menu.MpMenuMapper; 9 import cn.iocoder.yudao.module.mp.dal.mysql.menu.MpMenuMapper;
8 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; 10 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
  11 +import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
9 import cn.iocoder.yudao.module.mp.service.message.MpMessageService; 12 import cn.iocoder.yudao.module.mp.service.message.MpMessageService;
10 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; 13 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
11 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
@@ -15,13 +18,15 @@ import me.chanjar.weixin.mp.api.WxMpService; @@ -15,13 +18,15 @@ import me.chanjar.weixin.mp.api.WxMpService;
15 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 18 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
16 import org.springframework.context.annotation.Lazy; 19 import org.springframework.context.annotation.Lazy;
17 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
  21 +import org.springframework.transaction.annotation.Transactional;
18 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
19 23
20 import javax.annotation.Resource; 24 import javax.annotation.Resource;
21 import java.util.List; 25 import java.util.List;
22 26
23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
24 -import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MENU_NOT_EXISTS; 28 +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MENU_DELETE_FAIL;
  29 +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MENU_SAVE_FAIL;
25 30
26 /** 31 /**
27 * 公众号菜单 Service 实现类 32 * 公众号菜单 Service 实现类
@@ -35,6 +40,9 @@ public class MpMenuServiceImpl implements MpMenuService { @@ -35,6 +40,9 @@ public class MpMenuServiceImpl implements MpMenuService {
35 40
36 @Resource 41 @Resource
37 private MpMessageService mpMessageService; 42 private MpMessageService mpMessageService;
  43 + @Resource
  44 + @Lazy // 延迟加载,避免循环引用报错
  45 + private MpAccountService mpAccountService;
38 46
39 @Resource 47 @Resource
40 @Lazy // 延迟加载,避免循环引用报错 48 @Lazy // 延迟加载,避免循环引用报错
@@ -44,43 +52,64 @@ public class MpMenuServiceImpl implements MpMenuService { @@ -44,43 +52,64 @@ public class MpMenuServiceImpl implements MpMenuService {
44 private MpMenuMapper mpMenuMapper; 52 private MpMenuMapper mpMenuMapper;
45 53
46 @Override 54 @Override
47 - public Long saveMenu(MpMenuSaveReqVO createReqVO) {  
48 - String appId = "wx5b23ba7a5589ecbb";  
49 - // 插入  
50 - MpMenuDO menu = MpMenuConvert.INSTANCE.convert(createReqVO);  
51 -// mpMenuMapper.insert(menu);  
52 -  
53 - // TODO 同步菜单  
54 - WxMpService mpService = mpServiceFactory.getRequiredMpService(appId); 55 + @Transactional(rollbackFor = Exception.class)
  56 + public void saveMenu(MpMenuSaveReqVO createReqVO) {
  57 + MpAccountDO account = mpAccountService.getRequiredAccount(createReqVO.getAccountId());
  58 + WxMpService mpService = mpServiceFactory.getRequiredMpService(createReqVO.getAccountId());
  59 +
  60 + // 第一步,同步公众号
55 WxMenu wxMenu = new WxMenu(); 61 WxMenu wxMenu = new WxMenu();
56 - wxMenu.setButtons(createReqVO.getButtons()); 62 + wxMenu.setButtons(MpMenuConvert.INSTANCE.convert(createReqVO.getMenus()));
57 try { 63 try {
58 mpService.getMenuService().menuCreate(wxMenu); 64 mpService.getMenuService().menuCreate(wxMenu);
59 } catch (WxErrorException e) { 65 } catch (WxErrorException e) {
60 - throw new RuntimeException(e); 66 + throw exception(MENU_SAVE_FAIL, e.getError().getErrorMsg());
61 } 67 }
62 68
63 - // 返回  
64 - return menu.getId(); 69 + // 第二步,存储到数据库
  70 + mpMenuMapper.deleteByAccountId(createReqVO.getAccountId());
  71 + createReqVO.getMenus().forEach(menu -> {
  72 + // 先保存顶级菜单
  73 + MpMenuDO menuDO = createMenu(menu, null, account);
  74 + // 再保存子菜单
  75 + if (CollUtil.isEmpty(menu.getChildren())) {
  76 + return;
  77 + }
  78 + menu.getChildren().forEach(childMenu -> createMenu(childMenu, menuDO, account));
  79 + });
65 } 80 }
66 81
67 @Override 82 @Override
68 - public void deleteMenu(Long id) {  
69 - // 校验存在  
70 - validateMenuExists(id);  
71 - // 删除  
72 - mpMenuMapper.deleteById(id);  
73 - }  
74 -  
75 - private void validateMenuExists(Long id) {  
76 - if (mpMenuMapper.selectById(id) == null) {  
77 - throw exception(MENU_NOT_EXISTS); 83 + public void deleteMenuByAccountId(Long accountId) {
  84 + WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
  85 + // 第一步,同步公众号
  86 + try {
  87 + mpService.getMenuService().menuDelete();
  88 + } catch (WxErrorException e) {
  89 + throw exception(MENU_DELETE_FAIL, e.getError().getErrorMsg());
78 } 90 }
  91 +
  92 + // 第二步,存储到数据库
  93 + mpMenuMapper.deleteByAccountId(accountId);
79 } 94 }
80 95
81 - @Override  
82 - public MpMenuDO getMenu(Long id) {  
83 - return mpMenuMapper.selectById(id); 96 + private MpMenuDO createMenu(MpMenuSaveReqVO.Menu wxMenu, MpMenuDO parentMenu, MpAccountDO account) {
  97 + MpMenuDO menu = CollUtil.isNotEmpty(wxMenu.getChildren())
  98 + ? new MpMenuDO().setName(wxMenu.getName())
  99 + : MpMenuConvert.INSTANCE.convert02(wxMenu);
  100 + if (account != null) {
  101 + menu.setAccountId(account.getId()).setAppId(account.getAppId());
  102 + }
  103 + if (parentMenu != null) {
  104 + menu.setParentId(parentMenu.getId());
  105 + } else {
  106 + menu.setParentId(MpMenuDO.ID_ROOT);
  107 + }
  108 + if (StrUtil.isNotEmpty(wxMenu.getReplyMediaId())) {
  109 + throw new IllegalArgumentException("未实现");
  110 + }
  111 + mpMenuMapper.insert(menu);
  112 + return menu;
84 } 113 }
85 114
86 @Override 115 @Override
yudao-ui-admin/src/api/mp/menu.js
@@ -7,3 +7,23 @@ export function getMenuList(accountId) { @@ -7,3 +7,23 @@ export function getMenuList(accountId) {
7 method: 'get', 7 method: 'get',
8 }) 8 })
9 } 9 }
  10 +
  11 +// 保存公众号菜单
  12 +export function saveMenu(accountId, menus) {
  13 + return request({
  14 + url: '/mp/menu/save',
  15 + method: 'post',
  16 + data: {
  17 + accountId,
  18 + menus
  19 + }
  20 + })
  21 +}
  22 +
  23 +// 删除公众号菜单
  24 +export function deleteMenu(accountId) {
  25 + return request({
  26 + url: '/mp/menu/delete?accountId=' + accountId,
  27 + method: 'delete',
  28 + })
  29 +}
yudao-ui-admin/src/views/mp/menu/index.vue
@@ -63,8 +63,8 @@ SOFTWARE. @@ -63,8 +63,8 @@ SOFTWARE.
63 <div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div> 63 <div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
64 </div> 64 </div>
65 <div class="save_div"> 65 <div class="save_div">
66 - <!--<el-button class="save_btn" type="warning" size="small" @click="saveFun">保存菜单</el-button>-->  
67 - <el-button class="save_btn" type="success" size="small" @click="handleSaveAndReleaseFun">保存并发布菜单</el-button> 66 + <el-button class="save_btn" type="success" size="small" @click="handleSave">保存并发布菜单</el-button>
  67 + <el-button class="save_btn" type="danger" size="small" @click="handleDelete">清空菜单</el-button>
68 </div> 68 </div>
69 </div> 69 </div>
70 <!--右边配置--> 70 <!--右边配置-->
@@ -141,7 +141,7 @@ SOFTWARE. @@ -141,7 +141,7 @@ SOFTWARE.
141 import WxReplySelect from '@/views/mp/components/wx-news/main.vue' 141 import WxReplySelect from '@/views/mp/components/wx-news/main.vue'
142 import WxNews from '@/views/mp/components/wx-news/main.vue'; 142 import WxNews from '@/views/mp/components/wx-news/main.vue';
143 import WxMaterialSelect from '@/views/mp/components/wx-news/main.vue' 143 import WxMaterialSelect from '@/views/mp/components/wx-news/main.vue'
144 -import { getMenuList } from "@/api/mp/menu"; 144 +import {deleteMenu, getMenuList, saveMenu} from "@/api/mp/menu";
145 import { getSimpleAccounts } from "@/api/mp/account"; 145 import { getSimpleAccounts } from "@/api/mp/account";
146 146
147 export default { 147 export default {
@@ -164,11 +164,6 @@ export default { @@ -164,11 +164,6 @@ export default {
164 children: [], 164 children: [],
165 }, 165 },
166 166
167 - menu:{ // 横向菜单  
168 - button:[  
169 - ]  
170 - },  
171 -  
172 // ======================== 菜单操作 ======================== 167 // ======================== 菜单操作 ========================
173 isActive: -1,// 一级菜单点中样式 168 isActive: -1,// 一级菜单点中样式
174 isSubMenuActive: -1, // 一级菜单点中样式 169 isSubMenuActive: -1, // 一级菜单点中样式
@@ -178,7 +173,7 @@ export default { @@ -178,7 +173,7 @@ export default {
178 showRightFlag: false, // 右边配置显示默认详情还是配置详情 173 showRightFlag: false, // 右边配置显示默认详情还是配置详情
179 nameMaxLength: 0, // 菜单名称最大长度;1 级是 4 字符;2 级是 7 字符; 174 nameMaxLength: 0, // 菜单名称最大长度;1 级是 4 字符;2 级是 7 字符;
180 showConfigureContent: true, // 是否展示配置内容;如果有子菜单,就不显示配置内容 175 showConfigureContent: true, // 是否展示配置内容;如果有子菜单,就不显示配置内容
181 - 176 + hackResetWxReplySelect: false, // 重置 WxReplySelect 组件
182 177
183 tempObj:{}, // 右边临时变量,作为中间值牵引关系 178 tempObj:{}, // 右边临时变量,作为中间值牵引关系
184 tempSelfObj: { // 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数 179 tempSelfObj: { // 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数
@@ -218,7 +213,6 @@ export default { @@ -218,7 +213,6 @@ export default {
218 label: '选择地理位置' 213 label: '选择地理位置'
219 }], 214 }],
220 dialogNewsVisible: false, 215 dialogNewsVisible: false,
221 - hackResetWxReplySelect: false,  
222 216
223 // 公众号账号列表 217 // 公众号账号列表
224 accounts: [], 218 accounts: [],
@@ -246,7 +240,6 @@ export default { @@ -246,7 +240,6 @@ export default {
246 this.loading = false; 240 this.loading = false;
247 getMenuList(this.accountId).then(response => { 241 getMenuList(this.accountId).then(response => {
248 this.menuList = this.handleTree(response.data, "id"); 242 this.menuList = this.handleTree(response.data, "id");
249 - console.log(this.menuList)  
250 }).finally(() => { 243 }).finally(() => {
251 this.loading = false; 244 this.loading = false;
252 }) 245 })
@@ -354,31 +347,16 @@ export default { @@ -354,31 +347,16 @@ export default {
354 }, 347 },
355 348
356 // ======================== 菜单编辑 ======================== 349 // ======================== 菜单编辑 ========================
357 - handleSaveAndReleaseFun(){  
358 - this.$confirm('确定要保证并发布该菜单吗?', '提示', {  
359 - confirmButtonText: '确定',  
360 - cancelButtonText: '取消',  
361 - type: 'warning'  
362 - }).then(() => { 350 + handleSave() {
  351 + this.$modal.confirm('确定要保证并发布该菜单吗?').then(() => {
363 this.loading = true 352 this.loading = true
364 - saveAndRelease({  
365 - strWxMenu:this.menu  
366 - }).then(response => {  
367 - this.loading = false  
368 - if(response.code == 200){  
369 - this.$message({  
370 - showClose: true,  
371 - message: '发布成功',  
372 - type: 'success'  
373 - })  
374 - }else{  
375 - this.$message.error(response.data.msg)  
376 - }  
377 - }).catch(() => {  
378 - this.loading = false  
379 - })  
380 - }).catch(() => {  
381 - }) 353 + return saveMenu(this.accountId, this.menuList);
  354 + }).then(() => {
  355 + this.getList();
  356 + this.$modal.msgSuccess("发布成功");
  357 + }).catch(() => {}).finally(() => {
  358 + this.loading = false
  359 + });
382 }, 360 },
383 // 表单 Editor 重置 361 // 表单 Editor 重置
384 resetEditor() { 362 resetEditor() {
@@ -387,6 +365,17 @@ export default { @@ -387,6 +365,17 @@ export default {
387 this.hackResetEditor = true // 重建组件 365 this.hackResetEditor = true // 重建组件
388 }) 366 })
389 }, 367 },
  368 + handleDelete() {
  369 + this.$modal.confirm('确定要清空所有菜单吗?').then(() => {
  370 + this.loading = true
  371 + return deleteMenu(this.accountId);
  372 + }).then(() => {
  373 + this.getList();
  374 + this.$modal.msgSuccess("清空成功");
  375 + }).catch(() => {}).finally(() => {
  376 + this.loading = false
  377 + });
  378 + },
390 379
391 // TODO 芋艿:未归类 380 // TODO 芋艿:未归类
392 381
@@ -431,7 +420,7 @@ div{ @@ -431,7 +420,7 @@ div{
431 left:0px; 420 left:0px;
432 width: 300px; 421 width: 300px;
433 height:64px; 422 height:64px;
434 - background: transparent url(assets/menu_head.png) no-repeat 0 0; 423 + background: transparent url("assets/menu_head.png") no-repeat 0 0;
435 background-position: 0 0; 424 background-position: 0 0;
436 background-size: 100% 425 background-size: 100%
437 } 426 }
@@ -445,7 +434,7 @@ div{ @@ -445,7 +434,7 @@ div{
445 left: 0px; 434 left: 0px;
446 } 435 }
447 .weixin-menu{ 436 .weixin-menu{
448 - background: transparent url(assets/menu_foot.png) no-repeat 0 0; 437 + background: transparent url("assets/menu_foot.png") no-repeat 0 0;
449 padding-left: 43px; 438 padding-left: 43px;
450 font-size: 12px 439 font-size: 12px
451 } 440 }
@@ -581,26 +570,26 @@ div{ @@ -581,26 +570,26 @@ div{
581 <!--</style>--> 570 <!--</style>-->
582 <!--素材样式--> 571 <!--素材样式-->
583 <style lang="scss" scoped> 572 <style lang="scss" scoped>
584 -.pagination{ 573 +.pagination {
585 text-align: right; 574 text-align: right;
586 margin-right: 25px; 575 margin-right: 25px;
587 } 576 }
588 -.select-item{ 577 +.select-item {
589 width: 280px; 578 width: 280px;
590 padding: 10px; 579 padding: 10px;
591 margin: 0 auto 10px auto; 580 margin: 0 auto 10px auto;
592 border: 1px solid #eaeaea; 581 border: 1px solid #eaeaea;
593 } 582 }
594 -.select-item2{ 583 +.select-item2 {
595 padding: 10px; 584 padding: 10px;
596 margin: 0 auto 10px auto; 585 margin: 0 auto 10px auto;
597 border: 1px solid #eaeaea; 586 border: 1px solid #eaeaea;
598 } 587 }
599 -.ope-row{ 588 +.ope-row {
600 padding-top: 10px; 589 padding-top: 10px;
601 text-align: center; 590 text-align: center;
602 } 591 }
603 -.item-name{ 592 +.item-name {
604 font-size: 12px; 593 font-size: 12px;
605 overflow: hidden; 594 overflow: hidden;
606 text-overflow:ellipsis; 595 text-overflow:ellipsis;