微信公众号开发相关功能

1、controller代码

@Controller
@RequestMapping("/wechat/platform")
public class WechatController extends AbstractController {
	// get请求,用于微信公众号配置url时验证
	@ResponseBody
	@GetMapping("test")
	public String test(String signature, String timestamp, String nonce, String echostr) {
		// 验签
		boolean bool = WechatUtils.checkSign(signature, timestamp, nonce);
		return bool ? echostr : null;
	}

	// post请求,公众号有聊天,扫码等事件时推送相关内容
	@ResponseBody
	@PostMapping("test")
	public String test(HttpServletRequest request) throws Exception {
		// 解密微信推送的消息
		Map<String, String> map = WechatUtils.decryptWxMsg2Map(request);
		// 用户openid和本次推送的消息类型
		String openid = request.getParameter("openid"), msgType = map.get("MsgType");
		if ("text".equals(msgType)) { // 文本消息
			// 做相应处理
		} else if ("event".equals(msgType)) { // 事件
			// event 事件类型,key 事件中的值
			String event = map.get("Event"), key = map.get("EventKey");
			// 玩家扫码绑定留言
			if ("SCAN".equalsIgnoreCase(event)) {
			} else if ("CLICK".equalsIgnoreCase(event)) {
			}
		}
		// 如果想返回自定义内容 
		return WechatUtils.encryptWxMsg2Xml(openid, platformId, "xxxxxx");
		// 不需要返回自定义内容或者五秒内业务处理不完,返回success,否则公众号提示异常
		return "success";
	}

	// 生成二维码,用于扫码登录
	@ResponseBody
	@RequestMapping(value = "makeCode")
	public R makeCode(HttpServletRequest request) throws Exception {
		String state = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
		// java延时队列,多服务时,可存储redis
		BusinessDelayQueue instance = BusinessDelayQueue.getInstance();
		// 状态:false、未扫码,null、码失效,其他、扫码成功
		PublicTask task = new PublicTask("wx_" + state, "false");
		long expire = System.currentTimeMillis() + 300000;
		DelayTask<?> delay = new DelayTask<>(expire, task);
		instance.put(delay);
		return R.ok().put("url", String.format(wxConfig.getWxQrcode(), state)).put("state", state);
	}

	// 轮询扫码状态,最好使用websocket主动推送
	@ResponseBody
	@PostMapping(value = "checkState")
	public R checkState(String state) {
		PublicTask task = new PublicTask("wx_" + state, null);
		String value = task.getValue(String.class, false);
		if (value == null) {
			return R.error(-1, "二维码已失效").put(SysLogUtils.SYSLOG_NOT_SAVE, true);
		}
		if ("false".equals(value)) {
			return R.error(-2, "未扫码登录").put(SysLogUtils.SYSLOG_NOT_SAVE, true);
		}
		// info自定义的信息,比如user信息
		String info = task.getValue(String.class, true);
		return r;
	}

	// 用户扫码后微信端调用
	@RequestMapping(value = "openid")
	public String openid(String code, String state, Model model, HttpServletRequest request) throws IOException {
		// 获取openid
		String resp = HttpTookit.doGet(String.format(wxConfig.getWxOpenid(), code), null);
		String openid = JSON.parseObject(resp).getString("openid");
		// 获取用户信息,进行业务处理
		JSONObject user = WechatUtils.userInfo(openid);
		return "xxx";
	}

	// 模板消息推送
	public void pushMessage(String str, Integer type, String ext) {
		// 构造固定格式map
		Map<String, Object> data = new HashMap<>(), map = new HashMap<>(), first = new HashMap<>(),
				k1 = new HashMap<>(), k2 = new HashMap<>(), k3 = new HashMap<>(), k4 = new HashMap<>(),
				remark = new HashMap<>();
		data.put("touser", openid);
		map.put("first", first);
		map.put("keyword1", k1);
		map.put("keyword2", k2);
		map.put("keyword3", k3);
		map.put("keyword4", k4);
		map.put("remark", remark);
		first.put("color", "#159815");
		k1.put("color", "#003AFF");
		k2.put("color", "#FF0066");
		k3.put("color", "#003AFF");
		k4.put("color", "#003AFF");
		remark.put("color", "#159815");
		
		data.put("template_id", "模板ID");
		data.put("data", map);
		WechatUtils.pushMessage(data);
	}
}

2、WechatUtils工具类

public class WechatUtils {
	public static String access_token = null;
	private static WXBizMsgCrypt crypt = null;
	
	static {
		try {
			// 微信消息解密器
			crypt = new WXBizMsgCrypt(wxConfig.getToken(), wxConfig.getAesKey(), wxConfig.getAppid());
		} catch (AesException e) {
		}
	}

	// 获取全局access_token
	public static void getAccessToken() {
		// wxConfig.getAccessToken():单独的微信服务器,提供全局access_token
		String format = String.format(wxConfig.getAccessToken(), access_token);
		access_token = HttpTookit.doGet(format, null);
	}

	// 获取用户信息
	public static void userInfo(String openid) {
		String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s";
		// 获取用户信息
		String info = HttpTookit.doGet(String.format(url, access_token, openid), null);
	}

