Commit bcc3ecad authored by liuyang's avatar liuyang

完成微信相关功能

parent abf43a59
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
<baidu.aip.version>4.11.3</baidu.aip.version> <baidu.aip.version>4.11.3</baidu.aip.version>
<shiro.version>1.4.0</shiro.version> <shiro.version>1.4.0</shiro.version>
<fastjson.version>1.2.60</fastjson.version> <fastjson.version>1.2.60</fastjson.version>
<weixin-java.version>3.6.0</weixin-java.version>
</properties> </properties>
<dependencies> <dependencies>
...@@ -235,6 +236,12 @@ ...@@ -235,6 +236,12 @@
<artifactId>springfox-swagger-ui</artifactId> <artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version> <version>2.4.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin-java.version}</version>
</dependency>
</dependencies> </dependencies>
......
package com.qkdata.biz.wx.buider;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public abstract class AbstractBuilder {
protected final Logger logger = LoggerFactory.getLogger(getClass());
public abstract WxMpXmlOutMessage build(String content,
WxMpXmlMessage wxMessage, WxMpService service);
}
package com.qkdata.biz.wx.buider;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public class ImageBuilder extends AbstractBuilder {
@Override
public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
WxMpService service) {
WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
return m;
}
}
package com.qkdata.biz.wx.buider;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public class TextBuilder extends AbstractBuilder {
@Override
public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
WxMpService service) {
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
return m;
}
}
package com.qkdata.biz.wx.config;
import com.qkdata.biz.wx.handler.*;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.stream.Collectors;
import static me.chanjar.weixin.common.api.WxConsts.EventType;
import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
/**
* wechat mp configuration
*
* @author Binary Wang(https://github.com/binarywang)
*/
@AllArgsConstructor
@Configuration
@EnableConfigurationProperties(WxMpProperties.class)
public class WxMpConfiguration {
private final LogHandler logHandler;
private final NullHandler nullHandler;
private final KfSessionHandler kfSessionHandler;
private final StoreCheckNotifyHandler storeCheckNotifyHandler;
private final LocationHandler locationHandler;
private final MenuHandler menuHandler;
private final MsgHandler msgHandler;
private final UnsubscribeHandler unsubscribeHandler;
private final SubscribeHandler subscribeHandler;
private final ScanHandler scanHandler;
private final WxMpProperties properties;
@Bean
public WxMpService wxMpService() {
// 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
final List<WxMpProperties.MpConfig> configs = properties.getConfigs();
if (configs == null) {
throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
}
WxMpService service = new WxMpServiceImpl();
service.setMultiConfigStorages(configs
.stream().map(a -> {
WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(a.getAppId());
configStorage.setSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
return configStorage;
}).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
return service;
}
@Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 接收客服会话管理事件
newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
.handler(this.kfSessionHandler).end();
// 门店审核事件
newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
// 自定义菜单事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
// 点击菜单连接事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
// 取消关注事件
newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
// 上报地理位置事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();
// 接收地理位置消息
newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end();
// 扫码事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();
return newRouter;
}
}
package com.qkdata.biz.wx.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* wechat mp properties
*
* @author Binary Wang(https://github.com/binarywang)
*/
@Data
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {
private List<MpConfig> configs;
@Data
public static class MpConfig {
/**
* 设置微信公众号的appid
*/
private String appId;
/**
* 设置微信公众号的app secret
*/
private String secret;
/**
* 设置微信公众号的token
*/
private String token;
/**
* 设置微信公众号的EncodingAESKey
*/
private String aesKey;
}
}
package com.qkdata.biz.wx.controller;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/wx/jsapi")
public class WxJsApiController {
@Autowired
private WxMpService wxMpService;
@GetMapping("/signature")
public WxJsapiSignature getSignature(@RequestParam String url) throws WxErrorException {
return wxMpService.createJsapiSignature(url);
}
}
package com.qkdata.biz.wx.controller;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/wx/portal")
public class WxPortalController {
private final WxMpService wxService;
private final WxMpMessageRouter messageRouter;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(
@RequestParam(name = "signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature,
timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
if (wxService.checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法请求";
}
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(
@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!wxService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = null;
if (encType == null) {
// 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toXml();
} else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
}
log.debug("\n组装回复信息:{}", out);
return out;
}
private WxMpXmlOutMessage route(WxMpXmlMessage message) {
try {
return this.messageRouter.route(message);
} catch (Exception e) {
log.error("路由消息时出现异常!", e);
}
return null;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public abstract class AbstractHandler implements WxMpMessageHandler {
protected Logger logger = LoggerFactory.getLogger(getClass());
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class KfSessionHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
//TODO 对会话做处理
return null;
}
}
package com.qkdata.biz.wx.handler;
import com.qkdata.biz.wx.buider.TextBuilder;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class LocationHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) {
//TODO 接收处理用户发送的地理位置消息
try {
String content = "感谢反馈,您的的地理位置已收到!";
return new TextBuilder().build(content, wxMessage, null);
} catch (Exception e) {
this.logger.error("位置消息接收处理失败", e);
return null;
}
}
//上报地理位置事件
this.logger.info("上报地理位置,纬度 : {},经度 : {},精度 : {}",
wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision()));
//TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用
return null;
}
}
package com.qkdata.biz.wx.handler;
import com.qkdata.biz.wx.utils.JsonUtils;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class LogHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
this.logger.info("\n接收到请求消息,内容:{}", JsonUtils.toJson(wxMessage));
return null;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.EventType;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class MenuHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) {
String msg = String.format("type:%s, event:%s, key:%s",
wxMessage.getMsgType(), wxMessage.getEvent(),
wxMessage.getEventKey());
if (EventType.VIEW.equals(wxMessage.getEvent())) {
return null;
}
return WxMpXmlOutMessage.TEXT().content(msg)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
}
}
package com.qkdata.biz.wx.handler;
import com.qkdata.biz.wx.buider.TextBuilder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Component;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
@Slf4j
public class MsgHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) {
if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
//TODO 可以选择将消息保存到本地
}
String nickName = getNickNameByOpenId(wxMessage.getFromUser(),weixinService);
try {
return new TextBuilder().build("您好,"+nickName+",欢迎关注本公众号。", wxMessage, weixinService);
} catch (Exception e) {
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(e.getMessage())
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
return m;
}
}
private String getNickNameByOpenId(String openId,WxMpService wxMpService) {
String nickName = "";
try {
WxMpUser user = wxMpService.getUserService().userInfo(openId,"zh_CN");
nickName = user.getNickname();
} catch (WxErrorException e) {
log.error(e.getMessage(),e);
}
return nickName;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class NullHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
return null;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class ScanHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map,
WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
// 扫码事件处理
return null;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 门店审核事件处理
*
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class StoreCheckNotifyHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
// TODO 处理门店审核事件
return null;
}
}
package com.qkdata.biz.wx.handler;
import com.qkdata.biz.wx.buider.TextBuilder;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class SubscribeHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) throws WxErrorException {
this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUser());
try {
WxMpXmlOutMessage responseResult = this.handleSpecial(wxMessage);;
if (responseResult != null) {
return responseResult;
}
return new TextBuilder().build("感谢您关注本公众号", wxMessage, weixinService);
} catch (WxErrorException e) {
if (e.getError().getErrorCode() == 48001) {
this.logger.info("该公众号没有获取用户信息权限!");
}
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
}
return null;
}
/**
* 处理特殊请求,比如如果是扫码进来的,可以做相应处理
*/
private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
throws Exception {
//TODO
return null;
}
}
package com.qkdata.biz.wx.handler;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
@Component
public class UnsubscribeHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
String openId = wxMessage.getFromUser();
this.logger.info("取消关注用户 OPENID: " + openId);
// TODO 可以更新本地数据库为取消关注状态
return null;
}
}
package com.qkdata.biz.wx.manager.controller;
import com.qkdata.biz.wx.manager.form.MaterialFileDeleteForm;
import com.qkdata.common.base.exception.BusinessException;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.material.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* 微信公众号素材管理
* 参考官方文档:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html
* 参考WxJava开发文档:https://github.com/Wechat-Group/WxJava/wiki/MP_永久素材管理
*/
@Api
@RestController
@RequestMapping("/api/wx/manage/wxAssets")
@RequiredArgsConstructor
public class WxAssetsManageController {
Logger logger = LoggerFactory.getLogger(this.getClass());
public static final int PAGE_SIZE_SMALL = 20;
public static final int PAGE_SIZE_MEDIUM = 50;
private final WxMpService wxService;
/**
* 获取素材总数
* @return
* @throws WxErrorException
*/
@GetMapping("/materialCount")
public WxMpMaterialCountResult materialCount() throws WxErrorException {
WxMpMaterialCountResult res = wxService.getMaterialService().materialCount();
return res;
}
/**
* 根据类别分页获取非图文素材列表
* @param type
* @param page
* @return
* @throws WxErrorException
*/
@GetMapping("/materialFileBatchGet")
@RequiresPermissions("wx:material:list")
public WxMpMaterialFileBatchGetResult materialFileBatchGet(@RequestParam(defaultValue = "image") String type,
@RequestParam(defaultValue = "1") int page) {
int offset=(page-1)* PAGE_SIZE_SMALL;
try {
WxMpMaterialFileBatchGetResult res = wxService.getMaterialService().materialFileBatchGet(type,offset, PAGE_SIZE_SMALL);
return res;
}catch (WxErrorException e){
throw new BusinessException(e.getMessage());
}
}
/**
* 分页获取图文素材列表
* @param page
* @return
* @throws WxErrorException
*/
@GetMapping("/materialNewsBatchGet")
@RequiresPermissions("wx:material:list")
public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(@RequestParam(defaultValue = "1") int page) throws WxErrorException {
int offset=(page-1)*PAGE_SIZE_SMALL;
WxMpMaterialNewsBatchGetResult res = wxService.getMaterialService().materialNewsBatchGet(offset, PAGE_SIZE_SMALL);
return res;
}
/**
* 添加图文永久素材
* @param mpMaterialNewsArticle
* @return
* @throws WxErrorException
*/
@PostMapping("/materialNewsUpload")
@RequiresPermissions("wx:material:save")
public WxMpMaterialUploadResult materialNewsUpload(@RequestBody WxMpMaterialNews.WxMpMaterialNewsArticle mpMaterialNewsArticle) throws WxErrorException {
WxMpMaterialNews wxMpMaterialNewsSingle = new WxMpMaterialNews();
mpMaterialNewsArticle.setShowCoverPic(true);
wxMpMaterialNewsSingle.addArticle(mpMaterialNewsArticle);
WxMpMaterialUploadResult res = wxService.getMaterialService().materialNewsUpload(wxMpMaterialNewsSingle);
return res;
}
/**
* 添加多媒体永久素材
* @param file
* @param fileName
* @param mediaType
* @return
* @throws WxErrorException
* @throws IOException
*/
@PostMapping("/materialFileUpload")
@RequiresPermissions("wx:material:save")
public String materialFileUpload(MultipartFile file, String fileName, String mediaType) throws WxErrorException, IOException {
if(file==null)throw new BusinessException("文件不得为空");
String originalFilename=file.getOriginalFilename();
File tempFile = File.createTempFile("tmp", originalFilename.substring(originalFilename.lastIndexOf(".")));
file.transferTo(tempFile);
WxMpMaterial wxMaterial = new WxMpMaterial();
wxMaterial.setFile(tempFile);
wxMaterial.setName(fileName);
WxMpMaterialUploadResult res = wxService.getMaterialService().materialFileUpload(mediaType,wxMaterial);
tempFile.deleteOnExit();
return "ok";
}
/**
* 删除素材
* @param form
* @return
* @throws WxErrorException
* @throws IOException
*/
@PostMapping("/materialDelete")
// @RequiresPermissions("wx:material:delete")
public boolean materialDelete(@RequestBody MaterialFileDeleteForm form) throws WxErrorException, IOException {
boolean res = wxService.getMaterialService().materialDelete(form.getMediaId());
return true;
}
}
package com.qkdata.biz.wx.manager.controller;
import io.swagger.annotations.Api;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.menu.WxMpMenu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Api
@RestController
@RequestMapping("/api/wx/manage/wxMenu")
public class WxMenuManagerController {
@Autowired
private WxMpService wxMpService;
/**
* 获取公众号菜单
*/
@GetMapping("/getMenu")
public WxMpMenu getMenu() throws WxErrorException {
WxMpMenu wxMpMenu = wxMpService.getMenuService().menuGet();
return wxMpMenu;
}
/**
* 创建、更新菜单
*/
@PostMapping("/updateMenu")
public void updateMenu(@RequestBody WxMenu wxMenu) throws WxErrorException{
wxMpService.getMenuService().menuCreate(wxMenu);
}
}
package com.qkdata.biz.wx.manager.controller;
import com.qkdata.biz.wx.manager.form.TemplateMsgParam;
import io.swagger.annotations.Api;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api/wx/manage/templateMsg")
@Api
public class WxTemplateMsgController {
@Autowired
private WxMpService wxMpService;
@Value("${wx.mp.configs[0].templateId}")
private String templateId;
@PostMapping("/send")
public String sendTemplateMsg(@RequestBody TemplateMsgParam param) throws WxErrorException {
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(param.getToUser())
.templateId(templateId)
.url(param.getUrl())
.build();
for (Map.Entry<String,String> entry : param.getParams().entrySet()){
templateMessage.addData(new WxMpTemplateData(entry.getKey(),entry.getValue()));
}
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
return "ok";
}
}
package com.qkdata.biz.wx.manager.form;
import com.qkdata.biz.wx.utils.JsonUtils;
public class MaterialFileDeleteForm {
String mediaId;
@Override
public String toString() {
return JsonUtils.toJson(this);
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
}
package com.qkdata.biz.wx.manager.form;
import com.google.common.collect.Maps;
import lombok.Data;
import java.util.Map;
@Data
public class TemplateMsgParam {
private String toUser;
private String url;
private Map<String,String> params = Maps.newConcurrentMap();
}
package com.qkdata.biz.wx.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* @author Binary Wang(https://github.com/binarywang)
*/
public class JsonUtils {
public static String toJson(Object obj) {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
return gson.toJson(obj);
}
}
...@@ -72,3 +72,11 @@ jwt: ...@@ -72,3 +72,11 @@ jwt:
aud: backend-java aud: backend-java
exp: 24 exp: 24
wx:
mp:
configs:
- appid: wxe97fd2b45e0951b3
secret: b0b25c67b35f05c949fa04ef9cd07c48
token: jingxiang
aesKey:
templateId: h7BghfwRb7P-2FeR8qbeS5c2EGQqA6fXO_I9FpwFGi8
\ No newline at end of file
...@@ -36,7 +36,6 @@ INSERT INTO `sys_menu` VALUES (2, 1, '管理员列表', 'api/sys/user', NULL, 1, ...@@ -36,7 +36,6 @@ INSERT INTO `sys_menu` VALUES (2, 1, '管理员列表', 'api/sys/user', NULL, 1,
INSERT INTO `sys_menu` VALUES (3, 1, '角色管理', 'api/sys/role', NULL, 1, 'role', 2); INSERT INTO `sys_menu` VALUES (3, 1, '角色管理', 'api/sys/role', NULL, 1, 'role', 2);
INSERT INTO `sys_menu` VALUES (4, 1, '菜单管理', 'api/sys/menu', NULL, 1, 'menu', 3); INSERT INTO `sys_menu` VALUES (4, 1, '菜单管理', 'api/sys/menu', NULL, 1, 'menu', 3);
INSERT INTO `sys_menu` VALUES (6, 0, '微信管理', NULL, NULL, 0, 'el-icon-s-promotion', 1); INSERT INTO `sys_menu` VALUES (6, 0, '微信管理', NULL, NULL, 0, 'el-icon-s-promotion', 1);
INSERT INTO `sys_menu` VALUES (7, 0, '内容管理', '', '', 0, 'el-icon-document-copy', 2);
INSERT INTO `sys_menu` VALUES (9, 0, '日志报表', '', '', 0, 'el-icon-s-order', 4); INSERT INTO `sys_menu` VALUES (9, 0, '日志报表', '', '', 0, 'el-icon-s-order', 4);
INSERT INTO `sys_menu` VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0); INSERT INTO `sys_menu` VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0);
INSERT INTO `sys_menu` VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0); INSERT INTO `sys_menu` VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0);
...@@ -56,21 +55,6 @@ INSERT INTO `sys_menu` VALUES (32, 6, '公众号菜单', 'api/wx/wx-menu', '', 1 ...@@ -56,21 +55,6 @@ INSERT INTO `sys_menu` VALUES (32, 6, '公众号菜单', 'api/wx/wx-menu', '', 1
INSERT INTO `sys_menu` VALUES (33, 6, '图文素材', 'api/wx/material-news', '', 1, 'log', 0); INSERT INTO `sys_menu` VALUES (33, 6, '图文素材', 'api/wx/material-news', '', 1, 'log', 0);
INSERT INTO `sys_menu` VALUES (34, 6, '媒体素材', 'api/wx/material-file', NULL, 1, 'log', 0); INSERT INTO `sys_menu` VALUES (34, 6, '媒体素材', 'api/wx/material-file', NULL, 1, 'log', 0);
INSERT INTO `sys_menu` VALUES (35, 34, '新增', '', 'wx:material:save', 2, '', 0); INSERT INTO `sys_menu` VALUES (35, 34, '新增', '', 'wx:material:save', 2, '', 0);
INSERT INTO `sys_menu` VALUES (41, 7, '文章公告', 'api/wx/article', NULL, 1, 'config', 6);
INSERT INTO `sys_menu` VALUES (42, 41, '查看', NULL, 'wx:article:list,wx:article:info', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (43, 41, '新增', NULL, 'wx:article:save', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (44, 41, '修改', NULL, 'wx:article:update', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (45, 41, '删除', NULL, 'wx:article:delete', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (71, 6, '消息模板', 'api/wx/msgtemplate', NULL, 1, 'config', 6);
INSERT INTO `sys_menu` VALUES (72, 71, '查看', NULL, 'wx:msgtemplate:list,wx:msgtemplate:info', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (73, 71, '新增', NULL, 'wx:msgtemplate:save', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (74, 71, '修改', NULL, 'wx:msgtemplate:update', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (75, 71, '删除', NULL, 'wx:msgtemplate:delete', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (81, 9, '模版消息发送记录', 'api/wx/templatemsglog', NULL, 1, 'config', 6);
INSERT INTO `sys_menu` VALUES (82, 81, '查看', NULL, 'wx:templatemsglog:list,wx:templatemsglog:info', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (83, 81, '新增', NULL, 'wx:templatemsglog:save', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (84, 81, '修改', NULL, 'wx:templatemsglog:update', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (85, 81, '删除', NULL, 'wx:templatemsglog:delete', 2, NULL, 6);
INSERT INTO `sys_menu` VALUES (99, 32, '更新公众号菜单', '', 'wx:menu:save', 2, '', 0); INSERT INTO `sys_menu` VALUES (99, 32, '更新公众号菜单', '', 'wx:menu:save', 2, '', 0);
INSERT INTO `sys_menu` VALUES (100, 33, '图文素材-查看', '', 'wx:material:list', 2, '', 0); INSERT INTO `sys_menu` VALUES (100, 33, '图文素材-查看', '', 'wx:material:list', 2, '', 0);
INSERT INTO `sys_menu` VALUES (101, 33, '图文素材-新增', '', 'wx:material:save', 2, '', 0); INSERT INTO `sys_menu` VALUES (101, 33, '图文素材-新增', '', 'wx:material:save', 2, '', 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment