From a2198685da53002d03d020e482e323105b6541b8 Mon Sep 17 00:00:00 2001 From: 15090180611 <215509543@qq.com> Date: Wed, 27 Nov 2024 18:15:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E6=A0=B7=E9=9D=92=E6=98=A5=E5=A4=9C?= =?UTF-8?q?=E6=A0=A1=20=E8=AF=BE=E6=97=B6=E8=BF=87=E6=9C=9F=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1(=E8=BF=9B=E8=A1=8C=E4=B8=AD)=20?= =?UTF-8?q?=E8=AF=BE=E6=97=B6=E5=8F=96=E6=B6=88=E8=AE=B0=E5=BD=95=E6=AC=A1?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E5=90=8C=E8=AF=BE=E6=97=B6=E8=B6=85n?= =?UTF-8?q?=E6=AC=A1=E6=97=A0=E6=B3=95=E5=8F=96=E6=B6=88=EF=BC=88=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=EF=BC=89=20=E5=85=8D=E8=B4=B9=E8=AF=BE?= =?UTF-8?q?=E7=A8=8B=E8=AF=BE=E6=97=B6=E5=88=B0=E5=BC=80=E5=A7=8B=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=97=A0=E6=B3=95=E5=8F=96=E6=B6=88=EF=BC=88=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=EF=BC=89=20=E5=A2=9E=E5=8A=A0=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=99=A8=E5=B0=81=E8=A3=85=E5=92=8C=E9=98=9F=E5=88=97?= =?UTF-8?q?=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/general/Crontab.php | 93 +++++++++++++ .../admin/controller/general/CrontabLog.php | 61 +++++++++ .../admin/controller/manystore/Index.php | 11 ++ .../controller/school/classes/ClassesLib.php | 15 ++- .../admin/lang/zh-cn/general/crontab.php | 22 ++++ .../admin/lang/zh-cn/general/crontab_log.php | 14 ++ application/admin/model/Crontab.php | 63 +++++++++ application/admin/model/CrontabLog.php | 34 +++++ .../admin/view/general/crontab/add.html | 119 +++++++++++++++++ .../admin/view/general/crontab/edit.html | 123 ++++++++++++++++++ .../admin/view/general/crontab/index.html | 30 +++++ .../view/general/crontab_log/detail.html | 46 +++++++ .../admin/view/general/crontab_log/index.html | 30 +++++ application/api/controller/Crontab.php | 85 ++++++++++++ application/api/controller/Demo.php | 2 + application/api/controller/school/Classes.php | 3 +- application/api/controller/school/Shop.php | 2 + application/common/hooks.php | 3 + application/common/job/test/DemoJob.php | 27 ++++ .../common/listener/manystore/ShopHook.php | 25 ++++ application/common/model/BaseModel.php | 77 +++++++++++ .../model/school/classes/ClassesLib.php | 92 ++++++++++++- .../model/school/classes/ClassesSpec.php | 3 +- .../common/model/school/classes/Collect.php | 13 +- .../model/school/classes/hourorder/Order.php | 22 ++++ .../model/school/classes/order/Order.php | 11 ++ .../school/classes/order/OrderDetail.php | 3 +- .../manystore/controller/general/Profile.php | 4 + .../controller/school/classes/ClassesLib.php | 6 + public/assets/js/backend/general/crontab.js | 104 +++++++++++++++ .../assets/js/backend/general/crontab_log.js | 67 ++++++++++ 31 files changed, 1201 insertions(+), 9 deletions(-) create mode 100644 application/admin/controller/general/Crontab.php create mode 100644 application/admin/controller/general/CrontabLog.php create mode 100644 application/admin/lang/zh-cn/general/crontab.php create mode 100644 application/admin/lang/zh-cn/general/crontab_log.php create mode 100644 application/admin/model/Crontab.php create mode 100644 application/admin/model/CrontabLog.php create mode 100644 application/admin/view/general/crontab/add.html create mode 100644 application/admin/view/general/crontab/edit.html create mode 100644 application/admin/view/general/crontab/index.html create mode 100644 application/admin/view/general/crontab_log/detail.html create mode 100644 application/admin/view/general/crontab_log/index.html create mode 100644 application/api/controller/Crontab.php create mode 100644 application/common/job/test/DemoJob.php create mode 100644 public/assets/js/backend/general/crontab.js create mode 100644 public/assets/js/backend/general/crontab_log.js diff --git a/application/admin/controller/general/Crontab.php b/application/admin/controller/general/Crontab.php new file mode 100644 index 0000000..003c88d --- /dev/null +++ b/application/admin/controller/general/Crontab.php @@ -0,0 +1,93 @@ +model = model('Crontab'); + $this->view->assign('typeList', \app\admin\model\Crontab::getTypeList()); + $this->assignconfig('typeList', \app\admin\model\Crontab::getTypeList()); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + $time = time(); + foreach ($list as $k => &$v) { + $cron = CronExpression::factory($v['schedule']); + $v['nexttime'] = $time > $v['endtime'] ? __('None') : $cron->getNextRunDate()->getTimestamp(); + } + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 判断Crontab格式是否正确 + * @internal + */ + public function check_schedule() + { + $row = $this->request->post("row/a"); + $schedule = $row['schedule'] ?? ''; + if (CronExpression::isValidExpression($schedule)) { + $this->success(); + } else { + $this->error(__('Crontab format invalid')); + } + } + + /** + * 根据Crontab表达式读取未来七次的时间 + * @internal + */ + public function get_schedule_future() + { + $time = []; + $schedule = $this->request->post('schedule'); + $days = (int)$this->request->post('days'); + try { + $cron = CronExpression::factory($schedule); + for ($i = 0; $i < $days; $i++) { + $time[] = $cron->getNextRunDate(null, $i)->format('Y-m-d H:i:s'); + } + } catch (\Exception $e) { + + } + + $this->success("", null, ['futuretime' => $time]); + } + +} diff --git a/application/admin/controller/general/CrontabLog.php b/application/admin/controller/general/CrontabLog.php new file mode 100644 index 0000000..ad17087 --- /dev/null +++ b/application/admin/controller/general/CrontabLog.php @@ -0,0 +1,61 @@ +model = model('CrontabLog'); + $this->view->assign('statusList', $this->model->getStatusList()); + $this->assignconfig('statusList', $this->model->getStatusList()); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + public function detail($ids = null) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/manystore/Index.php b/application/admin/controller/manystore/Index.php index e529569..a932297 100644 --- a/application/admin/controller/manystore/Index.php +++ b/application/admin/controller/manystore/Index.php @@ -230,6 +230,7 @@ class Index extends Backend $order = Order::where("manystore_id",$id)->where("status","in","0,3")->find(); if($order)$this->error("存在正在使用中的课程订单或存在正在售后中的课程订单无法继续操作!"); // 课程存在售后订单则不允许操作 + } @@ -367,6 +368,11 @@ class Index extends Backend $manystoreAuthGroupAccessModel->insert($group_access); + + //调用事件 + $data = ['shop' => $shop]; + \think\Hook::listen('shop_create_after', $data); + db()->commit(); $this->success(); }catch (Exception $e){ @@ -445,6 +451,10 @@ class Index extends Backend $this->error($row->getError()); } + //调用事件 + $data = ['shop' => $shop_info]; + \think\Hook::listen('shop_update_after', $data); + $this->success(); } $this->error(); @@ -465,6 +475,7 @@ class Index extends Backend */ public function del($ids = "") { + if ($ids) { $row = $this->model->get(['id' => $ids,'is_main'=>1]); if(!$row){ diff --git a/application/admin/controller/school/classes/ClassesLib.php b/application/admin/controller/school/classes/ClassesLib.php index a0725a5..0219e96 100644 --- a/application/admin/controller/school/classes/ClassesLib.php +++ b/application/admin/controller/school/classes/ClassesLib.php @@ -246,7 +246,10 @@ class ClassesLib extends Backend || empty($params["city"]) || empty($params["district"]) || empty($params["longitude"]) - || empty($params["latitude"])) $this->error("独立地点需传定位信息"); + || empty($params["latitude"]) + || empty($params["address"]) + || empty($params["address_detail"]) + ) $this->error("独立地点需传定位信息"); }else{ //地址取机构的 @@ -259,7 +262,10 @@ class ClassesLib extends Backend || empty($shop["city"]) || empty($shop["district"]) || empty($shop["longitude"]) - || empty($shop["latitude"])) $this->error("当前机构地址并未完善,请在机构处完善!"); + || empty($shop["latitude"]) + || empty($shop["address"]) + || empty($shop["address_detail"]) + ) $this->error("当前机构地址并未完善,请在机构处完善!"); @@ -269,7 +275,10 @@ class ClassesLib extends Backend $params["district"] = $shop["district"]; $params["longitude"] = $shop["longitude"]; $params["latitude"] = $shop["latitude"]; - + $params["address"] = $shop["address"]; + $params["address_detail"] = $shop["address_detail"]; + //address +// var_dump($params); } //特有认证判断 diff --git a/application/admin/lang/zh-cn/general/crontab.php b/application/admin/lang/zh-cn/general/crontab.php new file mode 100644 index 0000000..9bdf31b --- /dev/null +++ b/application/admin/lang/zh-cn/general/crontab.php @@ -0,0 +1,22 @@ + '任务标题', + 'Maximums' => '最多执行', + 'Sleep' => '延迟秒数', + 'Schedule' => '执行周期', + 'Executes' => '执行次数', + 'Completed' => '已完成', + 'Expired' => '已过期', + 'Hidden' => '已禁用', + 'Logs' => '日志信息', + 'Crontab rules' => 'Crontab规则', + 'No limit' => '无限制', + 'Execute time' => '最后执行时间', + 'Request Url' => '请求URL', + 'Execute Sql Script' => '执行SQL', + 'Execute Shell' => '执行Shell', + 'Crontab format invalid' => 'Crontab格式错误', + 'Next execute time' => '下次预计时间', + 'The next %s times the execution time' => '接下来 %s 次的执行时间', +]; diff --git a/application/admin/lang/zh-cn/general/crontab_log.php b/application/admin/lang/zh-cn/general/crontab_log.php new file mode 100644 index 0000000..9ad50bf --- /dev/null +++ b/application/admin/lang/zh-cn/general/crontab_log.php @@ -0,0 +1,14 @@ + '任务标题', + 'Crontab_id' => '定时任务ID', + 'Success' => '成功', + 'Failure' => '失败', + 'Processid' => '进程ID', + 'Inprogress' => '执行中', + 'Content' => '返回结果', + 'Result' => '执行结果', + 'Complete time' => '完成时间', + 'Execute time' => '最后执行时间', +]; diff --git a/application/admin/model/Crontab.php b/application/admin/model/Crontab.php new file mode 100644 index 0000000..9b1c3dc --- /dev/null +++ b/application/admin/model/Crontab.php @@ -0,0 +1,63 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + public static function getTypeList() + { + return [ + 'url' => __('Request Url'), + 'sql' => __('Execute Sql Script'), + 'shell' => __('Execute Shell'), + ]; + } + + public function getTypeTextAttr($value, $data) + { + $typelist = self::getTypeList(); + $value = $value ? $value : $data['type']; + return $value && isset($typelist[$value]) ? $typelist[$value] : $value; + } + + protected function setBegintimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setEndtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setExecutetimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + +} diff --git a/application/admin/model/CrontabLog.php b/application/admin/model/CrontabLog.php new file mode 100644 index 0000000..05e1eea --- /dev/null +++ b/application/admin/model/CrontabLog.php @@ -0,0 +1,34 @@ + __('Success'), 'failure' => __('Failure'), 'inprogress' => __('Inprogress')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ?: ($data['status'] ?? ''); + $list = $this->getStatusList(); + return $list[$value] ?? ''; + } + +} diff --git a/application/admin/view/general/crontab/add.html b/application/admin/view/general/crontab/add.html new file mode 100644 index 0000000..08aedca --- /dev/null +++ b/application/admin/view/general/crontab/add.html @@ -0,0 +1,119 @@ + + +
+
+ +
+ +
+
+
+ +
+ {:build_select('row[type]', $typeList, null, ['class'=>'form-control', 'data-rule'=>'required'])} +
+
+
+ +
+ +
+
+
+ +
+ +
+
*    *    *    *    *
+-    -    -    -    -
+|    |    |    |    +--- day of week (0 - 7) (Sunday=0 or 7)
+|    |    |    +-------- month (1 - 12)
+|    |    +------------- day of month (1 - 31)
+|    +------------------ hour (0 - 23)
++----------------------- min (0 - 59)
+ + +
minute   hour   day   month   week
+
+minute:表示分钟,可以是0~59的任意整数。
+hour:表示小时,可以是2~23的任意整数
+day:表示日期,可以是1~31的任意整数
+month:表示月份,可以是1~12的任意整数
+week:表示星期几,可以是0~7之间的整数,0或者7表示星期天
+ + +
星号(*):表示所有可能的值,`* * * * * `表示为每分钟都执行。
+逗号(,):表示一个列表范围,如`1,2,3,5,10 * * * *`表示每天每小时的第一、第二、第三、第五、第十分钟执行。
+中杠(-):表示一个数值范围,如`1-15 * * * *`表示每天每小时的1到15分钟执行。
+正斜线(/):表示间隔频率,如`0 10-12/3 * * *`表示每天的10点到12点间隔3小时执行,`/`也可以配合`*`使用,如:`*/5 * * * * *`表示每隔5分钟执行。
+ +

