package cn.com.softvan.common.wechat; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.log4j.Logger; import cn.com.softvan.bean.wechat.TcWxMenuBean; import cn.com.softvan.bean.wechat.TcWxUserBean; import cn.com.softvan.common.CommonConstant; import cn.com.softvan.common.IdUtils; import cn.com.softvan.common.JedisHelper; import cn.com.softvan.common.Resources; import cn.com.softvan.common.Validator; import cn.com.softvan.common.WebUtils; /** * <p>微信服务号 开放接口API</p> * <ol>[提供机能] * <li>List<String> getCallbackIp(String access_token) 获得微信服务器IP地址列表</li> * <li>public String getAccessToken(Boolean falg,JedisHelper jedisHelper,String appid, String secret) 获取认证信息 access_token 缓存时间7150秒</li> * <li>public String getAccessToken(String appid, String secret) 获取认证信息 access_token [直接访问微信服务api]不建议 会引起混乱</li> * <li>uploadMedia 上传多媒体文件</li> * <li>downMedia 下载多媒体文件</li> * <li>getMenu 获取微信自定义菜单</li> * <li>getUserList 获取帐号的关注者列表</li> * <li>getUser 根据OpenID获取用户基本信息</li> * <li>isErrAccessToken 验证是否认证码问题 认证码有问题返回true 认证码正确返回false</li> * <li>sendCustomerService 通过POST一个JSON数据包来发送消息给普通用户,在48小时内不限制发送次数。此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务。</li> * <li>getQR_SCENE 生成带参数的二维码 临时二维码 </li> * <li>getQR_LIMIT_SCENE 生成带参数的二维码 永久二维码 </li> * </ol> */ public final class WxApiUtil { /** 日志 */ private static final transient Logger log = Logger.getLogger("weixin"); /**公众号每次调用接口时,可能获得正确或错误的返回码,开发者可以根据返回码信息调试接口,排查错误。*/ private static final Map<String,String> errCodeMap=Collections.unmodifiableMap(new HashMap<String,String>(){ private static final long serialVersionUID = -906268846628152015L; { Properties prop = new Properties(); InputStream in = null; try { in=Resources.getResourceAsStream("wxcode.properties"); } catch (Exception e) { log.error("读取微信全局返回码资源文件1异常.", e); } if(in!=null){ try { prop.load(in); Set<Object> keyset = prop.keySet(); for (Object object : keyset) { if(object!=null){ String propValue= prop.getProperty(object.toString()); if(propValue!=null){ put(object.toString(), propValue.trim()); } } } } catch (Exception e) { log.error("读取微信全局返回码说明文件2信息异常.", e); } } } }); /** * 获得微信服务器IP地址列表 * @param access_token 公众号的access_token * @return IP地址列表 */ public List<String> getCallbackIp(String access_token){ //如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表。 String apizhname="【获得微信服务器IP地址列表】"; //错误标记 boolean error_flag=false; List<String> ips=new ArrayList<String>(); String url = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=" + access_token; log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); } if(!error_flag){ //{"ip_list":["127.0.0.1","127.0.0.1"]} JSONArray jsonArray=(dataJson.getJSONArray("ip_list")); if(jsonArray!=null){ for(int i=0;i<jsonArray.size();i++){ String ip=jsonArray.getString(i); ips.add(ip); } } } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return ips; } /** * 获取认证信息 access_token 缓存时间7150秒 * @param falg [true:重新获取 flase:缓存中读取] * @param jedisHelper 缓存服务工具类 * @param appid 第三方用户唯一凭证 * @param secret 第三方用户唯一凭证密钥,即appsecret * @return 获取到的凭证 */ public String getAccessToken(Boolean falg,JedisHelper jedisHelper,String appid, String secret){ //公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在微信公众平台官网-开发者中心页中获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有微信接口时均需使用https协议。 String access_token=null; if(jedisHelper!=null && !falg){ access_token=(String) jedisHelper.get(CommonConstant.SESSION_KEY_USER_WECHAT_ACCESS_TOKEN); } if(null==access_token||falg){ access_token=getAccessToken(appid, secret); if(jedisHelper!=null && access_token!=null){ //认证信息缓存7150秒 jedisHelper.set(CommonConstant.SESSION_KEY_USER_WECHAT_ACCESS_TOKEN,access_token,7150); } } return access_token; } /** * 获取认证信息 access_token [直接访问微信服务api] * @param appid 第三方用户唯一凭证 * @param secret 第三方用户唯一凭证密钥,即appsecret * @return 获取到的凭证 */ public String getAccessToken(String appid, String secret) { String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; String apizhname="【获取认证信息 access_token】"; //错误标记 boolean error_flag=false; HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { client.executeMethod(method); respStr = method.getResponseBodyAsString(); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); } if(!error_flag){ return getJsonInfo(dataJson,"access_token"); } } catch (Exception e) { log.error("="+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return null; } /** * 上传多媒体文件 * 注意事项 上传的多媒体文件有格式和大小限制,如下: 图片(image): 128K,支持JPG格式 * 语音(voice):256K,播放长度不超过60s,支持AMRMP3格式 视频(video):1MB,支持MP4格式 * 缩略图(thumb):64KB,支持JPG格式 * 媒体文件在后台保存时间为3天,即3天后media_id失效。对于需要重复使用的多媒体文件,可以每3天循环上传一次,更新media_id。 * @param access_token 调用接口凭证 * @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb) * @param localFile 文件本地路径 * @return 媒体文件上传后,获取时的唯一标识 */ public String uploadMedia(String access_token,String type, String localFile) { //公众号可调用本接口来上传图片、语音、视频等文件到微信服务器,上传后服务器会返回对应的media_id,公众号此后可根据该media_id来获取多媒体。请注意,media_id是可复用的,调用该接口需http协议。 String apizhname="【上传多媒体文件】"; //错误标记 boolean error_flag=false; String media_id = null; String url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=" + access_token + "&type=" + type; //将服务器相对地址转为物理地址 String local_url = StrUtil.escapeRemoteToLocal(localFile); OutputStream out =null; DataInputStream in =null; BufferedReader reader =null; try { File file = new File(local_url); if (!file.exists() || !file.isFile()) { log.error("文件路径错误==" + local_url); return null; } URL urlObj = new URL(url); HttpURLConnection con = (HttpURLConnection) urlObj.openConnection(); con.setRequestMethod("POST"); // 以Post方式提交表单,默认get方式 con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); // post方式不能使用缓存 // 设置请求头信息 con.setRequestProperty("Connection", "Keep-Alive"); con.setRequestProperty("Charset", "UTF-8"); // 设置边界 String BOUNDARY = "----------" + System.currentTimeMillis(); con.setRequestProperty("content-type","multipart/form-data; boundary=" + BOUNDARY); // 请求正文信息 // 第一部分: StringBuilder sb = new StringBuilder(); sb.append("--"); //必须多两道线 sb.append(BOUNDARY); sb.append("\r\n"); sb.append("Content-Disposition: form-data;name=\"file\";filename=\"" + file.getName() + "\"\r\n"); sb.append("Content-Type:application/octet-stream\r\n\r\n"); byte[] head = sb.toString().getBytes("utf-8"); // 获得输出流 out = new DataOutputStream(con.getOutputStream()); out.write(head); // 文件正文部分 in = new DataInputStream(new FileInputStream(file)); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = in.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } in.close(); // 结尾部分 byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线 out.write(foot); out.flush(); out.close(); /** * 读取服务器响应,必须读取,否则提交不成功 */ // con.getResponseCode(); try { // 定义BufferedReader输入流来读取URL的响应 StringBuffer buffer = new StringBuffer(); reader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8")); String line = null; while ((line = reader.readLine()) != null) { buffer.append(line); } String respStr =buffer.toString(); log.debug("==respStr==" + respStr); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; logErrCode(apizhname, getJsonInfo(dataJson,"errcode")); return getJsonInfo(dataJson,"errcode"); } if(!error_flag){ media_id = getJsonInfo(dataJson,"media_id"); } reader.close(); } catch (Exception e) { log.error("发送POST请求出现异常!" + e); } } catch (Exception e) { log.error("调用微信"+apizhname+"接口上传文件失败!文件路径="+local_url,e); } finally { try {if(in!=null){in.close();}} catch (IOException e) {} try {if(out!=null){ out.close();}} catch (IOException e) {} try {if(reader!=null){reader.close();}} catch (IOException e) {} } return media_id; } /** * 下载多媒体文件 请注意,视频文件不支持下载,调用该接口需http协议。 * @param access_token 调用接口凭证 * @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb) * @param media_id 媒体文件上传后,获取时的唯一标识 * @return 文件下载后保存的本地路径 */ public Map<String,String> downMedia(String access_token,String type, String media_id) { Map<String,String> map=new HashMap<String,String>(); String localFile = null; String apizhname="【下载多媒体文件】"; //错误标记 boolean error_flag=false; SimpleDateFormat df = new SimpleDateFormat("/yyyyMM/"); InputStream in=null; FileOutputStream outStream =null; InputStream inputStream=null; try { String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=" + access_token + "&media_id=" + media_id; //相对路径 String relative_path="/"+type+"/weixin"+df.format(new Date()); // log.error(path); // 图片未保存 下载保存 URL urlObj = new URL(url); HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); String respStr=conn.getHeaderField("Content-disposition"); try { log.debug("===调用微信公共平台 "+apizhname+"==返回文件信息=="+respStr); if(respStr==null){ in=conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8")); String line = null; StringBuffer result = new StringBuffer(); while ((line = reader.readLine()) != null) { result.append(line); } log.debug(result); JSONObject dataJson = JSONObject.fromObject(result); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; logErrCode(apizhname, getJsonInfo(dataJson,"errcode")); map.put("errcode",getJsonInfo(dataJson,"errcode")); } } } catch (Exception e) { }finally{ try {if(in!=null){in.close();}} catch (Exception e) {} } if (!error_flag && conn.getResponseCode() == 200) { String Content_disposition=conn.getHeaderField("Content-disposition"); inputStream = conn.getInputStream(); // 文件类型 String fileType = conn.getContentType(); map.put("fileType",fileType); // // 文件大小 Long fileSize = conn.getContentLengthLong(); map.put("fileSize",""+fileSize); //文件夹 根目录+相对路径 String savePath = Resources.getData("UPLOAD_ROOT_FOLDER")+relative_path; // 文件名 String fileName = WebUtils.getTime("yyyyMMddHHmmss")+WebUtils.getRandomString(5)+"."+WebUtils.getFileExt(Content_disposition); map.put("fileName",fileName); // 创建文件夹 File saveDirFile = new File(savePath); if (!saveDirFile.exists()) { saveDirFile.mkdirs(); } // 检查目录写权限 if (!saveDirFile.canWrite()) { log.error("目录没有写权限,写入文件失败"); throw new Exception("目录没有写权限,写入文件失败"); } // 文件保存目录路径 File file = new File(saveDirFile, fileName); outStream = new FileOutputStream(file); int len = -1; byte[] b = new byte[1024]; while ((len = inputStream.read(b)) != -1) { outStream.write(b, 0, len); } outStream.flush(); outStream.close(); inputStream.close(); //服务器访问路径 localFile=Resources.getData("UPLOAD_ROOT_FOLDER_URL")+relative_path+fileName; map.put("fileUrl",localFile); } } catch (Exception e) { log.error("="+apizhname+"接口访问异常=", e); } finally { try {if(in!=null){in.close();}} catch (IOException e) {} try {if(outStream!=null){outStream.close();}} catch (IOException e) {} try {if(inputStream!=null){inputStream.close();}} catch (IOException e) {} } return map; } /** * 下载微信图片 如果微信号没有多媒体接口权限 替换方案 * @param remote_url 图片远程路径 * @return */ public String downImg(String remote_url){ String local_path=null; // SimpleDateFormat df = new SimpleDateFormat("/yyyyMM/"); try { //相对路径 String relative_path="/image/weixin"+df.format(new Date()); // log.error(path); // 图片未保存 下载保存 URL url = new URL(remote_url); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); if (conn.getResponseCode() == 200) { InputStream inputStream = conn.getInputStream(); // // 文件大小 // Long fileSize = conn.getContentLengthLong(); //文件夹 根目录+相对路径 String savePath = Resources.getData("UPLOAD_ROOT_FOLDER")+relative_path; // 文件名 String fileName = WebUtils.getTime("yyyyMMddHHmmss") + WebUtils.getRandomString(5) + ".jpg"; // 创建文件夹 File saveDirFile = new File(savePath); if (!saveDirFile.exists()) { saveDirFile.mkdirs(); } // 检查目录写权限 if (!saveDirFile.canWrite()) { log.error("目录没有写权限,写入文件失败"); throw new Exception(); } // 文件保存目录路径 File file = new File(saveDirFile, fileName); FileOutputStream outStream = new FileOutputStream(file); int len = -1; byte[] b = new byte[1024]; while ((len = inputStream.read(b)) != -1) { outStream.write(b, 0, len); } outStream.flush(); outStream.close(); inputStream.close(); //服务器访问路径 local_path=Resources.getData("UPLOAD_ROOT_FOLDER_URL")+relative_path+fileName; } } catch (Exception e) { log.error("微信上传图片保存失败!",e); } return local_path; } /** * 获取微信自定义菜单 * @param access_token 调用接口凭证 * @return 微信自定义菜单 列表 */ public List<TcWxMenuBean> getMenu(String access_token) { //---------beans------------- List<TcWxMenuBean> beans=new ArrayList<TcWxMenuBean>(); String url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=" + access_token; String apizhname="【获取微信自定义菜单】"; log.debug("=向微信服务器发起"+apizhname+"="+url); boolean error_flag=false; HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); try { if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); TcWxMenuBean bean=new TcWxMenuBean(); bean.setErrcode(getJsonInfo(dataJson,"errcode")); bean.setErrmsg(getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); beans.add(bean); } } catch (Exception e) { } if(!error_flag){ log.debug(dataJson); //{"menu":{"button":[{"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC","sub_button":[]},{"type":"click","name":"歌手简介","key":"V1001_TODAY_SINGER","sub_button":[]},{"name":"菜单","sub_button":[{"type":"view","name":"搜索","url":"http://www.soso.com/","sub_button":[]},{"type":"view","name":"视频","url":"http://v.qq.com/","sub_button":[]},{"type":"click","name":"赞一下我们","key":"V1001_GOOD","sub_button":[]}]}]}} JSONArray jsonArray=(dataJson.getJSONObject("menu").getJSONArray("button")); if(jsonArray!=null){ //1.获取一级菜单 for(int i=0;i<jsonArray.size();i++){ String s1=jsonArray.getString(i); log.debug("==s1=="+s1); JSONObject j1=JSONObject.fromObject(s1); String uuid=IdUtils.createUUID(32); //TODO-- TcWxMenuBean bean=new TcWxMenuBean(); bean.setId(uuid);//主键id bean.setMenu_name(j1.getString("name")); JSONArray j2Array=j1.getJSONArray("sub_button"); bean.setBeans(new ArrayList<TcWxMenuBean>()); bean.setSort_num((i+1)*10); //判断是否有子菜单 if(j2Array!=null && j2Array.size()>0){ for(int n=0;n<j2Array.size();n++){ String s2=j2Array.getString(n); log.debug("==s2=="+s2); JSONObject j2=JSONObject.fromObject(s2); //TODO-- TcWxMenuBean bean2=new TcWxMenuBean(); bean2.setId(IdUtils.createUUID(32));//主键id bean2.setParent_id(bean.getId());//父菜单id bean2.setMenu_name(j2.getString("name")); try { bean2.setMenu_type(j2.getString("type")); } catch (Exception e1) { } try { bean2.setMenu_key(j2.getString("key")); } catch (Exception e) { } try { bean2.setMenu_url(j2.getString("url")); } catch (Exception e) { } bean2.setSort_num(n+1); bean.getBeans().add(bean2); } }else{ try { bean.setMenu_type(j1.getString("type")); } catch (Exception e) { } try { bean.setMenu_key(j1.getString("key")); } catch (Exception e) { } try { bean.setMenu_url(j1.getString("url")); } catch (Exception e) { } } //add beans.add(bean); } } } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return beans; } /** * 公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。 * @param access_token * @param next_openid 第一个拉取的OPENID,不填默认从头开始拉取 * @return */ public List<TcWxUserBean> getUserList(String access_token,String next_openid){ /* * total 关注该公众账号的总用户数 * count 拉取的OPENID个数,最大值为10000 * data 列表数据,OPENID的列表 * next_openid 拉取列表的后一个用户的OPENID */ String apizhname="【获取帐号的关注者列表】"; boolean error_flag=false; List<TcWxUserBean> beans=new ArrayList<TcWxUserBean>(); String url="https://api.weixin.qq.com/cgi-bin/user/get?access_token="+access_token; if(Validator.notEmpty(next_openid)){ url+="&next_openid="+next_openid; } log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); try { if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); TcWxUserBean bean=new TcWxUserBean(); bean.setErrcode(getJsonInfo(dataJson,"errcode")); bean.setErrmsg(getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); beans.add(bean); } } catch (Exception e) { } if(!error_flag){ Long total=dataJson.getLong("total"); Integer count=dataJson.getInt("count"); String next_oid=dataJson.getString("next_openid"); log.debug("=====next_oid==="+next_oid+"===2=="); JSONObject data=dataJson.getJSONObject("data"); JSONArray array=data.getJSONArray("openid"); Object[] oIdArray=array.toArray(); if(oIdArray!=null){ TcWxUserBean bean=null; for(Object oid:oIdArray){ bean=getUser(access_token,oid.toString()); if(bean!=null){ bean.setTotal(total); bean.setCount(count); bean.setNext_openid(next_oid); //add beans.add(bean); } } } } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return beans; } /** * 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。 * @param access_token 是 调用接口凭证 * @param openid 是 普通用户的标识,对当前公众号唯一 * @return */ public TcWxUserBean getUser(String access_token,String openid){ /* * access_token 是 调用接口凭证 openid 是 普通用户的标识,对当前公众号唯一 lang 否 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语 */ String apizhname="【获取用户基本信息】"; boolean error_flag=false; TcWxUserBean bean=new TcWxUserBean(); bean.setOpenid(openid); String url="https://api.weixin.qq.com/cgi-bin/user/info?access_token="+access_token+"&openid="+openid+"&lang=zh_CN"; log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); try { if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); bean.setErrcode(getJsonInfo(dataJson,"errcode")); bean.setErrmsg(getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); } } catch (Exception e) { } if(!error_flag){ // subscribe 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。 bean.setSubscribe(dataJson.getString("subscribe")); // openid 用户的标识,对当前公众号唯一 //=------------- // nickname 用户的昵称 bean.setNickname(dataJson.getString("nickname")); // sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 bean.setSex(dataJson.getString("sex")); // city 用户所在城市 bean.setCity(dataJson.getString("city")); // country 用户所在国家 bean.setCountry(dataJson.getString("country")); // province 用户所在省份 bean.setProvince(dataJson.getString("province")); // language 用户的语言,简体中文为zh_CN bean.setLanguage(dataJson.getString("language")); // headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空 bean.setHeadimgurl(dataJson.getString("headimgurl")); // subscribe_time 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间 bean.setSubscribe_time(dataJson.getString("subscribe_time")); } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return bean; } /** * 验证是否认证码问题 认证码有问题返回true 认证码正确返回false * @param errorCode * @return */ public boolean isErrAccessToken(String errorCode){ if(errorCode!=null && ("40001".equals(errorCode)||"40014".equals(errorCode)||"42001".equals(errorCode))){ return true; } return false; } /** * 通过POST一个JSON数据包来发送消息给普通用户,在48小时内不限制发送次数。此接口主要用于客服等有人工消息处理环节的功能,方便开发者为用户提供更加优质的服务。 * @param access_token 是 调用接口凭证 * @param openid 是 普通用户的标识,对当前公众号唯一 * @return */ public String sendCustomerService(String access_token,String openid,String json){ String apizhname="【发送消息给普通用户】"; boolean error_flag=false; String msg="1"; String url="https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+access_token; log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { // method.setRequestBody(json); RequestEntity entity = new StringRequestEntity(json, "text/xml", "utf-8"); method.setRequestEntity(entity); client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); msg=getJsonInfo(dataJson,"errcode"); } if(!error_flag){ log.debug("信息发送完成!"); } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return msg; } /** * 生成带参数的二维码 临时二维码 * @param access_token 该二维码有效时间,以秒为单位。 最大不超过1800。 * @param expire_seconds * @param scene_id 场景值ID,临时二维码时为32位非0整型 * @return */ public String getQRScene(String access_token,int expire_seconds,String scene_id){ String apizhname="【生成带参数的二维码 临时二维码】"; boolean error_flag=false; String msg=null; String json="{\"expire_seconds\": "+expire_seconds+", \"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": "+scene_id+"}}}"; String url="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+access_token; log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { // method.setRequestBody(json); RequestEntity entity = new StringRequestEntity(json, "text/xml", "utf-8"); method.setRequestEntity(entity); client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); msg=getJsonInfo(dataJson,"errcode"); } if(!error_flag){ msg=getJsonInfo(dataJson,"ticket"); } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return msg; } /** * 生成带参数的二维码 永久二维码 * @param access_token * @param scene_id 永久二维码时最大值为100000(目前参数只支持1--100000) * @return */ public String getQRSceneLimit(String access_token,String scene_id){ String apizhname="【生成带参数的二维码 永久二维码】"; boolean error_flag=false; String msg=null; String json="{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": "+scene_id+"}}}"; String url="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+access_token; log.debug("=向微信服务器发起"+apizhname+"="+url); HttpClient client = new HttpClient(); PostMethod method = new PostMethod(url); method.getParams().setContentCharset("utf-8"); // 发送http请求 String respStr = ""; try { // method.setRequestBody(json); RequestEntity entity = new StringRequestEntity(json, "text/xml", "utf-8"); method.setRequestEntity(entity); client.executeMethod(method); respStr = method.getResponseBodyAsString(); log.debug("=获得"+apizhname+"反馈消息="+respStr); JSONObject dataJson = JSONObject.fromObject(respStr); if(dataJson!=null && null!=getJsonInfo(dataJson,"errcode")){ error_flag=true; log.error("="+apizhname+"="+getJsonInfo(dataJson,"errcode")+":"+getErrMsg(dataJson,getJsonInfo(dataJson,"errcode"))); msg=getJsonInfo(dataJson,"errcode"); } if(!error_flag){ msg=getJsonInfo(dataJson,"ticket"); } } catch (Exception e) { log.error("=向微信服务器发起"+apizhname+"接口访问异常="+respStr,e); }finally{ try {method.releaseConnection();} catch (Exception e) {} try {client.getHttpConnectionManager().closeIdleConnections(0);} catch (Exception e) {} } return msg; } /** * 获取json对象的具体数据 * @param jsonObject * @param key * @return */ private String getJsonInfo(JSONObject jsonObject,String key){ String info=null; try { if(jsonObject!=null){ info=jsonObject.getString(key); if(info!=null){ info=StringUtils.trim(info); } } } catch (Exception e) { } return info; } /** * 获取错误代码 描述信息 * @param dataJson * @param errcode * @return */ private String getErrMsg(JSONObject dataJson,String errcode){ String errmsg=errCodeMap.get(errcode); if(errmsg==null){ errmsg= getJsonInfo(dataJson,"errmsg"); } return errmsg; } /** * 记录微信接口反馈的错误信息 * @param apizhname 接口描述 * @param errcode 错误code */ private void logErrCode(String apizhname,String errcode){ log.error("="+apizhname+"="+errcode+":"+errCodeMap.get(errcode)); } // http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html // 1 上传图文消息素材【订阅号与服务号认证后均可用】 // 2 根据分组进行群发【订阅号与服务号认证后均可用】 // 3 根据OpenID列表群发【订阅号不可用,服务号认证后可用】 // 4 删除群发【订阅号与服务号认证后均可用】 // 5 预览接口【订阅号与服务号认证后均可用】 // 6 查询群发消息发送状态【订阅号与服务号认证后均可用】 // http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html // 7 事件推送群发结果 // 1 设置所属行业 // 2 获得模板ID // 3 发送模板消息 // 4 事件推送 // http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html // 1 客服帐号管理 // 1.1 添加客服帐号 // 1.2 修改客服帐号 // 1.3 删除客服帐号 // 1.4 设置客服帐号的头像 // 1.5 获取所有客服账号 // 1.6 接口的统一参数说明 // 2 客服接口-发消息 // http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html // -用户分组管理- // 1 创建分组 // 2 查询所有分组 // 3 查询用户所在分组 // 4 修改分组名 // 5 移动用户分组 // 6 批量移动用户分组 // http://mp.weixin.qq.com/wiki/1/4a566d20d67def0b3c1afc55121d2419.html // 设置用户备注名 // 获取用户基本信息(UnionID机制) // 获取用户列表 // 获取用户地理位置 // 网页授权获取用户基本信息 // 网页获取用户网络状态(JS接口) // http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html // 自定义菜单创建接口 // 自定义菜单查询接口 // 自定义菜单删除接口 // 自定义菜单事件推送 // http://mp.weixin.qq.com/wiki/10/165c9b15eddcfbd8699ac12b0bd89ae6.html // 长链接转短链接接口 // http://mp.weixin.qq.com/wiki/5/ae230189c9bd07a6b221f48619aeef35.html // 将消息转发到多客服 // 客服管理 // 获取客服聊天记录 // PC客户端自定义插件接口 // http://mp.weixin.qq.com/wiki/0/0ce78b3c9524811fee34aba3e33f3448.html // 语义理解 /** * 微信认证 * @param token * @param signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 * @param timestamp 时间戳 * @param nonce 随机数 * @return */ public boolean checkSignature(String token,String signature,String timestamp,String nonce){ String[] arr = new String[]{token,timestamp,nonce}; Arrays.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"); byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { log.error("微信认证错误!", e); } content = null; return tmpStr!=null?tmpStr.equals(signature.toUpperCase()):false; } /** * 将字节转换为十六进制字符串 * @param ib * @return */ private String byteToHexStr(byte ib) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] ob = new char[2]; ob[0] = Digit[(ib >>> 4) & 0X0F]; ob[1] = Digit[ib & 0X0F]; String s = new String(ob); return s; } /** * 将字节数组转换为十六进制字符串 * @param bytearray * @return */ private String byteToStr(byte[] bytearray) { String strDigest = ""; for (int i = 0; i < bytearray.length; i++) { strDigest += byteToHexStr(bytearray[i]); } return strDigest; } // test public static void main(String[] args) throws Exception { // WxApiMpUtil api=new WxApiMpUtil(); System.out.println(errCodeMap.get("-1")); // System.out.println(StrUtil.escapeRemoteToLocal("/upload/image/1.jpg")); // System.out.println(StrUtil.escapeLocalToRemote("/home/baseos/upload/image/1.jpg")); } }