微信公众号(卡券) A Little Demo

1、项目目录:
在这里插入图片描述
二、、pojo相关实体类
1>、图文消息类

package com.winxin.pojo;
 
/**
 * 图文消息
 * @author cdj
 * @date 2018年8月3日 上午8:45:56
 */
public class Article {
	/**
	 * 图文消息名称
	 */
	private String Title;
 
	/**
	 * 图文消息描述
	 */
	private String Description;
 
	/**
	 * 图片链接,支持JPG、PNG格式,<br>
	 * 较好的效果为大图640*320,小图80*80
	 */
	private String PicUrl;
 
	/**
	 * 点击图文消息跳转链接
	 */
	private String Url;
 
	public String getTitle() {
		return Title;
	}
 
	public void setTitle(String title) {
		Title = title;
	}
 
	public String getDescription() {
		return null == Description ? "" : Description;
	}
 
	public void setDescription(String description) {
		Description = description;
	}
 
	public String getPicUrl() {
		return null == PicUrl ? "" : PicUrl;
	}
 
	public void setPicUrl(String picUrl) {
		PicUrl = picUrl;
	}
 
	public String getUrl() {
		return null == Url ? "" : Url;
	}
 
	public void setUrl(String url) {
		Url = url;
	}
 
}

2>、消息体基础类

package com.winxin.pojo;
 
 
/**
 * 消息体基础类
 * @author cdj
 */
public class BaseMessage {
	private String ToUserName;
	private String FromUserName;
	private long CreateTime;
	private String MsgType;
	private long MsgId;
	private int FuncFlag;

	public int getFuncFlag() {
		return FuncFlag;
	}

	public void setFuncFlag(int funcFlag) {
		FuncFlag = funcFlag;
	}

	public String getToUserName() {
		return ToUserName;
	}
	public void setToUserName(String toUserName) {
		ToUserName = toUserName;
	}
	public String getFromUserName() {
		return FromUserName;
	}
	public void setFromUserName(String fromUserName) {
		FromUserName = fromUserName;
	}
	public long getCreateTime() {
		return CreateTime;
	}
	public void setCreateTime(long createTime) {
		CreateTime = createTime;
	}
	public String getMsgType() {
		return MsgType;
	}
	public void setMsgType(String msgType) {
		MsgType = msgType;
	}
	public long getMsgId() {
		return MsgId;
	}
	public void setMsgId(long msgId) {
		MsgId = msgId;
	}
	
}

3>、音乐消息类

package com.winxin.pojo;
 
/**
 * 音乐消息
 * @author cdj
 * @date 2018年8月3日 上午8:46:59
 */
public class Music {
	/**
	 * 音乐名称
	 */
	private String Title;
	
	/**
	 * 音乐描述
	 */
	private String Description;
	
	/**
	 * 音乐链接
	 */
	private String MusicUrl;
	
	/**
	 * 高质量音乐链接,WIFI环境优先使用该链接播放音乐
	 */
	private String HQMusicUrl;
 
	public String getTitle() {
		return Title;
	}
 
	public void setTitle(String title) {
		Title = title;
	}
 
	public String getDescription() {
		return Description;
	}
 
	public void setDescription(String description) {
		Description = description;
	}
 
	public String getMusicUrl() {
		return MusicUrl;
	}
 
	public void setMusicUrl(String musicUrl) {
		MusicUrl = musicUrl;
	}
 
	public String getHQMusicUrl() {
		return HQMusicUrl;
	}
 
	public void setHQMusicUrl(String musicUrl) {
		HQMusicUrl = musicUrl;
	}
 
}

4>、语音消息类

package com.winxin.pojo;
 
/**
 * 语音消息
 * 
 * @author cdj
 * @date 2018年8月3日 上午8:29:49
 */
public class VoiceMessage extends BaseMessage {
	/**
	 * 媒体ID
	 */
	private String MediaId;
	/**
	 * 语音格式
	 */
	private String Format;
 
	public String getMediaId() {
		return MediaId;
	}
 
	public void setMediaId(String mediaId) {
		MediaId = mediaId;
	}
 
	public String getFormat() {
		return Format;
	}
 
	public void setFormat(String format) {
		Format = format;
	}
 
}

5>、文本消息类

package com.winxin.pojo;
 
/**
 * 文本消息类
 * @author cdj
 */
public class TextMessage extends BaseMessage{
	private String Content;
	public String getContent() {
		return Content;
	}
	public void setContent(String content) {
		Content = content;
	}
}

6>、 多图文消息