+                    使用前准备工作
+
+                    Linux下使用crontab -e -u 用户名添加一条记录
+
+* * * * * /usr/bin/php /www/yoursite/public/index.php /addons/crontab/autotask/index > /dev/null  2>&1 &
+ +
{:__('The next %s times the execution time', '')}
+
    +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
+
+ + +
diff --git a/application/admin/view/general/crontab/edit.html b/application/admin/view/general/crontab/edit.html new file mode 100644 index 0000000..39def6d --- /dev/null +++ b/application/admin/view/general/crontab/edit.html @@ -0,0 +1,123 @@ + + +
+
+ +
+ +
+
+
+ +
+ {:build_select('row[type]', $typeList, $row['type'], ['class'=>'form-control', 'data-rule'=>'required'])} +
+
+
+ +
+ +
+
+
+ +
+ + +
+
*    *    *    *    *
+-    -    -    -    -
+|    |    |    |    +--- day of week (0 - 7) (Sunday=0 or 7)
+|    |    |    +-------- month (1 - 12)
+|    |    +------------- day of month (1 - 31)
+|    +------------------ hour (0 - 23)
++----------------------- min (0 - 59)
+ + +
minute   hour   day   month   week
+
+minute:表示分钟,可以是0~59的任意整数。
+hour:表示小时,可以是2~23的任意整数
+day:表示日期,可以是1~31的任意整数
+month:表示月份,可以是1~12的任意整数
+week:表示星期几,可以是0~7之间的整数,0或者7表示星期天
+ + +
星号(*):表示所有可能的值,`* * * * * `表示为每分钟都执行。
+逗号(,):表示一个列表范围,如`1,2,3,5,10 * * * *`表示每天每小时的第一、第二、第三、第五、第十分钟执行。
+中杠(-):表示一个数值范围,如`1-15 * * * *`表示每天每小时的1到15分钟执行。
+正斜线(/):表示间隔频率,如`0 10-12/3 * * *`表示每天的10点到12点间隔3小时执行,`/`也可以配合`*`使用,如:`*/5 * * * * *`表示每隔5分钟执行。
+ +

