【微信公众号】开发

微信公众号开发官方网站,建议开发前详细读其中内容。https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html

  1. 申请个人微信公众号开发平台
    个人用户选择申请订阅号。
    申请成功后在开发->基本配置->服务器配置中启用服务。
    在这里插入图片描述
    在这里插入图片描述

  2. 内网穿透
    通过NatApp将内网地址映射到外网上,可以在外网访问你本地服务器。
    NatApp网址:https://natapp.cn/。
    进行内网穿透是因为微信公众号开发需要通过域名通讯(微信会访问我们配置的域名地址:服务器基本配置中的URL),也就是我们各自开发环境需要拥有独立的域名,微信就能通过这个域名请求到我们的本地开发服务,各自进行开发测试。
    在这里插入图片描述
    从NatApp官网将对应电脑版本的natapp下载,解压到某个文件夹,将该文件夹的路径复制好之后配置path环境变量。
    在这里插入图片描述
    在natapp上购买免费的隧道,购买成功后会出现authtoken。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    配置环境变量成功后,双击natapp.exe,输入命令natapp -authtoken yourtoken出现如下图路径即可。

yourtoken:指的是购买成功的authtoken 。在这里插入图片描述
此时外网的用户可以直接使用这个域名访问到我内网的127.0.0.1:8080服务器了。

  1. 此时内网穿透已经好了,简单的创建一个JavaWeb项目,新建一个index.jsp测试是否可以访问。
    在这里插入图片描述
    在body里输入hello lijing,运行项目,在游览器中输入127.0.0.1:8080/weChatProject,以及内网穿透地址**http://aj3vqi.natappfree.cc/weChatProject/**都可以访问到,此时成功。
    在这里插入图片描述
    在这里插入图片描述
  2. 通过Eclipse新建JavaWeb项目,新建一个servlet。
    在这里插入图片描述
package cn.lijingCute.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.lijingCute.service.WxService;

@WebServlet("/wxServlet")
public class wxServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public wxServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		/*
		 *  signature 	微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
			timestamp 	时间戳
			nonce 		随机数
			echostr 	随机字符串
		 */
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		String echostr = request.getParameter("echostr");
//		System.out.println(signature);
//		System.out.println(timestamp);
//		System.out.println(nonce);
//		System.out.println(echostr);
		//校验签名		
		if(WxService.check(timestamp,nonce,signature)){
			PrintWriter pw = response.getWriter();
			//原样返回echostr参数	
			pw.print(echostr);
			pw.flush();
			pw.close();
//			System.out.println("接入成功");
		}else {
			System.out.println("接入失败");
		}

	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
		System.out.println("post");
	}

}


新建一个service。
在这里插入图片描述

package cn.lijingCute.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;




public class WxService {
	
	private static final String TOKEN = "nice";
	//验证签名
	public static boolean check(String timestamp,String nonce,String signature) {
		 //1)将token、timestamp、nonce三个参数进行字典序排序 
		String[] strs = new String[] {TOKEN,timestamp,nonce};
		Arrays.sort(strs);
		 //2)将三个参数字符串拼接成一个字符串进行sha1加密 
		String str = strs[0]+strs[1]+strs[2];
		String mysig = sha1(str);
		 //3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
		return mysig.equalsIgnoreCase(signature);
	}
	//sha1加密	
	private static String sha1(String src){
		try {
			//获取一个加密对象
			MessageDigest md = MessageDigest.getInstance("sha1");
			//加密
			byte[] digest = md.digest(src.getBytes());
			char[] chars= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
			StringBuilder sb = new StringBuilder();
			//处理加密结果
			for(byte b:digest) {
				sb.append(chars[(b>>4)&15]);
				sb.append(chars[b&15]);
			}
			return sb.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}
}

在微信公众号开发的测试平台进行测试,Token需要和Service中的Token一致。

微信公众号开发的测试平台:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

在微信公众号测试平台点击测试,后台控制台报接入成功,此时接入正确。在这里插入图片描述在这里插入图片描述

