引入netty服务模块并支持消息分发
This commit is contained in:
parent
0cc69cff01
commit
083147c270
3
src/main/java/com/foxinmy/weixin4j/README.md
Normal file
3
src/main/java/com/foxinmy/weixin4j/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
config.properties包含了app跟接口url信息
|
||||
|
||||
WeixinProxy类为接口调用入口
|
||||
@ -0,0 +1,53 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
import com.foxinmy.weixin4j.xml.XStream;
|
||||
|
||||
/**
|
||||
* 继承的类需实现execute(M inMessage)
|
||||
*
|
||||
* @className AbstractAction
|
||||
* @author jy
|
||||
* @date 2014年10月12日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public abstract class AbstractAction<M extends BaseMessage> implements
|
||||
WeixinAction {
|
||||
|
||||
public abstract String execute(M inMessage);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String execute(String msg) throws DocumentException {
|
||||
BaseMessage message = MessageUtil.xml2msg(msg);
|
||||
if (message == null) {
|
||||
Class<M> messageClass = getGenericType();
|
||||
XStream xstream = new XStream();
|
||||
xstream.ignoreUnknownElements();
|
||||
xstream.autodetectAnnotations(true);
|
||||
xstream.processAnnotations(messageClass);
|
||||
xstream.alias("xml", messageClass);
|
||||
return execute(xstream.fromXML(msg, messageClass));
|
||||
}
|
||||
return execute((M) message);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<M> getGenericType() {
|
||||
Class<M> clazz = null;
|
||||
Type type = getClass().getGenericSuperclass();
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = ((ParameterizedType) type);
|
||||
Type[] args = ptype.getActualTypeArguments();
|
||||
clazz = (Class<M>) args[0];
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/foxinmy/weixin4j/action/Action.java
Normal file
26
src/main/java/com/foxinmy/weixin4j/action/Action.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
/**
|
||||
* 标注
|
||||
* @className Action
|
||||
* @author jy
|
||||
* @date 2014年10月12日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public @interface Action {
|
||||
|
||||
MessageType msgType();
|
||||
|
||||
EventType[] eventType() default {};
|
||||
}
|
||||
23
src/main/java/com/foxinmy/weixin4j/action/BlankAction.java
Normal file
23
src/main/java/com/foxinmy/weixin4j/action/BlankAction.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
|
||||
/**
|
||||
* 返回空白消息
|
||||
*
|
||||
* @className BlankAction
|
||||
* @author jy.hu
|
||||
* @date 2014年10月2日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public abstract class BlankAction<M extends BaseMessage> extends
|
||||
AbstractAction<M> {
|
||||
|
||||
private final String BLANK = "";
|
||||
|
||||
@Override
|
||||
public String execute(M message) {
|
||||
return BLANK;
|
||||
}
|
||||
}
|
||||
23
src/main/java/com/foxinmy/weixin4j/action/DebugAction.java
Normal file
23
src/main/java/com/foxinmy/weixin4j/action/DebugAction.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
import com.foxinmy.weixin4j.msg.TextMessage;
|
||||
|
||||
/**
|
||||
* 显示调试信息
|
||||
*
|
||||
* @className DebugAction
|
||||
* @author jy
|
||||
* @date 2014年10月8日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public abstract class DebugAction<M extends BaseMessage> extends
|
||||
AbstractAction<M> {
|
||||
|
||||
@Override
|
||||
public String execute(M message) {
|
||||
BaseMessage response = new TextMessage(message.toString(), message);
|
||||
return response.toXml();
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/foxinmy/weixin4j/action/ImageAction.java
Normal file
18
src/main/java/com/foxinmy/weixin4j/action/ImageAction.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.in.ImageMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 图片消息响应
|
||||
*
|
||||
* @className ImageAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.in.ImageMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.image)
|
||||
public class ImageAction extends DebugAction<ImageMessage> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/foxinmy/weixin4j/action/LinkAction.java
Normal file
18
src/main/java/com/foxinmy/weixin4j/action/LinkAction.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.in.LinkMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 链接消息响应
|
||||
*
|
||||
* @className LinkAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.in.LinkMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.link)
|
||||
public class LinkAction extends DebugAction<LinkMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.in.LocationMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 地理位置响应
|
||||
*
|
||||
* @className LocationAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.in.LocationMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.location)
|
||||
public class LocationAction extends DebugAction<LocationMessage> {
|
||||
|
||||
}
|
||||
1
src/main/java/com/foxinmy/weixin4j/action/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/action/README.md
Normal file
@ -0,0 +1 @@
|
||||
普通消息类型对应的Action
|
||||
@ -0,0 +1,47 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
/**
|
||||
* 用于校验消息安全性
|
||||
*
|
||||
* @className SignatureAction
|
||||
* @author jy
|
||||
* @date 2014年10月24日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
@Action(msgType = MessageType.signature)
|
||||
public class SignatureAction implements WeixinAction {
|
||||
|
||||
@Override
|
||||
public String execute(String uri) throws DocumentException {
|
||||
String[] paths = uri.split("\\?");
|
||||
if (paths == null || paths.length < 2) {
|
||||
return "";
|
||||
}
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(paths[1],
|
||||
false);
|
||||
Map<String, List<String>> parameters = queryDecoder.parameters();
|
||||
String echostr = parameters.containsKey("echostr") ? parameters.get(
|
||||
"echostr").get(0) : null;
|
||||
String timestamp = parameters.containsKey("timestamp") ? parameters
|
||||
.get("timestamp").get(0) : null;
|
||||
String nonce = parameters.containsKey("nonce") ? parameters
|
||||
.get("nonce").get(0) : null;
|
||||
String signature = parameters.containsKey("signature") ? parameters
|
||||
.get("signature").get(0) : null;
|
||||
String token = ConfigUtil.getValue("app_token");
|
||||
return MessageUtil.signature(token, echostr, timestamp, nonce,
|
||||
signature);
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/foxinmy/weixin4j/action/TextAction.java
Normal file
22
src/main/java/com/foxinmy/weixin4j/action/TextAction.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.TextMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 文字消息响应
|
||||
*
|
||||
* @className TextAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.TextMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.text)
|
||||
public class TextAction extends AbstractAction<TextMessage> {
|
||||
|
||||
@Override
|
||||
public String execute(TextMessage inMessage) {
|
||||
return new TextMessage("Hello World!", inMessage).toXml();
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/foxinmy/weixin4j/action/VideoAction.java
Normal file
18
src/main/java/com/foxinmy/weixin4j/action/VideoAction.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.in.VideoMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 视频消息响应
|
||||
*
|
||||
* @className VideoAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.in.VideoMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.video)
|
||||
public class VideoAction extends DebugAction<VideoMessage> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/foxinmy/weixin4j/action/VoiceAction.java
Normal file
18
src/main/java/com/foxinmy/weixin4j/action/VoiceAction.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import com.foxinmy.weixin4j.msg.in.VoiceMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 语音消息响应
|
||||
*
|
||||
* @className VoiceAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.in.VoiceMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.voice)
|
||||
public class VoiceAction extends DebugAction<VoiceMessage> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/foxinmy/weixin4j/action/WeixinAction.java
Normal file
18
src/main/java/com/foxinmy/weixin4j/action/WeixinAction.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.action;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
/**
|
||||
* 消息处理接口
|
||||
*
|
||||
* @className Action
|
||||
* @author jy.hu
|
||||
* @date 2014年10月2日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.action.AbstractAction
|
||||
* @see com.foxinmy.weixin4j.action.BlankAction
|
||||
* @see com.foxinmy.weixin4j.action.DebugAction
|
||||
*/
|
||||
public interface WeixinAction {
|
||||
public String execute(String msg) throws DocumentException;
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.LocationEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 上报地理位置后触发
|
||||
*
|
||||
* @className LocationAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.LocationEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.location })
|
||||
public class LocationAction extends DebugAction<LocationEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.MassEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 群发消息发送动作完成后触发
|
||||
*
|
||||
* @className MassSendAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.MassEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.massendjobfinish })
|
||||
public class MassSendAction extends DebugAction<MassEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 菜单点击click事件时触发
|
||||
*
|
||||
* @className MenuClickAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.click })
|
||||
public class MenuClickAction extends DebugAction<MenuEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 菜单发送地理位置时触发
|
||||
*
|
||||
* @className MenuLocationAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.location_select })
|
||||
public class MenuLocationAction extends DebugAction<MenuLocationEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 菜单发送图片时触发
|
||||
*
|
||||
* @className MenuPhotoAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = {
|
||||
EventType.pic_photo_or_album, EventType.pic_sysphoto,
|
||||
EventType.pic_weixin })
|
||||
public class MenuPhotoAction extends DebugAction<MenuPhotoEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 菜单扫描时触发
|
||||
*
|
||||
* @className MenuScanAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.scancode_push,
|
||||
EventType.scancode_waitmsg })
|
||||
public class MenuScanAction extends DebugAction<MenuScanEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 菜单点击view事件时触发
|
||||
*
|
||||
* @className MenuViewAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.view })
|
||||
public class MenuViewAction extends DebugAction<MenuEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
事件消息类型对应的Action
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.ScanEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 扫描事件时触发
|
||||
*
|
||||
* @className ScanAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.scan })
|
||||
public class ScanAction extends DebugAction<ScanEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 关注时触发
|
||||
*
|
||||
* @className SubscribeAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.subscribe })
|
||||
public class SubscribeAction extends DebugAction<ScribeEventMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 模板消息发送动作完成时触发
|
||||
*
|
||||
* @className TemplateSendAction
|
||||
* @author jy
|
||||
* @date 2014年10月10日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.templatesendjobfinish })
|
||||
public class TemplateSendAction extends
|
||||
DebugAction<TemplatesendjobfinishMessage> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 取消关注时触发
|
||||
*
|
||||
* @className UnsubscribeAction
|
||||
* @author jy
|
||||
* @date 2014年10月10日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.event, eventType = { EventType.unsubscribe })
|
||||
public class UnsubscribeAction extends DebugAction<ScribeEventMessage> {
|
||||
|
||||
}
|
||||
1
src/main/java/com/foxinmy/weixin4j/api/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/api/README.md
Normal file
@ -0,0 +1 @@
|
||||
API的实现
|
||||
1
src/main/java/com/foxinmy/weixin4j/api/token/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/api/token/README.md
Normal file
@ -0,0 +1 @@
|
||||
Token的实现
|
||||
1
src/main/java/com/foxinmy/weixin4j/http/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/http/README.md
Normal file
@ -0,0 +1 @@
|
||||
基于HttpClient封装的针对微信API的HttpRequest
|
||||
1
src/main/java/com/foxinmy/weixin4j/msg/event/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/msg/event/README.md
Normal file
@ -0,0 +1 @@
|
||||
事件消息
|
||||
@ -0,0 +1 @@
|
||||
底部菜单消息
|
||||
1
src/main/java/com/foxinmy/weixin4j/msg/in/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/msg/in/README.md
Normal file
@ -0,0 +1 @@
|
||||
微信push过来的消息
|
||||
1
src/main/java/com/foxinmy/weixin4j/msg/model/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/msg/model/README.md
Normal file
@ -0,0 +1 @@
|
||||
不同的消息类型中的模型
|
||||
1
src/main/java/com/foxinmy/weixin4j/msg/notify/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/msg/notify/README.md
Normal file
@ -0,0 +1 @@
|
||||
客服消息
|
||||
1
src/main/java/com/foxinmy/weixin4j/msg/out/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/msg/out/README.md
Normal file
@ -0,0 +1 @@
|
||||
回复消息
|
||||
1
src/main/java/com/foxinmy/weixin4j/server/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/server/README.md
Normal file
@ -0,0 +1 @@
|
||||
微信服务netty启动类
|
||||
@ -0,0 +1,48 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.foxinmy.weixin4j.action.Action;
|
||||
import com.foxinmy.weixin4j.action.WeixinAction;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
import com.foxinmy.weixin4j.util.ClassUtil;
|
||||
|
||||
public class WeixinActionMapping {
|
||||
private final Map<String, WeixinAction> actionMap;
|
||||
|
||||
public WeixinActionMapping() {
|
||||
actionMap = new HashMap<String, WeixinAction>();
|
||||
Set<Class<?>> weixinActions = ClassUtil.getClasses(WeixinAction.class
|
||||
.getPackage());
|
||||
for (Class<?> clazz : weixinActions) {
|
||||
Action action = clazz.getAnnotation(Action.class);
|
||||
if (action == null) {
|
||||
continue;
|
||||
}
|
||||
WeixinAction weixinAction = null;
|
||||
try {
|
||||
weixinAction = (WeixinAction) clazz.newInstance();
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
MessageType msgType = action.msgType();
|
||||
EventType[] eventTypes = action.eventType();
|
||||
if (eventTypes != null && eventTypes.length > 0) {
|
||||
for (EventType e : eventTypes) {
|
||||
actionMap.put(
|
||||
(msgType.name() + "_" + e.name()).toLowerCase(),
|
||||
weixinAction);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
actionMap.put(msgType.name().toLowerCase(), weixinAction);
|
||||
}
|
||||
}
|
||||
|
||||
public WeixinAction getAction(String key) {
|
||||
return actionMap.get(key.toLowerCase());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.foxinmy.weixin4j.action.WeixinAction;
|
||||
import com.foxinmy.weixin4j.util.MessageUtil;
|
||||
|
||||
public class WeixinServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final WeixinActionMapping weixinActionMapping;
|
||||
|
||||
public WeixinServerHandler(WeixinActionMapping weixinActionMapping) {
|
||||
this.weixinActionMapping = weixinActionMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg)
|
||||
throws DocumentException {
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
FullHttpRequest req = (FullHttpRequest) msg;
|
||||
if (HttpHeaders.is100ContinueExpected(req)) {
|
||||
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
|
||||
return;
|
||||
}
|
||||
String xmlMsg = req.content().toString(StandardCharsets.UTF_8);
|
||||
log.info("\n=================message in=================\n{}",
|
||||
xmlMsg);
|
||||
String key = MessageUtil.getMappingKey(xmlMsg);
|
||||
WeixinAction action = weixinActionMapping.getAction(key);
|
||||
if (action == null) {
|
||||
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
String content = action.execute(xmlMsg);
|
||||
log.info("\n=================message out=================\n{}",
|
||||
content);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
|
||||
OK, Unpooled.copiedBuffer(content, StandardCharsets.UTF_8));
|
||||
response.headers().set(CONTENT_TYPE, "text/plain;charset=utf-8");
|
||||
response.headers().set(CONTENT_LENGTH,
|
||||
response.content().readableBytes());
|
||||
if (!HttpHeaders.isKeepAlive(req)) {
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
} else {
|
||||
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
|
||||
ctx.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
|
||||
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final WeixinActionMapping weixinActionMapping;
|
||||
|
||||
public WeixinServerInitializer() {
|
||||
this.weixinActionMapping = new WeixinActionMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel channel) throws Exception {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new WeixinServerHandler(weixinActionMapping));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.foxinmy.weixin4j.server;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* 微信服务启动程序
|
||||
*
|
||||
* @className WeixinBootstrap
|
||||
* @author jy
|
||||
* @date 2014年10月12日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public final class WeixinServiceBootstrap {
|
||||
|
||||
private final static int port;
|
||||
private final static int workerThreads;
|
||||
static {
|
||||
ResourceBundle config = ResourceBundle
|
||||
.getBundle("com.foxinmy.weixin4j.server.netty");
|
||||
port = Integer.parseInt(config.getString("port"));
|
||||
workerThreads = Integer.parseInt(config.getString("workerThreads"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.option(ChannelOption.SO_BACKLOG, 1024);
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler())
|
||||
.childHandler(new WeixinServerInitializer());
|
||||
Channel ch = b.bind(port).sync().channel();
|
||||
System.err.println("weixin server startup OK:" + port);
|
||||
ch.closeFuture().sync();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
port=8090
|
||||
workerThreads=20
|
||||
5
src/main/java/com/foxinmy/weixin4j/spider/MpWeixin.java
Normal file
5
src/main/java/com/foxinmy/weixin4j/spider/MpWeixin.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.foxinmy.weixin4j.spider;
|
||||
|
||||
public final class MpWeixin {
|
||||
|
||||
}
|
||||
3
src/main/java/com/foxinmy/weixin4j/spider/README.md
Normal file
3
src/main/java/com/foxinmy/weixin4j/spider/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
模拟微信公众平台登陆
|
||||
|
||||
(模拟登录|启用开发者模式|修改服务器配置|修改回调地址|创建自定义菜单....more)
|
||||
1
src/main/java/com/foxinmy/weixin4j/type/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/type/README.md
Normal file
@ -0,0 +1 @@
|
||||
消息类型、事件类型等枚举
|
||||
56
src/main/java/com/foxinmy/weixin4j/util/ClassUtil.java
Normal file
56
src/main/java/com/foxinmy/weixin4j/util/ClassUtil.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.foxinmy.weixin4j.action.WeixinAction;
|
||||
|
||||
public class ClassUtil {
|
||||
|
||||
public static Set<Class<?>> getClasses(Package _package) {
|
||||
ClassLoader classLoader = Thread.currentThread()
|
||||
.getContextClassLoader();
|
||||
String subPath = _package.getName().replace(".", File.separator);
|
||||
URL fullPath = classLoader.getResource(subPath);
|
||||
File dir = new File(fullPath.getPath());
|
||||
return findClasses(dir, _package.getName());
|
||||
}
|
||||
|
||||
private static Set<Class<?>> findClasses(File dir, String packageName) {
|
||||
Set<Class<?>> classes = new HashSet<Class<?>>();
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File file, String name) {
|
||||
return file.isDirectory() || file.getName().endsWith(".class");
|
||||
}
|
||||
});
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
classes.addAll(findClasses(file,
|
||||
packageName + "." + file.getName()));
|
||||
} else {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(packageName + "."
|
||||
+ file.getName().replace(".class", ""));
|
||||
if (clazz.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
classes.add(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ClassNotFoundException,
|
||||
IOException, InstantiationException, IllegalAccessException {
|
||||
System.out.println(getClasses(WeixinAction.class.getPackage()));
|
||||
}
|
||||
}
|
||||
76
src/main/java/com/foxinmy/weixin4j/util/RandomUtil.java
Normal file
76
src/main/java/com/foxinmy/weixin4j/util/RandomUtil.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.foxinmy.weixin4j.util;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 随机码工具类
|
||||
* @className RandomUtil
|
||||
* @author jy
|
||||
* @date 2014年10月22日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class RandomUtil {
|
||||
|
||||
private static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String LETTERCHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String NUMBERCHAR = "0123456789";
|
||||
|
||||
// 返回一个定长的随机字符串(包含数字和大小写字母)
|
||||
public static String generateString(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// 返回一个定长的随机纯数字字符串(只包含数字)
|
||||
public static String generateStringByNumberChar(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(NUMBERCHAR.charAt(random.nextInt(NUMBERCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// 返回一个定长的随机纯字母字符串(只包含大小写字母)
|
||||
public static String generateStringByLetterCharr(int length) {
|
||||
StringBuilder sb = new StringBuilder(length);
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(LETTERCHAR.charAt(random.nextInt(LETTERCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
|
||||
public static String generateLowerString(int length) {
|
||||
return generateStringByLetterCharr(length).toLowerCase();
|
||||
}
|
||||
|
||||
// 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
|
||||
public static String generateUpperString(int length) {
|
||||
return generateStringByLetterCharr(length).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获取UUID字符串(无中划线)
|
||||
*
|
||||
* @return UUID字符串
|
||||
*/
|
||||
public static String getUUID() {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
return uuid.substring(0, 8) + uuid.substring(9, 13)
|
||||
+ uuid.substring(14, 18) + uuid.substring(19, 23)
|
||||
+ uuid.substring(24);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(System.nanoTime());
|
||||
System.out.println(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
1
src/main/java/com/foxinmy/weixin4j/xml/README.md
Normal file
1
src/main/java/com/foxinmy/weixin4j/xml/README.md
Normal file
@ -0,0 +1 @@
|
||||
添加<![CDATA[]>支持
|
||||
49
src/test/java/com/foxinmy/weixin4j/test/msg/MessagePush.java
Normal file
49
src/test/java/com/foxinmy/weixin4j/test/msg/MessagePush.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.foxinmy.weixin4j.test.msg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
|
||||
public class MessagePush {
|
||||
|
||||
private HttpClient httpClient;
|
||||
private final HttpPost httpPost;
|
||||
|
||||
public MessagePush() {
|
||||
this.httpClient = new DefaultHttpClient();
|
||||
ResourceBundle config = ResourceBundle
|
||||
.getBundle("com.foxinmy.weixin4j.server.netty");
|
||||
httpPost = new HttpPost();
|
||||
httpPost.setURI(URI.create(String.format("http://localhost:%s",
|
||||
Integer.parseInt(config.getString("port")))));
|
||||
}
|
||||
|
||||
public String push(String xml) throws WeixinException, IOException {
|
||||
httpPost.setEntity(new StringEntity(xml, StandardCharsets.UTF_8));
|
||||
HttpResponse httpResponse = httpClient.execute(httpPost);
|
||||
StatusLine statusLine = httpResponse.getStatusLine();
|
||||
|
||||
int status = statusLine.getStatusCode();
|
||||
if (status != HttpStatus.SC_OK) {
|
||||
throw new WeixinException(status, "request fail");
|
||||
}
|
||||
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|
||||
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
|
||||
throw new WeixinException(status, "uri moved");
|
||||
}
|
||||
return EntityUtils.toString(httpResponse.getEntity(),
|
||||
StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user