+                    使用前准备工作
+
+                    Linux下使用crontab -e -u 用户名添加一条记录
+
+* * * * * /usr/bin/php /www/yoursite/public/index.php /addons/crontab/autotask/index > /dev/null  2>&1 &
+
{:__('The next %s times the execution time', '')}
+
    +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {:build_radios('row[status]', ['normal'=>__('Normal'), 'completed'=>__('Completed'), 'expired'=>__('Expired'), 'hidden'=>__('Hidden')], $row['status'])} +
+
+ + +
diff --git a/application/admin/view/general/crontab/index.html b/application/admin/view/general/crontab/index.html new file mode 100644 index 0000000..217252e --- /dev/null +++ b/application/admin/view/general/crontab/index.html @@ -0,0 +1,30 @@ +
+ +
+ {:build_heading(null,FALSE)} + +
+ +
+
+
+
+
+ {:build_toolbar('refresh,add,edit,del')} +
+ +
+
+
+ +
+
+
diff --git a/application/admin/view/general/crontab_log/detail.html b/application/admin/view/general/crontab_log/detail.html new file mode 100644 index 0000000..20a1564 --- /dev/null +++ b/application/admin/view/general/crontab_log/detail.html @@ -0,0 +1,46 @@ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ {if $row['status']=='success'}{:__('Success')}{else/}{:__('Failure')}{/if} +
+
+
+ + +
diff --git a/application/admin/view/general/crontab_log/index.html b/application/admin/view/general/crontab_log/index.html new file mode 100644 index 0000000..c3c24f0 --- /dev/null +++ b/application/admin/view/general/crontab_log/index.html @@ -0,0 +1,30 @@ +
+ +
+ {:build_heading(null,FALSE)} + +
+ +
+
+
+
+
+ {:build_toolbar('refresh,del')} +
+ +
+
+
+ +
+
+
diff --git a/application/api/controller/Crontab.php b/application/api/controller/Crontab.php new file mode 100644 index 0000000..aa981c3 --- /dev/null +++ b/application/api/controller/Crontab.php @@ -0,0 +1,85 @@ +error("执行失败:".$e->getMessage()); + } + $this->success("执行成功:本次取消的模拟单数量【{$res}】"); + } + + + /** + * 每五分钟执行的任务 + */ + public function minute() + { + try{ + $lock = new UrlLock(2,"mock-lock-suffix",5,"您的请求过于频繁,请您稍后再试!"); + $lock->lock(); + var_dump("進入時間:".date("Y-m-d H:i:s ")); + sleep(2); + $lock->free(); + var_dump("釋放時間:".date("Y-m-d H:i:s ")); +// MockOrder::distributionFailSmsNotice(MockOrder::where("id",438)->find()); + //${name},手机号${phone}有${number}圈已被分配到您的${car_number}号车上,请及时查看! +// $event = "distribution_success"; +//// $event = "distribution_fail"; +// $config = get_addon_config('alisms'); +// $template_id = $config['template'][$event]; +// $res = Smslib::notice(15090180611, ['name'=>"小绿",'phone'=>"15555555555",'number'=>"3",'car_number'=>"22"], $template_id); +//// $params = [ +//// 'mobile' => 15090180611, +//// 'msg' => ['name'=>"小明",'phone'=>"15555555555",'number'=>"3",'car_number'=>"22"], +//// 'template' => "SMS_275410075" +//// ]; +//// $result = \think\Hook::listen('sms_notice', $params, null, true); +// var_dump($res);die; +// +// //得到返解析的内容 +// $qrcode = new \Zxing\QrReader( cdnurl("/uploads/20221117/f8c6c1ba12938daf9767719fa022d6b8.png",true)); //绝对路径 +// $common_content = $qrcode->decode(); //返回二维码的内容 + + +// $qrcode = new \Zxing\QrReader(cdnurl("/uploads/20221116/ffcf9f4bd725ec49ebb53d97166a486b.png",true)); //二维码图片路径 +// $text = $qrcode->text(); //返回识别后的文本 +// $ids = AppointmentOrder::where("mock_code_url",'not null')->page(1,500)->column("id"); +// $num = count($ids); +// foreach ($ids as $id) { +// //取消模拟资格 +// AppointmentOrder::cancelMockCode($id, true); +// //插入订单日志 +// OrderAction::log($id, "预约订单取消模拟资格,原小票练车码(如果有)作废", "admin", 1); +// } +// $qrcode = new QrReader(cdnurl("/uploads/20221117/f8c6c1ba12938daf9767719fa022d6b8.png",true)); //图片路径 +// $text = $qrcode->decode(); //返回识别后的文本 + +// $res = MockOrder::repairOrders(1000,$trans = false); + +// $sss = Common::getLinkMiniQrCode(29716); +// var_dump($sss->toArray());die; + + }catch (\Exception $e){ + $this->error("执行失败:".$e->getMessage()); + } + $this->success("执行成功条"); + } +} diff --git a/application/api/controller/Demo.php b/application/api/controller/Demo.php index 3a992e4..17555b3 100644 --- a/application/api/controller/Demo.php +++ b/application/api/controller/Demo.php @@ -3,6 +3,7 @@ namespace app\api\controller; use app\common\controller\Api; +use app\common\job\test\DemoJob; /** * 示例接口 @@ -40,6 +41,7 @@ class Demo extends Api */ public function test() { + \think\Queue::push(DemoJob::class, ["name"=>"hello world!"], "school"); $this->success('返回成功', $this->request->param()); } diff --git a/application/api/controller/school/Classes.php b/application/api/controller/school/Classes.php index dd3ab5c..2b625d7 100644 --- a/application/api/controller/school/Classes.php +++ b/application/api/controller/school/Classes.php @@ -244,6 +244,7 @@ class Classes extends Base * @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字") * @ApiParams(name = "page", type = "string",required=true,description = "页数") * @ApiParams(name = "limit", type = "string",required=true,description = "条数") + * @ApiParams(name = "has_shop", type = "int",required=false,description = "是否查机构") * @ApiParams(name = "user_id", type = "int",required=false,description = "主讲师用户id") * @ApiParams(name = "shop_id", type = "int",required=false,description = "机构店铺id") * @ApiParams(name = "teacher_id", type = "int",required=false,description = "老师id") @@ -304,7 +305,7 @@ class Classes extends Base $params["order"] = $this->request->get('order/s', ''); //机构店铺id $params["nearby"] = $this->request->get('nearby/s', ''); //机构店铺id - + $params["has_shop"] = $this->request->get('has_shop/d', ''); //主讲师用户id $params["latitude"] = $this->request->get('latitude/s', ''); //机构店铺id $params["longitude"] = $this->request->get('longitude/s', ''); //机构店铺id diff --git a/application/api/controller/school/Shop.php b/application/api/controller/school/Shop.php index af2813f..357c6ce 100644 --- a/application/api/controller/school/Shop.php +++ b/application/api/controller/school/Shop.php @@ -168,6 +168,7 @@ class Shop extends Base * @ApiSummary(通用机构大索索列表) * @ApiMethod(GET) * @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字") + * @ApiParams(name = "page", type = "string",required=true,description = "页数") * @ApiParams(name = "limit", type = "string",required=true,description = "条数") * @ApiParams(name = "user_id", type = "int",required=false,description = "主讲师用户id") @@ -204,6 +205,7 @@ class Shop extends Base $params["type"] = $this->request->get('type/s', ''); //机构店铺id $params["province"] = $this->request->get('province/s', ''); //机构店铺id + $params["city"] = $this->request->get('city/s', ''); //机构店铺id $params["district"] = $this->request->get('district/s', ''); //机构店铺id $params["status"] = $this->request->get('status/s', ''); //机构店铺id diff --git a/application/common/hooks.php b/application/common/hooks.php index 8148ada..cc115fd 100644 --- a/application/common/hooks.php +++ b/application/common/hooks.php @@ -75,6 +75,9 @@ $manystoreHooks = [ 'shop_auth_fail_after' => [ // 机构审核失败后 'app\\common\\listener\\manystore\\ShopHook' ], + 'shop_update_after' => [ // 机构数据变更 + 'app\\common\\listener\\manystore\\ShopHook' + ], ]; diff --git a/application/common/job/test/DemoJob.php b/application/common/job/test/DemoJob.php new file mode 100644 index 0000000..a597329 --- /dev/null +++ b/application/common/job/test/DemoJob.php @@ -0,0 +1,27 @@ +attempts() > 3) { + //通过这个方法可以检查这个任务已经重试了几次了 + } + + + //如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法 + $job->delete(); + + // 也可以重新发布这个任务 +// $job->release($delay); //$delay为延迟时间 + + } + + public function failed($data){ + + // ...任务达到最大重试次数后,失败了 + } +} \ No newline at end of file diff --git a/application/common/listener/manystore/ShopHook.php b/application/common/listener/manystore/ShopHook.php index cf082b6..47b4a7c 100644 --- a/application/common/listener/manystore/ShopHook.php +++ b/application/common/listener/manystore/ShopHook.php @@ -1,5 +1,6 @@ $shop] = $params; + + //同步所有机构地址 + $classesLibs = \app\common\model\school\classes\ClassesLib::where("shop_id",$shop["id"])->where("address_type","1")->select(); + foreach ($classesLibs as $classesLib){ + $classesLib["address_city"] = $shop["address_city"]; + $classesLib["province"] = $shop["province"]; + $classesLib["city"] = $shop["city"]; + $classesLib["district"] = $shop["district"]; + $classesLib["longitude"] = $shop["longitude"]; + $classesLib["latitude"] = $shop["latitude"]; + $classesLib["address"] = $shop["address"]; + $classesLib["address_detail"] = $shop["address_detail"]; + $classesLib->save(); + \app\common\model\school\classes\ClassesLib::update_classes($classesLib["id"]); + } + + } + + + // 机构账号提交审核申请后 public function shopApplyAfter(&$params) diff --git a/application/common/model/BaseModel.php b/application/common/model/BaseModel.php index d2ce356..1e705b3 100644 --- a/application/common/model/BaseModel.php +++ b/application/common/model/BaseModel.php @@ -17,6 +17,83 @@ class BaseModel extends Model public $timeKey = 'createtime'; public static $staticTimeKey = 'createtime'; + //允许修改的字段 + public $no_auth_fields = []; + + //更新数据是否需要触发审核开关 + + public $have_auth = false; + + + + + public function checkAssemblyParameters($get=[],$exclude = []){ + //得到所有get参数 + $get = $get ?: request()->get(); + //得到当前model所有字段 + + + $fields = $this->getTableFields(); + +// $commonFields = (new Field())->getCommonFields(); +// var_dump($commonFields); + $fieldLists = $fields; +// foreach ($commonFields as $commonField) { +// if (!in_array($commonField['column_name'], $fields)) { +// $fieldLists[] = $commonField; +// } +// } + $q_fields = []; + + foreach ($get as $kay=>$getField) { + if (in_array($kay, $fieldLists) && !in_array($kay, $exclude)) { + $q_fields[$kay] = $getField; + } + } + + return $q_fields; + + //将q_fields塞入模板中 +// foreach ($q_fields as $k=>$v) { +// //渲染站点配置 +// $this->assign('q_'.$k, $v); +// } + +// foreach ($this->qFields as $k) { +// //渲染站点配置 +// if(!isset($q_fields[$k]))$this->assign('q_'.$k, ""); +// } + + + } + + + + + public function no_auth_fields_check($params,$row){ + + foreach ($params as $k=>$v){ + + //说明数值有变动 + //$params[$k] 去掉两端空格 + $params[$k] = trim($v); + + if($row[$k]!=$params[$k]){ + //当修改参数不在允许修改的字段中 + if(!in_array($k,$this->no_auth_fields)){ + + $this->have_auth = true;break; + + } + } + + } + + return $this->have_auth; + + } + + /**得到基础条件 * @param $status * @param null $model diff --git a/application/common/model/school/classes/ClassesLib.php b/application/common/model/school/classes/ClassesLib.php index 22121bb..80d5053 100644 --- a/application/common/model/school/classes/ClassesLib.php +++ b/application/common/model/school/classes/ClassesLib.php @@ -44,8 +44,38 @@ class ClassesLib extends BaseModel 'new_text', 'selfhot_text', 'distance_text', + 'start_time_text', + 'end_time_text', ]; + + public function getStartTimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['start_time']) ? $data['start_time'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + public function getEndTimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['end_time']) ? $data['end_time'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + + protected function setStartTimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + protected function setEndTimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + + public function getDistanceTextAttr($value, $data) { $distance_text = ''; $distance = $data['distance'] ?? 0; @@ -264,6 +294,13 @@ class ClassesLib extends BaseModel return $this->hasMany(ClassesSpec::class,'classes_lib_id'); } +// public function collect() +// { +// return $this->hasMany(Collect::class,'classes_lib_id'); +// } + + // + @@ -485,11 +522,21 @@ $user_unpaid_order = $user_paid_order =null; public static function getVaildList($params) { extract($params); $a = (new self)->getWithAlisaName().'.'; + $with = ['teacher']; + if (isset($has_shop) && $has_shop) { + $with[] = 'shop'; + } + + + + + + // 查询自提点 if(isset($status) && in_array($status, ['1','2','3'])){ - $selfetch = self::with(['teacher']); + $selfetch = self::with($with); }else{ - $selfetch = self::with(['teacher'])->where($a.'status', '1')->where("{$a}auth_status",1); + $selfetch = self::with($with)->where($a.'status', '1')->where("{$a}auth_status",1); } @@ -662,6 +709,11 @@ $user_unpaid_order = $user_paid_order =null; foreach ($selfetch as $row) { //迭代器魔术方法遍历,填值自动引用传值 //设置是否已收藏 $row->is_collect = in_array($row->id,$collect_classes_lib_ids) ? 1 : 0; + + if($row->is_collect){ + $row["collect"] = Collect::where("user_id",$my_user_id)->find(); + } + } return $selfetch; @@ -684,11 +736,47 @@ $user_unpaid_order = $user_paid_order =null; $virtual_num = VirtualUser::where("havetype",'1')->where("classes_lib_id",$classes_lib_id)->count(); //如果课程虚拟报名者字段 数量小于 虚拟报名者实际数量 ,则等于虚拟报名者实际数量 havetype $classes_lib->virtual_num = $classes_lib->virtual_num < $virtual_num ? $virtual_num : $classes_lib->virtual_num; + + //更新课程信息开始和结束时间信息 + //课程开始和结束时间等于所有课时的最早开始和最晚结束时间 + $classes_lib->start_time = ClassesSpec::where("classes_lib_id",$classes_lib_id)->min("start_time"); + $classes_lib->end_time = ClassesSpec::where("classes_lib_id",$classes_lib_id)->max("end_time"); + $classes_lib->save(); + + //将课程信息和课时信息同步到所有已下单的订单信息中 + self::orderInfoSync($classes_lib_id); } } + public static function orderInfoSync($classes_lib_id){ + $classes_lib = self::get($classes_lib_id); + if($classes_lib){ + //查询所有课程订单更新课程单信息 + $order = \app\common\model\school\classes\order\OrderDetail::where("classes_lib_id",$classes_lib_id)->select(); + + foreach ($order as $row){ + $update = $classes_lib->toArray(); + $update_data = $row->checkAssemblyParameters($update,["id","createtime","updatetime"]); + $row->save($update_data); + } + //同步更新订单课时信息 + $specs = $classes_lib->specs; + foreach ($specs as $spec){ + $hourorders = \app\common\model\school\classes\hourorder\Order::where("classes_lib_spec_id",$spec->id)->select(); + foreach ($hourorders as $hourorder){ + $update = $spec->toArray(); + $update_data = $hourorder->checkAssemblyParameters($update,["id","createtime","updatetime"]); + $hourorder->save($update_data); + } + } + + } + + } + + public static function add_virtual_init($classes_lib_id){ //更新课程规格库存 diff --git a/application/common/model/school/classes/ClassesSpec.php b/application/common/model/school/classes/ClassesSpec.php index 266be84..ca032fb 100644 --- a/application/common/model/school/classes/ClassesSpec.php +++ b/application/common/model/school/classes/ClassesSpec.php @@ -2,10 +2,11 @@ namespace app\common\model\school\classes; +use app\common\model\BaseModel; use think\Model; use traits\model\SoftDelete; -class ClassesSpec extends Model +class ClassesSpec extends BaseModel { use SoftDelete; diff --git a/application/common/model/school/classes/Collect.php b/application/common/model/school/classes/Collect.php index ff8035e..c073c8d 100644 --- a/application/common/model/school/classes/Collect.php +++ b/application/common/model/school/classes/Collect.php @@ -25,8 +25,19 @@ class Collect extends Model // 追加属性 protected $append = [ - + 'createtime_text' ]; + + + public function getCreatetimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['createtime']) ? $data['createtime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + + protected static function init() diff --git a/application/common/model/school/classes/hourorder/Order.php b/application/common/model/school/classes/hourorder/Order.php index f61c616..8bfee0f 100644 --- a/application/common/model/school/classes/hourorder/Order.php +++ b/application/common/model/school/classes/hourorder/Order.php @@ -769,6 +769,28 @@ class Order extends BaseModel if($check){ //用户操作权限检测 self::checkOptionAuth($order['classes_order_id'],$user_id ?: $oper_id,$oper_type); + + //检测免费课程课时到开始时间无法取消(用户端) + if(config("site.free_classes_cancel_check")){ + if($order->detail["feel"]=='1'){ + //免费课才判断 + //课时到开始时间无法取消 + if($order["start_time"] <= time()){ + throw new \Exception("免费课程课时到开始时间无法取消!"); + } + } + } + //同课时已取消超n次无法再取消 n取配置值 + $cancel_num = config("site.same_hour_cancel_number"); + if($cancel_num >= 0){ + $cancel_count = Order::where("classes_lib_spec_id",$order["classes_lib_spec_id"]) + ->where("status","in",["-3"]) + ->count(); + if($cancel_count >= $cancel_num){ + throw new \Exception("同课时已取消超".$cancel_num."次,无法再取消!"); + } + + } } //判断逻辑 diff --git a/application/common/model/school/classes/order/Order.php b/application/common/model/school/classes/order/Order.php index 6c0e0fb..106ded5 100644 --- a/application/common/model/school/classes/order/Order.php +++ b/application/common/model/school/classes/order/Order.php @@ -441,12 +441,20 @@ class Order extends BaseModel //判断订单是否已创建 $order_info = self::where(['order_no'=>$order_no])->find(); if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!"); + + } //校验订单参数 //课程是否存在并上架 $classes_lib_info = ClassesLib::where('id',$classes_lib_id)->find(); if(!$classes_lib_info || $classes_lib_info['status']!='1') throw new \Exception("该课程不存在或已下架!"); + //执行免费课黑名单判断 + //免费课才进行判断 + if($classes_lib_info && $classes_lib_info['feel']=='1'){ + \app\common\model\school\classes\hourorder\Order::checkBlackList($user_id,true); + } + //用户存不存在 $user_info = User::where('id',$user_id)->find(); if(!$user_info) throw new \Exception("用户不存在!"); @@ -1025,8 +1033,11 @@ class Order extends BaseModel if($check){ //用户操作权限检测 \app\common\model\school\classes\hourorder\Order::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type); + } + // + //判断逻辑 if($trans){ self::beginTrans(); diff --git a/application/common/model/school/classes/order/OrderDetail.php b/application/common/model/school/classes/order/OrderDetail.php index 31b5cb0..2572b54 100644 --- a/application/common/model/school/classes/order/OrderDetail.php +++ b/application/common/model/school/classes/order/OrderDetail.php @@ -2,11 +2,12 @@ namespace app\common\model\school\classes\order; +use app\common\model\BaseModel; use app\common\model\dyqc\ManystoreShop; use think\Model; use traits\model\SoftDelete; -class OrderDetail extends Model +class OrderDetail extends BaseModel { use SoftDelete; diff --git a/application/manystore/controller/general/Profile.php b/application/manystore/controller/general/Profile.php index 824c33f..c371558 100644 --- a/application/manystore/controller/general/Profile.php +++ b/application/manystore/controller/general/Profile.php @@ -67,6 +67,10 @@ class Profile extends ManystoreBase $manystore->save($params); Session::set("manystore", $manystore->toArray()); + + //调用事件 + $data = ['shop' => $manystore]; + \think\Hook::listen('shop_update_after', $data); $this->success(); } $this->error(); diff --git a/application/manystore/controller/school/classes/ClassesLib.php b/application/manystore/controller/school/classes/ClassesLib.php index f0cc0d9..36f63e4 100644 --- a/application/manystore/controller/school/classes/ClassesLib.php +++ b/application/manystore/controller/school/classes/ClassesLib.php @@ -238,6 +238,8 @@ class ClassesLib extends ManystoreBase || empty($params["city"]) || empty($params["district"]) || empty($params["longitude"]) + || empty($params["address"]) + || empty($params["address_detail"]) || empty($params["latitude"])) $this->error("独立地点需传定位信息"); }else{ @@ -252,6 +254,8 @@ class ClassesLib extends ManystoreBase || empty($shop["city"]) || empty($shop["district"]) || empty($shop["longitude"]) + || empty($shop["address"]) + || empty($shop["address_detail"]) || empty($shop["latitude"])) $this->error("当前机构地址并未完善!请去【个人资料】完善信息"); @@ -266,6 +270,8 @@ class ClassesLib extends ManystoreBase $params["district"] = $shop["district"]; $params["longitude"] = $shop["longitude"]; $params["latitude"] = $shop["latitude"]; + $params["address"] = $shop["address"]; + $params["address_detail"] = $shop["address_detail"]; } //收费免费判断 diff --git a/public/assets/js/backend/general/crontab.js b/public/assets/js/backend/general/crontab.js new file mode 100644 index 0000000..c25168b --- /dev/null +++ b/public/assets/js/backend/general/crontab.js @@ -0,0 +1,104 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'general/crontab/index', + add_url: 'general/crontab/add', + edit_url: 'general/crontab/edit', + del_url: 'general/crontab/del', + multi_url: 'general/crontab/multi', + table: 'crontab' + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + sortName: 'weigh', + fixedColumns: true, + fixedRightNumber: 1, + columns: [ + [ + {field: 'state', checkbox: true,}, + {field: 'id', title: 'ID'}, + {field: 'type', title: __('Type'), searchList: Config.typeList, formatter: Table.api.formatter.label, custom: {sql: 'warning', url: 'info', shell: 'success'}}, + {field: 'title', title: __('Title')}, + {field: 'maximums', title: __('Maximums'), formatter: Controller.api.formatter.maximums}, + {field: 'executes', title: __('Executes')}, + {field: 'begintime', title: __('Begin time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange'}, + {field: 'endtime', title: __('End time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange'}, + {field: 'nexttime', title: __('Next execute time'), formatter: Controller.api.formatter.nexttime, operate: false, addclass: 'datetimerange', sortable: true}, + {field: 'executetime', title: __('Execute time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true}, + {field: 'weigh', title: __('Weigh')}, + {field: 'status', title: __('Status'), searchList: {"normal": __('Normal'), "hidden": __('Hidden'), "expired": __('Expired'), "completed": __('Completed')}, formatter: Table.api.formatter.status}, + { + field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate, + buttons: [ + { + name: "detail", + icon: "fa fa-list", + title: function (row, index) { + return __('Logs') + "[" + row['title'] + "]"; + }, + text: __('Logs'), + classname: "btn btn-xs btn-info btn-dialog", + url: "general/crontab_log/index?crontab_id={ids}", + } + ] + } + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + $('#schedule').on('valid.field', function (e, result) { + $("#pickdays").trigger("change"); + }); + Form.api.bindevent($("form[role=form]")); + $(document).on("change", "#pickdays", function () { + Fast.api.ajax({url: "general/crontab/get_schedule_future", data: {schedule: $("#schedule").val(), days: $(this).val()}}, function (data, ret) { + if (typeof data.futuretime !== 'undefined' && $.isArray(data.futuretime)) { + var result = []; + $.each(data.futuretime, function (i, j) { + result.push("
  • " + j + "" + (i + 1) + "
  • "); + }); + $("#scheduleresult").html(result.join("")); + } else { + $("#scheduleresult").html(""); + } + return false; + }); + }); + $("#pickdays").trigger("change"); + }, + formatter: { + nexttime: function (value, row, index) { + if (isNaN(value)) { + return value; + } else { + return Table.api.formatter.datetime.call(this, value, row, index); + } + }, + maximums: function (value, row, index) { + return value === 0 ? __('No limit') : value; + } + } + } + }; + return Controller; +}); diff --git a/public/assets/js/backend/general/crontab_log.js b/public/assets/js/backend/general/crontab_log.js new file mode 100644 index 0000000..09ff444 --- /dev/null +++ b/public/assets/js/backend/general/crontab_log.js @@ -0,0 +1,67 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'general/crontab_log/index', + add_url: 'general/crontab_log/add', + edit_url: '', + del_url: 'general/crontab_log/del', + multi_url: 'general/crontab_log/multi', + table: 'crontab' + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + sortName: 'id', + fixedColumns: true, + fixedRightNumber: 1, + columns: [ + [ + {field: 'state', checkbox: true,}, + {field: 'id', title: 'ID'}, + {field: 'crontab_id', title: __('Crontab_id'), formatter: Table.api.formatter.search}, + {field: 'executetime', title: __('Execute time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true}, + {field: 'completetime', title: __('Complete time'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true}, + {field: 'status', title: __('Status'), searchList: Config.statusList, custom: {success: 'success', failure: 'danger', 'inprogress': 'warning'}, formatter: Table.api.formatter.status}, + { + field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate, + buttons: [ + { + name: "detail", + text: __("Result"), + classname: "btn btn-xs btn-info btn-dialog", + icon: "fa fa-file", + url: "general/crontab_log/detail", + extend: "data-window='parent'" + } + ] + } + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + + }, + } + }; + return Controller; +}); \ No newline at end of file