  1. 文本消息
    在servlet中写doPost代码如下。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletInputStream sis  = request.getInputStream();
		byte[] b= new byte[1024];
		int len;
		StringBuilder sb = new StringBuilder();
		while ((len=sis.read(b))!=-1) {
			sb.append(new String(b,0,len));
		}
		System.out.println(sb.toString());
	}

此时在手机测试公众号中发送信息“你好”给公众号,会在后台打印如下内容。
在这里插入图片描述

<xml><ToUserName><![CDATA[gh_cad3a83e72dc]]></ToUserName>
<FromUserName><![CDATA[oEmP3vm8jWeg875M2p-eknt5HPj8]]></FromUserName>
<CreateTime>1582871620</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[浣犲ソ]]></Content>
<MsgId>22661272279269388</MsgId>
</xml>

字段详情可以对应官方文档。
在这里插入图片描述

  1. 聊天机器人
聊天机器人的接口通过调用第三方聚合数据的接口:https://www.juhe.cn/docs/api/id/377
/**
	 * WxService中调用聊天机器人
	 */
	private static String chat(String msg) {
        String result =null;
        String url ="http://op.juhe.cn/robot/index";//请求接口地址
        Map params = new HashMap();//请求参数
        params.put("key",APPKEY);//您申请到的本接口专用的APPKEY
        params.put("info",msg);//要发送给机器人的内容,不要超过30个字符
        params.put("dtype","");//返回的数据的格式,json或xml,默认为json
        params.put("loc","");//地点,如北京中关村
        params.put("lon","");//经度,东经116.234632(小数点后保留6位),需要写为116234632
        params.put("lat","");//纬度,北纬40.234632(小数点后保留6位),需要写为40234632
        params.put("userid","");//1~32位,此userid针对您自己的每一个用户,用于上下文的关联
        try {
            result =Util.net(url, params, "GET");
            //解析json
            JSONObject jsonObject = JSONObject.fromObject(result);
            //取出error_code
            int code = jsonObject.getInt("error_code");
            if(code!=0) {
            		return null;
            }
            //取出返回的消息的内容
            String resp = jsonObject.getJSONObject("result").getString("text");
            return resp;
        } catch (Exception e) {
            e.printStackTrace();
        }
		return null;
	}

调用聚合数据的聊天机器人。

package cn.lijingCute.util;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;

import net.sf.json.JSONObject;
import cn.lijingCute.service.WxService;

public class Util {
	public static final String DEF_CHATSET = "UTF-8";
	public static final int DEF_CONN_TIMEOUT = 30000;
	public static final int DEF_READ_TIMEOUT = 30000;
	public static String userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";

	// 配置申请的KEY
	public static final String APPKEY = "*************************";
	
