个人案例
- 肥肥运维
后台管理服务
肥肥运维扫码体验
- 肥肥盒子
线上点餐,线下无人机器制作!
肥肥盒子扫码体验
- Java多线程详解
引言随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 程序,进程,线程的基本概念+并行与并发:程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期线程:进程可进一步细化为线程,是一个程序内部的一条执行路径即:线程《线程(一个程序可以有多个线程) 程序:静态的代码 进程:动态执行的程序 线程:进程中要同时干几件事时,每一件事的执行路径成为线程。 并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事线程的相关API//获取当前线程的名字 Thread.currentThread().getName() 1.start():1.启动当前线程2.调用线程中的run方法 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 3.currentThread():静态方法,返回执行当前代码的线程 4.getName():获取当前线程的名字 5.setName():设置当前线程的名字 6.yield():主动释放当前线程的执行权 7.join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去 8.stop():过时方法。当执行此方法时,强制结束当前线程。 9.sleep(long millitime):线程休眠一段时间 10.isAlive():判断当前线程是否存活 判断是否是多线程一条线程即为一条执行路径,即当能用一条路径画出来时即为一个线程 例:如下看似既执行了方法一,又执行了方法2,但是其实质就是主线程在执行方法2和方法1这一条路径,所以就是一个线程 public class Sample{ public void method1(String str){ System.out.println(str); } public void method2(String str){ method1(str); } public static void main(String[] args){ Sample s = new Sample(); s.method2("hello"); } } [图片] 线程的调度调度策略: 时间片:线程的调度采用时间片轮转的方式 抢占式:高优先级的线程抢占CPU Java的调度方法: 1.对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略 2.对高优先级,使用优先调度的抢占式策略 线程的优先级等级: MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5 方法: getPriority():返回线程优先级 setPriority(int newPriority):改变线程的优先级 注意!:高优先级的线程要抢占低优先级的线程的cpu的执行权。但是仅是从概率上来说的,高优先级的线程更有可能被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。 多线程的创建方式1. 方式1:继承于Thread类1.创建一个集成于Thread类的子类 (通过ctrl+o(override)输入run查找run方法)2.重写Thread类的run()方法3.创建Thread子类的对象4.通过此对象调用start()方法start与run方法的区别:start方法的作用:1.启动当前线程 2.调用当前线程的重写的run方法(在主线程中生成子线程,有两条线程) 调用start方法以后,一条路径代表一个线程,同时执行两线程时,因为时间片的轮换,所以执行过程随机分配,且一个线程对象只能调用一次start方法。 run方法的作用:在主线程中调用以后,直接在主线程一条线程中执行了该线程中run的方法。(调用线程中的run方法,只调用run方法,并不新开线程) 总结:我们不能通过run方法来新开一个线程,只能调用线程中重写的run方法(可以在线程中不断的调用run方法,但是不能开启子线程,即不能同时干几件事),start是开启线程,再调用方法(即默认开启一次线程,调用一次run方法,可以同时执行几件事) 多线程例子(火车站多窗口卖票问题) package com.example.paoduantui.Thread; import android.view.Window; /** * * 创建三个窗口卖票,总票数为100张,使用继承自Thread方式 * 用静态变量保证三个线程的数据独一份 * * 存在线程的安全问题,有待解决 * * */ public class ThreadDemo extends Thread{ public static void main(String[] args){ window t1 = new window(); window t2 = new window(); window t3 = new window(); t1.setName("售票口1"); t2.setName("售票口2"); t3.setName("售票口3"); t1.start(); t2.start(); t3.start(); } } class window extends Thread{ private static int ticket = 100; //将其加载在类的静态区,所有线程共享该静态变量 @Override public void run() { while(true){ if(ticket>0){ // try { // sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } System.out.println(getName()+"当前售出第"+ticket+"张票"); ticket--; }else{ break; } } } } 2. 方式2:实现Runable接口方式1.创建一个实现了Runable接口的类2.实现类去实现Runnable中的抽象方法:run()3.创建实现类的对象4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象5.通过Thread类的对象调用start()具体操作,将一个类实现Runable接口,(插上接口一端)。 另外一端,通过实现类的对象与线程对象通过此Runable接口插上接口实现 package com.example.paoduantui.Thread; public class ThreadDemo01 { public static void main(String[] args){ window1 w = new window1(); //虽然有三个线程,但是只有一个窗口类实现的Runnable方法,由于三个线程共用一个window对象,所以自动共用100张票 Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } } class window1 implements Runnable{ private int ticket = 100; @Override public void run() { while(true){ if(ticket>0){ // try { // sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票"); ticket--; }else{ break; } } } } 比较创建线程的两种方式: 开发中,优先选择实现Runable接口的方式 原因1:实现的方式没有类的单继承性的局限性 2:实现的方式更适合用来处理多个线程有共享数据的情况 联系:Thread也是实现自Runable,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中 3.新增的两种创建多线程方式1.实现callable接口方式:与使用runnable方式相比,callable功能更强大些:runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值方法可以抛出异常支持泛型的返回值需要借助FutureTask类,比如获取返回结果package com.example.paoduantui.Thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * 创建线程的方式三:实现callable接口。---JDK 5.0新增 *是否多线程?否,就一个线程 * * 比runable多一个FutureTask类,用来接收call方法的返回值。 * 适用于需要从线程中接收返回值的形式 * * //callable实现新建线程的步骤: * 1.创建一个实现callable的实现类 * 2.实现call方法,将此线程需要执行的操作声明在call()中 * 3.创建callable实现类的对象 * 4.将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象 * 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值) * * */ //实现callable接口的call方法 class NumThread implements Callable{ private int sum=0;// //可以抛出异常 @Override public Object call() throws Exception { for(int i = 0;i<=100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName()+":"+i); sum += i; } } return sum; } } public class ThreadNew { public static void main(String[] args){ //new一个实现callable接口的对象 NumThread numThread = new NumThread(); //通过futureTask对象的get方法来接收futureTask的值 FutureTask futureTask = new FutureTask(numThread); Thread t1 = new Thread(futureTask); t1.setName("线程1"); t1.start(); try { //get返回值即为FutureTask构造器参数callable实现类重写的call的返回值 Object sum = futureTask.get(); System.out.println(Thread.currentThread().getName()+":"+sum); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } 使用线程池的方式:背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。(数据库连接池) 好处:提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止 。。。。。。 JDK 5.0 起提供了线程池相关API:ExecutorService 和 Executors ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor. void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable void shutdown():关闭连接池。 Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池Executors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池Executors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 线程池构造批量线程代码如下: package com.example.paoduantui.Thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 创建线程的方式四:使用线程池(批量使用线程) *1.需要创建实现runnable或者callable接口方式的对象 * 2.创建executorservice线程池 * 3.将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。 * 4.关闭线程池 * * */ class NumberThread implements Runnable{ @Override public void run() { for(int i = 0;i<=100;i++){ if (i % 2 ==0 ) System.out.println(Thread.currentThread().getName()+":"+i); } } } class NumberThread1 implements Runnable{ @Override public void run() { for(int i = 0;i<100; i++){ if(i%2==1){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class ThreadPool { public static void main(String[] args){ //创建固定线程个数为十个的线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); //new一个Runnable接口的对象 NumberThread number = new NumberThread(); NumberThread1 number1 = new NumberThread1(); //执行线程,最多十个 executorService.execute(number1); executorService.execute(number);//适合适用于Runnable //executorService.submit();//适合使用于Callable //关闭线程池 executorService.shutdown(); } } 目前两种方式要想调用新线程,都需要用到Thread中的start方法。 java virtual machine(JVM):java虚拟机内存结构程序(一段静态的代码)——————》加载到内存中——————》进程(加载到内存中的代码,动态的程序) 进程可细分为多个线程,一个线程代表一个程序内部的一条执行路径 每个线程有其独立的程序计数器(PC,指导着程序向下执行)与运行栈(本地变量等,本地方法等) [图片]
2021-01-07 - recycle-view 商品长列表应用
recycle-view 在商品长列表中的应用 参考官方文档 WXML: [代码]<recycle-view class="list" batch="{{batchSetRecycleData}}" id="recycleId" bindscrolltolower="bindscrolltolower" scroll-y="true"> <recycle-item class="record" wx:for="{{recycleList}}" wx:key="id"> <view class="record_image" style="background:url( {{item.images[0] }}&x-oss-process=image/resize,s_320 )"></view> <view class="record_view">{{index}} . {{item.title}}</view> </recycle-item> <view slot="after">加载中...</view> </recycle-view> [代码] JS: [代码]const app = getApp() const createRecycleContext = require('miniprogram-recycle-view'); Page({ pageNum:1,//页码 listobj: Object,//RecycleContext对象 postflg:true,//是否可以加载列表,用户误触控制 windowWidth:0,//系统页面可视宽度 data: {}, onReady: function () { var than = this; //获取系统参数 wx.getSystemInfo({ success: function(res) { than.windowWidth = res.windowWidth; //创建RecycleContext对象来管理 recycle-view 定义的的数据 than.listobj = createRecycleContext({ id: 'recycleId', dataKey: 'recycleList', page: than, itemSize: than.itemSizeFunc, }) than.getlist();//请求接口 }, }) }, //设置item宽高信息,样式所设必须与之相同 itemSizeFunc: function (item, idx) { var than = this; return { width: than.windowWidth * 0.47, height: than.windowWidth * 0.61 } }, //滚动到底部监听,分页加载 bindscrolltolower(e) { console.log('滚动到底部----'); if(this.postflg){ this.postflg = false;//请求完成前不再更改页码请求接口 this.pageNum++; this.getlist(); } }, //数据请求 getlist(){ var than = this; wx.request({ url: 'https://w.taopaitang.com/api/discover?page=1&pagenum=10', data:{ page: than.pageNum, pagenum:10, }, method: 'get', success(res){ console.log('数据请求成功----' + than.pageNum +'---',res); if(res.data.message){ //append RecycleContext 对象提供的方法:在当前的长列表数据上追加list数据 than.listobj.append(res.data.data.items); than.postflg = true; } } }) } }) [代码] 全部代码 -------> 代码片段 <--------- 我想把此列表改成瀑布流展示,求路过的大神指点下!!!!!!
2019-10-07 - 如何使用第三方 npm 扩展包
上一篇传参的示例中,不知道你是否注意到,像 [代码]isPast[代码](判断日期是否为过去) 或者 [代码]number[代码](距离当前的天数) 参数,根本就不需要传输嘛,这些应该是计算出来的。 然而,如果让你来写日期计算的 API,我相信绝大多数开发者,都不一定能够写出来,好在,这块有成熟的库可以使用,例如:[代码]moment.js[代码],还有 [代码]dayjs[代码],这篇文章就来介绍一下,如何在小程序中使用第三方 npm 扩展? 这块内容,官方文档已经写得比较清楚了,在工具 — 开发辅助 — npm 支持下,目录层次比较深,所以,在最开始的时候,建议通读一遍文档,还是非常有必要的。 npm 支持的要求 小程序并非从一开始就支持 npm,而是在基础库版本 2.2.1 或以上、及开发者工具 1.02.1808300 或以上的时候,才开始支持。 所以,这两个条件一定要注意,当然,如果才开始学习小程序开发,用最新版的就可以了。 安装 dayjs 用命令行进入项目目录,使用下面命令安装 dayjs [代码]npm init npm i dayjs --production [代码] 第一条命令是初始化 npm,第二条命令是安装 dayjs 包。 然后打开开发者工具,在本地设置中,勾选使用 npm 模块,如下图: [图片] 最后,打开工具菜单,点击构建 npm 即可。 [图片] 目录变化 构建完成后,项目目录中会多出几个文件: package.json package-lock.json node_modules miniprogram_npm 前面三个是 npm 初始化时,默认生成的,最后一个目录是开发者工具构建 npm 时生成的,可以理解为小程序版的 npm 包。 [图片] 使用方法与 node.js 开发一致,下面就来完善上一篇中的例子。 页面改写 打开 days 的 index.wxml 文件,将页面静态内容替换成变量,代码如下: [代码]<view class="list" bindtap='goto' data-title="{{ day.title }}" data-date="{{ day.date }}" data-isPast="{{ day.isPast }}" data-number="{{ day.number }}"> <view class="title">{{ day.title }}</view> <view class="date-number {{ day.isPast?'primary':'danger' }}">{{ day.number }}</view> <view class="date-desc">{{ day.isPast?"已过天数":"还剩天数" }}</view> <view class="desc">{{ day.date }}</view> </view> [代码] 可以看出,从逻辑层传过来一个 day 的对象。 逻辑层添加 day 对象数据,打开 days 目录的 index.js 文件,编写代码如下: [代码]data: { day: { date: '2019-10-01', title: '新中国成立 70 周年' } }, [代码] 这里只有 date 和 title 属性值,并没有 isPast 和 number,因为 isPast 和 number 值应该是实时计算出来的,而无需储存。 当前的页面是不完整的,接下来就是 [代码]dayjs[代码] 上场的时候了。 引入 dayjs 初始化数据 接着编辑 js 代码,引入 dayjs 模块,编写初始化数据函数,如下代码所示: [代码]// 引入 dayjs const dayjs = require('dayjs'); // 当天日期 const today = dayjs().format('YYYY-MM-DD'); // 初始化数据 const init_data = function(day){ let date = day.date; day.isPast = today > date; day.number = dayjs(date).diff(dayjs(today), 'day'); return day; } [代码] dayjs 的用法不多介绍了,可自行查看文档,[代码]init_data[代码] 函数在初始数据上,添加了两个参数 isPast 和 number,使用 dayjs 模块提供的 API 可以快速计算它们的值。 最后,在页面事件函数 [代码]onShow[代码] 中,调用初始化函数,代码如下: [代码]onShow: function () { this.setData({ day: init_data(this.data.day) }); }, [代码] 这样,整体代码就写完了,只需要一个 [代码]title[代码] 和 [代码]date[代码],就可以实时计算出相隔的天数,引入 [代码]dayjs[代码] 模块,让这个计算的过程,简单了许多。 总结 这篇文章介绍了 npm 包的引入,安装以及使用。并实际演示了 dayjs 日期库的使用,进而完善了上一篇中的例子。 这里留一个作业题,看能不能结合前面的内容,完成一个完整的「计算日子」功能。 PS. 目前 demo 中只缺少添加日期,显示日期列表,以及存储日期的功能了,正好这些知识点,前面已经都介绍过了。 写完这篇文章,距离国庆节还剩下 22 天。 [图片] 更多文章:https://github.com/pengloo53/miniprogram-articles
2019-09-09 - 获取素材列表返回40004 invalid media type
问题一: 调用 https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN 后,我用的是url传参,无论type值是voice, image还是news, 总是给我返回 {"errcode":40004,"errmsg":"invalid media type"} 问题二: 我改用postman、用post传值还是出现问题 [图片] 解决:传参必须json传,下面是示例。不过我用的测试号,获取到的素材列表是空的。 [图片]
2019-08-03 - JAVA 小程序支付 统一付款
商户系统和微信支付系统主要交互说明 [图片] 步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。 步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。 步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay 步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】 步骤5:商户后台接收支付通知。api参见【支付结果通知API】 步骤6:商户后台查询支付结果。,api参见【查询订单API】 API链接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3 支付的流程为: 先调用统一下单API 接着在小程序wx.requestPayment发起支付 支付完之后会调用支付结果通知 [图片] 后台Java代码:(基于SpringMVC) [代码]/**[代码] [代码] [代码][代码]* 统一下单[代码] [代码] [代码][代码]* @param userId[代码] [代码] [代码][代码]* @param activityId[代码] [代码] [代码][代码]* @param openId[代码] [代码] [代码][代码]* @param orderNo[代码] [代码] [代码][代码]* @param money[代码] [代码] [代码][代码]* @param describe[代码] [代码] [代码][代码]* @param detail[代码] [代码] [代码][代码]* @return[代码] [代码] [代码][代码]*/[代码] [代码] [代码][代码]private[代码] [代码]WxappPayDto prePay(String userId,String activityId,String openId,String orderNo,String money,String describe,String detail){[代码] [代码] [代码][代码]money = String.valueOf(Long.valueOf(money.substring([代码][代码]0[代码][代码], money.length()-[代码][代码]1[代码][代码]))*[代码][代码]100[代码][代码]);[代码] [代码] [代码][代码]String currTime = PayUtils.getCurrTime();[代码] [代码] [代码][代码]//8位日期[代码] [代码] [代码][代码]String strTime = currTime.substring([代码][代码]8[代码][代码], currTime.length());[代码] [代码] [代码][代码]//四位随机数[代码] [代码] [代码][代码]String strRandom = PayUtils.buildRandom([代码][代码]4[代码][代码]) + [代码][代码]""[代码][代码];[代码] [代码] [代码][代码]//10位序列号,可以自行调整。[代码] [代码] [代码][代码]String nonceStr = strTime + strRandom;[代码] [代码] [代码][代码]//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。[代码] [代码] [代码][代码]String notifyUrl = baseUrl+[代码][代码]"/notify"[代码][代码];[代码] [代码] [代码][代码]//附加数据,以一定格式保存userId和activityId。原样返回。[代码] [代码] [代码][代码]String attach = userId+[代码][代码]"#wx#"[代码][代码]+activityId;[代码] [代码] [代码][代码]SortedMap<String, String> packageParams = [代码][代码]new[代码] [代码]TreeMap<String, String>();[代码] [代码] [代码][代码]packageParams.put([代码][代码]"appid"[代码][代码], appId);[代码] [代码] [代码][代码]packageParams.put([代码][代码]"attach"[代码][代码], attach);[代码][代码]//附加数据[代码] [代码] [代码][代码]packageParams.put([代码][代码]"body"[代码][代码], describe);[代码][代码]//商品描述[代码] [代码] [代码][代码]packageParams.put([代码][代码]"detail"[代码][代码], detail);[代码] [代码] [代码][代码]packageParams.put([代码][代码]"mch_id"[代码][代码], mchId);[代码][代码]//商户号[代码] [代码] [代码][代码]packageParams.put([代码][代码]"nonce_str"[代码][代码], nonceStr);[代码][代码]//随机数[代码] [代码] [代码][代码]packageParams.put([代码][代码]"notify_url"[代码][代码], notifyUrl);[代码] [代码] [代码][代码]packageParams.put([代码][代码]"openid"[代码][代码], openId);[代码] [代码] [代码][代码]packageParams.put([代码][代码]"out_trade_no"[代码][代码], orderNo);[代码][代码]//商户订单号[代码] [代码] [代码][代码]packageParams.put([代码][代码]"spbill_create_ip"[代码][代码], spBillCreateIp);[代码][代码]//订单生成的机器 IP[代码] [代码] [代码][代码]packageParams.put([代码][代码]"total_fee"[代码][代码], money);[代码][代码]//总金额[代码] [代码] [代码][代码]packageParams.put([代码][代码]"trade_type"[代码][代码], [代码][代码]"JSAPI"[代码][代码]); [代码] [代码] [代码] [代码] [代码][代码]String sign = PayUtils.createSign(packageParams,key);[代码] [代码] [代码][代码]String xml=[代码][代码]"<xml>"[代码][代码]+[代码] [代码] [代码][代码]"<appid>"[代码][代码]+appId+[代码][代码]"</appid>"[代码][代码]+[代码] [代码] [代码][代码]"<attach>"[代码][代码]+attach+[代码][代码]"</attach>"[代码][代码]+[代码] [代码] [代码][代码]"<body><![CDATA["[代码][代码]+describe+[代码][代码]"]]></body>"[代码][代码]+[代码] [代码] [代码][代码]"<detail><![CDATA["[代码][代码]+detail+[代码][代码]"]]></detail>"[代码][代码]+[代码] [代码] [代码][代码]"<mch_id>"[代码][代码]+mchId+[代码][代码]"</mch_id>"[代码][代码]+[代码] [代码] [代码][代码]"<nonce_str>"[代码][代码]+nonceStr+[代码][代码]"</nonce_str>"[代码][代码]+[代码] [代码] [代码][代码]"<sign>"[代码][代码]+sign+[代码][代码]"</sign>"[代码][代码]+[代码] [代码] [代码][代码]"<notify_url>"[代码][代码]+notifyUrl+[代码][代码]"</notify_url>"[代码][代码]+[代码] [代码] [代码][代码]"<openid>"[代码][代码]+openId+[代码][代码]"</openid>"[代码][代码]+[代码] [代码] [代码][代码]"<out_trade_no>"[代码][代码]+orderNo+[代码][代码]"</out_trade_no>"[代码][代码]+[代码] [代码] [代码][代码]"<spbill_create_ip>"[代码][代码]+spBillCreateIp+[代码][代码]"</spbill_create_ip>"[代码][代码]+[代码] [代码] [代码][代码]"<total_fee>"[代码][代码]+money+[代码][代码]"</total_fee>"[代码][代码]+[代码] [代码] [代码][代码]"<trade_type>JSAPI</trade_type>"[代码][代码]+[代码] [代码] [代码][代码]"</xml>"[代码][代码];[代码] [代码] [代码][代码]String prepay_id=[代码][代码]""[代码][代码];[代码] [代码] [代码][代码]try[代码] [代码]{[代码] [代码] [代码][代码]prepay_id = PayUtils.getPayNo(createOrderURL, xml);[代码] [代码] [代码][代码]if[代码][代码](prepay_id.equals([代码][代码]""[代码][代码])){[代码] [代码] [代码][代码]//错误提示[代码] [代码] [代码][代码]System.out.println([代码][代码]"统一支付接口获取预支付订单出错"[代码][代码]);[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e1) {[代码] [代码] [代码][代码]e1.printStackTrace();[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]SortedMap<String, String> finalpackage = [代码][代码]new[代码] [代码]TreeMap<String, String>();[代码] [代码] [代码][代码]String timestamp = PayUtils.getTimeStamp();[代码] [代码] [代码][代码]String packages = [代码][代码]"prepay_id="[代码][代码]+prepay_id;[代码] [代码] [代码][代码]finalpackage.put([代码][代码]"appId"[代码][代码], appId);[代码] [代码] [代码][代码]finalpackage.put([代码][代码]"nonceStr"[代码][代码], nonceStr); [代码] [代码] [代码][代码]finalpackage.put([代码][代码]"package"[代码][代码], packages); [代码] [代码] [代码][代码]finalpackage.put([代码][代码]"signType"[代码][代码], [代码][代码]"MD5"[代码][代码]);[代码] [代码] [代码][代码]finalpackage.put([代码][代码]"timeStamp"[代码][代码], timestamp); [代码] [代码] [代码][代码]String finalsign = PayUtils.createSign(finalpackage,key);[代码] [代码] [代码] [代码] [代码][代码]WxappPayDto dto = [代码][代码]new[代码] [代码]WxappPayDto();[代码] [代码] [代码][代码]dto.setNonceStr(nonceStr);[代码] [代码] [代码][代码]dto.setPackage_(packages);[代码] [代码] [代码][代码]dto.setPaySign(finalsign);[代码] [代码] [代码][代码]dto.setSignType([代码][代码]"MD5"[代码][代码]);[代码] [代码] [代码][代码]dto.setTimeStamp(timestamp);[代码] [代码] [代码][代码]return[代码] [代码]dto;[代码] [代码] [代码][代码]}[代码] 付款回调地址 [代码]/**[代码] [代码] [代码][代码]* 支付完成通知[代码] [代码] [代码][代码]* @param request[代码] [代码] [代码][代码]* @param response[代码] [代码] [代码][代码]* @return[代码] [代码] [代码][代码]* @throws Exception[代码] [代码] [代码][代码]*/[代码] [代码] [代码][代码]@RequestMapping[代码][代码](value = [代码][代码]"/notify"[代码][代码],method = RequestMethod.POST)[代码] [代码] [代码][代码]public[代码] [代码]String notify(HttpServletRequest request, HttpServletResponse response) [代码][代码]throws[代码] [代码]Exception {[代码] [代码] [代码][代码]BufferedReader br = [代码][代码]new[代码] [代码]BufferedReader([代码][代码]new[代码] [代码]InputStreamReader((ServletInputStream)request.getInputStream()));[代码] [代码] [代码][代码]String line = [代码][代码]null[代码][代码];[代码] [代码] [代码][代码]StringBuilder sb = [代码][代码]new[代码] [代码]StringBuilder();[代码] [代码] [代码][代码]while[代码][代码]((line = br.readLine())!=[代码][代码]null[代码][代码]){[代码] [代码] [代码][代码]sb.append(line);[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]//解析并给微信发回收到通知确认[代码] [代码] [代码][代码]Map map = PayUtils.doXMLParse(sb.toString());[代码] [代码] [代码][代码]String returnCode = map.get([代码][代码]"return_code"[代码][代码]).toString();[代码] [代码] [代码][代码]if[代码][代码](returnCode.equals([代码][代码]"SUCCESS"[代码][代码])){[代码] [代码] [代码][代码]String resultCode = map.get([代码][代码]"result_code"[代码][代码]).toString();[代码] [代码] [代码][代码]if[代码][代码](resultCode.equals([代码][代码]"SUCCESS"[代码][代码])){[代码] [代码] [代码][代码]SortedMap<String, String> packageParams = [代码][代码]new[代码] [代码]TreeMap<String, String>();[代码] [代码] [代码][代码]packageParams.put([代码][代码]"appid"[代码][代码], map.get([代码][代码]"appid"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"attach"[代码][代码], map.get([代码][代码]"attach"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"bank_type"[代码][代码], map.get([代码][代码]"bank_type"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"cash_fee"[代码][代码], map.get([代码][代码]"cash_fee"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"fee_type"[代码][代码], map.get([代码][代码]"fee_type"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"is_subscribe"[代码][代码], map.get([代码][代码]"is_subscribe"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"mch_id"[代码][代码], map.get([代码][代码]"mch_id"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"nonce_str"[代码][代码], map.get([代码][代码]"nonce_str"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"openid"[代码][代码], map.get([代码][代码]"openid"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"out_trade_no"[代码][代码], map.get([代码][代码]"out_trade_no"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"result_code"[代码][代码], map.get([代码][代码]"result_code"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"return_code"[代码][代码], map.get([代码][代码]"return_code"[代码][代码]).toString()); [代码] [代码] [代码][代码]packageParams.put([代码][代码]"time_end"[代码][代码], map.get([代码][代码]"time_end"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"total_fee"[代码][代码], map.get([代码][代码]"total_fee"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"trade_type"[代码][代码], map.get([代码][代码]"trade_type"[代码][代码]).toString());[代码] [代码] [代码][代码]packageParams.put([代码][代码]"transaction_id"[代码][代码], map.get([代码][代码]"transaction_id"[代码][代码]).toString());[代码] [代码] [代码][代码]String sign = PayUtils.createSign(packageParams,key);[代码] [代码] [代码][代码]String originSign = map.get([代码][代码]"sign"[代码][代码]).toString();[代码] [代码] [代码][代码]if[代码][代码](sign.equals(originSign)){[代码] [代码] [代码][代码]//签名一致,保存支付流水[代码] [代码] [代码][代码]String xml=[代码][代码]"<xml>"[代码] [代码] [代码][代码]+[代码][代码]"<return_code>SUCCESS</return_code>"[代码] [代码] [代码][代码]+[代码][代码]"<return_msg>OK</return_msg>"[代码] [代码] [代码][代码]+[代码][代码]"</xml>"[代码][代码];[代码] [代码] [代码][代码]ShopPayLog payLog = [代码][代码]new[代码] [代码]ShopPayLog();[代码] [代码] [代码][代码]payLog.setCreatedAt([代码][代码]new[代码] [代码]Date());[代码] [代码] [代码][代码]payLog.setSource(Source.WeiXin);[代码] [代码] [代码][代码]DecimalFormat df = [代码][代码]new[代码] [代码]DecimalFormat([代码][代码]"######0.00"[代码][代码]); [代码] [代码] [代码][代码]payLog.setTotalFee(String.valueOf(df.format((Double.valueOf(map.get([代码][代码]"total_fee"[代码][代码]).toString())/[代码][代码]100[代码][代码]))));[代码] [代码] [代码][代码]payLog.setTradeNo(map.get([代码][代码]"out_trade_no"[代码][代码]).toString());[代码] [代码] [代码][代码]payLog.setTransactionId(map.get([代码][代码]"transaction_id"[代码][代码]).toString());[代码] [代码] [代码][代码]String attach = map.get([代码][代码]"attach"[代码][代码]).toString();[代码][代码]//userId+"#wx#"+activityId[代码] [代码] [代码][代码]payLog.setUserId(attach.split([代码][代码]"#wx#"[代码][代码])[[代码][代码]0[代码][代码]]);[代码] [代码] [代码][代码]payLog = wxappPayService.save(payLog);[代码] [代码] [代码][代码]WxappUser user = wxappUserService.find(Long.valueOf(attach.split([代码][代码]"#wx#"[代码][代码])[[代码][代码]0[代码][代码]]));[代码] [代码] [代码][代码]WxappActivity activity = wxappActivityService.find(Long.valueOf(attach.split([代码][代码]"#wx#"[代码][代码])[[代码][代码]1[代码][代码]]));[代码] [代码] [代码][代码]WxappActivityApply activityApply = wxappActivityApplyService.findActivityApplyByUserAndActivity(user, activity);[代码] [代码] [代码][代码]//在活动申请表中关联上支付流水的id activityApply.setPayLogId(String.valueOf(payLog.getId()));[代码] [代码] [代码][代码]wxappActivityApplyService.save(activityApply);[代码] [代码] [代码][代码]return[代码] [代码]xml;[代码] [代码] [代码][代码]}[代码][代码]else[代码][代码]{[代码] [代码] [代码][代码]String xml=[代码][代码]"<xml>"[代码] [代码] [代码][代码]+[代码][代码]"<return_code>FAIL</return_code>"[代码] [代码] [代码][代码]+[代码][代码]"<return_msg>签名不一致</return_msg>"[代码] [代码] [代码][代码]+[代码][代码]"</xml>"[代码][代码];[代码] [代码] [代码][代码]return[代码] [代码]xml;[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]}[代码][代码]else[代码][代码]{[代码] [代码] [代码][代码]String xml=[代码][代码]"<xml>"[代码] [代码] [代码][代码]+[代码][代码]"<return_code>FAIL</return_code>"[代码] [代码] [代码][代码]+[代码][代码]"<return_msg>支付通知失败</return_msg>"[代码] [代码] [代码][代码]+[代码][代码]"</xml>"[代码][代码];[代码] [代码] [代码][代码]return[代码] [代码]xml;[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码] [代码] [代码][代码]String xml=[代码][代码]"<xml>"[代码] [代码] [代码][代码]+[代码][代码]"<return_code>FAIL</return_code>"[代码] [代码] [代码][代码]+[代码][代码]"<return_msg>支付通知失败</return_msg>"[代码] [代码] [代码][代码]+[代码][代码]"</xml>"[代码][代码];[代码] [代码] [代码][代码]return[代码] [代码]xml;[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]}[代码] PayUtils.java [代码]import[代码] [代码]java.io.BufferedReader;[代码][代码]import[代码] [代码]java.io.ByteArrayInputStream;[代码][代码]import[代码] [代码]java.io.File;[代码][代码]import[代码] [代码]java.io.FileInputStream;[代码][代码]import[代码] [代码]java.io.IOException;[代码][代码]import[代码] [代码]java.io.InputStream;[代码][代码]import[代码] [代码]java.io.InputStreamReader;[代码][代码]import[代码] [代码]java.io.UnsupportedEncodingException;[代码][代码]import[代码] [代码]java.net.URLEncoder;[代码][代码]import[代码] [代码]java.security.KeyStore;[代码][代码]import[代码] [代码]java.security.MessageDigest;[代码][代码]import[代码] [代码]java.text.SimpleDateFormat;[代码][代码]import[代码] [代码]java.util.Date;[代码][代码]import[代码] [代码]java.util.HashMap;[代码][代码]import[代码] [代码]java.util.Iterator;[代码][代码]import[代码] [代码]java.util.List;[代码][代码]import[代码] [代码]java.util.Map;[代码][代码]import[代码] [代码]java.util.Random;[代码][代码]import[代码] [代码]java.util.Set;[代码][代码]import[代码] [代码]java.util.SortedMap;[代码][代码]import[代码] [代码]java.util.TreeMap;[代码] [代码]import[代码] [代码]javax.net.ssl.SSLContext;[代码][代码]import[代码] [代码]javax.servlet.http.HttpServletRequest;[代码][代码]import[代码] [代码]javax.servlet.http.HttpServletResponse;[代码] [代码]import[代码] [代码]org.apache.http.HttpEntity;[代码][代码]import[代码] [代码]org.apache.http.HttpResponse;[代码][代码]import[代码] [代码]org.apache.http.client.methods.CloseableHttpResponse;[代码][代码]import[代码] [代码]org.apache.http.client.methods.HttpPost;[代码][代码]import[代码] [代码]org.apache.http.conn.ssl.SSLConnectionSocketFactory;[代码][代码]import[代码] [代码]org.apache.http.entity.StringEntity;[代码][代码]import[代码] [代码]org.apache.http.impl.client.CloseableHttpClient;[代码][代码]import[代码] [代码]org.apache.http.impl.client.DefaultHttpClient;[代码][代码]import[代码] [代码]org.apache.http.impl.client.HttpClients;[代码][代码]import[代码] [代码]org.apache.http.ssl.SSLContexts;[代码][代码]import[代码] [代码]org.apache.http.util.EntityUtils;[代码][代码]import[代码] [代码]org.jdom.Document;[代码][代码]import[代码] [代码]org.jdom.Element;[代码][代码]import[代码] [代码]org.jdom.input.SAXBuilder;[代码] [代码]import[代码] [代码]com.pro.profwxappapi.api.PayApi;[代码] [代码]@SuppressWarnings[代码][代码]([代码][代码]"deprecation"[代码][代码])[代码][代码]public[代码] [代码]class[代码] [代码]PayUtils {[代码][代码] [代码][代码]private[代码] [代码]static[代码] [代码]Object Server;[代码][代码] [代码][代码]@SuppressWarnings[代码][代码]([代码][代码]"deprecation"[代码][代码])[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]DefaultHttpClient httpclient;[代码][代码] [代码][代码]private[代码] [代码]static[代码] [代码]SortedMap parameters;[代码][代码] [代码] [代码] [代码][代码]static[代码] [代码]{[代码][代码] [代码][代码]httpclient = [代码][代码]new[代码] [代码]DefaultHttpClient();[代码][代码] [代码][代码]httpclient = (DefaultHttpClient) HttpClientConnectionManager.getSSLInstance(httpclient);[代码][代码] [代码][代码]parameters = [代码][代码]new[代码] [代码]TreeMap();[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 把对象转换成字符串[代码][代码] [代码][代码]* [代码][代码] [代码][代码]* @param obj[代码][代码] [代码][代码]* @return String 转换成字符串,若对象为null,则返回空字符串.[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String toString(Object obj) {[代码][代码] [代码][代码]if[代码] [代码](obj == [代码][代码]null[代码][代码])[代码][代码] [代码][代码]return[代码] [代码]""[代码][代码];[代码] [代码] [代码][代码]return[代码] [代码]obj.toString();[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 把对象转换为int数值.[代码][代码] [代码][代码]* [代码][代码] [代码][代码]* @param obj[代码][代码] [代码][代码]* 包含数字的对象.[代码][代码] [代码][代码]* @return int 转换后的数值,对不能转换的对象返回0。[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]int[代码] [代码]toInt(Object obj) {[代码][代码] [代码][代码]int[代码] [代码]a = [代码][代码]0[代码][代码];[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]if[代码] [代码](obj != [代码][代码]null[代码][代码]) {[代码][代码] [代码][代码]a = Integer.parseInt(obj.toString());[代码][代码] [代码][代码]}[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]a;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取从1970年开始到现在的秒数[代码][代码] [代码][代码]* [代码][代码] [代码][代码]* @param date[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getTimeStamp() {[代码][代码] [代码][代码]long[代码] [代码]seconds = System.currentTimeMillis() / [代码][代码]1000[代码][代码];[代码][代码] [代码][代码]return[代码] [代码]String.valueOf(seconds);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取当前时间 yyyyMMddHHmmss[代码][代码] [代码][代码]* @return String[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getCurrTime() {[代码][代码] [代码][代码]Date now = [代码][代码]new[代码] [代码]Date();[代码][代码] [代码][代码]SimpleDateFormat outFormat = [代码][代码]new[代码] [代码]SimpleDateFormat([代码][代码]"yyyyMMddHHmmss"[代码][代码]);[代码][代码] [代码][代码]String s = outFormat.format(now);[代码][代码] [代码][代码]return[代码] [代码]s;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取当前日期 yyyyMMdd[代码][代码] [代码][代码]* @param date[代码][代码] [代码][代码]* @return String[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String formatDate(Date date) {[代码][代码] [代码][代码]SimpleDateFormat formatter = [代码][代码]new[代码] [代码]SimpleDateFormat([代码][代码]"yyyyMMdd"[代码][代码]);[代码][代码] [代码][代码]String strDate = formatter.format(date);[代码][代码] [代码][代码]return[代码] [代码]strDate;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 取出一个指定长度大小的随机正整数.[代码][代码] [代码][代码]* @param length int 设定所取出随机数的长度。length小于11[代码][代码] [代码][代码]* @return int 返回生成的随机数。[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]int[代码] [代码]buildRandom([代码][代码]int[代码] [代码]length) {[代码][代码] [代码][代码]int[代码] [代码]num = [代码][代码]1[代码][代码];[代码][代码] [代码][代码]double[代码] [代码]random = Math.random();[代码][代码] [代码][代码]if[代码] [代码](random < [代码][代码]0.1[代码][代码]) {[代码][代码] [代码][代码]random = random + [代码][代码]0.1[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]for[代码] [代码]([代码][代码]int[代码] [代码]i = [代码][代码]0[代码][代码]; i < length; i++) {[代码][代码] [代码][代码]num = num * [代码][代码]10[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]([代码][代码]int[代码][代码]) ((random * num));[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取编码字符集[代码][代码] [代码][代码]* @param request[代码][代码] [代码][代码]* @param response[代码][代码] [代码][代码]* @return String[代码][代码] [代码][代码]*/[代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {[代码] [代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]== request || [代码][代码]null[代码] [代码]== response) {[代码][代码] [代码][代码]return[代码] [代码]"utf-8"[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]String enc = request.getCharacterEncoding();[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]== enc || [代码][代码]""[代码][代码].equals(enc)) {[代码][代码] [代码][代码]enc = response.getCharacterEncoding();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]== enc || [代码][代码]""[代码][代码].equals(enc)) {[代码][代码] [代码][代码]enc = [代码][代码]"utf-8"[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]enc;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]String URLencode(String content) {[代码][代码] [代码][代码]String URLencode;[代码][代码] [代码][代码]URLencode = replace(Server.equals(content), [代码][代码]"+"[代码][代码], [代码][代码]"%20"[代码][代码]);[代码][代码] [代码][代码]return[代码] [代码]URLencode;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]private[代码] [代码]static[代码] [代码]String replace([代码][代码]boolean[代码] [代码]equals, String string, String string2) {[代码][代码] [代码][代码]return[代码] [代码]null[代码][代码];[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取unix时间,从1970-01-01 00:00:00开始的秒数[代码][代码] [代码][代码]* @param date[代码][代码] [代码][代码]* @return long[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]long[代码] [代码]getUnixTime(Date date) {[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]== date) {[代码][代码] [代码][代码]return[代码] [代码]0[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]date.getTime() / [代码][代码]1000[代码][代码];[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]String QRfromGoogle(String chl) {[代码][代码] [代码][代码]int[代码] [代码]widhtHeight = [代码][代码]300[代码][代码];[代码][代码] [代码][代码]String EC_level = [代码][代码]"L"[代码][代码];[代码][代码] [代码][代码]int[代码] [代码]margin = [代码][代码]0[代码][代码];[代码][代码] [代码][代码]String QRfromGoogle;[代码][代码] [代码][代码]chl = URLencode(chl);[代码][代码] [代码][代码]QRfromGoogle = [代码][代码]"http://chart.apis.google.com/chart?chs="[代码] [代码]+ widhtHeight + [代码][代码]"x"[代码] [代码]+ widhtHeight + [代码][代码]"&cht=qr&chld="[代码][代码] [代码][代码]+ EC_level + [代码][代码]"|"[代码] [代码]+ margin + [代码][代码]"&chl="[代码] [代码]+ chl;[代码][代码] [代码][代码]return[代码] [代码]QRfromGoogle;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 时间转换成字符串[代码][代码] [代码][代码]* @param date 时间[代码][代码] [代码][代码]* @param formatType 格式化类型[代码][代码] [代码][代码]* @return String[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String date2String(Date date, String formatType) {[代码][代码] [代码][代码]SimpleDateFormat sdf = [代码][代码]new[代码] [代码]SimpleDateFormat(formatType);[代码][代码] [代码][代码]return[代码] [代码]sdf.format(date);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getNonceStr() {[代码][代码] [代码][代码]Random random = [代码][代码]new[代码] [代码]Random();[代码][代码] [代码][代码]return[代码] [代码]MD5Utils.MD5Encode(String.valueOf(random.nextInt([代码][代码]10000[代码][代码])), [代码][代码]"UTF-8"[代码][代码]);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 创建签名SHA1[代码][代码] [代码][代码]* @param signParams[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]* @throws Exception[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String createSHA1Sign(SortedMap<String, String> signParams) [代码][代码]throws[代码] [代码]Exception {[代码][代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]Set es = signParams.entrySet();[代码][代码] [代码][代码]Iterator it = es.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Map.Entry entry = (Map.Entry) it.next();[代码][代码] [代码][代码]String k = (String) entry.getKey();[代码][代码] [代码][代码]String v = (String) entry.getValue();[代码][代码] [代码][代码]sb.append(k + [代码][代码]"="[代码] [代码]+ v + [代码][代码]"&"[代码][代码]);[代码][代码] [代码][代码]// 要采用URLENCODER的原始值![代码][代码] [代码][代码]}[代码][代码] [代码][代码]String params = sb.substring([代码][代码]0[代码][代码], sb.lastIndexOf([代码][代码]"&"[代码][代码]));[代码][代码] [代码][代码]return[代码] [代码]getSha1(params);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* Sha1签名[代码][代码] [代码][代码]* @param str[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getSha1(String str) {[代码][代码] [代码][代码]if[代码] [代码](str == [代码][代码]null[代码] [代码]|| str.length() == [代码][代码]0[代码][代码]) {[代码][代码] [代码][代码]return[代码] [代码]null[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]char[代码] [代码]hexDigits[] = { [代码][代码]'0'[代码][代码], [代码][代码]'1'[代码][代码], [代码][代码]'2'[代码][代码], [代码][代码]'3'[代码][代码], [代码][代码]'4'[代码][代码], [代码][代码]'5'[代码][代码], [代码][代码]'6'[代码][代码], [代码][代码]'7'[代码][代码], [代码][代码]'8'[代码][代码], [代码][代码]'9'[代码][代码], [代码][代码]'a'[代码][代码], [代码][代码]'b'[代码][代码], [代码][代码]'c'[代码][代码], [代码][代码]'d'[代码][代码], [代码][代码]'e'[代码][代码], [代码][代码]'f'[代码] [代码]};[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]MessageDigest mdTemp = MessageDigest.getInstance([代码][代码]"SHA1"[代码][代码]);[代码][代码] [代码][代码]mdTemp.update(str.getBytes([代码][代码]"UTF-8"[代码][代码]));[代码] [代码] [代码][代码]byte[代码][代码][] md = mdTemp.digest();[代码][代码] [代码][代码]int[代码] [代码]j = md.length;[代码][代码] [代码][代码]char[代码] [代码]buf[] = [代码][代码]new[代码] [代码]char[代码][代码][j * [代码][代码]2[代码][代码]];[代码][代码] [代码][代码]int[代码] [代码]k = [代码][代码]0[代码][代码];[代码][代码] [代码][代码]for[代码] [代码]([代码][代码]int[代码] [代码]i = [代码][代码]0[代码][代码]; i < j; i++) {[代码][代码] [代码][代码]byte[代码] [代码]byte0 = md[i];[代码][代码] [代码][代码]buf[k++] = hexDigits[byte0 >>> [代码][代码]4[代码] [代码]& [代码][代码]0xf[代码][代码]];[代码][代码] [代码][代码]buf[k++] = hexDigits[byte0 & [代码][代码]0xf[代码][代码]];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]new[代码] [代码]String(buf);[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]return[代码] [代码]null[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获得预支付订单号[代码][代码] [代码][代码]* @param url[代码][代码] [代码][代码]* @param xmlParam[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getPayNo(String url, String xmlParam) {[代码][代码] [代码][代码]String prepay_id = [代码][代码]""[代码][代码];[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]String jsonStr = postWithXmlParams(url, xmlParam);[代码][代码] [代码][代码]if[代码] [代码](jsonStr.indexOf([代码][代码]"FAIL"[代码][代码]) != -[代码][代码]1[代码][代码]) {[代码][代码] [代码][代码]return[代码] [代码]prepay_id;[代码][代码] [代码][代码]}[代码][代码] [代码][代码]Map<String, Object> map = doXMLParse(jsonStr);[代码][代码] [代码][代码]prepay_id = (String) map.get([代码][代码]"prepay_id"[代码][代码]);[代码][代码] [代码][代码]System.out.println([代码][代码]"prepay_id:"[代码] [代码]+ prepay_id);[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]prepay_id;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 发送请求[代码][代码] [代码][代码]* @param url 请求路径[代码][代码] [代码][代码]* @param xmlParams xml字符串[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String postWithXmlParams(String url, String xmlParams) {[代码][代码] [代码][代码]HttpPost httpost = HttpClientConnectionManager.getPostMethod(url);[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]httpost.setEntity([代码][代码]new[代码] [代码]StringEntity(xmlParams, [代码][代码]"UTF-8"[代码][代码]));[代码][代码] [代码][代码]HttpResponse response = httpclient.execute(httpost);[代码][代码] [代码][代码]return[代码] [代码]EntityUtils.toString(response.getEntity(), [代码][代码]"UTF-8"[代码][代码]);[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]return[代码] [代码]""[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。[代码][代码] [代码][代码]* @param strxml[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]* @throws JDOMException[代码][代码] [代码][代码]* @throws IOException[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]Map doXMLParse(String strxml) [代码][代码]throws[代码] [代码]Exception {[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]== strxml || [代码][代码]""[代码][代码].equals(strxml)) {[代码][代码] [代码][代码]return[代码] [代码]null[代码][代码];[代码][代码] [代码][代码]}[代码][代码] [代码][代码]Map m = [代码][代码]new[代码] [代码]HashMap();[代码][代码] [代码][代码]InputStream in = String2Inputstream(strxml);[代码][代码] [代码][代码]SAXBuilder builder = [代码][代码]new[代码] [代码]SAXBuilder();[代码][代码] [代码][代码]Document doc = builder.build(in);[代码][代码] [代码][代码]Element root = doc.getRootElement();[代码][代码] [代码][代码]List list = root.getChildren();[代码][代码] [代码][代码]Iterator it = list.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Element e = (Element) it.next();[代码][代码] [代码][代码]String k = e.getName();[代码][代码] [代码][代码]String v = [代码][代码]""[代码][代码];[代码][代码] [代码][代码]List children = e.getChildren();[代码][代码] [代码][代码]if[代码] [代码](children.isEmpty()) {[代码][代码] [代码][代码]v = e.getTextNormalize();[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]v = getChildrenText(children);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]m.put(k, v);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]// 关闭流[代码][代码] [代码][代码]in.close();[代码][代码] [代码][代码]return[代码] [代码]m;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取子结点的xml[代码][代码] [代码][代码]* @param children[代码][代码] [代码][代码]* @return String[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String getChildrenText(List children) {[代码][代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]if[代码] [代码](!children.isEmpty()) {[代码][代码] [代码][代码]Iterator it = children.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Element e = (Element) it.next();[代码][代码] [代码][代码]String name = e.getName();[代码][代码] [代码][代码]String value = e.getTextNormalize();[代码][代码] [代码][代码]List list = e.getChildren();[代码][代码] [代码][代码]sb.append([代码][代码]"<"[代码] [代码]+ name + [代码][代码]">"[代码][代码]);[代码][代码] [代码][代码]if[代码] [代码](!list.isEmpty()) {[代码][代码] [代码][代码]sb.append(getChildrenText(list));[代码][代码] [代码][代码]}[代码][代码] [代码][代码]sb.append(value);[代码][代码] [代码][代码]sb.append([代码][代码]"</"[代码] [代码]+ name + [代码][代码]">"[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]sb.toString();[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]InputStream String2Inputstream(String str) {[代码][代码] [代码][代码]return[代码] [代码]new[代码] [代码]ByteArrayInputStream(str.getBytes());[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]public[代码] [代码]String getParameter(String parameter) {[代码][代码] [代码][代码]String s = (String) [代码][代码]this[代码][代码].parameters.get(parameter);[代码][代码] [代码][代码]return[代码] [代码]([代码][代码]null[代码] [代码]== s) ? [代码][代码]""[代码] [代码]: s;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 特殊字符处理[代码][代码] [代码][代码]* @param src[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]* @throws UnsupportedEncodingException[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]String UrlEncode(String src) [代码][代码]throws[代码] [代码]UnsupportedEncodingException {[代码][代码] [代码][代码]return[代码] [代码]URLEncoder.encode(src, [代码][代码]"UTF-8"[代码][代码]).replace([代码][代码]"+"[代码][代码], [代码][代码]"%20"[代码][代码]);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 获取package的签名包[代码][代码] [代码][代码]* @param packageParams[代码][代码] [代码][代码]* @param key[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]* @throws UnsupportedEncodingException[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]String genPackage(SortedMap<String, String> packageParams, String key) [代码][代码]throws[代码] [代码]UnsupportedEncodingException {[代码][代码] [代码][代码]String sign = createSign(packageParams, key);[代码] [代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]Set es = packageParams.entrySet();[代码][代码] [代码][代码]Iterator it = es.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Map.Entry entry = (Map.Entry) it.next();[代码][代码] [代码][代码]String k = (String) entry.getKey();[代码][代码] [代码][代码]String v = (String) entry.getValue();[代码][代码] [代码][代码]sb.append(k + [代码][代码]"="[代码] [代码]+ UrlEncode(v) + [代码][代码]"&"[代码][代码]);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]// 去掉最后一个&[代码][代码] [代码][代码]String packageValue = sb.append([代码][代码]"sign="[代码] [代码]+ sign).toString();[代码][代码] [代码][代码]return[代码] [代码]packageValue;[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]static[代码] [代码]String createSign(SortedMap<String, String> packageParams, String key) {[代码][代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]Set es = packageParams.entrySet();[代码][代码] [代码][代码]Iterator it = es.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Map.Entry entry = (Map.Entry) it.next();[代码][代码] [代码][代码]String k = (String) entry.getKey();[代码][代码] [代码][代码]String v = (String) entry.getValue();[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]!= v && ![代码][代码]""[代码][代码].equals(v) && ![代码][代码]"sign"[代码][代码].equals(k) && ![代码][代码]"key"[代码][代码].equals(k)) {[代码][代码] [代码][代码]sb.append(k + [代码][代码]"="[代码] [代码]+ v + [代码][代码]"&"[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]sb.append([代码][代码]"key="[代码] [代码]+ key);[代码][代码] [代码][代码]System.out.println([代码][代码]"md5:"[代码] [代码]+ sb.toString());[代码][代码] [代码][代码]String sign = MD5Utils.MD5Encode(sb.toString(), [代码][代码]"UTF-8"[代码][代码]).toUpperCase();[代码][代码] [代码][代码]System.out.println([代码][代码]"packge签名:"[代码] [代码]+ sign);[代码][代码] [代码][代码]return[代码] [代码]sign;[代码] [代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 创建package签名[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]boolean[代码] [代码]createMd5Sign(String signParams) {[代码][代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]Set es = [代码][代码]this[代码][代码].parameters.entrySet();[代码][代码] [代码][代码]Iterator it = es.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Map.Entry entry = (Map.Entry) it.next();[代码][代码] [代码][代码]String k = (String) entry.getKey();[代码][代码] [代码][代码]String v = (String) entry.getValue();[代码][代码] [代码][代码]if[代码] [代码](![代码][代码]"sign"[代码][代码].equals(k) && [代码][代码]null[代码] [代码]!= v && ![代码][代码]""[代码][代码].equals(v)) {[代码][代码] [代码][代码]sb.append(k + [代码][代码]"="[代码] [代码]+ v + [代码][代码]"&"[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]// 算出摘要[代码][代码] [代码][代码]String sign = MD5Utils.MD5Encode(sb.toString(), [代码][代码]"utf-8"[代码][代码]).toLowerCase();[代码][代码] [代码][代码]String paySign = [代码][代码]this[代码][代码].getParameter([代码][代码]"sign"[代码][代码]).toLowerCase();[代码][代码] [代码][代码]return[代码] [代码]paySign.equals(sign);[代码][代码] [代码][代码]}[代码] [代码] [代码][代码]/**[代码][代码] [代码][代码]* 输出XML[代码][代码] [代码][代码]* @return[代码][代码] [代码][代码]*/[代码][代码] [代码][代码]public[代码] [代码]String parseXML() {[代码][代码] [代码][代码]StringBuffer sb = [代码][代码]new[代码] [代码]StringBuffer();[代码][代码] [代码][代码]sb.append([代码][代码]"<xml>"[代码][代码]);[代码][代码] [代码][代码]Set es = [代码][代码]this[代码][代码].parameters.entrySet();[代码][代码] [代码][代码]Iterator it = es.iterator();[代码][代码] [代码][代码]while[代码] [代码](it.hasNext()) {[代码][代码] [代码][代码]Map.Entry entry = (Map.Entry) it.next();[代码][代码] [代码][代码]String k = (String) entry.getKey();[代码][代码] [代码][代码]String v = (String) entry.getValue();[代码][代码] [代码][代码]if[代码] [代码]([代码][代码]null[代码] [代码]!= v && ![代码][代码]""[代码][代码].equals(v) && ![代码][代码]"appkey"[代码][代码].equals(k)) {[代码] [代码] [代码][代码]sb.append([代码][代码]"<"[代码] [代码]+ k + [代码][代码]">"[代码] [代码]+ getParameter(k) + [代码][代码]"</"[代码] [代码]+ k + [代码][代码]">\n"[代码][代码]);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]sb.append([代码][代码]"</xml>"[代码][代码]);[代码][代码] [代码][代码]return[代码] [代码]sb.toString();[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]public[代码] [代码]static[代码] [代码]String post(String url, String xmlParam){[代码][代码] [代码][代码]StringBuilder sb = [代码][代码]new[代码] [代码]StringBuilder();[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]KeyStore keyStore = KeyStore.getInstance([代码][代码]"PKCS12"[代码][代码]);[代码][代码] [代码][代码]FileInputStream instream = [代码][代码]new[代码] [代码]FileInputStream([代码][代码]new[代码] [代码]File(PayApi.[代码][代码]class[代码][代码].getClassLoader().getResource([代码][代码]"apiclient_cert.p12"[代码][代码]).getPath()));[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]keyStore.load(instream, [代码][代码]"1344023801"[代码][代码].toCharArray());[代码][代码] [代码][代码]} [代码][代码]finally[代码] [代码]{[代码][代码] [代码][代码]instream.close();[代码][代码] [代码][代码]}[代码][代码] [代码] [代码] [代码][代码]// 证书[代码][代码] [代码][代码]SSLContext sslcontext = SSLContexts.custom()[代码][代码] [代码][代码].loadKeyMaterial(keyStore, [代码][代码]"1344023801"[代码][代码].toCharArray())[代码][代码] [代码][代码].build();[代码][代码] [代码][代码]// 只允许TLSv1协议[代码][代码] [代码][代码]SSLConnectionSocketFactory sslsf = [代码][代码]new[代码] [代码]SSLConnectionSocketFactory([代码][代码] [代码][代码]sslcontext,[代码][代码] [代码][代码]new[代码] [代码]String[] { [代码][代码]"TLSv1"[代码] [代码]},[代码][代码] [代码][代码]null[代码][代码],[代码][代码] [代码][代码]SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);[代码][代码] [代码][代码]//创建基于证书的httpClient,后面要用到[代码][代码] [代码][代码]CloseableHttpClient client = HttpClients.custom()[代码][代码] [代码][代码].setSSLSocketFactory(sslsf)[代码][代码] [代码][代码].build();[代码][代码] [代码] [代码] [代码][代码]HttpPost httpPost = [代码][代码]new[代码] [代码]HttpPost(url);[代码][代码]//退款接口[代码][代码] [代码][代码]StringEntity reqEntity = [代码][代码]new[代码] [代码]StringEntity(xmlParam);[代码][代码] [代码][代码]// 设置类型 [代码][代码] [代码][代码]reqEntity.setContentType([代码][代码]"application/x-www-form-urlencoded"[代码][代码]); [代码][代码] [代码][代码]httpPost.setEntity(reqEntity);[代码][代码] [代码][代码]CloseableHttpResponse response = client.execute(httpPost);[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]HttpEntity entity = response.getEntity();[代码][代码] [代码][代码]System.out.println(response.getStatusLine());[代码][代码] [代码][代码]if[代码] [代码](entity != [代码][代码]null[代码][代码]) {[代码][代码] [代码][代码]BufferedReader bufferedReader = [代码][代码]new[代码] [代码]BufferedReader([代码][代码]new[代码] [代码]InputStreamReader(entity.getContent(),[代码][代码]"UTF-8"[代码][代码]));[代码][代码] [代码][代码]String text=[代码][代码]""[代码][代码];[代码][代码] [代码][代码]while[代码] [代码]((text = bufferedReader.readLine()) != [代码][代码]null[代码][代码]) {[代码][代码] [代码][代码]sb.append(text);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]EntityUtils.consume(entity);[代码][代码] [代码][代码]} [代码][代码]catch[代码][代码](Exception e){[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码]finally[代码] [代码]{[代码][代码] [代码][代码]try[代码] [代码]{[代码][代码] [代码][代码]response.close();[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](IOException e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]} [代码][代码]catch[代码] [代码](Exception e) {[代码][代码] [代码][代码]e.printStackTrace();[代码][代码] [代码][代码]}[代码][代码]finally[代码] [代码]{[代码][代码] [代码][代码]httpclient.close();[代码][代码] [代码][代码]}[代码][代码] [代码][代码]return[代码] [代码]sb.toString();[代码][代码] [代码][代码]}[代码][代码]}[代码]
2019-07-03 - 肥肥盒子
~~~~
2019-02-20