有位朋友让我给他新开的网站帮忙做几个小功能,如下:

  1. 输入快递公司、快递单号,查询出这个快件的所有动态(从哪里出发,到了哪里)
  2. 在注册、登录等场景下的手机验证码(要求有一定的防刷策略)
  3. 通过输入公司名的关键词,查看这个公司是否已经注册、法人信息、有类似名称的公司等等

并且可以用的接口、文档都提供给我了。
其中需求 1、2,都通过 聚合数据 这家网站提供的接口实现;需求 3 通过 云聚数据 来实现。

本项目的文件

因为朋友的网站是用 ThinkPHP 写的,为了保持将来代码的兼容,这三个功能也用 ThinkPHP 写成。

项目的所有文件都放在了 GitHub 上,部分敏感数据已经隐藏,你需要自行替换,地址如下:

GitHub 地址:使用聚合数据API查询快递数据-短信验证码-企业核名

因为这三个功能并不是正式产品,将来会需要嵌入到网站的各个功能模块中去,所以为了查看起来方便,三个功能的代码都写在一个文件里,你只要重点关注以下几个文件就好:

  • /Home/Conf/config.php 参数配置文件
  • /Home/Controller/IndexController.class.php 后端代码、API的请求与处理
  • /Home/View/Index_index.html 前端 html

申请 KEY 和阅读开发文档

分别到上面两家网站上找到“快递”、“短信”、“核名”的文档地址,根据里面的说明,把 KEY、URL 等信息放入配置文件Home/Conf/config.php,方便后面重复使用。

常用快递API文档

短信API文档

核名-文档

注意 短信的 API 服务,要先到网站的后台添加“短信模板”,让管理员审核后才可以正常调用

最后,Home/Conf/config.php 配置文件里的内容如下

return array(// 快递查询'EXPRESS_APP_KEY' => '你的快递 APPKEY','EXPRESS_QUERY_URL' => 'http://v.juhe.cn/exp/index', //快递单号查询'EXPRESS_COM_URL' => 'http://v.juhe.cn/exp/com', //快递公司查询// 发短信'SEND_SMS_KEY' => '你的短信接口 APPKEY','SEND_SMS_URL' => 'http://v.juhe.cn/sms/send',// 核名'COMPANY_KEY' => '核名 APPKEY','COMPANY_URL' => 'http://eci.yjapi.com/ECIFast/Search',//数据库配置信息'DB_TYPE'   => 'mysql', // 数据库类型'DB_HOST'   => 'localhost', // 服务器地址'DB_NAME'   => '你的数据库名', // 数据库名'DB_USER'   => '你的数据库用户名', // 用户名'DB_PWD'    => '密码', // 密码'DB_PORT'   => 3306, // 端口'DB_PREFIX' => 'pre_', // 数据库表前缀 'DB_CHARSET'=> 'utf8', // 字符集
);

设置数据库

为了防止恶意用户利用暴露在外的短信接口捣乱,需要对每个手机号码的行为进行记录。例如:

  • 向某个手机号码发送短信验证码后, 60 秒内不能再次发送
  • 必须在 1 小时内填写短信验证码,否则会过期
  • 避免 ajax 请求地址被机器人程序利用,在表单里要使用图片验证码才能提交数据

关于“表单验证码”我们后面代码会说明,这里先创建表结构如下,用于记录短信发送记录:

CREATE TABLE `pre_smsrecord` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`mobile` varchar(11) NOT NULL DEFAULT '',`tpl_id` int(11) NOT NULL,`code` int(6) NOT NULL,`time` int(11) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=M

  • mobile 是手机号码
  • tpl_id是在网站后台添加并通过审核的短信模板
  • code是发送的验证码(一般是4位或6位)
  • time是发送时间戳

直接下载sql进行还原:在本项目的 GitHub 地址上也可以直接从 /Pubic 目录找到 sql 文件,你可以直接把它还原你的 MySQL 上。

表单验证码的实现

最终效果如下:

因为三个功能都需要表单验证码,所以首先实现它。

打开 /Home/View/Index_index.html,注意里面图片验证码 img 标签,以及对应的 javascript