	/**
	 * 向指定的地址发送一个post请求,带着data数据
	 * 
	 * @param url
	 * @param data
	 */
	public static String post(String url, String data) {
		try {
			URL urlObj = new URL(url);
			URLConnection connection = urlObj.openConnection();
			// 要发送数据出去,必须要设置为可发送数据状态
			connection.setDoOutput(true);
			// 获取输出流
			OutputStream os = connection.getOutputStream();
			// 写出数据
			os.write(data.getBytes());
			os.close();
			// 获取输入流
			InputStream is = connection.getInputStream();
			byte[] b = new byte[1024];
			int len;
			StringBuilder sb = new StringBuilder();
			while ((len = is.read(b)) != -1) {
				sb.append(new String(b, 0, len));
			}
			return sb.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 向指定的地址发送get请求
	 * 
	 * @param url
	 */
	public static String get(String url) {
		try {
			URL urlObj = new URL(url);
			// 开连接
			URLConnection connection = urlObj.openConnection();
			InputStream is = connection.getInputStream();
			byte[] b = new byte[1024];
			int len;
			StringBuilder sb = new StringBuilder();
			while ((len = is.read(b)) != -1) {
				sb.append(new String(b, 0, len));
			}
			return sb.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 *
	 * @param strUrl
	 *            请求地址
	 * @param params
	 *            请求参数
	 * @param method
	 *            请求方法
	 * @return 网络请求字符串
	 * @throws Exception
	 */
	public static String net(String strUrl, Map params, String method) throws Exception {
		HttpURLConnection conn = null;
		BufferedReader reader = null;
		String rs = null;
		try {
			StringBuffer sb = new StringBuffer();
			if (method == null || method.equals("GET")) {
				strUrl = strUrl + "?" + urlencode(params);
			}
			URL url = new URL(strUrl);
			conn = (HttpURLConnection) url.openConnection();
			if (method == null || method.equals("GET")) {
				conn.setRequestMethod("GET");
			} else {
				conn.setRequestMethod("POST");
				conn.setDoOutput(true);
			}
			conn.setRequestProperty("User-agent", userAgent);
			conn.setUseCaches(false);
			conn.setConnectTimeout(DEF_CONN_TIMEOUT);
			conn.setReadTimeout(DEF_READ_TIMEOUT);
			conn.setInstanceFollowRedirects(false);
			conn.connect();
			if (params != null && method.equals("POST")) {
				try {
					DataOutputStream out = new DataOutputStream(conn.getOutputStream());
					out.writeBytes(urlencode(params));
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
			InputStream is = conn.getInputStream();
			reader = new BufferedReader(new InputStreamReader(is, DEF_CHATSET));
			String strRead = null;
			while ((strRead = reader.readLine()) != null) {
				sb.append(strRead);
			}
			rs = sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				reader.close();
			}
			if (conn != null) {
				conn.disconnect();
			}
		}
		return rs;
	}

	// 将map型转为请求参数型
	public static String urlencode(Map<String, Object> data) {
		StringBuilder sb = new StringBuilder();
		for (Map.Entry i : data.entrySet()) {
			try {
				sb.append(i.getKey()).append("=").append(URLEncoder.encode(i.getValue() + "", "UTF-8")).append("&");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

}

此时在微信公众号测试,可以自动回复消息。
在这里插入图片描述
控制台可以打印出来。
在这里插入图片描述

  1. 获取Token
//微信公众号
	private static final String APPID="*********";
	private static final String APPSECRET="*********";
	//用于存储token
	private static AccessToken at;
//获取token
	private static void getToken() {
		String url = GET_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
		String tokenStr = Util.get(url);
		JSONObject jsonObject = JSONObject.fromObject(tokenStr);
		String token = jsonObject.getString("access_token");
		String expireIn = jsonObject.getString("expires_in");
		//创建token对象,并存起来。
		at = new AccessToken(token, expireIn);
	}
	/**
	 * 向处暴露的获取token的方法
	 */
	public static String getAccessToken() {
		if(at==null||at.isExpired()) {
			getToken();
		}
		return at.getAccessToken();
	}
  1. 创建菜单
public class CreateMenu {

	public static void main(String[] args) {
		//菜单对象
		Button btn = new Button();
		//第一个一级菜单
		btn.getButton().add(new ClickButton("一级点击", "1"));
		//第二个一级菜单
		btn.getButton().add(new ViewButton("一级跳转", "http://www.baidu.com"));
		//创建第三个一级菜单
		SubButton sb = new SubButton("有子菜单");
		//为第三个一级菜单增加子菜单
		sb.getSub_button().add(new PhotoOrAlbumButton("传图", "31"));
		sb.getSub_button().add(new ClickButton("点击", "32"));
		sb.getSub_button().add(new ViewButton("网易新闻", "http://news.163.com"));
		//加入第三个一级菜单
		btn.getButton().add(sb);
		//转为json
		JSONObject jsonObject = JSONObject.fromObject(btn);
		//准备url
		String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
		url = url.replace("ACCESS_TOKEN", WxService.getAccessToken());
		//发送请求
		String result = Util.post(url, jsonObject.toString());
		System.out.println(result);
		
	}
	
}

自定义菜单的响应

	/**
	 * 处理click菜单
	 * @param requestMap
	 */
	private static BaseMessage dealClick(Map<String, String> requestMap) {
		String key = requestMap.get("EventKey");
		switch (key) {
			//点击一菜单点
			case "1":
				//处理点击了第一个一级菜单
				return new TextMessage(requestMap, "你点了一点第一个一级菜单");
			case "32":
				//处理点击了第三个一级菜单的第二个子菜单
				break;
			default:
				break;
		}
		return null;
	}
  1. 通过百度AI实现图片识别
    申请百度AI账号,登录成功后创建应用。
    百度AI的地址:http://ai.baidu.com/
    在这里插入图片描述
    下载JavaSDK,并将下载好的jar包放入WEB-INF的lib中。
    在这里插入图片描述
    在这里插入图片描述
	//百度AI
	public static final String APP_ID = "你申请账号的APP_ID";
	public static final String API_KEY = "你申请账号的API_KEY";
	public static final String SECRET_KEY = "你申请账号的SECRET_KEY";
	/**
	 * 进行图片识别
	 * @param requestMap
	 */
	private static BaseMessage dealImage(Map<String, String> requestMap) {
		// 初始化一个AipOcr
		AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
		// 可选:设置网络连接参数
		client.setConnectionTimeoutInMillis(2000);
		client.setSocketTimeoutInMillis(60000);
		// 调用接口
		String path = requestMap.get("PicUrl");
		
		//进行网络图片的识别
		org.json.JSONObject res = client.generalUrl(path, new HashMap<String,String>());
		String json = res.toString();
		//转为jsonObject
		JSONObject jsonObject = JSONObject.fromObject(json);
		JSONArray jsonArray = jsonObject.getJSONArray("words_result");
		Iterator<JSONObject> it = jsonArray.iterator();
		StringBuilder sb = new StringBuilder();
		while(it.hasNext()) {
			JSONObject next = it.next();
			sb.append(next.getString("words"));
		}
		return new TextMessage(requestMap, sb.toString());
	}

在这里插入图片描述
10. 获取行业与查询,发送模板消息
可以在微信测试账号中直接新增测试模板,但是需要注意,测试模板必须严格依照微信官方公众号开发中要求的模板样式。

例如:
简历反馈提醒

{{first.DATA}}
公司名:{{company.DATA}}
投递时间:{{time.DATA}}
反馈结果:{{result.DATA}}
{{remark.DATA}}

在这里插入图片描述

public class TemplateMessageManager {
	
	/**
	 * 设置行业
	 */
	@Test
	public void set() {
		String at = WxService.getAccessToken();
		String url="https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token="+at;
		String data="{\n" + 
				"          \"industry_id1\":\"1\",\n" + 
				"          \"industry_id2\":\"4\"\n" + 
				"       }";
		String result = Util.post(url, data);
		System.out.println(result);
	}
	
	/**
	 * 获取行业
	 */
	@Test
	public void get() {
		String at = WxService.getAccessToken();
		String url="https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token="+at;
		String result = Util.get(url);
		System.out.println(result);
	}
	
	/**
	 * 发送模板消息
	 */
	@Test
	public void sendTemplateMessage() {
		String at = WxService.getAccessToken();
		String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+at;
		String data="{\n" + 
				"           \"touser\":\"oEmP3vm8jWeg875M2p-eknt5HPj8\",\n" + 
				"           \"template_id\":\"P5uLRraltasI25g8rRXu7qPOWVc90CziX4Ub513OQeg\",         \n" + 
				"           \"data\":{\n" + 
				"                   \"first\": {\n" + 
				"                       \"value\":\"您有新的反馈信息啦!\",\n" + 
				"                       \"color\":\"#abcdef\"\n" + 
				"                   },\n" + 
				"                   \"company\":{\n" + 
				"                       \"value\":\"Lijing未来的公司\",\n" + 
				"                       \"color\":\"#fff000\"\n" + 
				"                   },\n" + 
				"                   \"time\": {\n" + 
				"                       \"value\":\"2020年3月4日\",\n" + 
				"                       \"color\":\"#1f1f1f\"\n" + 
				"                   },\n" + 
				"                   \"result\": {\n" + 
				"                       \"value\":\"一切顺顺利利\",\n" + 
				"                       \"color\":\"#173177\"\n" + 
				"                   },\n" + 
				"                   \"remark\":{\n" + 
				"                       \"value\":\"请和Lijing联系哦\",\n" + 
				"                       \"color\":\"#173177\"\n" + 
				"                   }\n" + 
				"           }\n" + 
				"       }";
		String result = Util.post(url, data);
		System.out.println(result);
	}

}

在这里插入图片描述

  1. 处理图文信息
	/**
	 * 处理文本消息
	 */
	private static BaseMessage dealTextMessage(Map<String, String> requestMap) {
		//用户发来的内容
		String msg = requestMap.get("Content");
		if(msg.equals("李静")) {
			List<Article> articles = new ArrayList<>();
			articles.add(new Article("李静是个仙女", "李静不用减肥,一点都不胖!", "http://mmbiz.qpic.cn/mmbiz_jpg/RwRicImz9c1B7KXrlnYm5HRWPBE6VgvBnuicONnhc8S9rSIbdT3hOPiavz74efYmW4EvYczo2g1wZxqRE6K6pSzgQ/0", "http://www.baidu.com"));
			NewsMessage nm = new NewsMessage(requestMap, articles);
			return nm;
		}
		//调用方法返回聊天的内容
		String resp = chat(msg);
		TextMessage tm = new TextMessage(requestMap, resp);
		return tm;
	}

当输入李静时,可自动回复图文消息。
在这里插入图片描述

  1. 二维码生成与扫描事件
	/**
	 * 获取带参数二维码的ticket
	 */
	public static String getQrCodeTicket() {
		String at = getAccessToken();
		String url="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+at;
		//生成临时字符二维码
		String data="{\"expire_seconds\": 600, \"action_name\": \"QR_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"lijingCute\"}}}";
		String result = Util.post(url, data);
		String ticket = JSONObject.fromObject(result).getString("ticket");
		return ticket;
	}

	@Test
	public void testQrCode() {
		String ticket = WxService.getQrCodeTicket();
		System.out.println(ticket);
	}

测试类里运行得到ticket:gQH87jwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySmlEdWd3aXpiVC0xcFdrdk51Y1MAAgQikl9eAwRYAgAA
在这里插入图片描述
通过请求https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 可以得到二维码,将TICKET换为刚刚生成的ticket即可。
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQH87jwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySmlEdWd3aXpiVC0xcFdrdk51Y1MAAgQikl9eAwRYAgAA
在这里插入图片描述
也可以通过jsp页面获取到二维码。

@WebServlet("/GetTicket")
public class GetTicket extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		String ticket = WxService.getQrCodeTicket();
		out.print(ticket);
		out.flush();
		out.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript">
	$(function(){
		$("button").click(function(){
			var url = "GetTicket";
			$.get(url,function(ticket){
				var src="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+ticket;
				$("img").attr("src",src);
			});
		});
	});
</script>
</head>
<body>
	<button>生成二维码</button><br>
	<img alt="" src="">
</body>
</html>

在这里插入图片描述

  1. 获取用户的基本信息
    只可以获取关注公众号的用户信息。
	/**
	 * 获取用户的基本信息
	 */
	public static String getUserInfo(String openid) {
		String url="https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
		url = url.replace("ACCESS_TOKEN", getAccessToken()).replace("OPENID", openid);
		String result = Util.get(url);
		return result;
	}

写一个测试类进行测试。
在这里插入图片描述

	@Test
	public void testGetUserInfo() {
		String user="用户的微信号";
		String info = WxService.getUserInfo(user);
		System.out.println(info);
	}

可以获得用户的信息

{"subscribe":1,
"nickname":"Kikyo.",
"sex":2,
"language":"zh_CN",
"city":"",
"province":"",
"country":"圣多美和普林西比"}等信息

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值