	// 微信公众号配置url时的验签
	public static boolean checkSign(String signature, String timestamp, String nonce) {
		String arrs[] = { "微信公众号的token", timestamp, nonce };
		Arrays.sort(arrs);
		StringBuffer sb = new StringBuffer();
		for (String str : arrs) {
			sb.append(str);
		}
		String s= Sha1Util.encode(sb.toString());
		return s.equals(signature);
	}

	// 推送模板消息
	public static void pushMessage(Map<String, Object> data) throws Exception {
		String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
		String resp = HttpTookit.sendHttpPost(String.format(url, access_token), JSON.toJSONString(data));
	}

	// 对给微信公众号返回的消息加密
	public static String encryptWxMsg2Xml(String toOpenid, String fromOpenid, String content) throws Exception {
		String xml = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName>"
				+ "<CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>";
		String createTime = Long.toString(System.currentTimeMillis() / 1000), nonce = SerialNumUtil.randam(10);
		// 明文消息,用于非安全模式
		// return String.format(xml, toOpenid, fromOpenid, createTime, content);
		// 加密消息,安全模式
		return crypt.encryptMsg(String.format(xml, toOpenid, fromOpenid, createTime, content), createTime, nonce);
	}

	// 解密微信公众号推送的消息
	public static Map<String, String> decryptWxMsg2Map(HttpServletRequest request) throws Exception {
		request.setCharacterEncoding("UTF-8");
		StringBuilder sb = new StringBuilder();
		// 获取服务器发送过来的信息,因为不是参数,得用输入流读取
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"))) {
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
		} catch (Exception e) {
		}
		String s= request.getParameter("msg_signature"), timeStamp = request.getParameter("timestamp"),
				nonce = request.getParameter("nonce");
		System.out.println("用户发送过来信息解密前:" + sb);
		String decryptMsg = crypt.decryptMsg(s, timeStamp, nonce, sb.toString());
		System.out.println("用户发送过来信息解密后:" + decryptMsg);
		// xml转map
		return xmlToMap(decryptMsg);
	}

	// xml转map
	public static Map<String, String> xmlToMap(String strXML) throws Exception {
		Map<String, String> data = new HashMap<String, String>();
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		try (InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"))) {
			Document doc = documentBuilder.parse(stream);
			doc.getDocumentElement().normalize();
			NodeList nodeList = doc.getDocumentElement().getChildNodes();
			for (int idx = 0; idx < nodeList.getLength(); ++idx) {
				Node node = nodeList.item(idx);
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					org.w3c.dom.Element element = (org.w3c.dom.Element) node;
					data.put(element.getNodeName(), element.getTextContent());
				}
			}
		}
		return data;
	}

	// map转xml
	public static String mapToXml(String toOpenid, String fromOpenid, String timestamp, String content) {
		String xml = "<xml>" + "<ToUserName><![CDATA[%s]]></ToUserName>"
				+ "<FromUserName><![CDATA[%s}]]></FromUserName>" + "<CreateTime>%s</CreateTime>"
				+ "<MsgType><![CDATA[text]]></MsgType>" + "<Content><![CDATA[%s]]></Content>"
				+ "<MsgId>1234567890123456</MsgId> " + "</xml>";
		return String.format(xml, toOpenid, fromOpenid, timestamp, content);

	}

}

3、Java延时队列相关类

public class BusinessDelayQueue {
	private final static int DEFAULT_THREAD_NUM = 10;
	private ExecutorService executor;
	public DelayQueue<DelayTask<?>> delayQueue;
	private Thread daemonThread;
	private static BusinessDelayQueue instance = new BusinessDelayQueue();

	private BusinessDelayQueue() {
		executor = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);
		delayQueue = new DelayQueue<>();
		init();
	}

	public static BusinessDelayQueue getInstance() {
		return instance;
	}

	public void init() {
		daemonThread = new Thread(() -> {
			execute();
		});
		daemonThread.setName("DelayQueueMonitor");
		daemonThread.start();
	}

	private void execute() {
		while (true) {
			try {
				DelayTask<?> delayTask = delayQueue.take();
				if (delayTask != null) {
					Callable<Integer> task = delayTask.getTask();
					if (null == task) {
						continue;
					}
					// 提交到线程池执行task
					executor.submit(task);
				}
			} catch (Exception e) {
			}
		}
	}

	// 添加
	public void put(DelayTask<?> delay) {
		if(delayQueue.contains(delay)) {
			delayQueue.remove(delay);
		}
		delayQueue.put(delay);
	}

	// 停止任务
	public boolean removeTask(DelayTask<?> delay) {
		return delayQueue.remove(delay);
	}

}

public class DelayTask<T extends Callable<Integer>> implements Delayed {

	private final long expire; // 到期时间
	private T task; // 数据

	public DelayTask(long expire, T task) {
		this.expire = expire;
		this.task = task;
	}

	@Override
	public int compareTo(Delayed o) {
		long self = this.getDelay(TimeUnit.MILLISECONDS), other = o.getDelay(TimeUnit.MILLISECONDS);
		return self <= other ? -1 : 1;
	}

	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}

	public T getTask() {
		return task;
	}
	// 记得重写equals和hashcode
}

public class PublicTask implements Callable<Integer> {

	private String key;
	private Object value;

	public PublicTask(String key, Object value) {
		this.key = key;
		this.value = value;
	}

	@Override
	public Integer call() throws Exception {
		// 业务处理
		return null;
	}
	// 记得重写equals和hashcode
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值