<p>(通用)输入验证码 <input type="text" name="verify" id="verify"><img id="verify-img" src="/?m=Home&c=Index&a=verify" alt=""><a id="btn-refresh-verify" href="javascript:;" title="">刷新</a>
</p>
<script type="text/javascript" charset="utf-8">
jQuery(document).ready(function($) {// 刷新验证码按钮$("#btn-refresh-verify").click(function(event) {$("#verify-img").attr('src', '/?m=Home&c=Index&a=verify' + "&time=" + new Date().getTime());});
});
</script>

对应的后端代码在 /Home/Controller/IndexController.class.php 中

/**
* +--------------------------------------------------------------------------
* 生成验证码
*
* +--------------------------------------------------------------------------
*/
public function verify(){$Verify = new \Think\Verify();$Verify->entry();
}
/**
* +--------------------------------------------------------------------------
* 检查验证码
*
* @param string $code 输入的验证码
* @return boolean
* +--------------------------------------------------------------------------
*/
protected function check_verify($code){$verify = new \Think\Verify();return $verify->check($code);
}

通用的 Curl 方法,用来向第三方网站的 API 发起请求并获取返回值

因为 3 个功能实际上都是通过网络来请求一个第三方网站的 API 接口地址,因此可以统一成一个通用的方法。代码如下,可以传入三个变量,分别为 :url、参数数组、请求方式(是否是post,默认为0),返回一个 json 格式的数据。

/**
* +--------------------------------------------------------------------------
* 通用的“聚合数据”请求接口,返回JSON数据
*
* @param string $url 接口地址
* @param array $params 传递的参数
* @param int $ispost 是否以POST提交,默认GET
* @return json
* +--------------------------------------------------------------------------
*/
public function juhecurl($url,$params=false,$ispost=0){$httpInfo = array();$ch = curl_init();curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 );curl_setopt( $ch, CURLOPT_USERAGENT , 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22' );curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 30 );curl_setopt( $ch, CURLOPT_TIMEOUT , 30);curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true );if( $ispost ){curl_setopt( $ch , CURLOPT_POST , true );curl_setopt( $ch , CURLOPT_POSTFIELDS , $params );curl_setopt( $ch , CURLOPT_URL , $url );}else{if($params){curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params );}else{curl_setopt( $ch , CURLOPT_URL , $url);}}$response = curl_exec( $ch );if ($response === FALSE) {//echo "cURL Error: " . curl_error($ch);return false;}$httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE );$httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) );curl_close( $ch );return $response;
}

后面我们获取快递数据、发送短信、查询企业名称,都可以调用这个通用的方法。

获取快递数据列表的实现

最终效果如下:

打开 /Home/View/Index_index.html

<h1>获取快递数据</h1>
<div id="express-module"><p>选择公司<select name="express-company" id="express-company"><option value="sf">顺丰</option><option value="sto">申通</option><option value="yt">圆通</option><option value="yd">韵达</option><option value="tt">天天</option><option value="ems">EMS</option><option value="zto">中通</option><option value="ht">汇通</option></select></p><p> 输入单号 <input type="text" name="express-number" id="express-number"> </p><p><button id="btn-query-express" type="button" class="btn btn-default">查询快递</button> </p><p>返回结果:</p><p id="reason" style="color:#FF0000"></p><p>快件动态:</p><ul id="express-result"></ul>
</div>
<!-- 引入jquery库 -->
<script src="__PUBLIC__/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
jQuery(document).ready(function($) {//点击快递查询按钮
    $("#btn-query-express").click(function(event) {$("#reason").html("");// 更新验证码
        $("#verify-img").attr('src', '/?m=Home&c=Index&a=verify' + "&time=" + new Date().getTime());$.getJSON('/?m=Home&c=Index&a=getExpressData', {company: $("#express-company").val(),number: $("#express-number").val(),verify: $("#verify").val(),}, function(json, textStatus) {if(json['resultcode'] == 200){var result_list = json['result']['list'];$("#express-result").html("");for(var i = 0, l = result_list.length; i < l; i++) {$("#express-result").append("<li>" + result_list[i]['datetime'] + "," + result_list[i]['remark'] + "," +result_list[i]['zone'] + "</li>");}}$("#reason").html(json['reason']);});});
});
</script>

对应的后端代码为如下,特别注意,你要把 $content = $this->juhecurl(C("EXPRESS_QUERY_URL"), $params, 1); 这段的注释去掉(因为我开发的时候查询余额不足,所以使用了一个写死的数组来让程序能正常运行)

/**
* +--------------------------------------------------------------------------
* 获取快递数据
*
* @param string $get.company 快递公司代码
* @param string $get.number 快递单号
* @return json
* +--------------------------------------------------------------------------
*/
public function getExpressData(){// 传入 get 参数,包括公司代号、快递单号、验证码$com    = I("get.company");$no     = I("get.number");$verify = I("get.verify");// 处理验证码if ( !$this->check_verify($verify) ) {$content = array('resultcode'=>1000, 'reason'=>'验证码填写错误');echo json_encode($content);exit();}// 处理机器人程序刷接口(目前通过IP判断)$ip = get_client_ip(0, true);$Record = M("expressrecord");$express_record = $Record->where("ip='" . $ip . "'")->find();if( $express_record && ( (time() - $express_record['time']) < 60 ) ){echo json_encode(array('reason'=>'60秒内只能查询一次'));exit();}if ( $com && $no ) {$params = array('key' => C("EXPRESS_APP_KEY"),'com'  => $com,'no' => $no);// 开发测试阶段直接返回值,不请求 API// $content = $this->juhecurl(C("EXPRESS_QUERY_URL"), $params, 1);$content = array('resultcode'=>200, 'reason'=>'查询成功', 'result'=>array('list'=>array()));// 删除旧记录(如果有),然后添加新的记录$Record->where("ip='" . $ip . "'")->delete();$data = array('ip' => $ip,'time'=>time());$Record->add($data);//$returnArray = json_decode($content,true);echo json_encode($content, true);}
}

短信验证码的发送和检验

废话不多说,前端html直接上代码。这里实际上有两个动作,一个是“给我手机号码 xxxxx 发个验证码”,另一个是“我已经收到了,并填写了,请看我填写的验证码对不对”。

<h1>发送短信验证码与检查</h1>
<div id="sms-module"><p>短信模板:<select name="send-sms-tplid" id="send-sms-tplid"><option value="5596">申请注册</option><option value="5602">申请找回密码</option><option value="5603">在线核名</option></select></p><p>手机号码:<input type="text" name="send-sms-mobile" id="send-sms-mobile"></p><p>输入手机验证码:<input type="text" name="send-sms-code" id="send-sms-code"><span id="sms-count-down"></span><button id="btn-send-sms" type="button" class="btn btn-default">发送验证码</button></p><p><button id="btn-check-sms" type="button" class="btn btn-default">提交手机验证码</button></p>
</div>
<script type="text/javascript" charset="utf-8">
/**
* 发送短信验证码后,60秒倒计时
*/
var seconds_left = 60;
function sms_count_down(){if(seconds_left > 0){seconds_left = seconds_left-1;//var b=new Date();
        document.getElementById("sms-count-down").innerHTML = seconds_left + "秒";setTimeout("sms_count_down()",1000);} else {//根据 http://stackoverflow.com/questions/7526601/setattributedisabled-false-changes-editable-attribute-to-false// 不能为 setAttribute 设置任何值,都会变成 ”disabled“,要使用 removeAttribute
        document.getElementById("btn-send-sms").removeAttribute("disabled");document.getElementById("btn-send-sms").innerHTML = "重新发送";document.getElementById("sms-count-down").innerHTML = "";}
}
jQuery(document).ready(function($) {
//发送短信
    $("#btn-send-sms").click(function(event) {$("#reason").html("");$.getJSON('/?m=Home&c=Index&a=sendSMS', {tplid: $("#send-sms-tplid").val(),mobile: $("#send-sms-mobile").val(),}, function(json, textStatus) {$("#reason").html(json['reason']);var error_code = json['error_code'];if(error_code == 0){// 测试阶段,不禁用,可检查 60 秒重复发送//$("#btn-send-sms").attr("disabled", true);//开始倒计时
                    sms_count_down();}else{$("#btn-send-sms").html("重新发送");}});});//检查短信验证码
    $("#btn-check-sms").click(function(event) {$("#reason").html("");// 更新验证码
        $("#verify-img").attr('src', '/?m=Home&c=Index&a=verify' + "&time=" + new Date().getTime());$.getJSON('/?m=Home&c=Index&a=checkSmsCode', {tplid: $("#send-sms-tplid").val(),mobile: $("#send-sms-mobile").val(),code: $("#send-sms-code").val(),verify: $("#verify").val(),}, function(json, textStatus) {$("#reason").html(json['reason']);});});});

同样因为有两个动作,后端会稍微复杂一点点

/**
* +--------------------------------------------------------------------------
* 请求发送短信接口,60秒后才能重新发送
*
* @param int $get.tplid 短信模板id
* @param string $get.mobile 手机号码
* @return json
* +--------------------------------------------------------------------------
*/
public function sendSMS(){$tpl_id = I("get.tplid"); // 短信模板id:注册 5596 找回密码 5602 在线核名 5603$mobile = I("get.mobile"); // 手机号码// 检查数据库记录 ,是否在 60 秒内已经发送过一次$Record = M("smsrecord");$where = array('mobile' => $mobile,'tpl_id' => $tpl_id,);$sms_record = $Record->where($where)->find();if( $sms_record && ( (time() - $sms_record['time']) < 60 ) ){echo json_encode(array('reason'=>'60秒内不能多次发送'));exit();}// 如果60秒内没有发过,则发送验证码短信(6位随机数字)$code = mt_rand(100000, 999999);$smsConf = array('key'   => C("SEND_SMS_KEY"), //您申请的APPKEY'mobile'    => $mobile, //接受短信的用户手机号码'tpl_id'    => $tpl_id, //您申请的短信模板ID,根据实际情况修改'tpl_value' =>'#code#=' . $code //您设置的模板变量,根据实际情况修改 '#code#=1234&#company#=聚合数据');//测试阶段,不发短信,直接设置一个“发送成功” json 字符串$content = $this->juhecurl(C("SEND_SMS_URL") ,$smsConf, 1); //请求发送短信//$content = json_encode(array('error_code'=>0, 'reason'=>'发送成功'));if($content){$result = json_decode($content,true);$error_code = $result['error_code'];if($error_code == 0){// 状态为0,说明短信发送成功// 数据库存储发送记录,用于处理倒计时和输入验证,首先要删除旧记录$Record->where("mobile=" . $mobile)->delete();$data = array('mobile' => $mobile,'tpl_id'=> $tpl_id,'code'=>$code,'time'=>time());$Record->data($data)->add();//echo "短信发送成功,短信ID:".$result['result']['sid'];}else{//状态非0,说明失败//echo "短信发送失败(".$error_code."):".$msg;}}else{//返回内容异常,以下可根据业务逻辑自行修改//$result['reason'] = '短信发送失败';}echo $content;
}
/**
* +--------------------------------------------------------------------------
* 检查填写的手机验证码是否填写正确
* 可以添加更多字段改造成注册、登录等表单
*
* @param string $get.verify 验证码
* @param string $get.mobile 手机号码
* @param int $get.tplid 短信模板ID
* @param int $get.code 手机接收到的验证码
* +--------------------------------------------------------------------------
*/
public function checkSmsCode(){$verify = I("get.verify");$tpl_id = I("get.tplid"); // 短信模板id:注册 5596 找回密码 5602 在线核名 5603$mobile = I("get.mobile"); // 手机号码$code = I("get.code"); // 手机收到的验证码if(!$this->check_verify($verify)){$content = array('resultcode'=>1000, 'reason'=>'验证码填写错误');echo json_encode($content);exit();}// 检查数据库记录,输入的手机验证码是否和之前通过短信 API 发送到手机的一致$Record = M("smsrecord");$where = array('mobile' => $mobile,'tpl_id' => $tpl_id,'code' => $code,);$sms_record = $Record->where($where)->find();if($sms_record){echo json_encode(array('reason'=>'短信验证码核对成功'));// 处理后面的程序(如继续登录、注册等)}else{echo json_encode(array('reason'=>'短信验证码错误'));}
}

最后是企业核名

因为这个接口本来只提供了每次查询一个关键词,而朋友的网站要求能每次查询用“,”分隔的多个关键词,因此需要对提交的数据和返回到前端的进行一番处理(循环处理多个列表)。

前端代码比较简单