package com.winxin.pojo;
 
 
import java.util.List;
 
/**
 * 多图文消息,
 * 单图文的时候 Articles 只放一个就行了
 * @author cdj
 * @date 2018年8月3日 上午8:45:22
 */
public class NewsMessage extends BaseMessage {
	/**
	 * 图文消息个数,限制为10条以内
	 */
	private int ArticleCount;
	/**
	 * 多条图文消息信息,默认第一个item为大图
	 */
	private List<Article> Articles;
 
	public int getArticleCount() {
		return ArticleCount;
	}
 
	public void setArticleCount(int articleCount) {
		ArticleCount = articleCount;
	}
 
	public List<Article> getArticles() {
		return Articles;
	}
 
	public void setArticles(List<Article> articles) {
		Articles = articles;
	}
}
 

7>、音乐消息

package com.winxin.pojo;
 
/**
 * 音乐消息
 * @author cdj
 * @date 2018年8月3日 上午8:46:35
 */
public class MusicMessage extends BaseMessage {
	/**
	 * 音乐
	 */
	private Music Music;
 
	public Music getMusic() {
		return Music;
	}
 
	public void setMusic(Music music) {
		Music = music;
	}
}
 

8>、位置消息

package com.winxin.pojo;
 
/**
 * 位置消息
 * @author cdj
 * @date 2018年8月3日 上午8:30:35
 */
public class LocationMessage extends BaseMessage {
	/**
	 * 地理位置维度
	 */
	private String Location_X;
	/**
	 * 地理位置经度
	 */
	private String Location_Y;
 
	/**
	 * 地图缩放大小
	 */
	private String Scale;
 
	/**
	 * 地理位置信息
	 */
	private String Label;
 
	public String getLocation_X() {
		return Location_X;
	}
 
	public void setLocation_X(String location_X) {
		Location_X = location_X;
	}
 
	public String getLocation_Y() {
		return Location_Y;
	}
 
	public void setLocation_Y(String location_Y) {
		Location_Y = location_Y;
	}
 
	public String getScale() {
		return Scale;
	}
 
	public void setScale(String scale) {
		Scale = scale;
	}
 
	public String getLabel() {
		return Label;
	}
 
	public void setLabel(String label) {
		Label = label;
	}
 
}

9>、链接消息接受

package com.winxin.pojo;
 
/**
 * 链接消息接受
 * @author cdj
 * @date 2018年8月3日 上午8:29:06
 */
public class LinkMessage extends BaseMessage {
	/**
	 * 消息标题
	 */
	private String Title;
	/**
	 * 消息描述
	 */
	private String Description;
	/**
	 * 消息链接
	 */
	private String Url;
 
	public String getTitle() {
		return Title;
	}
 
	public void setTitle(String title) {
		Title = title;
	}
 
	public String getDescription() {
		return Description;
	}
 
	public void setDescription(String description) {
		Description = description;
	}
 
	public String getUrl() {
		return Url;
	}
 
	public void setUrl(String url) {
		Url = url;
	}
}

10>、 图片消息事件接受

package com.winxin.pojo;
 
/**
 * 图片消息事件接受
 * @author cdj
 * @date 2018年8月3日 上午8:28:01
 */
public class ImageMessage extends BaseMessage {
	private String picUrl;
	 
	public String getPicUrl() {
		return picUrl;
	}
 
	public void setPicUrl(String picUrl) {
		this.picUrl = picUrl;
	}
 
}

11>、公众号 菜单

package com.winxin.pojo;

public class Menu {
    private Button[] button;  
  
    public Button[] getButton() {  
        return button;  
    }  
  
    public void setButton(Button[] button) {  
        this.button = button;  
    }  
}  

12>、菜单按钮

package com.winxin.pojo;

public class Button {
    private String name;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
} 

13>、一级菜单按钮

package com.winxin.pojo;

public class CommonButton extends Button {
    private String type;  
    private String key;  
  
    public String getType() {  
        return type;  
    }  
  
    public void setType(String type) {  
        this.type = type;  
    }  
  
    public String getKey() {  
        return key;  
    }  
  
    public void setKey(String key) {  
        this.key = key;  
    }  
}  

14>、不同级菜单按钮

package com.winxin.pojo;

public class ComplexButton extends Button {
    private Button[] sub_button;  
  
    public Button[] getSub_button() {  
        return sub_button;  
    }  
  
    public void setSub_button(Button[] sub_button) {  
        this.sub_button = sub_button;  
    }  
}  

三、工具类
1、通用工具类

package com.winxin.util;

import com.winxin.pojo.Token;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import sun.net.www.protocol.http.HttpURLConnection;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.rmi.ConnectException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述: 通用工具类
 *
 * @description
 * @time 2019/3/18
 */
public class CommonUtil {

	public static volatile Map<String, Token> tokenMap = new ConcurrentHashMap<String, Token>();
	//微信通用接口凭证
	private static Token token = null;

	/**
	 * 发送https请求
	 *
	 * @param requestUrl    请求地址
	 * @param requestMethod 请求方式(GET、POST)
	 * @param outputStr     提交的数据
	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
	 */
	public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
		JSONObject jsonObject = null;
		try {
			// 创建SSLContext对象,并使用我们指定的信任管理器初始化
			TrustManager[] tm = {new MyX509TrustManager()};
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中得到SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();
			URL url = new URL(requestUrl);
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setSSLSocketFactory(ssf);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			// 设置请求方式(GET/POST)
			conn.setRequestMethod(requestMethod);
			// 当outputStr不为null时向输出流写数据
			if (null != outputStr) {
				OutputStream outputStream = conn.getOutputStream();
				// 注意编码格式
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			jsonObject = JSONObject.fromObject(buffer.toString());
		} catch (ConnectException ce) {
			System.out.println("连接超时:" + ce);
		} catch (Exception e) {
			System.out.println("https请求异常:" + e);
		}
		return jsonObject;
	}

	/**
	 * 2.发起http请求获取返回结果
	 *
	 * @param requestUrl 请求地址
	 * @return
	 */
	public static JSONObject httpRequest(String requestUrl) {
		JSONObject jsonObject = null;
		StringBuffer buffer = new StringBuffer();
		try {
			URL url = new URL(null, requestUrl, new sun.net.www.protocol.https.Handler());
			HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
			httpUrlConn.setDoOutput(false);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			httpUrlConn.setRequestMethod("GET");
			httpUrlConn.connect();
			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			jsonObject = JSONObject.fromObject(buffer.toString());
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();

		} catch (Exception e) {
			System.out.println(e);
		}
		return jsonObject;
	}

	/**
	 * 获取接口访问凭证
	 *
	 * @return
	 */
	public static Token getToken() {
		//如果tokenMap已经有缓存token,直接返回,不用请求微信接口
		String key = Constant.APP_ID + "_" + Constant.APP_SECRET;
		String requestUrl = Constant.GET_TOKEN_URL.replace("{APPID}", Constant.APP_ID).replace("{APPSECRET}", Constant.APP_SECRET);
		// 发起GET请求获取凭证
		JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
		if (null != jsonObject) {
			try {
				token = new Token();
				token.setAccessToken(jsonObject.getString("access_token"));
				token.setExpiresIn(jsonObject.getInt("expires_in"));
				System.out.println("获取token成功,缓存进tokenMap中 accessToken = " + token.getAccessToken());
				tokenMap.put(key, token);
			} catch (JSONException e) {
				token = null;
				// 获取token失败
				System.out.println("获取token失败 errcode:" + jsonObject.getInt("errcode") + " errmsg:" + jsonObject.getString("errmsg"));
			}
		}
		return token;
	}
}

2、微信公众号接口相关的常量

package com.winxin.util;

/**
 * @className:Constant
 * @description: TODO
 * @date: 2019-03-18 18:46
 */
public class Constant {

	public final static String APP_ID = "你的APPID";
	public final static String APP_SECRET = "你的密钥";
	/**
	 * 凭证获取(GET)
	 */
	public final static String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={APPSECRET}";

	/**
	 * 自定义菜单
	 */
	public final static String POST_CREATE_MENU = " https://api.weixin.qq.com/cgi-bin/menu/create?access_token={ACCESSTOKEN}";


	/**
	 * 查询自定义菜单
	 */
	public final static String GET_SELECT_MENU = " https://api.weixin.qq.com/cgi-bin/menu/get?access_token={ACCESSTOKEN}";

	/**
	 * 上传卡券logo
	 */
	public final static String POST_UPLOAD_LOGO = " https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token={ACCESSTOKEN}";

	/**
	 * 创建门店
	 */
	public final static String POST_CREATE_DOOR = "http://api.weixin.qq.com/cgi-bin/poi/addpoi?access_token={ACCESSTOKEN}";

	/**
	 * 创建会员卡
	 */
	public final static String POST_CREATE_CARD = "https://api.weixin.qq.com/card/create?access_token={ACCESSTOKEN}";

	/**
	 * 创建二维码
	 */
	public final static String POST_CREATE_QRCARD = "https://api.weixin.qq.com/card/qrcode/create?access_token={ACCESSTOKEN}";

	/**
	 * 查询code 接口
	 */
	public final static String POST_SELECT_CODE = "https://api.weixin.qq.com/card/code/get?access_token={ACCESSTOKEN}";


	/**
	 * 创建白名单
	 */
	public final static String POST_CREATE_WHITE = "https://api.weixin.qq.com/card/testwhitelist/set?access_token={ACCESSTOKEN}";

	/**
	 * 核销code 接口
	 */
	public final static String POST_EXAMMINE_CODE = "https://api.weixin.qq.com/card/code/consume?access_token={ACCESSTOKEN}";

	/**
	 * 会员卡激活
	 */
	public final static String POST_ACTIVATE_CARD = "https://api.weixin.qq.com/card/membercard/activate?access_token={ACCESSTOKEN}";





}

3、微信时间常量

package com.winxin.util;
 
/**
 * 微信事件常量
 * 
 * @author cdj
 * @date 2018年8月3日 下午1:17:08
 */
public class EventConstant {
	/**
	 * 返回消息类型:文本
	 */
	public static final String RESP_MESSAGE_TYPE_TEXT = "text";
 
	/**
	 * 返回消息类型:音乐
	 */
	public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
 
	/**
	 * 返回消息类型:图文
	 */
	public static final String RESP_MESSAGE_TYPE_NEWS = "news";
 
	/**
	 * 返回消息类型:图片
	 */
	public static final String RESP_MESSAGE_TYPE_Image = "image";
 
	/**
	 * 返回消息类型:语音
	 */
	public static final String RESP_MESSAGE_TYPE_Voice = "voice";
 
	/**
	 * 返回消息类型:视频
	 */
	public static final String RESP_MESSAGE_TYPE_Video = "video";
 
	/**
	 * 请求消息类型:文本
	 */
	public static final String REQ_MESSAGE_TYPE_TEXT = "text";
 
	/**
	 * 请求消息类型:图片
	 */
	public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
 
	/**
	 * 请求消息类型:链接
	 */
	public static final String REQ_MESSAGE_TYPE_LINK = "link";
 
	/**
	 * 请求消息类型:地理位置
	 */
	public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
 
	/**
	 * 请求消息类型:音频
	 */
	public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
 
	/**
	 * 请求消息类型:视频
	 */
	public static final String REQ_MESSAGE_TYPE_VIDEO = "video";
 
	/**
	 * 请求消息类型:推送
	 */
	public static final String REQ_MESSAGE_TYPE_EVENT = "event";
 
	/**
	 * 事件类型:subscribe(订阅)
	 */
	public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
 
	/**
	 * 事件类型:unsubscribe(取消订阅)
	 */
	public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
 
	/**
	 * 事件类型:CLICK(自定义菜单点击事件)
	 */
	public static final String EVENT_TYPE_CLICK = "CLICK";
 
	/**
	 * 事件类型:VIEW(自定义菜单URl视图)
	 */
	public static final String EVENT_TYPE_VIEW = "VIEW";
 
	/**
	 * 事件类型:LOCATION(上报地理位置事件)
	 */
	public static final String EVENT_TYPE_LOCATION = "LOCATION";
 
	/**
	 * 事件类型:LOCATION(上报地理位置事件)
	 */
	public static final String EVENT_TYPE_SCAN = "SCAN";
}

4、文本信息

package com.winxin.util;


public class MessageText {

    private String Content;
    private String CreateTime;
    private String FromUserName;
    private String MsgID;
    private String MsgType;
    private String ToUserName;

    public MessageText() {
        // TODO Auto-generated constructor stub
    }

    public MessageText(String toUserName, String fromUserName,
                       String createTime, String msgType, String content, String msgID) {
        ToUserName = toUserName;
        FromUserName = fromUserName;
        CreateTime = createTime;
        MsgType = msgType;
        Content = content;
        MsgID = msgID;
    }

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }

    public String getCreateTime() {
        return CreateTime;
    }

    public void setCreateTime(String createTime) {
        CreateTime = createTime;
    }

    public String getFromUserName() {
        return FromUserName;
    }

    public void setFromUserName(String fromUserName) {
        FromUserName = fromUserName;
    }

    public String getMsgID() {
        return MsgID;
    }

    public void setMsgID(String msgID) {
        MsgID = msgID;
    }

    public String getMsgType() {
        return MsgType;
    }

    public void setMsgType(String msgType) {
        MsgType = msgType;
    }

    public String getToUserName() {
        return ToUserName;
    }

    public void setToUserName(String toUserName) {
        ToUserName = toUserName;
    }

}

