diff --git a/application/admin/controller/setting/SystemGroupData.php b/application/admin/controller/setting/SystemGroupData.php index ed319378..f8f93628 100644 --- a/application/admin/controller/setting/SystemGroupData.php +++ b/application/admin/controller/setting/SystemGroupData.php @@ -109,7 +109,8 @@ class SystemGroupData extends AuthController foreach ($params as $key => $param) { foreach ($Fields['fields'] as $index => $field) { if($key == $field["title"]){ - if($param == "" || count($param) == 0) +// if($param == "" || count($param) == 0) + if($param == "") return Json::fail($field["name"]."不能为空!"); else{ $value[$key]["type"] = $field["type"]; diff --git a/application/admin/controller/store/StoreCategory.php b/application/admin/controller/store/StoreCategory.php index 71d72375..782f7eb7 100644 --- a/application/admin/controller/store/StoreCategory.php +++ b/application/admin/controller/store/StoreCategory.php @@ -1,5 +1,4 @@ $menu['id'],'label'=>$menu['html'].$menu['cate_name'],'disabled'=>$menu['pid']== 0];//,'disabled'=>$menu['pid']== 0]; } return $menus; - })->filterable(1)->multiple(1), - Form::input('store_name','产品名称')->col(Form::col(24)), + })->filterable(1)->multiple(1)->required(), + Form::input('store_name','产品名称')->col(Form::col(24))->validateFn(function($validate){ + $validate->min(5)->max(32); + })->required(), Form::input('store_info','产品简介')->type('textarea'), Form::input('keyword','产品关键字')->placeholder('多个用英文状态下的逗号隔开'), - Form::input('unit_name','产品单位','件'), - Form::frameImageOne('image','产品主图片(305*305px)',Url::build('admin/widget.images/index',array('fodder'=>'image')))->icon('image')->width('100%')->height('500px'), - Form::frameImages('slider_image','产品轮播图(640*640px)',Url::build('admin/widget.images/index',array('fodder'=>'slider_image')))->maxLength(5)->icon('images')->width('100%')->height('500px')->spin(0), - Form::number('price','产品售价')->min(0)->col(8), + Form::input('unit_name','产品单位','件')->required(), + Form::frameImageOne('image','产品主图片(305*305px)',Url::build('admin/widget.images/index',array('fodder'=>'image')))->icon('image')->width('100%')->height('500px')->required(), + Form::frameImages('slider_image','产品轮播图(640*640px)',Url::build('admin/widget.images/index',array('fodder'=>'slider_image')))->maxLength(5)->icon('images')->width('100%')->height('500px')->spin(0) + ->required()->validateFn(function($validate){ + $validate->min(1)->max(5); + }), + Form::number('price','产品售价')->min(0)->col(8)->required(), Form::number('ot_price','产品市场价')->min(0)->col(8), Form::number('give_integral','赠送积分')->min(0)->precision(0)->col(8), - Form::number('postage','邮费')->min(0)->col(Form::col(8)), + Form::number('postage','邮费')->min(0)->col(Form::col(8))->required(), Form::number('sales','销量',0)->min(0)->precision(0)->col(8)->readonly(1), Form::number('ficti','虚拟销量')->min(0)->precision(0)->col(8), - Form::number('stock','库存')->min(0)->precision(0)->col(8), + Form::number('stock','库存')->min(0)->precision(0)->col(8)->required(), Form::number('cost','产品成本价')->min(0)->col(8), - Form::number('sort','排序')->col(8), + Form::number('sort','排序',0)->col(8)->required(), Form::radio('is_show','产品状态',0)->options([['label'=>'上架','value'=>1],['label'=>'下架','value'=>0]])->col(8), Form::radio('is_hot','热卖单品',0)->options([['label'=>'是','value'=>1],['label'=>'否','value'=>0]])->col(8), Form::radio('is_benefit','促销单品',0)->options([['label'=>'是','value'=>1],['label'=>'否','value'=>0]])->col(8), diff --git a/application/admin/controller/system/SystemFile.php b/application/admin/controller/system/SystemFile.php index 87eb8417..09234cdc 100644 --- a/application/admin/controller/system/SystemFile.php +++ b/application/admin/controller/system/SystemFile.php @@ -20,11 +20,11 @@ class SystemFile extends AuthController public function opendir($filedir=''){ $fileAll = array('dir'=>[],'file'=>[]); if(Request::instance()->param('superior') && !empty(Request::instance()->param('dir'))){ - $path = '.'.DS.Request::instance()->param('dir'); + $path = './'.Request::instance()->param('dir'); $path = dirname($path); }else{ $path = !empty(Request::instance()->param('dir'))?Request::instance()->param('dir'):'.'; - $path = $path.DS.Request::instance()->param('filedir'); + $path = $path.'/'.Request::instance()->param('filedir'); } $list = scandir($path); foreach($list as $key=>$v) { @@ -38,6 +38,9 @@ class SystemFile extends AuthController } } // var_dump($fileAll['file']); + //兼容windows + $uname=php_uname('s'); + if(strstr($uname,'Windows')!==false) $path = ltrim($path,'\\'); $dir = ltrim($path,'./'); $this->assign(compact('fileAll','dir')); return $this->fetch(); @@ -65,6 +68,10 @@ class SystemFile extends AuthController $comment = $this->request->post('comment'); $filepath = $this->request->post('filepath'); if(!empty($comment) && !empty($filepath)){ + //兼容windows + $uname=php_uname('s'); + if(strstr($uname,'Windows')!==false) + $filepath = ltrim(str_replace('/', DS, $filepath),'.'); $res = FileClass::write_file($filepath,$comment); if($res){ return Json::successful('保存成功!'); diff --git a/application/admin/controller/ump/StoreCoupon.php b/application/admin/controller/ump/StoreCoupon.php index dc6a847e..a0c4aa9b 100644 --- a/application/admin/controller/ump/StoreCoupon.php +++ b/application/admin/controller/ump/StoreCoupon.php @@ -42,7 +42,7 @@ class StoreCoupon extends AuthController public function create() { $f = array(); - $f[] = Form::input('title','优惠券名称'); + $f[] = Form::input('title','优惠券名称')->required(); $f[] = Form::number('coupon_price','优惠券面值',0)->min(0); $f[] = Form::number('use_min_price','优惠券最低消费')->min(0); $f[] = Form::number('coupon_time','优惠券有效期限')->min(0); diff --git a/application/admin/controller/ump/StoreSeckill.php b/application/admin/controller/ump/StoreSeckill.php index 0e58be25..71e5605d 100644 --- a/application/admin/controller/ump/StoreSeckill.php +++ b/application/admin/controller/ump/StoreSeckill.php @@ -75,13 +75,13 @@ class StoreSeckill extends AuthController public function create() { $f = array(); - $f[] = Form::input('title','产品标题'); - $f[] = Form::input('info','秒杀活动简介')->type('textarea'); - $f[] = Form::input('unit_name','单位')->placeholder('个、位'); - $f[] = Form::dateTimeRange('section_time','活动时间'); - $f[] = Form::frameImageOne('image','产品主图片(305*305px)',Url::build('admin/widget.images/index',array('fodder'=>'image')))->icon('image'); - $f[] = Form::frameImages('images','产品轮播图(640*640px)',Url::build('admin/widget.images/index',array('fodder'=>'images')))->maxLength(5)->icon('images'); - $f[] = Form::number('price','秒杀价')->min(0)->col(12); + $f[] = Form::input('title','产品标题')->required(); + $f[] = Form::input('info','秒杀活动简介')->type('textarea')->required(); + $f[] = Form::input('unit_name','单位')->placeholder('个、位')->required(); + $f[] = Form::dateTimeRange('section_time','活动时间')->required(); + $f[] = Form::frameImageOne('image','产品主图片(305*305px)',Url::build('admin/widget.images/index',array('fodder'=>'image')))->icon('image')->required(); + $f[] = Form::frameImages('images','产品轮播图(640*640px)',Url::build('admin/widget.images/index',array('fodder'=>'images')))->maxLength(5)->icon('images')->required(); + $f[] = Form::number('price','秒杀价')->min(0)->col(12)->required(); $f[] = Form::number('ot_price','原价')->min(0)->col(12); $f[] = Form::number('cost','成本价')->min(0)->col(12); $f[] = Form::number('stock','库存')->min(0)->precision(0)->col(12); @@ -93,7 +93,7 @@ class StoreSeckill extends AuthController $f[] = Form::radio('is_postage','是否包邮',1)->options([['label'=>'是','value'=>1],['label'=>'否','value'=>0]])->col(12); $f[] = Form::radio('is_hot','热门推荐',1)->options([['label'=>'开启','value'=>1],['label'=>'关闭','value'=>0]])->col(12); $f[] = Form::radio('status','活动状态',1)->options([['label'=>'开启','value'=>1],['label'=>'关闭','value'=>0]])->col(12); - $form = Form::make_post_form('添加用户通知',$f,Url::build('save')); + $form = Form::make_post_form('开启秒杀',$f,Url::build('save')); $this->assign(compact('form')); return $this->fetch('public/form-builder'); } diff --git a/application/admin/model/order/StoreOrder.php b/application/admin/model/order/StoreOrder.php index bfbd1214..f4d76783 100644 --- a/application/admin/model/order/StoreOrder.php +++ b/application/admin/model/order/StoreOrder.php @@ -54,6 +54,7 @@ class StoreOrder extends ModelBasic $_info[$k]['cart_info'] = json_decode($v['cart_info'],true); } $item['_info'] = $_info; + $item['add_time'] = date('Y-m-d H:i:s',$item['add_time']); if($item['pink_id'] && $item['combination_id']){ $pinkStatus = StorePink::where('order_id_key',$item['id'])->value('status'); switch ($pinkStatus){ @@ -904,4 +905,21 @@ HTML; if(!$uid) return 0; return self::where('uid',$uid)->where('paid',1)->where('refund_status',0)->where('status',2)->count(); } + /** + * 获取已支付的订单 + * @param int $is_promoter + * @return int|string + */ + public static function getOrderPayCount($is_promoter = 0){ + return self::where('o.paid',1)->alias('o')->join('User u','u.uid=o.uid')->where('u.is_promoter',$is_promoter)->count(); + } + + /** + * 获取最后一个月已支付的订单 + * @param int $is_promoter + * @return int|string + */ + public static function getOrderPayMonthCount($is_promoter = 0){ + return self::where('o.paid',1)->alias('o')->whereTime('o.pay_time','last month')->join('User u','u.uid=o.uid')->where('u.is_promoter',$is_promoter)->count(); + } } \ No newline at end of file diff --git a/application/admin/model/store/StoreCategory.php b/application/admin/model/store/StoreCategory.php index 4b16e1cf..7a4e13e2 100644 --- a/application/admin/model/store/StoreCategory.php +++ b/application/admin/model/store/StoreCategory.php @@ -7,7 +7,6 @@ namespace app\admin\model\store; - use traits\ModelTrait; use basic\ModelBasic; use service\UtilService; @@ -20,16 +19,39 @@ class StoreCategory extends ModelBasic { use ModelTrait; + /* + * 异步获取分类列表 + * @param $where + * @return array + */ + public static function CategoryList($where){ + $data=($data=self::systemPage($where,true)->page((int)$where['page'],(int)$where['limit'])->select()) && count($data) ? $data->toArray() :[]; + foreach ($data as &$item){ + if($item['pid']){ + $item['pid_name'] = self::where('id',$item['pid'])->value('cate_name'); + }else{ + $item['pid_name'] = '顶级'; + } + } + $count=self::systemPage($where,true)->count(); + return compact('count','data'); + } /** * @param $where * @return array */ - public static function systemPage($where){ + public static function systemPage($where,$isAjax=false){ $model = new self; if($where['pid'] != '') $model = $model->where('pid',$where['pid']); else if($where['pid']=='' && $where['cate_name']=='') $model = $model->where('pid',0); if($where['is_show'] != '') $model = $model->where('is_show',$where['is_show']); if($where['cate_name'] != '') $model = $model->where('cate_name','LIKE',"%$where[cate_name]%"); + if($isAjax===true){ + if(isset($where['order']) && $where['order']!=''){ + $model=$model->order(self::setOrder($where['order'])); + } + return $model; + } return self::page($model,function ($item){ if($item['pid']){ $item['pid_name'] = self::where('id',$item['pid'])->value('cate_name'); diff --git a/application/admin/model/store/StoreProductAttr.php b/application/admin/model/store/StoreProductAttr.php index d685496b..910cb945 100644 --- a/application/admin/model/store/StoreProductAttr.php +++ b/application/admin/model/store/StoreProductAttr.php @@ -77,7 +77,7 @@ class StoreProductAttr extends ModelBasic ]; } foreach ($valueList as $k=>$value){ - ksort($value['detail'],SORT_STRING); + sort($value['detail'],SORT_STRING); $suk = implode(',',$value['detail']); $valueGroup[$suk] = [ 'product_id'=>$productId, diff --git a/application/admin/view/order/store_order/index.php b/application/admin/view/order/store_order/index.php index f06384ba..04c64692 100644 --- a/application/admin/view/order/store_order/index.php +++ b/application/admin/view/order/store_order/index.php @@ -82,7 +82,7 @@
@@ -112,32 +112,32 @@ {{# if(item.cart_info.productInfo.attrInfo!=undefined){ }}

- + {{item.cart_info.productInfo.store_name}} {{item.cart_info.productInfo.attrInfo.suk}} | ¥{{item.cart_info.truePrice}}×{{item.cart_info.cart_num}}

{{# }else{ }}

- + {{item.cart_info.productInfo.store_name}} | ¥{{item.cart_info.truePrice}}×{{item.cart_info.cart_num}}

{{# } }} {{# }); }} - - + + + + -
- - - - - - - - - - - - - - - {volist name="list" id="vo"} - - - - - - - - - - - {/volist} - -
编号父级分类名称分类图标排序是否显示操作
- {$vo.id} - - {$vo.pid_name} - - {$vo.cate_name} - - {$vo.cate_name} - - {$vo.sort} - - - -
-
- - -
-
-
-
- {include file="public/inner_page"} + {/block} {block name="script"} {/block} diff --git a/application/admin/view/store/store_product/index.php b/application/admin/view/store/store_product/index.php index f86bc0a3..c5877ccd 100644 --- a/application/admin/view/store/store_product/index.php +++ b/application/admin/view/store/store_product/index.php @@ -71,7 +71,7 @@
{switch name='type'} {case value="1"} - + {/case} {case value="2"} @@ -175,15 +175,15 @@ case 2: join=[ {type:'checkbox'}, - {field: 'id', title: 'ID', sort: true,event:'id',width:'5%'}, - {field: 'image', title: '产品图片',templet:'#image'}, + {field: 'id', title: 'ID', sort: true,event:'id',width:'6%'}, + {field: 'image', title: '产品图片',templet:'#image',width:'10%'}, {field: 'store_name', title: '产品名称',templet:'#store_name'}, - {field: 'price', title: '产品价格',edit:'price'}, - {field: 'ficti', title: '虚拟销量',edit:'ficti'}, - {field: 'stock', title: '库存',edit:'stock'}, - {field: 'sort', title: '排序',edit:'sort'}, - {field: 'sales', title: '销量',sort: true,event:'sales'}, - {field: 'status', title: '状态',templet:"#checkboxstatus"}, + {field: 'price', title: '价格',edit:'price',width:'8%'}, + {field: 'ficti', title: '虚拟销量',edit:'ficti',width:'8%'}, + {field: 'stock', title: '库存',edit:'stock',width:'6%'}, + {field: 'sort', title: '排序',edit:'sort',width:'6%'}, + {field: 'sales', title: '销量',sort: true,event:'sales',width:'6%'}, + {field: 'status', title: '状态',templet:"#checkboxstatus",width:'8%'}, {field: 'right', title: '操作',align:'center',toolbar:'#act',width:'14%'}, ]; break; diff --git a/application/admin/view/ump/store_combination/index.php b/application/admin/view/ump/store_combination/index.php index a89434de..f6816156 100644 --- a/application/admin/view/ump/store_combination/index.php +++ b/application/admin/view/ump/store_combination/index.php @@ -135,8 +135,8 @@ layList.tableList('combinationList',"{:Url('get_combination_list')}",function () { return [ {field: 'id', title: '编号', sort: true,event:'id'}, - {field: 'image', title: '拼团图片',templet: '

{{d.title}}

'}, - {field: 'title', title: '拼团名称'}, + {field: 'image', title: '拼团图片',width:'10%',templet: '

{{d.title}}

'}, + {field: 'title', title: '拼团名称',width:'10%'}, {field: 'ot_price', title: '原价'}, {field: 'price', title: '拼团价'}, {field: 'stock', title: '库存'}, diff --git a/application/admin/view/ump/store_seckill/index.php b/application/admin/view/ump/store_seckill/index.php index fc39be2a..5888cef4 100644 --- a/application/admin/view/ump/store_seckill/index.php +++ b/application/admin/view/ump/store_seckill/index.php @@ -113,17 +113,17 @@ layList.form.render(); layList.tableList('seckillList',"{:Url('get_seckill_list')}",function () { return [ - {field: 'id', title: '编号', sort: true,width:'5%',event:'id',unresize:true}, - {field: 'image', title: '产品图片',unresize:true, width: '8%',templet: '

{{d.title}}

'}, - {field: 'title', title: '活动标题',width:'14%',unresize:true}, - {field: 'info', title: '活动简介',width:'17%',unresize:true}, - {field: 'ot_price', title: '原价',width:'6%',unresize:true}, - {field: 'price', title: '秒杀价',unresize:true,width:'6%'}, - {field: 'stock', title: '库存',width:'7%',unresize:true}, - {field: 'start_name', title: '秒杀状态',width:'13%',toolbar:"#statusCn",unresize:true}, - {field: 'stop_time', title: '结束时间', width: '13%',toolbar: '#stopTime',unresize:true}, - {field: 'status', title: '状态',width:'6%',toolbar:"#status",unresize:true}, - {field: 'right', title: '操作', width: '5%', align: 'center', toolbar: '#barDemo',unresize:true} + {field: 'id', title: 'ID', sort: true,width:'6%',event:'id'}, + {field: 'image', title: '产品图片', width: '10%',templet: '

{{d.title}}

'}, + {field: 'title', title: '活动标题'}, + {field: 'info', title: '活动简介',width:'20%'}, + {field: 'ot_price', title: '原价',width:'6%'}, + {field: 'price', title: '秒杀价',width:'6%'}, + {field: 'stock', title: '库存',width:'6%'}, + {field: 'start_name', title: '秒杀状态',width:'8%',toolbar:"#statusCn"}, + {field: 'stop_time', title: '结束时间', width: '13%',toolbar: '#stopTime'}, + {field: 'status', title: '状态',width:'6%',toolbar:"#status"}, + {field: 'right', title: '操作', width: '6%', align: 'center', toolbar: '#barDemo'} ] }); layList.tool(function (event,data,obj) { diff --git a/application/admin/view/ump/user_point/index.php b/application/admin/view/ump/user_point/index.php index 1a7c87b3..dc0b8607 100644 --- a/application/admin/view/ump/user_point/index.php +++ b/application/admin/view/ump/user_point/index.php @@ -88,7 +88,7 @@ layList.form.render(); layList.tableList('userList',"{:Url('getponitlist')}",function () { return [ - {field: 'id', title: '编号', sort: true,event:'uid'}, + {field: 'id', title: 'ID', sort: true,event:'uid',width:'8%'}, {field: 'title', title: '标题' }, {field: 'balance', title: '积分余量',sort:true,event:'now_money'}, {field: 'number', title: '明细数字',sort:true}, diff --git a/application/admin/view/wechat/reply/keyword.php b/application/admin/view/wechat/reply/keyword.php index b2b50c8d..73ca5e03 100644 --- a/application/admin/view/wechat/reply/keyword.php +++ b/application/admin/view/wechat/reply/keyword.php @@ -64,7 +64,7 @@ {/switch} - + diff --git a/application/wap/model/store/StoreCart.php b/application/wap/model/store/StoreCart.php index 2cb52f6c..6cc90684 100644 --- a/application/wap/model/store/StoreCart.php +++ b/application/wap/model/store/StoreCart.php @@ -99,14 +99,15 @@ class StoreCart extends ModelBasic foreach ($list as $k=>$cart) { if ($cart['seckill_id']) { $product = StoreSeckill::field($seckillInfoField) - ->find($cart['seckill_id'])->toArray(); + ->find($cart['seckill_id']); }elseif($cart['bargain_id']){ $product = StoreBargain::field($bargainInfoField) - ->find($cart['bargain_id'])->toArray(); + ->find($cart['bargain_id']); }else{ $product = StoreProduct::field($productInfoField) - ->find($cart['product_id'])->toArray(); + ->find($cart['product_id']); } + if(!empty($product)) $product = $product->toArray(); $cart['productInfo'] = $product; //商品不存在 if (!$product) { diff --git a/extend/service/WechatService.php b/extend/service/WechatService.php index a18875db..347c069f 100644 --- a/extend/service/WechatService.php +++ b/extend/service/WechatService.php @@ -27,25 +27,24 @@ class WechatService { private static $instance = null; - /**获取微信配置参数 - * @return array - */ public static function options() { - $wechat = SystemConfigService::more(['wechat_appid','wechat_appsecret','wechat_token']); + $wechat = SystemConfigService::more(['wechat_appid','wechat_appsecret','wechat_token','wechat_encodingaeskey','wechat_encode']); $payment = SystemConfigService::more(['pay_weixin_mchid','pay_weixin_client_cert','pay_weixin_client_key','pay_weixin_key','pay_weixin_open']); $config = [ - 'app_id'=>isset($wechat['wechat_appid']) ? trim($wechat['wechat_appid']):'', - 'secret'=>isset($wechat['wechat_appsecret']) ? trim($wechat['wechat_appsecret']):'', - 'token'=>isset($wechat['wechat_token']) ? trim($wechat['wechat_token']):'', + 'app_id'=>isset($wechat['wechat_appid']) ? $wechat['wechat_appid']:'', + 'secret'=>isset($wechat['wechat_appsecret']) ? $wechat['wechat_appsecret']:'', + 'token'=>isset($wechat['wechat_token']) ? $wechat['wechat_token']:'', 'guzzle' => [ 'timeout' => 10.0, // 超时时间(秒) ], ]; + if((int)$wechat['wechat_encode']>0 && isset($wechat['wechat_encodingaeskey']) && !empty($wechat['wechat_encodingaeskey'])) + $config['aes_key'] = $wechat['wechat_encodingaeskey']; if(isset($payment['pay_weixin_open']) && $payment['pay_weixin_open'] == 1){ $config['payment'] = [ - 'merchant_id'=>trim($payment['pay_weixin_mchid']), - 'key'=>trim($payment['pay_weixin_key']), + 'merchant_id'=>$payment['pay_weixin_mchid'], + 'key'=>$payment['pay_weixin_key'], 'cert_path'=>realpath('.'.$payment['pay_weixin_client_cert']), 'key_path'=>realpath('.'.$payment['pay_weixin_client_key']), 'notify_url'=>SystemConfigService::get('site_url').Url::build('wap/Wechat/notify') @@ -62,9 +61,6 @@ class WechatService return self::$instance; } - /** - * 微信接口 - */ public static function serve() { $wechat = self::application(true); @@ -133,6 +129,7 @@ class WechatService $response = HookService::resultListen('wechat_message_other',$message,null,true,$behavior); break; } + return $response; }); } @@ -370,11 +367,6 @@ class WechatService return self::paymentService()->refund($orderNo,$refundNo,$totalFee,$refundFee,$opUserId,$type,$refundAccount,$refundReason); } - /**订单退款 - * @param $orderNo - * @param array $opt - * @return bool - */ public static function payOrderRefund($orderNo, array $opt) { if(!isset($opt['pay_price'])) exception('缺少pay_price'); @@ -420,10 +412,6 @@ class WechatService return self::application()->js; } - /** jsSdk - * @param string $url - * @return array|string - */ public static function jsSdk($url = '') { $apiList = ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'translateVoice', 'getNetworkType', 'openLocation', 'getLocation', 'hideOptionMenu', 'showOptionMenu', 'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'closeWindow', 'scanQRCode', 'chooseWXPay', 'openProductSpecificView', 'addCard', 'chooseCard', 'openCard']; diff --git a/public/system/css/layui-admin.css b/public/system/css/layui-admin.css index 564b9d60..efe36ee9 100644 --- a/public/system/css/layui-admin.css +++ b/public/system/css/layui-admin.css @@ -1,4 +1,11 @@ @charset "UTF-8"; +.layui-btn { + background-color: #0092DC; +} +.layui-btn-primary:hover { + border-color: #0092DC; + color: #333; +} .layui-table-box{ overflow: initial; } @@ -11,44 +18,14 @@ .table-responsive{ overflow: initial; } -.layui-table img{width: 100% !important;} -/* -部分样式重构 - */ -.layui-form-pane .layui-input, .layui-select, .layui-textarea { - height: 32px; - line-height: 1.5; - padding: 4px 7px; - font-size: 12px; - border: 1px solid #dddee1; - color: #495060; - background-color: #fff; +/*.layui-table img{width: 100% !important;}*/ +/*部分样式重构*/ +.layui-laypage-limits select{ + height: 26px; } .layui-card-body { } -.layui-form-pane{ - /*padding: 20px;*/ -} -.layui-form-pane .layui-form-label { - padding: 5px 15px; - height: 32px; -} -.layui-fluid { - padding: 0; -} -.layui-btn-normal{ - background-color:#0092DC; -} -.layui-btn .layui-icon { - font-size: 12px; -} -.layui-btn-sm i { - font-size: 12px!important; -} -.layui-form-onswitch { - border-color: #0092DC; - background-color: #0092DC; -} + .layui-table-cell p{ height: 20px; line-height: 20px; @@ -58,6 +35,10 @@ width: 14px; height: 14px; } + +.layui-laypage a, .layui-laypage button, .layui-laypage input, .layui-laypage select, .layui-laypage span { + color:#333 !important; +} .layui-form-checked[lay-skin=primary] i { border-color: #0093DE; background-color: #0093DE; @@ -73,23 +54,6 @@ .layui-tab-title .layui-this:after{ border-bottom: 2px solid #0092DC !important; } -.layui-form-label{ - width: auto; -} -.layui-table-cell { - white-space:normal; - height: auto!important; -} -.layui-input-block .layui-admin-input{ - width: 50%; - height: 34px; -} -.layui-form-item{ - margin-bottom: 0; -} -.layui-input-block .time-w{ - width: 200px; -} .layui-btn-group button i{ line-height: 30px; @@ -99,11 +63,8 @@ .back-f8{ background-color: #F8F8F8; } -.layui-input-block button{ - border: 1px solid #C9C9C9; -} .layui-card-body p.layuiadmin-big-font { - font-size: 36px; + font-size: 24px; color: #666; line-height: 36px; padding: 5px 0 10px; @@ -184,3 +145,92 @@ .layadmin-text-center{ text-align: center; } +/*表格样式*/ +.layui-table-view .layui-table td, .layui-table-view .layui-table th { + font-size: 13px; + font-weight: bolder; +} +.layui-table-cell { + white-space:normal; + height: auto!important; + font-size: 13px; + font-weight: normal; +} +/*form 样式*/ + +.layui-form-select .layui-input { + padding-right: 30px; + cursor: pointer; + font-size: 12px; + height: 32px; +} +.layui-form-select dl { + top: 32px; + font-size: 12px; +} +.layui-form-select dl dd.layui-this { + background-color: #0092DC; + color: #fff; +} +.layui-input{ + height: 32px; + font-size: 12px; +} +.layui-input-block .layui-admin-input{ + width: 50%; + font-size: 12px; + height: 32px; +} +.layui-form-item{ + margin-bottom: 0; +} +.layui-input-block .time-w{ + width: 200px; +} +.layui-form-pane .layui-input, .layui-select, .layui-textarea { + height: 34px; + line-height: 1.5; + padding: 4px 12px; + font-size: 12px; + border: 1px solid #e5e6e7; + color: #333; + background-color: #fff; +} +.layui-form-pane{ + /*padding: 20px;*/ +} +.layui-form-pane .layui-inline .layui-form-label { + padding: 5px 15px; + height: 34px; + line-height: 24px; + background: none; + border-radius: 5px 0 0 5px; +} +.layui-form-item .layui-btn{ + margin-top: -5px; +} +.layui-form-label{ + width: auto; + padding: 6px; +} +.layui-fluid { + padding: 0; +} +.layui-btn-normal{ + background-color:#0092DC; +} +.layui-btn .layui-icon { + font-size: 12px; +} +.layui-btn-sm i { + font-size: 12px!important; +} +.layui-form-onswitch { + border-color: #0092DC; + background-color: #0092DC; +} +.layui-btn-primary { + border: 1px solid #e5e6e7; + background-color: #fff; + color: #333; +} \ No newline at end of file diff --git a/thinkphp/library/think/db/Builder.php b/thinkphp/library/think/db/Builder.php index 213cb090..1a865a9d 100644 --- a/thinkphp/library/think/db/Builder.php +++ b/thinkphp/library/think/db/Builder.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,7 +11,6 @@ namespace think\db; -use BadMethodCallException; use PDO; use think\Exception; @@ -26,7 +25,7 @@ abstract class Builder protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; // SQL表达式 - protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%'; protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; @@ -99,8 +98,11 @@ abstract class Builder $result = []; foreach ($data as $key => $val) { - $item = $this->parseKey($key, $options); - if (is_object($val) && method_exists($val, '__toString')) { + $item = $this->parseKey($key, $options, true); + if ($val instanceof Expression) { + $result[$item] = $val->getValue(); + continue; + } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 $val = $val->__toString(); } @@ -110,8 +112,25 @@ abstract class Builder } } elseif (is_null($val)) { $result[$item] = 'NULL'; - } elseif (isset($val[0]) && 'exp' == $val[0]) { - $result[$item] = $val[1]; + } elseif (is_array($val) && !empty($val)) { + switch (strtolower($val[0])) { + case 'inc': +// $result[$item] = $item . '+' . floatval($val[1]); + if ($key == $val[1]) { + $result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]); + } + + break; + case 'dec': +// $result[$item] = $item . '-' . floatval($val[1]); + if ($key == $val[1]) { + $result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]); + } + + break; + case 'exp': + throw new Exception('not support data:[' . $val[0] . ']'); + } } elseif (is_scalar($val)) { // 过滤非标量数据 if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { @@ -133,7 +152,7 @@ abstract class Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { return $key; } @@ -174,8 +193,10 @@ abstract class Builder // 支持 'field1'=>'field2' 这样的字段别名定义 $array = []; foreach ($fields as $key => $field) { - if (!is_numeric($key)) { - $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); + if ($field instanceof Expression) { + $array[] = $field->getValue(); + } elseif (!is_numeric($key)) { + $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true); } else { $array[] = $this->parseKey($field, $options); } @@ -197,9 +218,6 @@ abstract class Builder $item = []; foreach ((array) $tables as $key => $table) { if (!is_numeric($key)) { - if (strpos($key, '@think')) { - $key = strstr($key, '@think', true); - } $key = $this->parseSqlTable($key); $item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table)); } else { @@ -257,7 +275,9 @@ abstract class Builder foreach ($where as $key => $val) { $str = []; foreach ($val as $field => $value) { - if ($value instanceof \Closure) { + if ($value instanceof Expression) { + $str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )'; + } elseif ($value instanceof \Closure) { // 使用闭包查询 $query = new Query($this->connection); call_user_func_array($value, [ & $query]); @@ -298,7 +318,7 @@ abstract class Builder protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) { // 字段分析 - $key = $field ? $this->parseKey($field, $options) : ''; + $key = $field ? $this->parseKey($field, $options, true) : ''; // 查询规则和条件 if (!is_array($val)) { @@ -331,13 +351,15 @@ abstract class Builder throw new Exception('where express error:' . $exp); } } - $bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field); + $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field); if (preg_match('/\W/', $bindName)) { // 处理带非单词字符的字段名 $bindName = md5($bindName); } - if (is_object($value) && method_exists($value, '__toString')) { + if ($value instanceof Expression) { + + } elseif (is_object($value) && method_exists($value, '__toString')) { // 对象数据写入 $value = $value->__toString(); } @@ -374,7 +396,11 @@ abstract class Builder } } elseif ('EXP' == $exp) { // 表达式查询 - $whereStr .= '( ' . $key . ' ' . $value . ' )'; + if ($value instanceof Expression) { + $whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )'; + } else { + throw new Exception('where express error:' . $exp); + } } elseif (in_array($exp, ['NOT NULL', 'NULL'])) { // NULL 查询 $whereStr .= $key . ' IS ' . $exp; @@ -492,6 +518,11 @@ abstract class Builder } } $bindName = $bindName ?: $key; + + if ($this->query->isBind($bindName)) { + $bindName .= '_' . str_replace('.', '_', uniqid('', true)); + } + $this->query->bind($bindName, $value, $bindType); return ':' . $bindName; } @@ -522,7 +553,9 @@ abstract class Builder list($table, $type, $on) = $item; $condition = []; foreach ((array) $on as $val) { - if (strpos($val, '=')) { + if ($val instanceof Expression) { + $condition[] = $val->getValue(); + } elseif (strpos($val, '=')) { list($val1, $val2) = explode('=', $val, 2); $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); } else { @@ -546,28 +579,29 @@ abstract class Builder */ protected function parseOrder($order, $options = []) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { - if (is_numeric($key)) { - if ('[rand]' == $val) { - if (method_exists($this, 'parseRand')) { - $array[] = $this->parseRand(); - } else { - throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand'); - } - } elseif (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } else { - $array[] = $val; - } - } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; - } - } - $order = implode(',', $array); + if (empty($order)) { + return ''; } + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); + } else { + if (is_numeric($key)) { + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); + } else { + $sort = $val; + } + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + $array[] = $this->parseKey($key, $options, true) . $sort; + } + } + $order = implode(',', $array); + return !empty($order) ? ' ORDER BY ' . $order : ''; } @@ -601,6 +635,9 @@ abstract class Builder */ protected function parseComment($comment) { + if (false !== strpos($comment, '*/')) { + $comment = strstr($comment, '*/', true); + } return !empty($comment) ? ' /* ' . $comment . ' */' : ''; } @@ -630,12 +667,12 @@ abstract class Builder unset($union['type']); foreach ($union as $u) { if ($u instanceof \Closure) { - $sql[] = $type . ' ' . $this->parseClosure($u, false); + $sql[] = $type . ' ' . $this->parseClosure($u); } elseif (is_string($u)) { - $sql[] = $type . ' ' . $this->parseSqlTable($u); + $sql[] = $type . ' ( ' . $this->parseSqlTable($u) . ' )'; } } - return implode(' ', $sql); + return ' ' . implode(' ', $sql); } /** @@ -650,11 +687,7 @@ abstract class Builder return ''; } - if (is_array($index)) { - $index = join(",", $index); - } - - return sprintf(" FORCE INDEX ( %s ) ", $index); + return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index); } /** @@ -749,7 +782,7 @@ abstract class Builder $fields = $options['field']; } - foreach ($dataSet as &$data) { + foreach ($dataSet as $data) { foreach ($data as $key => $val) { if (!in_array($key, $fields, true)) { if ($options['strict']) { @@ -770,19 +803,25 @@ abstract class Builder } $value = array_values($data); $values[] = 'SELECT ' . implode(',', $value); + + if (!isset($insertFields)) { + $insertFields = array_keys($data); + } } - $fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet))); - $sql = str_replace( + + foreach ($insertFields as $field) { + $fields[] = $this->parseKey($field, $options, true); + } + + return str_replace( ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], [ $replace ? 'REPLACE' : 'INSERT', $this->parseTable($options['table'], $options), - implode(' , ', $fields), + implode(' , ', $insertFields), implode(' UNION ALL ', $values), $this->parseComment($options['comment']), ], $this->insertAllSql); - - return $sql; } /** diff --git a/thinkphp/library/think/db/Connection.php b/thinkphp/library/think/db/Connection.php index 24fe07f4..7720282d 100644 --- a/thinkphp/library/think/db/Connection.php +++ b/thinkphp/library/think/db/Connection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -90,6 +90,8 @@ abstract class Connection 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, // 数据返回类型 @@ -354,15 +356,15 @@ abstract class Connection $this->bind = $bind; } - // 释放前次的查询结果 - if (!empty($this->PDOStatement)) { - $this->free(); - } - Db::$queryTimes++; try { // 调试开始 $this->debug(true); + + // 释放前次的查询结果 + if (!empty($this->PDOStatement)) { + $this->free(); + } // 预处理 if (empty($this->PDOStatement)) { $this->PDOStatement = $this->linkID->prepare($sql); @@ -378,7 +380,7 @@ abstract class Connection // 执行查询 $this->PDOStatement->execute(); // 调试结束 - $this->debug(false); + $this->debug(false, '', $master); // 返回结果集 return $this->getResult($pdo, $procedure); } catch (\PDOException $e) { @@ -386,6 +388,11 @@ abstract class Connection return $this->close()->query($sql, $bind, $master, $pdo); } throw new PDOException($e, $this->config, $this->getLastsql()); + } catch (\Throwable $e) { + if ($this->isBreak($e)) { + return $this->close()->query($sql, $bind, $master, $pdo); + } + throw $e; } catch (\Exception $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); @@ -397,13 +404,14 @@ abstract class Connection /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Query $query 查询对象 * @return int * @throws PDOException * @throws \Exception */ - public function execute($sql, $bind = []) + public function execute($sql, $bind = [], Query $query = null) { $this->initConnect(true); if (!$this->linkID) { @@ -416,15 +424,15 @@ abstract class Connection $this->bind = $bind; } - //释放前次的查询结果 - if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { - $this->free(); - } - Db::$executeTimes++; try { // 调试开始 $this->debug(true); + + //释放前次的查询结果 + if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { + $this->free(); + } // 预处理 if (empty($this->PDOStatement)) { $this->PDOStatement = $this->linkID->prepare($sql); @@ -440,18 +448,27 @@ abstract class Connection // 执行语句 $this->PDOStatement->execute(); // 调试结束 - $this->debug(false); + $this->debug(false, '', true); + + if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { + $query->readMaster(); + } $this->numRows = $this->PDOStatement->rowCount(); return $this->numRows; } catch (\PDOException $e) { if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind); + return $this->close()->execute($sql, $bind, $query); } throw new PDOException($e, $this->config, $this->getLastsql()); + } catch (\Throwable $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw $e; } catch (\Exception $e) { if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind); + return $this->close()->execute($sql, $bind, $query); } throw $e; } @@ -466,6 +483,10 @@ abstract class Connection */ public function getRealSql($sql, array $bind = []) { + if (is_array($sql)) { + $sql = implode(';', $sql); + } + foreach ($bind as $key => $val) { $value = is_array($val) ? $val[0] : $val; $type = is_array($val) ? $val[1] : PDO::PARAM_STR; @@ -478,8 +499,8 @@ abstract class Connection $sql = is_numeric($key) ? substr_replace($sql, $value, strpos($sql, '?'), 1) : str_replace( - [':' . $key . ')', ':' . $key . ',', ':' . $key . ' '], - [$value . ')', $value . ',', $value . ' '], + [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL], + [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL], $sql . ' '); } return rtrim($sql); @@ -648,6 +669,11 @@ abstract class Connection return $this->close()->startTrans(); } throw $e; + } catch (\Error $e) { + if ($this->isBreak($e)) { + return $this->close()->startTrans(); + } + throw $e; } } @@ -725,7 +751,7 @@ abstract class Connection * @param array $sqlArray SQL批处理指令 * @return boolean */ - public function batchQuery($sqlArray = []) + public function batchQuery($sqlArray = [], $bind = [], Query $query = null) { if (!is_array($sqlArray)) { return false; @@ -734,7 +760,7 @@ abstract class Connection $this->startTrans(); try { foreach ($sqlArray as $sql) { - $this->execute($sql); + $this->execute($sql, $bind, $query); } // 提交事务 $this->commit(); @@ -742,6 +768,7 @@ abstract class Connection $this->rollback(); throw $e; } + return true; } @@ -803,6 +830,7 @@ abstract class Connection 'SSL connection has been closed unexpectedly', 'Error writing data to the connection', 'Resource deadlock avoided', + 'failed with errno', ]; $error = $e->getMessage(); @@ -883,9 +911,10 @@ abstract class Connection * @access protected * @param boolean $start 调试开始标记 true 开始 false 结束 * @param string $sql 执行的SQL语句 留空自动获取 + * @param boolean $master 主从标记 * @return void */ - protected function debug($start, $sql = '') + protected function debug($start, $sql = '', $master = false) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 @@ -902,7 +931,7 @@ abstract class Connection $result = $this->getExplain($sql); } // SQL监听 - $this->trigger($sql, $runtime, $result); + $this->trigger($sql, $runtime, $result, $master); } } } @@ -924,19 +953,27 @@ abstract class Connection * @param string $sql SQL语句 * @param float $runtime SQL运行时间 * @param mixed $explain SQL分析 - * @return bool + * @param bool $master 主从标记 + * @return void */ - protected function trigger($sql, $runtime, $explain = []) + protected function trigger($sql, $runtime, $explain = [], $master = false) { if (!empty(self::$event)) { foreach (self::$event as $callback) { if (is_callable($callback)) { - call_user_func_array($callback, [$sql, $runtime, $explain]); + call_user_func_array($callback, [$sql, $runtime, $explain, $master]); } } } else { // 未注册监听则记录到日志中 - Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql'); + if ($this->config['deploy']) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + + Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql'); if (!empty($explain)) { Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); } diff --git a/thinkphp/library/think/db/Expression.php b/thinkphp/library/think/db/Expression.php new file mode 100644 index 00000000..f1b92abd --- /dev/null +++ b/thinkphp/library/think/db/Expression.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +class Expression +{ + /** + * 查询表达式 + * + * @var string + */ + protected $value; + + /** + * 创建一个查询表达式 + * + * @param string $value + * @return void + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * 获取表达式 + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php index e03ad8a7..b63c38e5 100644 --- a/thinkphp/library/think/db/Query.php +++ b/thinkphp/library/think/db/Query.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -53,14 +53,16 @@ class Query protected static $info = []; // 回调事件 private static $event = []; + // 读取主库 + private static $readMaster = []; /** * 构造函数 * @access public * @param Connection $connection 数据库对象实例 - * @param string $model 模型名 + * @param Model $model 模型对象 */ - public function __construct(Connection $connection = null, $model = '') + public function __construct(Connection $connection = null, $model = null) { $this->connection = $connection ?: Db::connect([], true); $this->prefix = $this->connection->getConfig('prefix'); @@ -131,15 +133,34 @@ class Query } /** - * 获取当前的模型对象名 + * 获取当前的模型对象实例 * @access public - * @return string + * @return Model|null */ public function getModel() { return $this->model; } + /** + * 设置后续从主库读取数据 + * @access public + * @param bool $allTable + * @return void + */ + public function readMaster($allTable = false) + { + if ($allTable) { + $table = '*'; + } else { + $table = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); + } + + static::$readMaster[$table] = true; + + return $this; + } + /** * 获取当前的builder实例对象 * @access public @@ -238,7 +259,7 @@ class Query */ public function execute($sql, $bind = []) { - return $this->connection->execute($sql, $bind); + return $this->connection->execute($sql, $bind, $this); } /** @@ -312,9 +333,9 @@ class Query * @param array $sql SQL批处理指令 * @return boolean */ - public function batchQuery($sql = []) + public function batchQuery($sql = [], $bind = []) { - return $this->connection->batchQuery($sql); + return $this->connection->batchQuery($sql, $bind); } /** @@ -403,7 +424,7 @@ class Query if (empty($this->options['table'])) { $this->options['table'] = $this->getTable(); } - $key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); + $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind)); $result = Cache::get($key); } if (false === $result) { @@ -420,7 +441,7 @@ class Query $result += 0; } - if (isset($cache)) { + if (isset($cache) && false !== $result) { // 缓存数据 $this->cacheData($key, $result, $cache); } @@ -447,7 +468,7 @@ class Query if (empty($this->options['table'])) { $this->options['table'] = $this->getTable(); } - $guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); + $guid = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind)); $result = Cache::get($guid); } if (false === $result) { @@ -534,22 +555,24 @@ class Query * MIN查询 * @access public * @param string $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function min($field) + public function min($field, $force = true) { - return $this->value('MIN(' . $field . ') AS tp_min', 0, true); + return $this->value('MIN(' . $field . ') AS tp_min', 0, $force); } /** * MAX查询 * @access public * @param string $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function max($field) + public function max($field, $force = true) { - return $this->value('MAX(' . $field . ') AS tp_max', 0, true); + return $this->value('MAX(' . $field . ') AS tp_max', 0, $force); } /** @@ -607,7 +630,7 @@ class Query return true; } } - return $this->setField($field, ['exp', $field . '+' . $step]); + return $this->setField($field, ['inc', $step]); } /** @@ -635,8 +658,9 @@ class Query $this->options = []; return true; } + return $this->setField($field, ['inc', $step]); } - return $this->setField($field, ['exp', $field . '-' . $step]); + return $this->setField($field, ['dec', $step]); } /** @@ -704,7 +728,8 @@ class Query { // 传入的表名为数组 if (is_array($join)) { - list($table, $alias) = each($join); + $table = $join; + $alias = array_shift($join); } else { $join = trim($join); if (false !== strpos($join, '(')) { @@ -725,13 +750,9 @@ class Query $table = $this->getTable($table); } } - } - if (isset($alias)) { - if (isset($this->options['alias'][$table])) { - $table = $table . '@think' . uniqid(); + if (isset($alias) && $table != $alias) { + $table = [$table => $alias]; } - $table = [$table => $alias]; - $this->alias($table); } return $table; } @@ -769,8 +790,15 @@ class Query { if (empty($field)) { return $this; + } elseif ($field instanceof Expression) { + $this->options['field'][] = $field; + return $this; } + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } $field = array_map('trim', explode(',', $field)); } if (true === $field) { @@ -794,12 +822,30 @@ class Query } if (isset($this->options['field'])) { - $field = array_merge($this->options['field'], $field); + $field = array_merge((array) $this->options['field'], $field); } $this->options['field'] = array_unique($field); return $this; } + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @param array $bind 参数绑定 + * @return $this + */ + public function fieldRaw($field, array $bind = []) + { + $this->options['field'][] = $this->raw($field); + + if ($bind) { + $this->bind($bind); + } + + return $this; + } + /** * 设置数据 * @access public @@ -828,7 +874,7 @@ class Query { $fields = is_string($field) ? explode(',', $field) : $field; foreach ($fields as $field) { - $this->data($field, ['exp', $field . '+' . $step]); + $this->data($field, ['inc', $step]); } return $this; } @@ -844,7 +890,7 @@ class Query { $fields = is_string($field) ? explode(',', $field) : $field; foreach ($fields as $field) { - $this->data($field, ['exp', $field . '-' . $step]); + $this->data($field, ['dec', $step]); } return $this; } @@ -858,25 +904,36 @@ class Query */ public function exp($field, $value) { - $this->data($field, ['exp', $value]); + $this->data($field, $this->raw($value)); return $this; } + /** + * 使用表达式设置数据 + * @access public + * @param mixed $value 表达式 + * @return Expression + */ + public function raw($value) + { + return new Expression($value); + } + /** * 指定JOIN查询字段 * @access public * @param string|array $table 数据表 * @param string|array $field 查询字段 - * @param string|array $on JOIN条件 + * @param mixed $on JOIN条件 * @param string $type JOIN类型 * @return $this */ public function view($join, $field = true, $on = null, $type = 'INNER') { $this->options['view'] = true; - if (is_array($join) && key($join) !== 0) { + if (is_array($join) && key($join) === 0) { foreach ($join as $key => $val) { - $this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER'); + $this->view($val[0], $val[1], isset($val[2]) ? $val[2] : null, isset($val[3]) ? $val[3] : 'INNER'); } } else { $fields = []; @@ -975,6 +1032,37 @@ class Query return $this; } + /** + * 指定表达式查询条件 + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereRaw($where, $bind = [], $logic = 'AND') + { + $this->options['where'][$logic][] = $this->raw($where); + + if ($bind) { + $this->bind($bind); + } + + return $this; + } + + /** + * 指定表达式查询条件 OR + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function whereOrRaw($where, $bind = []) + { + return $this->whereRaw($where, $bind, 'OR'); + } + /** * 指定Null查询条件 * @access public @@ -984,7 +1072,7 @@ class Query */ public function whereNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'null', null); + $this->parseWhereExp($logic, $field, 'null', null, [], true); return $this; } @@ -997,7 +1085,7 @@ class Query */ public function whereNotNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'notnull', null); + $this->parseWhereExp($logic, $field, 'notnull', null, [], true); return $this; } @@ -1037,7 +1125,7 @@ class Query */ public function whereIn($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'in', $condition); + $this->parseWhereExp($logic, $field, 'in', $condition, [], true); return $this; } @@ -1051,7 +1139,7 @@ class Query */ public function whereNotIn($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not in', $condition); + $this->parseWhereExp($logic, $field, 'not in', $condition, [], true); return $this; } @@ -1065,7 +1153,7 @@ class Query */ public function whereLike($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'like', $condition); + $this->parseWhereExp($logic, $field, 'like', $condition, [], true); return $this; } @@ -1079,7 +1167,7 @@ class Query */ public function whereNotLike($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not like', $condition); + $this->parseWhereExp($logic, $field, 'not like', $condition, [], true); return $this; } @@ -1093,7 +1181,7 @@ class Query */ public function whereBetween($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'between', $condition); + $this->parseWhereExp($logic, $field, 'between', $condition, [], true); return $this; } @@ -1107,7 +1195,7 @@ class Query */ public function whereNotBetween($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not between', $condition); + $this->parseWhereExp($logic, $field, 'not between', $condition, [], true); return $this; } @@ -1121,7 +1209,7 @@ class Query */ public function whereExp($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'exp', $condition); + $this->parseWhereExp($logic, $field, 'exp', $this->raw($condition), [], true); return $this; } @@ -1148,9 +1236,10 @@ class Query * @param mixed $op 查询表达式 * @param mixed $condition 查询条件 * @param array $param 查询参数 + * @param bool $strict 严格模式 * @return void */ - protected function parseWhereExp($logic, $field, $op, $condition, $param = []) + protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false) { $logic = strtoupper($logic); if ($field instanceof \Closure) { @@ -1161,8 +1250,17 @@ class Query if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { $field = $this->options['via'] . '.' . $field; } - if (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) { - $where[] = ['exp', $field]; + + if ($field instanceof Expression) { + return $this->whereRaw($field, is_array($op) ? $op : []); + } elseif ($strict) { + // 使用严格模式查询 + $where[$field] = [$op, $condition]; + + // 记录一个字段多次查询条件 + $this->options['multi'][$logic][$field][] = $where[$field]; + } elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) { + $where[] = ['exp', $this->raw($field)]; if (is_array($op)) { // 参数绑定 $this->bind($op); @@ -1183,21 +1281,28 @@ class Query $where[$field] = $param; } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { // null查询 - $where[$field] = [$op, '']; + $where[$field] = [$op, '']; + $this->options['multi'][$logic][$field][] = $where[$field]; } elseif (is_null($condition)) { // 字段相等查询 - $where[$field] = ['eq', $op]; + $where[$field] = ['eq', $op]; + $this->options['multi'][$logic][$field][] = $where[$field]; } else { - $where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; - if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { + if ('exp' == strtolower($op)) { + $where[$field] = ['exp', $this->raw($condition)]; // 参数绑定 - $this->bind($param[2]); + if (isset($param[2]) && is_array($param[2])) { + $this->bind($param[2]); + } + } else { + $where[$field] = [$op, $condition]; } // 记录一个字段多次查询条件 $this->options['multi'][$logic][$field][] = $where[$field]; } + if (!empty($where)) { if (!isset($this->options['where'][$logic])) { $this->options['where'][$logic] = []; @@ -1239,6 +1344,7 @@ class Query $logic = strtoupper($logic); if (isset($this->options['where'][$logic][$field])) { unset($this->options['where'][$logic][$field]); + unset($this->options['multi'][$logic][$field]); } return $this; } @@ -1414,31 +1520,59 @@ class Query */ public function order($field, $order = null) { - if (!empty($field)) { - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - $field = empty($order) ? $field : [$field => $order]; - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } + if (empty($field)) { + return $this; + } elseif ($field instanceof Expression) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; } - if (!isset($this->options['order'])) { - $this->options['order'] = []; - } - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); } else { - $this->options['order'][] = $field; + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } } } + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw($field, array $bind = []) + { + $this->options['order'][] = $this->raw($field); + + if ($bind) { + $this->bind($bind); + } + return $this; } @@ -1523,7 +1657,12 @@ class Query { if (is_array($alias)) { foreach ($alias as $key => $val) { - $this->options['alias'][$key] = $val; + if (false !== strpos($key, '__')) { + $table = $this->parseSqlTable($key); + } else { + $table = $key; + } + $this->options['alias'][$table] = $val; } } else { if (isset($this->options['table'])) { @@ -1651,46 +1790,49 @@ class Query * 查询日期或者时间 * @access public * @param string $field 日期字段名 - * @param string $op 比较运算符或者表达式 + * @param string|array $op 比较运算符或者表达式 * @param string|array $range 比较范围 * @return $this */ public function whereTime($field, $op, $range = null) { if (is_null($range)) { - // 使用日期表达式 - $date = getdate(); - switch (strtolower($op)) { - case 'today': - case 'd': - $range = ['today', 'tomorrow']; - break; - case 'week': - case 'w': - $range = 'this week 00:00:00'; - break; - case 'month': - case 'm': - $range = mktime(0, 0, 0, $date['mon'], 1, $date['year']); - break; - case 'year': - case 'y': - $range = mktime(0, 0, 0, 1, 1, $date['year']); - break; - case 'yesterday': - $range = ['yesterday', 'today']; - break; - case 'last week': - $range = ['last week 00:00:00', 'this week 00:00:00']; - break; - case 'last month': - $range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])]; - break; - case 'last year': - $range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])]; - break; - default: - $range = $op; + if (is_array($op)) { + $range = $op; + } else { + // 使用日期表达式 + switch (strtolower($op)) { + case 'today': + case 'd': + $range = ['today', 'tomorrow']; + break; + case 'week': + case 'w': + $range = ['this week 00:00:00', 'next week 00:00:00']; + break; + case 'month': + case 'm': + $range = ['first Day of this month 00:00:00', 'first Day of next month 00:00:00']; + break; + case 'year': + case 'y': + $range = ['this year 1/1', 'next year 1/1']; + break; + case 'yesterday': + $range = ['yesterday', 'today']; + break; + case 'last week': + $range = ['last week 00:00:00', 'this week 00:00:00']; + break; + case 'last month': + $range = ['first Day of last month 00:00:00', 'first Day of this month 00:00:00']; + break; + case 'last year': + $range = ['last year 1/1', 'this year 1/1']; + break; + default: + $range = $op; + } } $op = is_array($range) ? 'between' : '>'; } @@ -1735,7 +1877,7 @@ class Query $schema = $guid; } // 读取缓存 - if (is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { + if (!App::$debug && is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { $info = include RUNTIME_PATH . 'schema/' . $schema . '.php'; } else { $info = $this->connection->getFields($guid); @@ -1810,7 +1952,9 @@ class Query */ protected function getFieldBindType($type) { - if (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { + if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + $bind = PDO::PARAM_STR; + } elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { $bind = PDO::PARAM_INT; } elseif (preg_match('/bool/is', $type)) { $bind = PDO::PARAM_BOOL; @@ -1892,11 +2036,10 @@ class Query $with = explode(',', $with); } - $first = true; - $currentModel = $this->model; + $first = true; /** @var Model $class */ - $class = new $currentModel; + $class = $this->model; foreach ($with as $key => $relation) { $subRelation = ''; $closure = false; @@ -1955,7 +2098,7 @@ class Query $relation = $key; } $relation = Loader::parseName($relation, 1, false); - $count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; + $count = '(' . $this->model->$relation()->getRelationCountQuery($closure) . ')'; $this->field([$count => Loader::parseName($relation) . '_count']); } } @@ -2082,7 +2225,7 @@ class Query } // 执行操作 - $result = 0 === $sql ? 0 : $this->execute($sql, $bind); + $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this); if ($result) { $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); $lastInsId = $this->getLastInsID($sequence); @@ -2118,27 +2261,40 @@ class Query /** * 批量插入记录 * @access public - * @param mixed $dataSet 数据集 - * @param boolean $replace 是否replace + * @param mixed $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 * @return integer|string */ - public function insertAll(array $dataSet, $replace = false) + public function insertAll(array $dataSet, $replace = false, $limit = null) { // 分析查询表达式 $options = $this->parseExpress(); if (!is_array(reset($dataSet))) { return false; } + // 生成SQL语句 - $sql = $this->builder->insertAll($dataSet, $options, $replace); + if (is_null($limit)) { + $sql = $this->builder->insertAll($dataSet, $options, $replace); + } else { + $array = array_chunk($dataSet, $limit, true); + foreach ($array as $item) { + $sql[] = $this->builder->insertAll($item, $options, $replace); + } + } + // 获取参数绑定 $bind = $this->getBind(); if ($options['fetch_sql']) { // 获取实际执行的SQL语句 return $this->connection->getRealSql($sql, $bind); + } elseif (is_array($sql)) { + // 执行操作 + return $this->batchQuery($sql, $bind, $this); } else { // 执行操作 - return $this->execute($sql, $bind); + return $this->execute($sql, $bind, $this); } } @@ -2164,7 +2320,7 @@ class Query return $this->connection->getRealSql($sql, $bind); } else { // 执行操作 - return $this->execute($sql, $bind); + return $this->execute($sql, $bind, $this); } } @@ -2231,7 +2387,7 @@ class Query Cache::clear($options['cache']['tag']); } // 执行操作 - $result = '' == $sql ? 0 : $this->execute($sql, $bind); + $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this); if ($result) { if (is_string($pk) && isset($where[$pk])) { $data[$pk] = $where[$pk]; @@ -2300,7 +2456,7 @@ class Query // 判断查询缓存 $cache = $options['cache']; unset($options['cache']); - $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options) . serialize($this->bind)); + $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind)); $resultSet = Cache::get($key); } if (false === $resultSet) { @@ -2334,11 +2490,10 @@ class Query // 数据列表读取后的处理 if (!empty($this->model)) { // 生成模型对象 - $modelName = $this->model; if (count($resultSet) > 0) { foreach ($resultSet as $key => $result) { /** @var Model $model */ - $model = new $modelName($result); + $model = $this->model->newInstance($result); $model->isUpdate(true); // 关联查询 @@ -2358,7 +2513,7 @@ class Query // 模型数据集转换 $resultSet = $model->toCollection($resultSet); } else { - $resultSet = (new $modelName)->toCollection($resultSet); + $resultSet = $this->model->toCollection($resultSet); } } elseif ('collection' == $this->connection->getConfig('resultset_type')) { // 返回Collection对象 @@ -2402,10 +2557,16 @@ class Query } elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) { $data = $value[1]; } + $prefix = $this->connection->getConfig('database') . '.'; + if (isset($data)) { - return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; - } else { - return md5(serialize($options) . serialize($bind)); + return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; + } + + try { + return md5($prefix . serialize($options) . serialize($bind)); + } catch (\Exception $e) { + throw new Exception('closure not support cache(true)'); } } @@ -2442,11 +2603,11 @@ class Query // 判断查询缓存 $cache = $options['cache']; if (true === $cache['key'] && !is_null($data) && !is_array($data)) { - $key = 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; + $key = 'think:' . $this->connection->getConfig('database') . '.' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; } elseif (is_string($cache['key'])) { $key = $cache['key']; } elseif (!isset($key)) { - $key = md5(serialize($options) . serialize($this->bind)); + $key = md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind)); } $result = Cache::get($key); } @@ -2484,7 +2645,7 @@ class Query $result = isset($resultSet[0]) ? $resultSet[0] : null; } - if (isset($cache) && false !== $result) { + if (isset($cache) && $result) { // 缓存数据 $this->cacheData($key, $result, $cache); } @@ -2494,8 +2655,7 @@ class Query if (!empty($result)) { if (!empty($this->model)) { // 返回模型对象 - $model = $this->model; - $result = new $model($result); + $result = $this->model->newInstance($result); $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); // 关联查询 if (!empty($options['relation'])) { @@ -2526,7 +2686,8 @@ class Query protected function throwNotFound($options = []) { if (!empty($this->model)) { - throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options); + $class = get_class($this->model); + throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options); } else { $table = is_array($options['table']) ? key($options['table']) : $options['table']; throw new DataNotFoundException('table data not Found:' . $table, $table, $options); @@ -2574,48 +2735,54 @@ class Query public function chunk($count, $callback, $column = null, $order = 'asc') { $options = $this->getOptions(); - if (isset($options['table'])) { - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - } else { - $table = ''; - } - $column = $column ?: $this->getPk($table); - if (is_array($column)) { - $column = $column[0]; + if (empty($options['table'])) { + $options['table'] = $this->getTable(); } + $column = $column ?: $this->getPk($options); + if (isset($options['order'])) { if (App::$debug) { throw new \LogicException('chunk not support call order'); } unset($options['order']); } - $bind = $this->bind; - $resultSet = $this->options($options)->limit($count)->order($column, $order)->select(); - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); + $bind = $this->bind; + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); } else { - $key = $column; - } - if ($resultSet instanceof Collection) { - $resultSet = $resultSet->all(); - } - - while (!empty($resultSet)) { - if (false === call_user_func($callback, $resultSet)) { - return false; + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; } - $end = end($resultSet); - $lastId = is_array($end) ? $end[$key] : $end->$key; - $resultSet = $this->options($options) - ->limit($count) - ->bind($bind) - ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId) - ->order($column, $order) - ->select(); + $query = $this->options($options)->limit($count); + } + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { if ($resultSet instanceof Collection) { $resultSet = $resultSet->all(); } + + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (is_array($column)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = end($resultSet); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); } + return true; } @@ -2692,7 +2859,7 @@ class Query Cache::clear($options['cache']['tag']); } // 执行操作 - $result = $this->execute($sql, $bind); + $result = $this->execute($sql, $bind, $this); if ($result) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { list($a, $val) = explode('|', $key); @@ -2777,6 +2944,10 @@ class Query } } + if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) { + $options['master'] = true; + } + foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { if (!isset($options[$name])) { $options[$name] = ''; diff --git a/thinkphp/library/think/db/builder/Mysql.php b/thinkphp/library/think/db/builder/Mysql.php index 5bc9d03b..8eee746f 100644 --- a/thinkphp/library/think/db/builder/Mysql.php +++ b/thinkphp/library/think/db/builder/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,28 +12,93 @@ namespace think\db\builder; use think\db\Builder; +use think\Exception; /** * mysql数据库驱动 */ class Mysql extends Builder { - protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * 生成insertall SQL + * @access public + * @param array $dataSet 数据集 + * @param array $options 表达式 + * @param bool $replace 是否replace + * @return string + * @throws Exception + */ + public function insertAll($dataSet, $options = [], $replace = false) + { + // 获取合法的字段 + if ('*' == $options['field']) { + $fields = array_keys($this->query->getFieldsType($options['table'])); + } else { + $fields = $options['field']; + } + + foreach ($dataSet as $data) { + foreach ($data as $key => $val) { + if (!in_array($key, $fields, true)) { + if ($options['strict']) { + throw new Exception('fields not exists:[' . $key . ']'); + } + unset($data[$key]); + } elseif (is_null($val)) { + $data[$key] = 'NULL'; + } elseif (is_scalar($val)) { + $data[$key] = $this->parseValue($val, $key); + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $data[$key] = $val->__toString(); + } else { + // 过滤掉非标量数据 + unset($data[$key]); + } + } + $value = array_values($data); + $values[] = '( ' . implode(',', $value) . ' )'; + + if (!isset($insertFields)) { + $insertFields = array_map([$this, 'parseKey'], array_keys($data)); + } + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($options['table'], $options), + implode(' , ', $insertFields), + implode(' , ', $values), + $this->parseComment($options['comment']), + ], $this->insertAllSql); + } /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('$.', $key); - $key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; + return 'json_extract(' . $field . ', \'$.' . $name . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); if ('__TABLE__' == $table) { @@ -43,7 +108,8 @@ class Mysql extends Builder $table = $options['alias'][$table]; } } - if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) { $key = '`' . $key . '`'; } if (isset($table)) { diff --git a/thinkphp/library/think/db/builder/Pgsql.php b/thinkphp/library/think/db/builder/Pgsql.php index b690401c..acc22896 100644 --- a/thinkphp/library/think/db/builder/Pgsql.php +++ b/thinkphp/library/think/db/builder/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -44,12 +44,18 @@ class Pgsql extends Builder /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 diff --git a/thinkphp/library/think/db/builder/Sqlite.php b/thinkphp/library/think/db/builder/Sqlite.php index 28a5d6f1..c727f04b 100644 --- a/thinkphp/library/think/db/builder/Sqlite.php +++ b/thinkphp/library/think/db/builder/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -52,12 +52,18 @@ class Sqlite extends Builder /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); if (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); diff --git a/thinkphp/library/think/db/builder/Sqlsrv.php b/thinkphp/library/think/db/builder/Sqlsrv.php index 59ea021f..f79ae030 100644 --- a/thinkphp/library/think/db/builder/Sqlsrv.php +++ b/thinkphp/library/think/db/builder/Sqlsrv.php @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; /** * Sqlsrv数据库驱动 @@ -34,25 +35,29 @@ class Sqlsrv extends Builder */ protected function parseOrder($order, $options = []) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { - if (is_numeric($key)) { - if (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } elseif ('[rand]' == $val) { - $array[] = $this->parseRand(); - } else { - $array[] = $val; - } - } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; - } - } - $order = implode(',', $array); + if (empty($order)) { + return ' ORDER BY rand()'; } - return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()'; + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_numeric($key)) { + if (false === strpos($val, '(')) { + $array[] = $this->parseKey($val, $options); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); + } else { + $array[] = $val; + } + } else { + $sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : ''; + $array[] = $this->parseKey($key, $options, true) . ' ' . $sort; + } + } + + return ' ORDER BY ' . implode(',', $array); } /** @@ -68,12 +73,17 @@ class Sqlsrv extends Builder /** * 字段和表名处理 * @access protected - * @param string $key + * @param mixed $key * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } $key = trim($key); if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { list($table, $key) = explode('.', $key, 2); @@ -84,7 +94,7 @@ class Sqlsrv extends Builder $table = $options['alias'][$table]; } } - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { $key = '[' . $key . ']'; } if (isset($table)) { diff --git a/thinkphp/library/think/db/connector/Mysql.php b/thinkphp/library/think/db/connector/Mysql.php index 9d146c71..be1a85ce 100644 --- a/thinkphp/library/think/db/connector/Mysql.php +++ b/thinkphp/library/think/db/connector/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/thinkphp/library/think/db/connector/Pgsql.php b/thinkphp/library/think/db/connector/Pgsql.php index 761fbac4..bbcf5768 100644 --- a/thinkphp/library/think/db/connector/Pgsql.php +++ b/thinkphp/library/think/db/connector/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/thinkphp/library/think/db/connector/Sqlite.php b/thinkphp/library/think/db/connector/Sqlite.php index fd7f02b0..c4e3a724 100644 --- a/thinkphp/library/think/db/connector/Sqlite.php +++ b/thinkphp/library/think/db/connector/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/thinkphp/library/think/db/exception/BindParamException.php b/thinkphp/library/think/db/exception/BindParamException.php index d0e2387b..4ed19546 100644 --- a/thinkphp/library/think/db/exception/BindParamException.php +++ b/thinkphp/library/think/db/exception/BindParamException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/thinkphp/library/think/db/exception/DataNotFoundException.php b/thinkphp/library/think/db/exception/DataNotFoundException.php index e399b063..f2542ac6 100644 --- a/thinkphp/library/think/db/exception/DataNotFoundException.php +++ b/thinkphp/library/think/db/exception/DataNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/thinkphp/library/think/db/exception/ModelNotFoundException.php b/thinkphp/library/think/db/exception/ModelNotFoundException.php index 2180ab07..6e5f930c 100644 --- a/thinkphp/library/think/db/exception/ModelNotFoundException.php +++ b/thinkphp/library/think/db/exception/ModelNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- diff --git a/view/crmebN/pages/orders-con/orders-con.js b/view/crmebN/pages/orders-con/orders-con.js index 6f5b7919..b9bb340e 100644 --- a/view/crmebN/pages/orders-con/orders-con.js +++ b/view/crmebN/pages/orders-con/orders-con.js @@ -66,7 +66,7 @@ Page({ }) setTimeout(function () { wx.navigateTo({ //跳转至指定页面并关闭其他打开的所有页面(这个最好用在返回至首页的的时候) - url: '/pages/orders-con/orders-con?order_id=' + data.result.orderId + url: '/pages/orders-con/orders-con?order_id=' + data.result.order_id }) }, 1200) }, diff --git a/view/crmebN/pages/product-con/index.wxml b/view/crmebN/pages/product-con/index.wxml index f61f8452..fd8e9ff3 100644 --- a/view/crmebN/pages/product-con/index.wxml +++ b/view/crmebN/pages/product-con/index.wxml @@ -78,7 +78,7 @@ {{reply.comment}} {{reply.add_time}} - + 管理员回复: {{reply.merchant_reply_content}} {{reply.merchant_reply_time}}