Commit 2173cf0364254fb620df464c162085563d4306fe
1 parent
c0b95025
mp:menu 前端接入菜单的保存、清空操作
Showing
13 changed files
with
191 additions
and
140 deletions
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<MpMenuDO> { | @@ -18,4 +19,7 @@ public interface MpMenuMapper extends BaseMapperX<MpMenuDO> { | ||
| 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; |