5、微信时间消息处理

package com.winxin.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.winxin.pojo.Article;
import com.winxin.pojo.MusicMessage;
import com.winxin.pojo.NewsMessage;
import com.winxin.pojo.TextMessage;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * 微信事件消息处理
 * @author cdj
 * @date 2018年8月3日 上午9:26:27
 */
public class MessageUtil {
 
	/**
	 * 解析微信发来的请求(XML)
	 * 
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
		// 将解析结果存储在HashMap中
		Map<String, String> map = new HashMap<String, String>();
		// 读取输入流
		SAXReader reader = new SAXReader();
		// 从request中取得输入流
		InputStream inputStream = request.getInputStream();
		Document document = reader.read(inputStream);
		// 得到xml根元素
		Element root = document.getRootElement();
		// 得到根元素的所有子节点
 
		@SuppressWarnings("unchecked")
		List<Element> elementList = root.elements();
 
		// 遍历所有子节点
		for (Element e : elementList) {
			map.put(e.getName(), e.getText());
		}
 
		// 释放资源
		inputStream.close();
		inputStream = null;
 
		return map;
	}
 
	/**
	 * 文本消息对象转换成xml
	 * 
	 * @param textMessage
	 *            文本消息对象
	 * @return xml
	 */
	public static String textMessageToXml(TextMessage textMessage) {
		xstream.alias("xml", textMessage.getClass());
		return xstream.toXML(textMessage);
	}
 
	/**
	 * 音乐消息对象转换成xml
	 * 
	 * @param musicMessage
	 *            音乐消息对象
	 * @return xml
	 */
	public static String musicMessageToXml(MusicMessage musicMessage) {
		xstream.alias("xml", musicMessage.getClass());
		return xstream.toXML(musicMessage);
	}
 
	/**
	 * 图文消息对象转换成xml
	 * 
	 * @param newsMessage
	 *            图文消息对象
	 * @return xml
	 */
	public static String newsMessageToXml(NewsMessage newsMessage) {
		xstream.alias("xml", newsMessage.getClass());
		xstream.alias("item", new Article().getClass());
		return xstream.toXML(newsMessage);
	}
 
	/**
	 * 扩展xstream,使其支持CDATA块
	 * 
	 */
	private static XStream xstream = new XStream(new XppDriver() {
		public HierarchicalStreamWriter createWriter(Writer out) {
			return new PrettyPrintWriter(out) {
				// 对所有xml节点的转换都增加CDATA标记
				boolean cdata = true;
 
				@Override
				protected void writeText(QuickWriter writer, String text) {
					if (cdata) {
						writer.write("<![CDATA[");
						writer.write(text);
						writer.write("]]>");
					} else {
						writer.write(text);
					}
				}
			};
		}
	});
 
}

6、消息类型

package com.winxin.util;

public enum MsgType {
    text("文本"),image("图片"),location("地理位置"),link("连接"),event("事件"),news("图文信息") ;
    private String value;

    public String getValue() {
        return value;
    }

    private MsgType(String value) {
        this.value = value;
    }
}

7、信任管理器

package com.winxin.util;

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * 描述: 信任管理器
 *
 * @description
 * @time 2019/3/18
 */
public class MyX509TrustManager implements X509TrustManager {

	/**
	 * 检查客户端证书
	 *
	 * @param chain
	 * @param authType
	 * @throws CertificateException
	 */
	@Override
	public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
	}

	/**
	 * 检查服务器端证书
	 *
	 * @param chain
	 * @param authType
	 * @throws CertificateException
	 */
	@Override
	public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
	}

	/**
	 * 返回受信任的X509证书数组
	 *
	 * @return
	 */
	@Override
	public X509Certificate[] getAcceptedIssuers() {
		return null;
	}
}

8、请求校验工具类

package com.winxin.util;
 
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
 
/**
 * 请求校验工具类
 */
public class SignUtil {
	// 与公众接口配置信息中的Token要一致
	private static String token = "wsx";
 
	public static boolean checkSignature(String signature, String timestamp, String nonce) {
		// 从请求中(也就是微信服务器传过来的)拿到的token, timestamp, nonce
		String[] arr = new String[] { token, timestamp, nonce };
		// 将token、timestamp、nonce三个参数进行字典序排序
		sort(arr);
		StringBuilder content = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			content.append(arr[i]);
		}
		MessageDigest md = null;
		String tmpStr = null;
 