<h1>企业核名</h1>
<div id="company-module"><p>省份:上海<input type="hidden" name="company-province" id="company-province" value="SH"></p><p><input type="text" name="company-name" id="company-name"></p>
</div>
<button id="btn-company" type="button" class="btn btn-default">企业核名</button>
<p>公司查询列表:</p>
<ul id="company-result"></ul>
<javascript 略……
//企业核名(可输入多个以逗号分隔的名称来多次查询)
$("#btn-company").click(function(event) {$("#reason").html("");$("#company-result").html("");// 更新验证码$("#verify-img").attr('src', '/?m=Home&c=Index&a=verify' + "&time=" + new Date().getTime());$.getJSON('/?m=Home&c=Index&a=getCompanyName', {province: $("#company-province").val(),companyName: $("#company-name").val(),verify: $("#verify").val(),}, function(json, textStatus) {if(json['reason']){$("#reason").html(json['reason']);} else {//console.log(json);var json_list = JSON.parse(json);if (json_list.length > 0){for(i in json_list){var result_list = json_list[i].Result;if (result_list.length > 0){for(j in result_list){//console.log(result_list[j]);$("#company-result").append("<li> 名称:" + result_list[j].Name + ", 法人:" + result_list[j].OperName + ", 状态:" + result_list[j].Status + "</li>");}}}}}});
});
</javascript>

后端代码

/**
* +--------------------------------------------------------------------------
* 企业核名
* 该接口 http://eci.yjapi.com/ECIFast/Search 仅支持 GET 方式,因此 $param 为字符串形式
*
* @param string $get.province 省份
* @param string $get.companyName 公司名
* @return json
* +--------------------------------------------------------------------------
*/
public function getCompanyName(){// 传入 get 参数$province                = I("get.province");$companyName    = I("get.companyName");$verify         = I("get.verify");// 处理验证码if ( !$this->check_verify($verify) ) {$content = array('resultcode'=>1000, 'reason'=>'验证码填写错误');echo json_encode($content);exit();}// 处理机器人程序刷接口(目前通过IP判断)$ip = get_client_ip(0, true);$Record = M("companyrecord");$company_record = $Record->where("ip='" . $ip . "'")->find();// if( $company_record && ( (time() - $company_record['time']) < 60 ) ){//     echo json_encode(array('reason'=>'60秒内只能查询一次企业名称'));//     exit();// }$resultArray = array();if ( $province && $companyName ) {// 删除旧记录(如果有),然后添加新的记录$Record->where("ip='" . $ip . "'")->delete();// 循环处理多个公司名$companies = explode(",", $companyName);foreach ($companies as $key => $value) {$params = "key=" . C("COMPANY_KEY") . "&province={$province}&companyName={$value}";// 开发测试阶段直接返回值,不请求 API$content = $this->juhecurl(C("COMPANY_URL"), $params, 0);$returnArray = json_decode($content,true);// 循环插入新的结果$resultArray[] = $returnArray;//$content = array('resultcode'=>200, 'reason'=>'查询成功', 'result'=>array('list'=>array()));
        }$data = array('ip' => $ip,'time'=>time());$Record->add($data);$content = json_encode($resultArray, true);echo json_encode($content, true);}
}

结束

至此三个功能都已经完成了,但是在防止恶意用户利用接口干坏事的策略上,还可以更进一步。例如

  • 必须要注册用户才能发送请求
  • 手机号码每天只能限制发送有限的验证码(因为目前即使每分钟发送一次,一天下来也比较可观了)
  • 手机号码注册后,再次请求发送验证码时,只能发给“已有”的手机号码数据表里的手机号码,而不能把“找回密码”发给一个还没注册的人。

转载于:https://www.cnblogs.com/annie00/p/5810326.html

