Sfoglia il codice sorgente

微信公众号菜单

chao 3 anni fa
parent
commit
e2714efbd2

+ 29 - 17
db.sql

@@ -1,3 +1,4 @@
+-- ============================================== 系统表 start ===============================
 -- 系统用户表
 DROP TABLE IF EXISTS `sys_user`;
 CREATE TABLE `sys_user` (
@@ -65,20 +66,31 @@ CREATE TABLE `sys_role_menu` (
   PRIMARY KEY (`id`)
 ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='系统用户角色关联表';
 
--- 初始数据
-INSERT INTO `sys_user`(`id`,`username`,`password`,`create_time`) VALUES (1,'admin','$2a$10$QmDp1600wURZ.Dn2utkfXO4UTM3gdV42qVjMa81o3GMyW.IdfeEWm',NOW());
-INSERT INTO `sys_role`(`id`,`role_name`,`role_desc`,`create_time`) VALUES (1,'admin','管理员',NOW());
-INSERT INTO `sys_menu` (`id`, `title`, `name`, `icon`, `parent_id`, `hidden`, `create_time`)
-VALUES  ('1','系统设置','SysSetting','el-icon-s-tools','0','0',NOW()),
-        ('2','菜单管理','SysMenus','el-icon-menu','1','0',NOW()),
-        ('3','角色管理','SysRoles','peoples','1','0',NOW()),
-        ('4','用户管理','SysUsers','user','1','0',NOW()),
-        ('5','系统图标','SysIcons','el-icon-picture','1','0',NOW()),
-        ('6','菜单列表','SysMenuList','','2','1',NOW()),
-        ('7','菜单编辑','SysMenuEdit','','2','1',NOW()),
-        ('8','角色列表','SysRoleList','','3','1',NOW()),
-        ('9','角色编辑','SysRoleEdit','','3','1',NOW()),
-        ('10','用户列表','SysUserList','','4','1',NOW()),
-        ('11','用户编辑','SysUserEdit','','4','1',NOW());
-INSERT INTO `sys_role_user`(`user_id`, `role_id`) VALUES (1, 1);
-INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES (1, 1),(1, 2),(1, 3),(1, 4),(1, 5),(1, 6),(1, 7),(1, 8),(1, 9),(1, 10),(1, 11);
+-- ============================================== 系统表 end ===============================
+
+-- ============================================== 微信表 start =============================
+-- 微信公众号菜单表(原表:caimei_weixin.menu_tree)
+DROP TABLE IF EXISTS `wechat_menu`;
+CREATE TABLE `wechat_menu` (
+  `id` bigint(11) NOT NULL AUTO_INCREMENT,
+  `parentId` bigint(11) DEFAULT NULL COMMENT '父级编号',
+  `parentIds` varchar(2000) DEFAULT NULL COMMENT '所有父级编号',
+  `name` varchar(30) DEFAULT '' COMMENT '菜单标题',
+  `sort` int(11) DEFAULT '0' COMMENT '排序',
+  `type` varchar(30) DEFAULT '' COMMENT '菜单的响应动作类型',
+  `key` varchar(64) DEFAULT '' COMMENT '菜单KEY值,用于消息接口推送(click等点击类型必须)',
+  `url` text COMMENT '网页链接,用户点击菜单可打开链接(view类型必须)',
+  `mediaId` varchar(64) DEFAULT '' COMMENT '调用新增永久素材接口返回的合法media_id(media_id类型和view_limited类型必须)',
+  `appid` varchar(64) DEFAULT '' COMMENT '小程序appId,公众号跳转小程序时使用',
+  `pagepath` varchar(100) DEFAULT '' COMMENT '小程序页面路径,公众号跳转小程序时使用',
+  `wxType` varchar(20) DEFAULT '' COMMENT '微信公众号类型',
+  `createBy` bigint(11) DEFAULT NULL COMMENT '创建人',
+  `createDate` datetime DEFAULT NULL COMMENT '创建时间',
+  `updateBy` bigint(11) DEFAULT NULL COMMENT '最后更新人',
+  `updateDate` datetime DEFAULT NULL COMMENT '最后更新时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8 COMMENT='微信菜单tree';
+
+
+
+-- ============================================== 微信表 end ===============================

+ 1 - 1
src/main/java/com/caimei365/manager/config/security/UserDetailsServiceImpl.java

@@ -1,7 +1,7 @@
 package com.caimei365.manager.config.security;
 
 import com.caimei365.manager.dao.SysUserDao;
-import com.caimei365.manager.entity.SysUser;
+import com.caimei365.manager.entity.sys.SysUser;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;

+ 9 - 4
src/main/java/com/caimei365/manager/controller/SysUserApi.java

@@ -2,7 +2,12 @@ package com.caimei365.manager.controller;
 
 import com.caimei365.manager.config.security.ConstantKey;
 import com.caimei365.manager.config.security.JwtService;
-import com.caimei365.manager.entity.*;
+import com.caimei365.manager.entity.PaginationVo;
+import com.caimei365.manager.entity.ResponseJson;
+import com.caimei365.manager.entity.sys.SysMenu;
+import com.caimei365.manager.entity.sys.SysRole;
+import com.caimei365.manager.entity.sys.SysUser;
+import com.caimei365.manager.entity.sys.UserProfile;
 import com.caimei365.manager.service.SysUserService;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
@@ -177,9 +182,9 @@ public class SysUserApi {
      */
     @GetMapping("/menu/list")
     public ResponseJson<PaginationVo<SysMenu>> menuList(Integer status,
-            @RequestParam(value = "parentId", defaultValue = "0") Integer parentId,
-            @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
-            @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
+                                                        @RequestParam(value = "parentId", defaultValue = "0") Integer parentId,
+                                                        @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
+                                                        @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
         return sysUserService.getMenuList(parentId, status, pageNum, pageSize);
     }
 

+ 92 - 0
src/main/java/com/caimei365/manager/controller/WeChatApi.java

@@ -0,0 +1,92 @@
+package com.caimei365.manager.controller;
+
+import com.caimei365.manager.config.security.ConstantKey;
+import com.caimei365.manager.config.security.JwtService;
+import com.caimei365.manager.entity.ResponseJson;
+import com.caimei365.manager.entity.wechat.WechatMenu;
+import com.caimei365.manager.service.WeChatService;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+/**
+ * 微信后台管理
+ *
+ * @author : Charles
+ * @date : 2022/1/4
+ */
+@RestController
+@RequestMapping("/wechat")
+public class WeChatApi {
+
+    @Resource
+    private WeChatService weChatService;
+    @Resource
+    private JwtService jwtService;
+
+    /**
+     * 获取微信公众号菜单列表
+     *
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    @GetMapping("/menu/list")
+    public ResponseJson<List<WechatMenu>> wechatMenuList(Integer type) {
+        return weChatService.getWechatMenuList(type);
+    }
+
+    /**
+     * 根据ID获取微信公众号菜单
+     */
+    @GetMapping("/menu/{id}")
+    public ResponseJson<WechatMenu> getWechatMenu(@PathVariable("id") Integer id) {
+        if (null == id || id <= 0) {
+            return ResponseJson.error("菜单Id不能为空!", null);
+        }
+        return weChatService.getWechatMenu(id);
+    }
+
+    /**
+     * 根据ID更新微信公众号菜单
+     */
+    @PostMapping("/menu/update/{id}")
+    public ResponseJson<Void> updateWechatMenu(HttpServletRequest request, @PathVariable("id") Integer id, @RequestBody WechatMenu menu) {
+        if (null == id || id <= 0) {
+            return ResponseJson.error("菜单Id不能为空!", null);
+        }
+        String token = request.getHeader(ConstantKey.TOKEN_NAME);
+        String username = jwtService.getUsername(token);
+        return weChatService.updateWechatMenu(id, menu, username);
+    }
+
+    /**
+     * 添加微信公众号菜单
+     */
+    @PostMapping("/menu/create")
+    public ResponseJson<Void> addWechatMenu(HttpServletRequest request, @RequestBody WechatMenu menu) {
+        String token = request.getHeader(ConstantKey.TOKEN_NAME);
+        String username = jwtService.getUsername(token);
+        return weChatService.addWechatMenu(menu, username);
+    }
+
+    /**
+     * 根据ID删除微信公众号菜单
+     */
+    @PostMapping("/menu/delete/{id}")
+    public ResponseJson<Void> deleteWechatMenu(@PathVariable("id") Integer id) {
+        if (null == id || id <= 0) {
+            return ResponseJson.error("菜单Id不能为空!", null);
+        }
+        return weChatService.deleteWechatMenu(id);
+    }
+
+    /**
+     * 微信公众号菜单发布
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    @PostMapping("/menu/push/{type}")
+    public ResponseJson<Void> pushWechatMenu(@PathVariable("type") Integer type) {
+        return weChatService.pushWechatMenu(type);
+    }
+}

+ 4 - 3
src/main/java/com/caimei365/manager/dao/SysUserDao.java

@@ -1,9 +1,9 @@
 package com.caimei365.manager.dao;
 
 
-import com.caimei365.manager.entity.SysMenu;
-import com.caimei365.manager.entity.SysRole;
-import com.caimei365.manager.entity.SysUser;
+import com.caimei365.manager.entity.sys.SysMenu;
+import com.caimei365.manager.entity.sys.SysRole;
+import com.caimei365.manager.entity.sys.SysUser;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -21,6 +21,7 @@ public interface SysUserDao {
      * 根据用户名称查找可登录用户
      */
     SysUser findByUsername(String username);
+    Integer getUserIdByUsername(String username);
     /**
      * 新增用户角色关系表
      */

+ 41 - 0
src/main/java/com/caimei365/manager/dao/WeChatDao.java

@@ -0,0 +1,41 @@
+package com.caimei365.manager.dao;
+
+import com.caimei365.manager.entity.wechat.WechatMenu;
+
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/1/5
+ */
+public interface WeChatDao {
+    /**
+     * 获取微信公众号菜单列表
+     * @param parentId 父级ID
+     * @param wxType   微信公众号ID
+     */
+    List<WechatMenu> getWechatMenuList(Integer parentId, String wxType);
+    /**
+     * 根据ID获取微信公众号菜单
+     */
+    WechatMenu getWechatMenu(Integer id);
+    /**
+     * 根据ID更新微信公众号菜单
+     */
+    void updateWechatMenu(WechatMenu menu);
+    /**
+     * 添加微信公众号菜单
+     */
+    void insertWechatMenu(WechatMenu menu);
+    /**
+     * 根据ID删除微信公众号菜单
+     */
+    void deleteWechatMenu(Integer id);
+    /**
+     * 根据父级Id统计同级别菜单数量
+     * @param parentId 父级ID
+     */
+    Integer countChildByParentId(Integer parentId);
+}

+ 1 - 1
src/main/java/com/caimei365/manager/entity/SysMenu.java → src/main/java/com/caimei365/manager/entity/sys/SysMenu.java

@@ -1,4 +1,4 @@
-package com.caimei365.manager.entity;
+package com.caimei365.manager.entity.sys;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;

+ 1 - 1
src/main/java/com/caimei365/manager/entity/SysRole.java → src/main/java/com/caimei365/manager/entity/sys/SysRole.java

@@ -1,4 +1,4 @@
-package com.caimei365.manager.entity;
+package com.caimei365.manager.entity.sys;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;

+ 1 - 1
src/main/java/com/caimei365/manager/entity/SysUser.java → src/main/java/com/caimei365/manager/entity/sys/SysUser.java

@@ -1,4 +1,4 @@
-package com.caimei365.manager.entity;
+package com.caimei365.manager.entity.sys;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;

+ 1 - 1
src/main/java/com/caimei365/manager/entity/UserProfile.java → src/main/java/com/caimei365/manager/entity/sys/UserProfile.java

@@ -1,4 +1,4 @@
-package com.caimei365.manager.entity;
+package com.caimei365.manager.entity.sys;
 
 import lombok.Data;
 

+ 75 - 0
src/main/java/com/caimei365/manager/entity/wechat/WechatMenu.java

@@ -0,0 +1,75 @@
+package com.caimei365.manager.entity.wechat;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 微信公众号菜单
+ *
+ * @author : Charles
+ * @date : 2022/1/5
+ */
+@Data
+public class WechatMenu {
+    private static final long serialVersionUID = 1L;
+    /**
+     * Id
+     */
+    private Integer id;
+    /**
+     * 父级菜单Id
+     */
+    private Integer parentId;
+    /**
+     * 所有父级编号
+     */
+    private String parentIds;
+    /**
+     * 菜单标题
+     */
+    private String name;
+    /**
+     * 排序
+     */
+    private Integer sort;
+    /**
+     * 菜单的响应动作类型
+     */
+    private String type;
+    /**
+     * 菜单KEY值,用于消息接口推送(click等点击类型必须)
+     */
+    private String key;
+    /**
+     * 网页链接,用户点击菜单可打开链接(view类型必须)
+     */
+    private String url;
+    /**
+     * 调用新增永久素材接口返回的合法media_id(media_id类型和view_limited类型必须)
+     */
+    private String mediaId;
+    /**
+     * 小程序appID
+     */
+    private String appid;
+    /**
+     * 小程序页面路径
+     */
+    private String pagePath;
+    /**
+     * 微信公众号类型
+     */
+    /**
+     * 小程序页面路径
+     */
+    private String wxType;
+    /**
+     * 操作人用户Id
+     */
+    private Integer userId;
+    /**
+     * 子菜单列表
+     */
+    List<WechatMenu> children;
+}

+ 4 - 0
src/main/java/com/caimei365/manager/service/SysUserService.java

@@ -2,6 +2,10 @@ package com.caimei365.manager.service;
 
 
 import com.caimei365.manager.entity.*;
+import com.caimei365.manager.entity.sys.SysMenu;
+import com.caimei365.manager.entity.sys.SysRole;
+import com.caimei365.manager.entity.sys.SysUser;
+import com.caimei365.manager.entity.sys.UserProfile;
 
 import java.util.List;
 

+ 44 - 0
src/main/java/com/caimei365/manager/service/WeChatService.java

@@ -0,0 +1,44 @@
+package com.caimei365.manager.service;
+
+import com.caimei365.manager.entity.ResponseJson;
+import com.caimei365.manager.entity.wechat.WechatMenu;
+
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/1/5
+ */
+public interface WeChatService {
+    /**
+     * 获取微信公众号菜单列表
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    ResponseJson<List<WechatMenu>> getWechatMenuList(Integer type);
+    /**
+     * 根据ID获取微信公众号菜单
+     * @param id 菜单Id
+     */
+    ResponseJson<WechatMenu> getWechatMenu(Integer id);
+    /**
+     * 根据ID更新微信公众号菜单
+     * @param id 菜单Id
+     */
+    ResponseJson<Void> updateWechatMenu(Integer id, WechatMenu menu, String username);
+    /**
+     * 添加微信公众号菜单
+     */
+    ResponseJson<Void> addWechatMenu(WechatMenu menu, String username);
+    /**
+     * 根据ID删除微信公众号菜单
+     * @param id 菜单Id
+     */
+    ResponseJson<Void> deleteWechatMenu(Integer id);
+    /**
+     * 微信公众号菜单发布
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    ResponseJson<Void> pushWechatMenu(Integer type);
+}

+ 4 - 0
src/main/java/com/caimei365/manager/service/impl/SysUserServiceImpl.java

@@ -2,6 +2,10 @@ package com.caimei365.manager.service.impl;
 
 import com.caimei365.manager.dao.SysUserDao;
 import com.caimei365.manager.entity.*;
+import com.caimei365.manager.entity.sys.SysMenu;
+import com.caimei365.manager.entity.sys.SysRole;
+import com.caimei365.manager.entity.sys.SysUser;
+import com.caimei365.manager.entity.sys.UserProfile;
 import com.caimei365.manager.service.RedisService;
 import com.caimei365.manager.service.SysUserService;
 import com.github.pagehelper.PageHelper;

+ 318 - 0
src/main/java/com/caimei365/manager/service/impl/WeChatServiceImpl.java

@@ -0,0 +1,318 @@
+package com.caimei365.manager.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei365.manager.dao.SysUserDao;
+import com.caimei365.manager.dao.WeChatDao;
+import com.caimei365.manager.entity.ResponseJson;
+import com.caimei365.manager.entity.wechat.WechatMenu;
+import com.caimei365.manager.service.WeChatService;
+import com.caimei365.manager.utils.HttpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2022/1/5
+ */
+@Slf4j
+@Service
+public class WeChatServiceImpl implements WeChatService {
+
+    @Value("${wechat.publicUrl}")
+    private String publicUrl;
+    @Value("${wechat.getTokenUrl}")
+    private String getTokenUrl;
+    @Value("${wechat.createMenuUrl}")
+    private String createMenuUrl;
+
+    @Value("${wechat.caimei.id}")
+    private String caimeiId;
+    @Value("${wechat.caimei.appid}")
+    private String caimeiAppid;
+    @Value("${wechat.caimei.secret}")
+    private String caimeiSecret;
+
+    @Value("${wechat.hehe.id}")
+    private String heheId;
+    @Value("${wechat.hehe.appid}")
+    private String heheAppid;
+    @Value("${wechat.hehe.secret}")
+    private String heheSecret;
+
+    @Resource
+    private WeChatDao weChatDao;
+    @Resource
+    private SysUserDao sysUserDao;
+
+    /**
+     * 获取微信公众号菜单列表
+     *
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    @Override
+    public ResponseJson<List<WechatMenu>> getWechatMenuList(Integer type) {
+        String wxType = "";
+        if (null != type && 1 == type) {
+            wxType = caimeiId;
+        } else if (null != type && 2 == type) {
+            wxType = heheId;
+        } else {
+            return ResponseJson.error("菜单类型不能为空", null);
+        }
+        List<WechatMenu> list = weChatDao.getWechatMenuList(type, wxType);
+        // 获取子菜单
+        for (WechatMenu menu : list) {
+            List<WechatMenu> subList = weChatDao.getWechatMenuList(menu.getId(), wxType);
+            menu.setChildren(subList);
+        }
+        return ResponseJson.success(list);
+    }
+
+    /**
+     * 根据ID获取微信公众号菜单
+     *
+     * @param id 菜单Id
+     */
+    @Override
+    public ResponseJson<WechatMenu> getWechatMenu(Integer id) {
+        WechatMenu menu = weChatDao.getWechatMenu(id);
+        return ResponseJson.success(menu);
+    }
+
+    /**
+     * 根据ID更新微信公众号菜单
+     *
+     * @param id   菜单Id
+     */
+    @Override
+    public ResponseJson<Void> updateWechatMenu(Integer id, WechatMenu menu, String username) {
+        WechatMenu dbMenu = weChatDao.getWechatMenu(id);
+        if (null == dbMenu) {
+            return ResponseJson.error("当前菜单异常!", null);
+        }
+        if (StringUtils.hasLength(menu.getName())){
+            dbMenu.setName(menu.getName());
+        }
+        if (StringUtils.hasLength(menu.getType())){
+            dbMenu.setType(menu.getType());
+        }
+        if (StringUtils.hasLength(menu.getKey())){
+            dbMenu.setKey(menu.getKey());
+        }
+        if (StringUtils.hasLength(menu.getUrl())){
+            dbMenu.setUrl(menu.getUrl());
+        }
+        if (StringUtils.hasLength(menu.getMediaId())){
+            dbMenu.setMediaId(menu.getMediaId());
+        }
+        if (StringUtils.hasLength(menu.getAppid())){
+            dbMenu.setAppid(menu.getAppid());
+        }
+        if (StringUtils.hasLength(menu.getPagePath())){
+            dbMenu.setPagePath(menu.getPagePath());
+        }
+        if (null != menu.getSort()){
+            dbMenu.setSort(menu.getSort());
+        }
+        WechatMenu parentMenu = weChatDao.getWechatMenu(dbMenu.getParentId());
+        setParentIds(dbMenu, parentMenu, username);
+        // 更新
+        weChatDao.updateWechatMenu(dbMenu);
+        return ResponseJson.success();
+    }
+
+    /**
+     * 添加微信公众号菜单
+     */
+    @Override
+    public ResponseJson<Void> addWechatMenu(WechatMenu menu, String username) {
+        if (!StringUtils.hasLength(menu.getName())) {
+            return ResponseJson.error("菜单标题不能为空!", null);
+        }
+        if (null != menu.getParentId()) {
+            return ResponseJson.error("父级菜单Id不能为空!", null);
+        }
+        WechatMenu parentMenu = weChatDao.getWechatMenu(menu.getParentId());
+        if (null == parentMenu) {
+            return ResponseJson.error("父级菜单异常!", null);
+        }
+        menu.setWxType(parentMenu.getWxType());
+        // 根据父级Id统计同级别菜单数量
+        Integer count = weChatDao.countChildByParentId(menu.getParentId());
+        // 一级菜单
+        if (menu.getParentId() == 1 || menu.getParentId() == 2) {
+            if (count >= 3) {
+                return ResponseJson.error("最多包括3个一级菜单!", null);
+            }
+        } else {
+            if (count >= 3) {
+                return ResponseJson.error("每个一级菜单最多包含5个二级菜单!", null);
+            }
+        }
+        setParentIds(menu, parentMenu, username);
+        // 新增
+        weChatDao.insertWechatMenu(menu);
+        return ResponseJson.success();
+    }
+
+    /**
+     * 设置操作人与父级ID串
+     * @param menu     待更新菜单
+     * @param username 操作人登录名
+     */
+    private void setParentIds(WechatMenu menu, WechatMenu parentMenu, String username) {
+        Integer userId = sysUserDao.getUserIdByUsername(username);
+        menu.setUserId(userId);
+        String parentIds = "0,";
+        if (null != parentMenu && StringUtils.hasLength(parentMenu.getParentIds())) {
+            parentIds = parentMenu.getParentIds() + parentMenu.getId() + ",";
+        }
+        menu.setParentIds(parentIds);
+    }
+
+    /**
+     * 根据ID删除微信公众号菜单
+     *
+     * @param id 菜单Id
+     */
+    @Override
+    public ResponseJson<Void> deleteWechatMenu(Integer id) {
+        weChatDao.deleteWechatMenu(id);
+        return ResponseJson.success();
+    }
+
+    /**
+     * 微信公众号菜单发布
+     *
+     * @param type 类型: 1采美,2呵呵商城
+     */
+    @Override
+    public ResponseJson<Void> pushWechatMenu(Integer type) {
+        String wxType = "";
+        if (null != type && 1 == type) {
+            wxType = caimeiId;
+        } else if (null != type && 2 == type) {
+            wxType = heheId;
+        } else {
+            return ResponseJson.error("菜单类型不正确!", null);
+        }
+        // 一级菜单
+        List<WechatMenu> buttons = weChatDao.getWechatMenuList(type, wxType);
+        if (buttons.size() >= 3) {
+            return ResponseJson.error("最多包括3个一级菜单!", null);
+        }
+        // 组装请求数据
+        List<Map<String,Object>> buttonMapList = new ArrayList<>();
+        for (WechatMenu button : buttons) {
+            Map<String,Object> buttonMap = new HashMap();
+            buttonMap.put("name", button.getName());
+            if (StringUtils.hasLength(button.getType())) {
+                // type不为空,则只有一级菜单
+                buttonMap.put("type", button.getType());
+                setTypeValue(button, buttonMap);
+            } else {
+                // 二级菜单
+                List<WechatMenu> subList = weChatDao.getWechatMenuList(button.getId(), wxType);
+                if (subList.size() >= 5) {
+                    return ResponseJson.error("每个一级菜单最多包含5个二级菜单!", null);
+                }
+                List<Map<String,Object>> menuMapList = new ArrayList<>();
+                for (WechatMenu menu : subList) {
+                    Map<String,Object> menuMap = new HashMap();
+                    menuMap.put("name", menu.getName());
+                    menuMap.put("type", menu.getType());
+                    setTypeValue(menu, menuMap);
+                    menuMapList.add(menuMap);
+                }
+                // 把二级菜单放入上级菜单的集合中
+                buttonMap.put("sub_button", menuMapList);
+            }
+            buttonMapList.add(buttonMap);
+        }
+        Map<String,Object> map = new HashMap();
+        map.put("button", buttonMapList);
+        // json 字符串
+        String jsonString = JSON.toJSONString(map);
+        log.info("》》》微信公众号菜单发布,发布数据:" + jsonString);
+        // 组装请求链接
+        String url = createMenuUrl + "?access_token=";
+        if (1 == type) {
+            // 采美微信公众号
+            url += getAccessToken(caimeiAppid, caimeiSecret);
+        } else if (2 == type) {
+            // 呵呵商城公众号
+            url += getAccessToken(heheAppid, heheSecret);
+        }
+        // 得到结果String
+        String jsonResult = null;
+        try {
+            jsonResult = HttpUtil.httpRequest(url, "POST", jsonString);
+        } catch (Exception e) {
+            log.error("发布微信公众号菜单失败:", e);
+            return ResponseJson.error("微信接口请求失败,请联系管理员!", null);
+        }
+        if(StringUtils.hasLength(jsonResult)){
+            JSONObject jsonObject = JSON.parseObject(jsonResult);
+            if("0".equals(jsonObject.getString("errcode"))){
+                return ResponseJson.success("发布成功!", null);
+            }else{
+                log.info(jsonResult);
+                return ResponseJson.error(jsonObject.getString("errmsg"), null);
+            }
+        } else {
+            return ResponseJson.error("发布失败,请联系管理员!", null);
+        }
+    }
+
+    /**
+     * 组装菜单数据
+     */
+    private void setTypeValue(WechatMenu menu, Map<String, Object> map) {
+        if ("view".equals(menu.getType())) {
+            map.put("url", menu.getUrl());
+        } else if ("miniprogram".equals(menu.getType())) {
+            // 小程序跳转
+            map.put("url", publicUrl);
+            map.put("appid", menu.getAppid());
+            map.put("pagepath", menu.getPagePath());
+        } else if ("media_id".equals(menu.getType()) || "view_limited".equals(menu.getType())) {
+            map.put("media_id", menu.getMediaId());
+        } else {
+            map.put("key", menu.getKey());
+            if(!"click".equals(menu.getType())) {
+                map.put("sub_button", new ArrayList());
+            }
+        }
+    }
+
+    /**
+     * 换取 access_token
+     */
+    public String getAccessToken(String appid, String secret){
+        String url = getTokenUrl + "?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
+        try {
+            String jsonResult = HttpUtil.httpRequest(url, "GET", null);
+            if(StringUtils.hasLength(jsonResult)){
+                JSONObject jsonObject = JSON.parseObject(jsonResult);
+                String accessToken = jsonObject.getString("access_token");
+                if(StringUtils.hasLength(accessToken)){
+                    return	accessToken;
+                }else{
+                    log.info(jsonResult);
+                }
+            }
+        } catch (Exception ignored) {}
+        return "";
+    }
+}

+ 85 - 0
src/main/java/com/caimei365/manager/utils/HttpUtil.java

@@ -0,0 +1,85 @@
+package com.caimei365.manager.utils;
+
+import org.springframework.util.StringUtils;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+
+/**
+ * HTTP 请求工具类
+ *
+ * @author : Charles
+ * @date : 2022/1/5
+ */
+public class HttpUtil {
+    public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
+        StringBuffer buffer = new StringBuffer();
+        try {
+            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
+            TrustManager[] tm = {
+                    new javax.net.ssl.X509TrustManager(){
+                        @Override
+                        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+                        }
+                        @Override
+                        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+                        }
+                        @Override
+                        public X509Certificate[] getAcceptedIssuers() {
+                            return null;
+                        }
+                    }
+            };
+            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
+            sslContext.init(null, tm, new java.security.SecureRandom());
+            // 从上述SSLContext对象中得到SSLSocketFactory对象
+            SSLSocketFactory ssf = sslContext.getSocketFactory();
+
+            URL url = new URL(requestUrl);
+            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
+            httpUrlConn.setSSLSocketFactory(ssf);
+            httpUrlConn.setDoOutput(true);
+            httpUrlConn.setDoInput(true);
+            httpUrlConn.setUseCaches(false);
+            // 设置请求方式(GET/POST)
+            httpUrlConn.setRequestMethod(requestMethod);
+            if ("GET".equalsIgnoreCase(requestMethod)) {
+                httpUrlConn.connect();
+            }
+            // 当有数据需要提交时
+            if (StringUtils.hasLength(outputStr)) {
+                OutputStream outputStream = httpUrlConn.getOutputStream();
+                // 注意编码格式,防止中文乱码
+                outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8));
+                outputStream.close();
+            }
+
+            // 将返回的输入流转换成字符串
+            InputStream inputStream = httpUrlConn.getInputStream();
+            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+            String str = null;
+            while ((str = bufferedReader.readLine()) != null) {
+                buffer.append(str);
+            }
+            bufferedReader.close();
+            inputStreamReader.close();
+            // 释放资源
+            inputStream.close();
+            inputStream = null;
+            httpUrlConn.disconnect();
+            return buffer.toString();
+        } catch (Exception ignored) {}
+        return null;
+    }
+
+}

+ 19 - 0
src/main/resources/application.yml

@@ -18,3 +18,22 @@ spring:
 
 mybatis:
   mapper-locations: classpath:mapper/*.xml
+
+
+# 微信公众号
+wechat:
+  publicUrl: http://mp.weixin.qq.com
+  getTokenUrl: https://api.weixin.qq.com/cgi-bin/token
+  createMenuUrl: https://api.weixin.qq.com/cgi-bin/menu/create
+  caimei:
+    id: gh_ef75b1b89b74
+    appid: wx91c4152b60ca91a3
+    secret: a563dd2c07c9c815a4e697c8b6cb73dc
+  hehe:
+    id: gh_eecada09617d
+    appid: wxd81864ddacc0ed25
+    secret: 7873323db2984c75556f8d04e76d1f02
+
+
+
+

+ 14 - 11
src/main/resources/mapper/SysUserDao.xml

@@ -1,10 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.caimei365.manager.dao.SysUserDao">
-    <select id="findByUsername" resultType="com.caimei365.manager.entity.SysUser">
+    <select id="findByUsername" resultType="com.caimei365.manager.entity.sys.SysUser">
         SELECT id, username, PASSWORD, avatar, fullName, phone, login_flag AS loginFlag, del_flag AS delFlag
         FROM sys_user WHERE login_flag=0 AND username=#{username}
     </select>
+    <select id="getUserIdByUsername" resultType="java.lang.Integer">
+        SELECT id FROM sys_user WHERE username=#{username}
+    </select>
     <select id="getRoleNamesByUserId" resultType="java.lang.String">
         SELECT DISTINCT r.role_name FROM sys_role r
             LEFT JOIN sys_role_user ru ON r.id = ru.role_id
@@ -20,7 +23,7 @@
 <!--        LEFT JOIN sys_user u ON ru.user_id = u.id-->
 <!--        WHERE u.username=#{username}-->
 <!--    </select>-->
-    <select id="getUserList" resultType="com.caimei365.manager.entity.SysUser">
+    <select id="getUserList" resultType="com.caimei365.manager.entity.sys.SysUser">
         SELECT u.id, u.username, u.avatar, u.fullName, u.phone, u.create_time AS createTime ,u.update_time AS updateTime, login_flag AS loginFlag, del_flag AS delFlag
         FROM sys_user u
         WHERE u.del_flag = 0
@@ -31,7 +34,7 @@
             AND u.fullName=#{fullName}
         </if>
     </select>
-    <select id="getUser" resultType="com.caimei365.manager.entity.SysUser">
+    <select id="getUser" resultType="com.caimei365.manager.entity.sys.SysUser">
         SELECT u.id, u.username, u.avatar, u.fullName, u.phone, u.create_time AS createTime ,u.update_time AS updateTime, login_flag AS loginFlag, del_flag AS delFlag
         FROM sys_user u
         WHERE u.id = #{id}
@@ -57,21 +60,21 @@
     <delete id="deleteUserRoleRelation">
         DELETE FROM sys_role_user WHERE user_id = #{userId}
     </delete>
-    <select id="getRoleListByUserId" resultType="com.caimei365.manager.entity.SysRole">
+    <select id="getRoleListByUserId" resultType="com.caimei365.manager.entity.sys.SysRole">
         SELECT DISTINCT r.id, r.role_name AS roleName, r.role_desc AS roleDesc, r.create_time AS createTime, r.update_time AS updateTime, del_flag AS delFlag
         FROM sys_role r
         LEFT JOIN sys_role_user ru ON r.id = ru.role_id
         WHERE ru.user_id = #{userId} AND r.del_flag = 0
     </select>
-    <select id="getRoleList" resultType="com.caimei365.manager.entity.SysRole">
+    <select id="getRoleList" resultType="com.caimei365.manager.entity.sys.SysRole">
         SELECT  r.id, r.role_name AS roleName, r.role_desc AS roleDesc, r.create_time AS createTime, r.update_time AS updateTime, del_flag AS delFlag
         FROM sys_role r WHERE r.del_flag = 0
     </select>
-    <select id="getRole" resultType="com.caimei365.manager.entity.SysRole">
+    <select id="getRole" resultType="com.caimei365.manager.entity.sys.SysRole">
         SELECT  r.id, r.role_name AS roleName, r.role_desc AS roleDesc, r.create_time AS createTime, r.update_time AS updateTime, del_flag AS delFlag
         FROM sys_role r WHERE r.id = #{id}
     </select>
-    <select id="getRoleByRoleName" resultType="com.caimei365.manager.entity.SysRole">
+    <select id="getRoleByRoleName" resultType="com.caimei365.manager.entity.sys.SysRole">
         SELECT  r.id, r.role_name AS roleName, r.role_desc AS roleDesc, r.create_time AS createTime, r.update_time AS updateTime, del_flag AS delFlag
         FROM sys_role r WHERE r.role_name = #{roleName}
     </select>
@@ -95,7 +98,7 @@
     <update id="deleteRole">
         UPDATE sys_role SET del_flag=1, update_time=NOW() WHERE id = #{id}
     </update>
-    <select id="getMenusByRoleIds" resultType="com.caimei365.manager.entity.SysMenu">
+    <select id="getMenusByRoleIds" resultType="com.caimei365.manager.entity.sys.SysMenu">
         SELECT m.id, m.title , m.name, m.icon, m.parent_id AS parentId, m.hidden, m.status, m.sort, m.create_time AS createTime, del_flag AS delFlag
         FROM sys_menu m
         LEFT JOIN sys_role_menu rm ON m.id = rm.menu_id
@@ -105,7 +108,7 @@
         </foreach>
         ORDER BY m.sort
     </select>
-    <select id="getMenuList" resultType="com.caimei365.manager.entity.SysMenu">
+    <select id="getMenuList" resultType="com.caimei365.manager.entity.sys.SysMenu">
         SELECT m.id, m.title , m.name, m.icon, m.parent_id AS parentId, m.status, m.hidden, m.sort, m.create_time AS createTime, del_flag AS delFlag,
         (SELECT COUNT(*) FROM sys_menu WHERE parent_id=m.id) AS childCount
         FROM sys_menu m
@@ -118,12 +121,12 @@
         </if>
         ORDER BY m.sort
     </select>
-    <select id="getMenu" resultType="com.caimei365.manager.entity.SysMenu">
+    <select id="getMenu" resultType="com.caimei365.manager.entity.sys.SysMenu">
         SELECT m.id, m.title , m.name, m.icon, m.parent_id AS parentId, m.hidden, m.status, m.sort, m.create_time AS createTime, del_flag AS delFlag
         FROM sys_menu m
         WHERE m.id = #{id}
     </select>
-    <select id="getMenuByName" resultType="com.caimei365.manager.entity.SysMenu">
+    <select id="getMenuByName" resultType="com.caimei365.manager.entity.sys.SysMenu">
         SELECT m.id, m.title , m.name, m.icon, m.parent_id AS parentId, m.hidden, m.status, m.sort, m.create_time AS createTime, del_flag AS delFlag
         FROM sys_menu m
         WHERE m.name = #{name}

+ 93 - 0
src/main/resources/mapper/WeChatDao.xml

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.caimei365.manager.dao.WeChatDao">
+    <select id="getWechatMenuList" resultType="com.caimei365.manager.entity.wechat.WechatMenu">
+        SELECT
+        a.id,
+        a.parentId,
+        a.parentIds,
+        a.`name`,
+        a.sort,
+        a.type,
+        a.`key`,
+        a.url,
+        a.mediaId,
+        a.appid,
+        a.pagepath AS pagePath,
+        a.wxType
+        FROM wechat_menu a
+        WHERE a.wxType = #{wxType} AND a.parentId = #{parentId}
+        ORDER BY a.sort ASC
+    </select>
+    <select id="getWechatMenu" resultType="com.caimei365.manager.entity.wechat.WechatMenu">
+        SELECT
+            a.id,
+            a.parentId,
+            a.parentIds,
+            a.`name`,
+            a.sort,
+            a.type,
+            a.`key`,
+            a.url,
+            a.mediaId,
+            a.appid,
+            a.pagepath AS pagePath,
+            a.wxType
+        FROM wechat_menu a
+        WHERE a.id = #{id}
+    </select>
+    <select id="countChildByParentId" resultType="java.lang.Integer">
+        SELECT IFNULL(COUNT(*),0) FROM wechat_menu WHERE parentId = #{parentId}
+    </select>
+    <update id="updateWechatMenu">
+        UPDATE wechat_menu SET
+                             parentId = #{parentId},
+                             parentIds = #{parentIds},
+                             name = #{name},
+                             sort = #{sort},
+                             type = #{type},
+                             `key` = #{key},
+                             url = #{url},
+                             mediaId = #{mediaId},
+                             appid = #{appid},
+                             pagepath = #{pagePath},
+                             wxType = #{wxType},
+                             updateBy = #{userId},
+                             updateDate = NOW()
+        WHERE id = #{id}
+    </update>
+    <insert id="insertWechatMenu" keyProperty="id" useGeneratedKeys="true">
+        INSERT INTO wechat_menu(
+            parentId,
+            parentIds,
+            `name`,
+            sort,
+            type,
+            `key`,
+            url,
+            mediaId,
+            appid,
+            pagepath,
+            wxType,
+            createBy,
+            createDate
+        ) VALUES (
+                     #{parentId},
+                     #{parentIds},
+                     #{name},
+                     #{sort},
+                     #{type},
+                     #{key},
+                     #{url},
+                     #{mediaId},
+                     #{appid},
+                     #{pagePath},
+                     #{wxType},
+                     #{userId},
+                     NOW()
+                 )
+    </insert>
+    <delete id="deleteWechatMenu">
+        DELETE FROM wechat_menu WHERE id = #{id}
+    </delete>
+</mapper>