		try {
			md = MessageDigest.getInstance("SHA-1");
			// 将三个参数字符串拼接成一个字符串进行sha1加密
			byte[] digest = md.digest(content.toString().getBytes());
			// 将字节数组转成字符串
			tmpStr = byteToStr(digest);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
 
		content = null;
		// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
	}
 
	// 将加密后的字节数组变成字符串
	private static String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
			strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}
 
	private static String byteToHexStr(byte mByte) {
		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		char[] tempArr = new char[2];
		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
		tempArr[1] = Digit[mByte & 0X0F];
 
		String s = new String(tempArr);
		return s;
	}
 
	// 用于字典排序
	public static void sort(String a[]) {
		for (int i = 0; i < a.length - 1; i++) {
			for (int j = i + 1; j < a.length; j++) {
				if (a[j].compareTo(a[i]) < 0) {
					String temp = a[i];
					a[i] = a[j];
					a[j] = temp;
				}
			}
		}
	}
}

四、微信公众号请求项目的主要controller

package com.wx.controller;

import com.alibaba.fastjson.JSON;
import com.winxin.util.SignUtil;
import com.wx.service.WxEventService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

/**
 * 微信相关接口
 * 
 * @author cdj
 *
 */
@Controller
@RequestMapping("/WxUser")
public class WxUserController {
	@Autowired
	private WxEventService wxEventService;
	private static final Log LOG = LogFactory.getLog(WxUserController.class);
 
	@RequestMapping("/HandleWxEvent")
	public void HandleWxEvent(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		req.setCharacterEncoding("UTF-8");
		resp.setCharacterEncoding("UTF-8");
		Map<String, String[]> parameterMap = req.getParameterMap();
		System.out.println("requestData:" + JSON.toJSONString(parameterMap));
		LOG.info("requestData:" + JSON.toJSONString(parameterMap));
		// 微信加密签名
		String signature = req.getParameter("signature");
		// 时间戳
		String timestamp = req.getParameter("timestamp");
		// 随机数
		String nonce = req.getParameter("nonce");
		// 随机字符串
		String echostr = req.getParameter("echostr");
		System.out.println(
				"signature=" + signature + ";timestamp=" + timestamp + ";nonce=" + nonce + ";echostr=" + echostr);
		LOG.info("signature=" + signature + ";timestamp=" + timestamp + ";nonce=" + nonce + ";echostr=" + echostr);
		PrintWriter out = resp.getWriter();
		// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
		LOG.info("checkSignature:" + SignUtil.checkSignature(signature, timestamp, nonce));
		System.out.println("checkSignature:" + SignUtil.checkSignature(signature, timestamp, nonce));
 
		// 首次请求申请验证,返回echostr
		if (echostr != null) {
			out.println(echostr);
			if (out != null) {
				out.close();
			}
			return;
		}
		if (SignUtil.checkSignature(signature, timestamp, nonce)) {
			System.out.println("success");
			LOG.info("success");
			String message = "success";
			try {
				message = wxEventService.processRequest(req);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				out.println(message);
				if (out != null) {
					out.close();
				}
			}
		} else {
			System.out.println("The request signature is invalid");
			LOG.info("The request signature is invalid");
		}
	}
 
}

五、还有一个定时任务,因为token的有效期只有7200秒

package com.wx.util;

import com.winxin.util.CommonUtil;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 创建定时任务实现类
 * @description
 * @time 2019/3/19
 */
@Component
public class ScheduledTasks {

	/**
	 * 每7100秒执行一次
	 */
	@Scheduled(fixedRate = 7100000)
	public void reportCurrentTime() {
	//这个调用的是上面工具类的第一个类中的获取token的方法
		CommonUtil.getToken();
	}

}

六、
1、serviceImpl层(自己写一下service)

package com.wx.service.impl;

import com.winxin.pojo.Article;
import com.winxin.pojo.NewsMessage;
import com.winxin.pojo.TextMessage;
import com.winxin.util.EventConstant;
import com.winxin.util.MessageUtil;
import com.wx.service.WxEventService;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @className:WxEventServiceImpl
 * @description: TODO
 * @author: WX
 * @date: 2019-03-20 13:01
 */
@Component
@Service
public class WxEventServiceImpl implements WxEventService {