使用聚合数据API查询快递数据-短信验证码-企业核名相关推荐

  1. 通过聚合数据API实现快递数据查询-短信验证码-企业核名

    有位朋友让我给他新开的网站帮忙做几个小功能,如下: 输入快递公司.快递单号,查询出这个快件的所有动态(从哪里出发,到了哪里) 在注册.登录等场景下的手机验证码(要求有一定的防刷策略) 通过输入公司名的 ...

  2. 【django】用户注册时短信验证码校验、避免频繁发送短信验证码【17】

    注册时短信验证后端逻辑 class CheckSMScodeView(View):def get(self,request,phone):'''用户注册时短信验证码的校验功能:param reques ...

  3. 利用聚合数据API进行Android开发之短信验证码

    在说Android中的短信验证码这个知识点前,我们首先来了解下聚合数据 聚合数据介绍 聚合数据是一家国内最大的基础数据API提供商,专业从事互联网数据服务.免费提供从天气查询.空气质量.地图坐标到金融 ...

  4. 聚合数据短信验证码接口实现 Android开发短信验证码

    在说Android中的短信验证码这个知识点前,我们首先来了解下聚合数据 聚合数据介绍 聚合数据是一家国内最大的基础数据API提供商,专业从事互联网数据服务.免费提供从天气查询.空气质量.地图坐标到金融 ...

  5. 发送短信验证码 API数据接口

    1.前言 此接口发送短信验证码 短信模板 参数说明: 1449978 验证码为:{1}为您的登录验证码,请于{2}分钟内填写,如非本人操作,请忽略本短信. 1449979 验证码为:{1},您正在登录 ...

  6. 【项目优化01】使用Git管理项目及使用redis缓存短信验证码,菜品以及套餐数据

    文章目录 1. 使用Git管理项目 2. redis缓存 2.1 使用redis缓存短信验证码 2.2 使用redis缓存菜品数据 2.3 使用Spring Cache缓存套餐数据 1. 使用Git管 ...

  7. android can为啥能发收不到数据_拼多多登录时手机收不到短信验证码怎么办

    遇到拼多多登录时收不到短信验证码的情况,该怎么办? 使用无痕方式 进入到软件所在的文件夹,以v2.6.6版本为例 右键编辑webcrawl-v2.6.6\root_crawl\setting.js 打 ...

  8. 使用Spring Boot JPA Specification实现使用JSON数据来查询实体数据

    文章目录 使用Spring Boot JPA Specification实现使用JSON数据来查询实体数据 需求概要 JSON 结构的设计 使用策略模式执行不同的查询条件 构造查询条件 主逻辑具体的代 ...

  9. 使用阿里云短信验证码API发送短信验证码(配置,获取短信验证码,注册,登录,密码重置)

    获取阿里云短信验证码需要的配置信息. 如果是新用户,可以免费领取3个月,老用户的话就只能购买了,但是也不贵. 申请短信签名 申请短信模板 编写发送短信验证码的工具类 代码中我已经进行了详细的注释,也写 ...

最新文章

  1. myeclipse中安装svn插件
  2. Windows Azure ISV博客系列:ReedRex 的sociobridge
  3. ie与firefox兼容文档
  4. 数据结构与算法之贪心算法 C++实现
  5. monocross 环境搭建:MonoTouch Mono for Android
  6. Centos 7.X安装DB2 10.5
  7. 【Clickhouse】rsyslog服务器使用clickhouse列数据库存储日志
  8. php 正则表达式 取所有内容,php正则表达式获取内容所有链接
  9. 解决linux下内网域名的ping结果和nslookup结果不一致
  10. springnbsp;security总结nbsp;太有用了!!
  11. 令人眼睛一亮的履历表
  12. RestTemplate发送请求并携带header信息
  13. 仅需1秒!搞定100万行数据:超强Python数据分析利器
  14. 视觉SLAM笔记(14) Eigen几何模块
  15. MongoDB入门(一)——数据库概述
  16. 苹果Mac矢量图形设计工具:Affinity Designer Beta
  17. java json-rpc_JSON-RPC(jsonrpc4j)使用demo
  18. MDM数据分析设计方案
  19. 游戏数值策划入门介绍
  20. 根据excel列动态创建mysql表_根据数据库字段动态生成excel模版下载,上传模版获取数据存入数据库(poi 反射)...

热门文章

  1. hihoCoder挑战赛16 王胖浩与三角形
  2. 教你用Python开发的一款迷你跑步游戏
  3. 公交车信息查询管理系统(Java实现)
  4. linux txt file busy,linux使用cp报错 Text file busy
  5. [leetcode] 229. Majority Element II
  6. vi之列操作——步步为营+实例
  7. Java中抽象类与方法的重写
  8. mysql连接异常:The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zo
  9. autojs教程:找图函数
  10. Hetian lab Day 10:工欲善其事必先利其器