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"));
	}
}