weixin-mp & pay
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<assembly>
|
||||
<id>bin</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>*.sh</include>
|
||||
<include>*.bat</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target/classes</directory>
|
||||
<outputDirectory>/conf</outputDirectory>
|
||||
<includes>
|
||||
<include>*.properties</include>
|
||||
<include>*.xml</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.response.BaseResponse;
|
||||
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 BaseResponse 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 = XStream.get();
|
||||
xstream.processAnnotations(messageClass);
|
||||
xstream.alias("xml", messageClass);
|
||||
return execute(xstream.fromXML(msg, messageClass)).toXml();
|
||||
}
|
||||
return execute((M) message).toXml();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
|
||||
/**
|
||||
* 返回空白消息
|
||||
*
|
||||
* @className BlankAction
|
||||
* @author jy.hu
|
||||
* @date 2014年10月2日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class BlankAction<M extends BaseMessage> extends AbstractAction<M> {
|
||||
|
||||
@Override
|
||||
public TextResponse execute(M inMessage) {
|
||||
return new TextResponse("", inMessage);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.response.TextResponse;
|
||||
import com.foxinmy.weixin4j.msg.BaseMessage;
|
||||
|
||||
/**
|
||||
* 显示调试信息
|
||||
*
|
||||
* @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 TextResponse execute(M message) {
|
||||
return new TextResponse(message.toString(), message);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.msg.ImageMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 图片消息响应
|
||||
*
|
||||
* @className ImageAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.ImageMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.image)
|
||||
public class ImageAction extends DebugAction<ImageMessage> {
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.msg.LinkMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 链接消息响应
|
||||
*
|
||||
* @className LinkAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.LinkMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.link)
|
||||
public class LinkAction extends DebugAction<LinkMessage> {
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.msg.LocationMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 地理位置响应
|
||||
*
|
||||
* @className LocationAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.LocationMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.location)
|
||||
public class LocationAction extends DebugAction<LocationMessage> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
普通消息对应的Action
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.mp.response.TextResponse;
|
||||
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 TextResponse execute(TextMessage inMessage) {
|
||||
return new TextResponse("Hello World!", inMessage);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.msg.VideoMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 视频消息响应
|
||||
*
|
||||
* @className VideoAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.VideoMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.video)
|
||||
public class VideoAction extends DebugAction<VideoMessage> {
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
import com.foxinmy.weixin4j.msg.VoiceMessage;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 语音消息响应
|
||||
*
|
||||
* @className VoiceAction
|
||||
* @author jy
|
||||
* @date 2014年10月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.msg.VoiceMessage
|
||||
*/
|
||||
@Action(msgType = MessageType.voice)
|
||||
public class VoiceAction extends DebugAction<VoiceMessage> {
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.mp.action;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
/**
|
||||
* 消息处理接口
|
||||
*
|
||||
* @className Action
|
||||
* @author jy.hu
|
||||
* @date 2014年10月2日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.action.AbstractAction
|
||||
* @see com.foxinmy.weixin4j.mp.action.BlankAction
|
||||
* @see com.foxinmy.weixin4j.mp.action.DebugAction
|
||||
*/
|
||||
public interface WeixinAction {
|
||||
public String execute(String msg) throws DocumentException;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
事件消息对应的Action
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.foxinmy.weixin4j.mp.action.event;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.DebugAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.Action;
|
||||
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> {
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.foxinmy.weixin4j.mp.mapping;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
|
||||
/**
|
||||
* 获取默认的Mapping Key 如text,event_click
|
||||
*
|
||||
* @className AbstractActionMapping
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public abstract class AbstractActionMapping implements ActionMapping {
|
||||
|
||||
protected String getMappingKey(String xmlMsg) throws DocumentException {
|
||||
Document doc = DocumentHelper.parseText(xmlMsg);
|
||||
String msgType = doc.selectSingleNode("/xml/MsgType").getStringValue();
|
||||
if (msgType.equalsIgnoreCase(MessageType.event.name())) {
|
||||
msgType += "_"
|
||||
+ doc.selectSingleNode("/xml/Event").getStringValue();
|
||||
}
|
||||
return msgType.toLowerCase();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.foxinmy.weixin4j.mp.mapping;
|
||||
|
||||
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 {};
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.foxinmy.weixin4j.mp.mapping;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.WeixinAction;
|
||||
|
||||
/**
|
||||
* 可扩展的Mapping接口
|
||||
*
|
||||
* @className ActionMapping
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public interface ActionMapping {
|
||||
public WeixinAction getAction(String xmlMsg) throws DocumentException;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.foxinmy.weixin4j.mp.mapping;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.action.WeixinAction;
|
||||
import com.foxinmy.weixin4j.type.EventType;
|
||||
import com.foxinmy.weixin4j.type.MessageType;
|
||||
import com.foxinmy.weixin4j.util.ClassUtil;
|
||||
|
||||
/**
|
||||
* 注解实现的Mapping
|
||||
*
|
||||
* @className AnnotationActionMapping
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.mapping.Action
|
||||
*/
|
||||
public class AnnotationActionMapping extends AbstractActionMapping {
|
||||
private final Map<String, WeixinAction> actionMap;
|
||||
|
||||
public AnnotationActionMapping() {
|
||||
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 xmlMsg) throws DocumentException {
|
||||
String key = getMappingKey(xmlMsg);
|
||||
return actionMap.get(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
微信服务netty启动类
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.foxinmy.weixin4j.mp.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.mp.action.WeixinAction;
|
||||
import com.foxinmy.weixin4j.mp.mapping.ActionMapping;
|
||||
|
||||
public class WeixinServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final ActionMapping actionMapping;
|
||||
|
||||
public WeixinServerHandler(ActionMapping actionMapping) {
|
||||
this.actionMapping = actionMapping;
|
||||
}
|
||||
|
||||
@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);
|
||||
WeixinAction action = actionMapping.getAction(xmlMsg);
|
||||
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();
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.foxinmy.weixin4j.mp.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;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.mapping.ActionMapping;
|
||||
import com.foxinmy.weixin4j.mp.mapping.AnnotationActionMapping;
|
||||
|
||||
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final ActionMapping actionMapping;
|
||||
|
||||
public WeixinServerInitializer() {
|
||||
this.actionMapping = new AnnotationActionMapping();
|
||||
}
|
||||
|
||||
@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(actionMapping));
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.foxinmy.weixin4j.mp.statrup;
|
||||
|
||||
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;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.server.WeixinServerInitializer;
|
||||
|
||||
/**
|
||||
* 微信服务启动程序
|
||||
*
|
||||
* @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 netty = ResourceBundle.getBundle("netty");
|
||||
port = Integer.parseInt(netty.getString("port"));
|
||||
workerThreads = Integer.parseInt(netty.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,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- For assistance related to logback-translator or configuration -->
|
||||
<!-- files in general, please contact the logback user mailing list -->
|
||||
<!-- at http://www.qos.ch/mailman/listinfo/logback-user -->
|
||||
<!-- -->
|
||||
<!-- For professional support please see -->
|
||||
<!-- http://www.qos.ch/shop/products/professionalSupport -->
|
||||
<!-- -->
|
||||
<configuration>
|
||||
|
||||
<!-- 控制台输出日志 -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 文件输出指定项目日志 -->
|
||||
<appender name="file"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender -->
|
||||
<!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy -->
|
||||
<!--for further documentation -->
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>/tmp/weixin/log/weixin.%d{yyyy-MM-dd}.log
|
||||
</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步输出指定项目日志 -->
|
||||
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
|
||||
<queueSize>512</queueSize>
|
||||
<!-- 添加附加的appender,最多只能添加一个 -->
|
||||
<appender-ref ref="file" />
|
||||
</appender>
|
||||
|
||||
<logger name="org.apache" level="INFO">
|
||||
<appender-ref ref="stdout" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework" level="INFO">
|
||||
<appender-ref ref="stdout" />
|
||||
</logger>
|
||||
|
||||
<logger name="com.foxinmy.weixin4j" level="INFO">
|
||||
<appender-ref ref="async" />
|
||||
</logger>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,2 @@
|
||||
port=8090
|
||||
workerThreads=20
|
||||
@@ -0,0 +1,15 @@
|
||||
# \u516c\u4f17\u53f7\u4fe1\u606f
|
||||
account={"appId":"wx4ab8f8de58159a57","appSecret":"1d4eb0f4bf556aaed539f30ed05ca795",\
|
||||
"token":"\u5f00\u653e\u8005\u7684token \u975e\u5fc5\u987b","openId":"\u516c\u4f17\u53f7\u7684openid \u975e\u5fc5\u987b",\
|
||||
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7",\
|
||||
"partnerId":"\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7","partnerKey":"\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key",\
|
||||
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5"}
|
||||
|
||||
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
|
||||
token_path=/tmp/weixin/token
|
||||
# \u4e8c\u7ef4\u7801\u4fdd\u5b58\u8def\u5f84
|
||||
qr_path=/tmp/weixin/qr
|
||||
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
|
||||
media_path=/tmp/weixin/media
|
||||
# \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84
|
||||
bill_path=/tmp/weixin/bill
|
||||
@@ -0,0 +1,142 @@
|
||||
ulimit -n 110000
|
||||
#JDK home
|
||||
JAVA_HOME="/usr/local/java/"
|
||||
|
||||
#executing user
|
||||
RUNNING_USER=root
|
||||
|
||||
#Run home
|
||||
APP_HOME="/usr/local/weixin/weixin-service"
|
||||
|
||||
#main class
|
||||
APP_MAINCLASS=com.foxinmy.weixin4j.mp.startup.WeixinServiceBootstrap
|
||||
|
||||
#classpath
|
||||
CLASSPATH=$APP_HOME/classes
|
||||
for i in "$APP_HOME"/lib/*.jar; do
|
||||
CLASSPATH="$CLASSPATH":"$i"
|
||||
done
|
||||
|
||||
CLASSPATH="$CLASSPATH":"$APP_HOME"/conf
|
||||
|
||||
#jvm options
|
||||
JAVA_OPTS="-Xms256m -Xmx512m -Djava.awt.headless=true -XX:MaxPermSize=128m -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+DisableExplicitGC -Xnoclassgc -Xverify:none"
|
||||
|
||||
#psid
|
||||
psid=0
|
||||
|
||||
checkpid() {
|
||||
javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`
|
||||
|
||||
if [ -n "$javaps" ]; then
|
||||
psid=`echo $javaps | awk '{print $1}'`
|
||||
else
|
||||
psid=0
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#startup
|
||||
###################################
|
||||
start() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "================================"
|
||||
echo "warn: $APP_MAINCLASS already started! (pid=$psid)"
|
||||
echo "================================"
|
||||
else
|
||||
echo -n "Starting $APP_MAINCLASS ..."
|
||||
# JAVA_CMD="nohup $JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS >/dev/null 2>&1 &"
|
||||
JAVA_CMD="$JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS &"
|
||||
su - $RUNNING_USER -c "$JAVA_CMD"
|
||||
checkpid
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "(pid=$psid) [OK]"
|
||||
else
|
||||
echo "[Failed]"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#stop
|
||||
###################################
|
||||
stop() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "
|
||||
su - $RUNNING_USER -c "kill -9 $psid"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "[OK]"
|
||||
else
|
||||
echo "[Failed]"
|
||||
fi
|
||||
|
||||
checkpid
|
||||
if [ $psid -ne 0 ]; then
|
||||
stop
|
||||
fi
|
||||
else
|
||||
echo "================================"
|
||||
echo "warn: $APP_MAINCLASS is not running"
|
||||
echo "================================"
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#status
|
||||
###################################
|
||||
status() {
|
||||
checkpid
|
||||
|
||||
if [ $psid -ne 0 ]; then
|
||||
echo "$APP_MAINCLASS is running! (pid=$psid)"
|
||||
else
|
||||
echo "$APP_MAINCLASS is not running"
|
||||
fi
|
||||
}
|
||||
|
||||
###################################
|
||||
#info
|
||||
###################################
|
||||
info() {
|
||||
echo "System Information:"
|
||||
echo "****************************"
|
||||
echo `head -n 1 /etc/issue`
|
||||
echo `uname -a`
|
||||
echo
|
||||
echo "JAVA_HOME=$JAVA_HOME"
|
||||
echo `$JAVA_HOME/bin/java -version`
|
||||
echo
|
||||
echo "APP_HOME=$APP_HOME"
|
||||
echo "APP_MAINCLASS=$APP_MAINCLASS"
|
||||
echo "****************************"
|
||||
}
|
||||
|
||||
###################################
|
||||
#access only 1 argument:{start|stop|restart|status|info}
|
||||
###################################
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'restart')
|
||||
stop
|
||||
start
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
'info')
|
||||
info
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|info}"
|
||||
exit 1
|
||||
esac
|
||||
exit 0
|
||||
Reference in New Issue
Block a user