	@Override
	public String processRequest(HttpServletRequest req) {
		String respMessage = null;
		try {
			// 默认返回的文本消息内容
			String respContent = "请求处理异常,请稍候尝试!";
			// xml请求解析
			Map<String, String> requestMap = MessageUtil.parseXml(req);
			// 发送方帐号(open_id)
			String fromUserName = requestMap.get("FromUserName");
			// 公众帐号
			String toUserName = requestMap.get("ToUserName");
			// 消息类型
			String msgType = requestMap.get("MsgType");

			// 回复文本消息
			TextMessage textMessage = new TextMessage();
			textMessage.setToUserName(fromUserName);
			textMessage.setFromUserName(toUserName);
			textMessage.setCreateTime(System.currentTimeMillis());
			textMessage.setMsgType(EventConstant.RESP_MESSAGE_TYPE_TEXT);
			textMessage.setFuncFlag(0);

			// 文本消息
			if (msgType.equals(EventConstant.RESP_MESSAGE_TYPE_TEXT)) {
				respContent = "您发送的是文本消息!";
			}
			// 图片消息
			else if (msgType.equals(EventConstant.REQ_MESSAGE_TYPE_IMAGE)) {
				respContent = "您发送的是图片消息!";
			}
			// 地理位置消息
			else if (msgType.equals(EventConstant.REQ_MESSAGE_TYPE_LOCATION)) {
				respContent = "您发送的是地理位置消息!";
			}
			// 链接消息
			else if (msgType.equals(EventConstant.REQ_MESSAGE_TYPE_LINK)) {
				respContent = "您发送的是链接消息!";
			}
			// 音频消息
			else if (msgType.equals(EventConstant.REQ_MESSAGE_TYPE_VOICE)) {
				respContent = "您发送的是音频消息!";
			}
			// 事件推送
			else if (msgType.equals(EventConstant.REQ_MESSAGE_TYPE_EVENT)) {
				// 事件类型
				String eventType = requestMap.get("Event");
				// 订阅
				if (eventType.equals(EventConstant.EVENT_TYPE_SUBSCRIBE)) {
					respContent = "谢谢您的关注!";
				}
				// 取消订阅
				else if (eventType.equals(EventConstant.EVENT_TYPE_UNSUBSCRIBE)) {
					// 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
				}
				// 自定义菜单点击事件
				else if (eventType.equals(EventConstant.EVENT_TYPE_CLICK)) {
					// 事件KEY值,与创建自定义菜单时指定的KEY值对应
					String eventKey = requestMap.get("EventKey");
					if (eventKey.equals("11")) {
						respContent = "天气预报菜单项被点击!";
					} else if (eventKey.equals("12")) {
						respContent = "公交查询菜单项被点击!";
					} else if (eventKey.equals("13")) {
						respContent = "周边搜索菜单项被点击!";
					} else if (eventKey.equals("14")) {
						respContent = "历史上的今天菜单项被点击!";
					} else if (eventKey.equals("21")) {
						respContent = "歌曲点播菜单项被点击!";
					} else if (eventKey.equals("22")) {
						respContent = "经典游戏菜单项被点击!";
					} else if (eventKey.equals("23")) {
						respContent = "美女电台菜单项被点击!";
					} else if (eventKey.equals("24")) {
						respContent = "人脸识别菜单项被点击!";
					} else if (eventKey.equals("25")) {
						//聊天唠嗑菜单项被点击
						NewsMessage newsMessage = new NewsMessage();
						newsMessage.setToUserName(fromUserName);
						newsMessage.setFromUserName(toUserName);
						newsMessage.setCreateTime(System.currentTimeMillis());
						newsMessage.setMsgType(EventConstant.RESP_MESSAGE_TYPE_NEWS);
						newsMessage.setFuncFlag(0);
						List<Article> articleList = new ArrayList<Article>();
						Article article = new Article();
						article.setTitle("我是一条单图文消息");
						article.setDescription("我是描述信息,哈哈哈哈哈哈哈。。。");
						article.setPicUrl("http://www.iteye.com/upload/logo/user/603624/2dc5ec35-073c-35e7-9b88-274d6b39d560.jpg");
						article.setUrl("https://blog.csdn.net/atmknight");
						articleList.add(article);
						//多图文的话,新建多个article 放入articleList 中即可实现
						// 设置图文消息个数
						newsMessage.setArticleCount(articleList.size());
						// 设置图文消息包含的图文集合
						newsMessage.setArticles(articleList);
						// 将图文消息对象转换成xml字符串
						respMessage = MessageUtil.newsMessageToXml(newsMessage);
						return respMessage;
					} else if (eventKey.equals("32")) {
						respContent = "会员中心菜单项被点击!";
					} else if (eventKey.equals("33")) {
						respContent = "注册会员菜单项被点击!";
					}
				}
			}
			textMessage.setContent(respContent);
			respMessage = MessageUtil.textMessageToXml(textMessage);
			return respMessage;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
}

2、我自己写的自定义菜单

package com.wx.service.impl;

import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.winxin.pojo.Token;
import com.winxin.util.CommonUtil;
import com.winxin.util.Constant;
import com.wx.service.MenuService;
import com.wx.util.WeiXinUtil;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Service;

@Service
public class MenuServiceImpl implements MenuService {
    public static Log log = LogFactory.getLog(MenuService.class);
    @Override
    public JSONObject createMenu(String outputStr) {
            return WeiXinUtil.getObject(Constant.POST_CREATE_MENU, outputStr);
    }
}

3、还有一个工具类

package com.wx.util;

import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.winxin.pojo.*;
import com.winxin.util.CommonUtil;
import com.winxin.util.Constant;
import com.wx.service.MenuService;
import net.sf.json.JSONObject;

public class WeiXinUtil {
    public static Log log = LogFactory.getLog(MenuService.class);
    /**
    *这个是从serviceImpl提取出来的公用方法
    */
    public static JSONObject getObject(String urlPath, String outputStr) {
        String key = Constant.APP_ID + "_" + Constant.APP_SECRET;
        Token token = CommonUtil.tokenMap.get(key);
        if (token == null) {
            log.error("获取token为空,请重新获取");
            return null;
        } else {
            String requestUrl = urlPath.replace("{ACCESSTOKEN}", token.getAccessToken());
            return CommonUtil.httpsRequest(requestUrl, "POST", outputStr);
        }
    }

   /**
    * 组装菜单数据
    * @return
    */
    public static Menu getMenu() {
        /** -------------------------第一个大按钮--------------------------------------- */
        ComplexButton mainBtn1 = new ComplexButton();
        mainBtn1.setName("生活助手");
        /** -------------------------小按钮--------------------------------------- */
        CommonButton btn11 = new CommonButton();
        btn11.setName("天气预报");
        btn11.setType("click");
        btn11.setKey("11");
        CommonButton btn12 = new CommonButton();
        btn12.setName("公交查询");
        btn12.setType("click");
        btn12.setKey("12");
        CommonButton btn13 = new CommonButton();
        btn13.setName("周边搜索");
        btn13.setType("click");
        btn13.setKey("13");
        CommonButton btn14 = new CommonButton();
        btn14.setName("历史上的今天");
        btn14.setType("click");
        btn14.setKey("14");
        mainBtn1.setSub_button(new CommonButton[] { btn11, btn12, btn13, btn14 });
        /** -------------------------第二个大按钮--------------------------------------- */
        ComplexButton mainBtn2 = new ComplexButton();
        mainBtn2.setName("休闲驿站");
        /** -------------------------小按钮--------------------------------------- */
        CommonButton btn21 = new CommonButton();
        btn21.setName("歌曲点播");
        btn21.setType("click");
        btn21.setKey("21");
        CommonButton btn22 = new CommonButton();
        btn22.setName("经典游戏");
        btn22.setType("click");
        btn22.setKey("22");
        CommonButton btn23 = new CommonButton();
        btn23.setName("美女电台");
        btn23.setType("click");
        btn23.setKey("23");
        CommonButton btn24 = new CommonButton();
        btn24.setName("人脸识别");
        btn24.setType("click");
        btn24.setKey("24");
        CommonButton btn25 = new CommonButton();
        btn25.setName("聊天唠嗑");
        btn25.setType("click");
        btn25.setKey("25");
        mainBtn2.setSub_button(new CommonButton[] { btn21, btn22, btn23, btn24, btn25 });
        /** -------------------------第三个大按钮--------------------------------------- */
        ComplexButton mainBtn3 = new ComplexButton();
        mainBtn3.setName("会员权益");
        /** -------------------------小按钮--------------------------------------- */
        CommonButton btn32 = new CommonButton();
        btn32.setName("会员中心");
        btn32.setType("click");
        btn32.setKey("32");
        CommonButton btn33 = new CommonButton();
        btn33.setName("注册会员");
        btn33.setType("click");
        btn33.setKey("33");

        mainBtn3.setSub_button(new CommonButton[] { btn32, btn33 });

        Menu menu = new Menu();
        menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 });
        return menu;
    }
}

感觉写的特别乱,多多包涵。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值