多样青春夜校

课时过期定时任务(进行中)
课时取消记录次数,同课时超n次无法取消(用户端)
免费课程课时到开始时间无法取消(用户端)
增加定时器封装和队列封装
This commit is contained in:
15090180611 2024-11-27 18:15:32 +08:00
parent 9b8d02bd4b
commit a2198685da
31 changed files with 1201 additions and 9 deletions

View File

@ -0,0 +1,93 @@
<?php
namespace app\admin\controller\general;
use app\common\controller\Backend;
use Cron\CronExpression;
/**
* 定时任务
*
* @icon fa fa-tasks
* @remark 按照设定的时间进行任务的执行,目前支持三种任务:请求URL、执行SQL、执行Shell。
*/
class Crontab extends Backend
{
protected $model = null;
protected $noNeedRight = ['check_schedule', 'get_schedule_future'];
public function _initialize()
{
parent::_initialize();
$this->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]);
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace app\admin\controller\general;
use app\common\controller\Backend;
/**
* 定时任务
*
* @icon fa fa-tasks
* @remark 类似于Linux的Crontab定时任务,可以按照设定的时间进行任务的执行
*/
class CrontabLog extends Backend
{
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->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();
}
}

View File

@ -230,6 +230,7 @@ class Index extends Backend
$order = Order::where("manystore_id",$id)->where("status","in","0,3")->find(); $order = Order::where("manystore_id",$id)->where("status","in","0,3")->find();
if($order)$this->error("存在正在使用中的课程订单或存在正在售后中的课程订单无法继续操作!"); if($order)$this->error("存在正在使用中的课程订单或存在正在售后中的课程订单无法继续操作!");
// 课程存在售后订单则不允许操作 // 课程存在售后订单则不允许操作
} }
@ -367,6 +368,11 @@ class Index extends Backend
$manystoreAuthGroupAccessModel->insert($group_access); $manystoreAuthGroupAccessModel->insert($group_access);
//调用事件
$data = ['shop' => $shop];
\think\Hook::listen('shop_create_after', $data);
db()->commit(); db()->commit();
$this->success(); $this->success();
}catch (Exception $e){ }catch (Exception $e){
@ -445,6 +451,10 @@ class Index extends Backend
$this->error($row->getError()); $this->error($row->getError());
} }
//调用事件
$data = ['shop' => $shop_info];
\think\Hook::listen('shop_update_after', $data);
$this->success(); $this->success();
} }
$this->error(); $this->error();
@ -465,6 +475,7 @@ class Index extends Backend
*/ */
public function del($ids = "") public function del($ids = "")
{ {
if ($ids) { if ($ids) {
$row = $this->model->get(['id' => $ids,'is_main'=>1]); $row = $this->model->get(['id' => $ids,'is_main'=>1]);
if(!$row){ if(!$row){

View File

@ -246,7 +246,10 @@ class ClassesLib extends Backend
|| empty($params["city"]) || empty($params["city"])
|| empty($params["district"]) || empty($params["district"])
|| empty($params["longitude"]) || empty($params["longitude"])
|| empty($params["latitude"])) $this->error("独立地点需传定位信息"); || empty($params["latitude"])
|| empty($params["address"])
|| empty($params["address_detail"])
) $this->error("独立地点需传定位信息");
}else{ }else{
//地址取机构的 //地址取机构的
@ -259,7 +262,10 @@ class ClassesLib extends Backend
|| empty($shop["city"]) || empty($shop["city"])
|| empty($shop["district"]) || empty($shop["district"])
|| empty($shop["longitude"]) || 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["district"] = $shop["district"];
$params["longitude"] = $shop["longitude"]; $params["longitude"] = $shop["longitude"];
$params["latitude"] = $shop["latitude"]; $params["latitude"] = $shop["latitude"];
$params["address"] = $shop["address"];
$params["address_detail"] = $shop["address_detail"];
//address
// var_dump($params);
} }
//特有认证判断 //特有认证判断

View File

@ -0,0 +1,22 @@
<?php
return [
'Title' => '任务标题',
'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 次的执行时间',
];

View File

@ -0,0 +1,14 @@
<?php
return [
'Title' => '任务标题',
'Crontab_id' => '定时任务ID',
'Success' => '成功',
'Failure' => '失败',
'Processid' => '进程ID',
'Inprogress' => '执行中',
'Content' => '返回结果',
'Result' => '执行结果',
'Complete time' => '完成时间',
'Execute time' => '最后执行时间',
];

View File

@ -0,0 +1,63 @@
<?php
namespace app\admin\model;
use think\Model;
class Crontab extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 定义字段类型
protected $type = [
];
// 追加属性
protected $append = [
'type_text'
];
protected static function init()
{
self::afterInsert(function ($row) {
$pk = $row->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;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace app\admin\model;
use think\Model;
class CrontabLog extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
// 定义字段类型
protected $type = [
];
// 追加属性
protected $append = [
];
public function getStatusList()
{
return ['success' => __('Success'), 'failure' => __('Failure'), 'inprogress' => __('Inprogress')];
}
public function getStatusTextAttr($value, $data)
{
$value = $value ?: ($data['status'] ?? '');
$list = $this->getStatusList();
return $list[$value] ?? '';
}
}

View File

@ -0,0 +1,119 @@
<style type="text/css">
#schedulepicker {
padding-top: 7px;
}
#schedulepicker h5 {
line-height: 30px;
}
#schedulepicker .list-group {
margin-bottom: 0;
}
</style>
<style data-render="darktheme">
body.darktheme #schedulepicker pre {
background-color: #4c4c4c;
border-color: #262626;
color: #ccc;
}
</style>
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required"/>
</div>
</div>
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_select('row[type]', $typeList, null, ['class'=>'form-control', 'data-rule'=>'required'])}
</div>
</div>
<div class="form-group">
<label for="c-content" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[content]" id="c-content" cols="30" rows="5" class="form-control" data-rule="required"></textarea>
</div>
</div>
<div class="form-group">
<label for="schedule" class="control-label col-xs-12 col-sm-2">{:__('Schedule')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group margin-bottom-sm">
<input type="text" class="form-control" id="schedule" style="font-size:12px;font-family: Verdana;word-spacing:23px;" name="row[schedule]" value="* * * * *" data-rule="required; remote(general/crontab/check_schedule)"/>
<span class="input-group-btn">
<a href="https://www.fastadmin.net/store/crontab.html" target="_blank" class="btn btn-default"><i class="fa fa-info-circle"></i> {:__('Crontab rules')}</a>
</span>
<span class="msg-box n-right"></span>
</div>
<div id="schedulepicker">
<pre><code>* * * * *
- - - - -
| | | | +--- day of week (0 - 7) (Sunday=0 or 7)
| | | +-------- month (1 - 12)
| | +------------- day of month (1 - 31)
| +------------------ hour (0 - 23)
+----------------------- min (0 - 59)</code></pre>
<pre><code>minute hour day month week
minute表示分钟可以是0~59的任意整数。
hour表示小时可以是2~23的任意整数
day表示日期可以是1~31的任意整数
month表示月份可以是1~12的任意整数
week表示星期几可以是0~7之间的整数0或者7表示星期天</code></pre>
<pre><code>星号(*):表示所有可能的值,`* * * * * `表示为每分钟都执行。
逗号(,):表示一个列表范围,如`1,2,3,5,10 * * * *`表示每天每小时的第一、第二、第三、第五、第十分钟执行。
中杠(-):表示一个数值范围,如`1-15 * * * *`表示每天每小时的1到15分钟执行。
正斜线(/):表示间隔频率,如`0 10-12/3 * * *`表示每天的10点到12点间隔3小时执行`/`也可以配合`*`使用,如:`*/5 * * * * *`表示每隔5分钟执行。</code></pre>
<pre><code>
使用前准备工作
Linux下使用crontab -e -u 用户名添加一条记录
* * * * * /usr/bin/php /www/yoursite/public/index.php /addons/crontab/autotask/index > /dev/null 2>&1 &</code></pre>
<h5>{:__('The next %s times the execution time', '<input type="number" id="pickdays" class="form-control text-center" value="7" style="display: inline-block;width:80px;">')}</h5>
<ol id="scheduleresult" class="list-group">
</ol>
</div>
</div>
</div>
<div class="form-group">
<label for="maximums" class="control-label col-xs-12 col-sm-2">{:__('Maximums')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="number" class="form-control" id="maximums" name="row[maximums]" value="0" data-rule="required" size="6" data-tip="0表示无限制"/>
</div>
</div>
<div class="form-group">
<label for="begintime" class="control-label col-xs-12 col-sm-2">{:__('Begin time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="begintime" name="row[begintime]" value="" data-rule="{:__('Begin time')}:required" size="6"/>
</div>
</div>
<div class="form-group">
<label for="endtime" class="control-label col-xs-12 col-sm-2">{:__('End time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="endtime" name="row[endtime]" value="" data-rule="{:__('End time')}:required;match(gte, row[begintime], datetime)" size="6"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,123 @@
<style type="text/css">
#schedulepicker {
padding-top: 7px;
}
#schedulepicker h5 {
line-height: 30px;
}
#schedulepicker .list-group {
margin-bottom: 0;
}
</style>
<style data-render="darktheme">
body.darktheme #schedulepicker pre {
background-color: #4c4c4c;
border-color: #262626;
color: #ccc;
}
</style>
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="{$row.title|htmlentities}" data-rule="required"/>
</div>
</div>
<div class="form-group">
<label for="type" class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_select('row[type]', $typeList, $row['type'], ['class'=>'form-control', 'data-rule'=>'required'])}
</div>
</div>
<div class="form-group">
<label for="c-content" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[content]" id="c-content" cols="30" rows="5" class="form-control" data-rule="required">{$row.content|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label for="schedule" class="control-label col-xs-12 col-sm-2">{:__('Schedule')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group margin-bottom-sm">
<input type="text" class="form-control" id="schedule" style="font-size:12px;font-family: Verdana;word-spacing:23px;" name="row[schedule]" value="{$row.schedule|htmlentities}" data-rule="required; remote(general/crontab/check_schedule)"/>
<span class="input-group-btn">
<a href="https://www.fastadmin.net/store/crontab.html" target="_blank" class="btn btn-default"><i class="fa fa-info-circle"></i> {:__('Crontab rules')}</a>
</span>
<span class="msg-box n-right"></span>
</div>
<div id="schedulepicker">
<pre><code>* * * * *
- - - - -
| | | | +--- day of week (0 - 7) (Sunday=0 or 7)
| | | +-------- month (1 - 12)
| | +------------- day of month (1 - 31)
| +------------------ hour (0 - 23)
+----------------------- min (0 - 59)</code></pre>
<pre><code>minute hour day month week
minute表示分钟可以是0~59的任意整数。
hour表示小时可以是2~23的任意整数
day表示日期可以是1~31的任意整数
month表示月份可以是1~12的任意整数
week表示星期几可以是0~7之间的整数0或者7表示星期天</code></pre>
<pre><code>星号(*):表示所有可能的值,`* * * * * `表示为每分钟都执行。
逗号(,):表示一个列表范围,如`1,2,3,5,10 * * * *`表示每天每小时的第一、第二、第三、第五、第十分钟执行。
中杠(-):表示一个数值范围,如`1-15 * * * *`表示每天每小时的1到15分钟执行。
正斜线(/):表示间隔频率,如`0 10-12/3 * * *`表示每天的10点到12点间隔3小时执行`/`也可以配合`*`使用,如:`*/5 * * * * *`表示每隔5分钟执行。</code></pre>
<pre><code>
使用前准备工作
Linux下使用crontab -e -u 用户名添加一条记录
* * * * * /usr/bin/php /www/yoursite/public/index.php /addons/crontab/autotask/index > /dev/null 2>&1 &</code></pre>
<h5>{:__('The next %s times the execution time', '<input type="number" id="pickdays" class="form-control text-center" value="7" style="display: inline-block;width:80px;">')}</h5>
<ol id="scheduleresult" class="list-group">
</ol>
</div>
</div>
</div>
<div class="form-group">
<label for="maximums" class="control-label col-xs-12 col-sm-2">{:__('Maximums')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="number" class="form-control" id="maximums" name="row[maximums]" value="{$row.maximums}" data-rule="required" size="6"/>
</div>
</div>
<div class="form-group">
<label for="begintime" class="control-label col-xs-12 col-sm-2">{:__('Begin time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="begintime" name="row[begintime]" value="{$row.begintime|datetime}" data-rule="{:__('Begin time')}:required" size="6"/>
</div>
</div>
<div class="form-group">
<label for="endtime" class="control-label col-xs-12 col-sm-2">{:__('End time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="endtime" name="row[endtime]" value="{$row.endtime|datetime}" data-rule="{:__('End time')}:required;match(gte, row[begintime], datetime)" size="6"/>
</div>
</div>
<div class="form-group">
<label for="weigh" class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control" id="weigh" name="row[weigh]" value="{$row.weigh}" data-rule="required" size="6"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'completed'=>__('Completed'), 'expired'=>__('Expired'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,30 @@
<div class="panel panel-default panel-intro">
<div class="panel-heading">
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs" data-field="type">
<li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
{foreach name="typeList" item="vo"}
<li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
{/foreach}
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar('refresh,add,edit,del')}
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('general/crontab/edit')}"
data-operate-del="{:$auth->check('general/crontab/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,46 @@
<style type="text/css">
#schedulepicker {
padding-top:7px;
}
</style>
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-10">
<textarea name="row[content]" id="conent" cols="30" style="width:100%;" rows="20" class="form-control" data-rule="required" readonly>{$row.content|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label for="executetime" class="control-label col-xs-12 col-sm-2">{:__('End time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="executetime" name="row[executetime]" value="{$row.executetime|datetime}" data-rule="{:__('End time')}:required;match(gte, row[begintime], datetime)" size="6" disabled />
</div>
</div>
<div class="form-group">
<label for="completetime" class="control-label col-xs-12 col-sm-2">{:__('End time')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control datetimepicker" id="completetime" name="row[completetime]" value="{$row.completetime|datetime}" data-rule="{:__('End time')}:required;match(gte, row[begintime], datetime)" size="6" disabled />
</div>
</div>
<div class="form-group">
<label for="processid" class="control-label col-xs-12 col-sm-2">{:__('Processid')}:</label>
<div class="col-xs-12 col-sm-4">
<input type="text" class="form-control" id="processid" name="row[processid]" value="{$row.processid}" disabled />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
<div style="padding-top:8px;">
{if $row['status']=='success'}<span class="label label-success">{:__('Success')}</span>{else/}<span class="label label-danger">{:__('Failure')}</span>{/if}
</div>
</div>
</div>
<div class="form-group hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="button" class="btn btn-success btn-embossed" onclick="parent.Layer.close(parent.Layer.getFrameIndex(window.name))">{:__('Close')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,30 @@
<div class="panel panel-default panel-intro">
<div class="panel-heading">
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs" data-field="status">
<li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
{foreach name="statusList" item="vo"}
<li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
{/foreach}
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
{:build_toolbar('refresh,del')}
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-detail="{:$auth->check('general/crontab/detail')}"
data-operate-del="{:$auth->check('general/crontab/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,85 @@
<?php
namespace app\api\controller;
use app\common\controller\Api;
/** 定时任务控制器
* Class Crontab
* @package addons\shopro\controller
*/
class Crontab extends Api
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
/**
* 每天执行的任务
*/
public function day()
{
try{
$res = MockOrder::timeoutCheck(true);
MockOrder::cleanTodayCarNum(true);
}catch (\Exception $e){
$this->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("执行成功条");
}
}

View File

@ -3,6 +3,7 @@
namespace app\api\controller; namespace app\api\controller;
use app\common\controller\Api; use app\common\controller\Api;
use app\common\job\test\DemoJob;
/** /**
* 示例接口 * 示例接口
@ -40,6 +41,7 @@ class Demo extends Api
*/ */
public function test() public function test()
{ {
\think\Queue::push(DemoJob::class, ["name"=>"hello world!"], "school");
$this->success('返回成功', $this->request->param()); $this->success('返回成功', $this->request->param());
} }

View File

@ -244,6 +244,7 @@ class Classes extends Base
* @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字") * @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字")
* @ApiParams(name = "page", type = "string",required=true,description = "页数") * @ApiParams(name = "page", type = "string",required=true,description = "页数")
* @ApiParams(name = "limit", 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 = "user_id", type = "int",required=false,description = "主讲师用户id")
* @ApiParams(name = "shop_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") * @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["order"] = $this->request->get('order/s', ''); //机构店铺id
$params["nearby"] = $this->request->get('nearby/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["latitude"] = $this->request->get('latitude/s', ''); //机构店铺id
$params["longitude"] = $this->request->get('longitude/s', ''); //机构店铺id $params["longitude"] = $this->request->get('longitude/s', ''); //机构店铺id

View File

@ -168,6 +168,7 @@ class Shop extends Base
* @ApiSummary(通用机构大索索列表) * @ApiSummary(通用机构大索索列表)
* @ApiMethod(GET) * @ApiMethod(GET)
* @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字") * @ApiParams(name = "keywords", type = "string",required=false,description = "搜索关键字")
* @ApiParams(name = "page", type = "string",required=true,description = "页数") * @ApiParams(name = "page", type = "string",required=true,description = "页数")
* @ApiParams(name = "limit", type = "string",required=true,description = "条数") * @ApiParams(name = "limit", type = "string",required=true,description = "条数")
* @ApiParams(name = "user_id", type = "int",required=false,description = "主讲师用户id") * @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["type"] = $this->request->get('type/s', ''); //机构店铺id
$params["province"] = $this->request->get('province/s', ''); //机构店铺id $params["province"] = $this->request->get('province/s', ''); //机构店铺id
$params["city"] = $this->request->get('city/s', ''); //机构店铺id $params["city"] = $this->request->get('city/s', ''); //机构店铺id
$params["district"] = $this->request->get('district/s', ''); //机构店铺id $params["district"] = $this->request->get('district/s', ''); //机构店铺id
$params["status"] = $this->request->get('status/s', ''); //机构店铺id $params["status"] = $this->request->get('status/s', ''); //机构店铺id

View File

@ -75,6 +75,9 @@ $manystoreHooks = [
'shop_auth_fail_after' => [ // 机构审核失败后 'shop_auth_fail_after' => [ // 机构审核失败后
'app\\common\\listener\\manystore\\ShopHook' 'app\\common\\listener\\manystore\\ShopHook'
], ],
'shop_update_after' => [ // 机构数据变更
'app\\common\\listener\\manystore\\ShopHook'
],
]; ];

View File

@ -0,0 +1,27 @@
<?php
namespace app\common\job\test;
use think\queue\Job;
class DemoJob
{
public function fire(Job $job, $data){
//....这里执行具体的任务
if ($job->attempts() > 3) {
//通过这个方法可以检查这个任务已经重试了几次了
}
//如果任务执行成功后 记得删除任务不然这个任务会重复执行直到达到最大重试次数后失败后执行failed方法
$job->delete();
// 也可以重新发布这个任务
// $job->release($delay); //$delay为延迟时间
}
public function failed($data){
// ...任务达到最大重试次数后,失败了
}
}

View File

@ -1,5 +1,6 @@
<?php <?php
namespace app\common\listener\manystore; namespace app\common\listener\manystore;
use app\common\model\school\classes\ClassesLib;
use app\common\model\school\Message; use app\common\model\school\Message;
class ShopHook class ShopHook
@ -18,6 +19,30 @@ class ShopHook
} }
// 机构账号更新成功后(审核之前)
public function shopUpdateAfter(&$params)
{
["shop"=>$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) public function shopApplyAfter(&$params)

View File

@ -17,6 +17,83 @@ class BaseModel extends Model
public $timeKey = 'createtime'; public $timeKey = 'createtime';
public static $staticTimeKey = '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 $status
* @param null $model * @param null $model

View File

@ -44,8 +44,38 @@ class ClassesLib extends BaseModel
'new_text', 'new_text',
'selfhot_text', 'selfhot_text',
'distance_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) { public function getDistanceTextAttr($value, $data) {
$distance_text = ''; $distance_text = '';
$distance = $data['distance'] ?? 0; $distance = $data['distance'] ?? 0;
@ -264,6 +294,13 @@ class ClassesLib extends BaseModel
return $this->hasMany(ClassesSpec::class,'classes_lib_id'); 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) { public static function getVaildList($params) {
extract($params); extract($params);
$a = (new self)->getWithAlisaName().'.'; $a = (new self)->getWithAlisaName().'.';
$with = ['teacher'];
if (isset($has_shop) && $has_shop) {
$with[] = 'shop';
}
// 查询自提点 // 查询自提点
if(isset($status) && in_array($status, ['1','2','3'])){ if(isset($status) && in_array($status, ['1','2','3'])){
$selfetch = self::with(['teacher']); $selfetch = self::with($with);
}else{ }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) { //迭代器魔术方法遍历,填值自动引用传值 foreach ($selfetch as $row) { //迭代器魔术方法遍历,填值自动引用传值
//设置是否已收藏 //设置是否已收藏
$row->is_collect = in_array($row->id,$collect_classes_lib_ids) ? 1 : 0; $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; 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(); $virtual_num = VirtualUser::where("havetype",'1')->where("classes_lib_id",$classes_lib_id)->count();
//如果课程虚拟报名者字段 数量小于 虚拟报名者实际数量 ,则等于虚拟报名者实际数量 havetype //如果课程虚拟报名者字段 数量小于 虚拟报名者实际数量 ,则等于虚拟报名者实际数量 havetype
$classes_lib->virtual_num = $classes_lib->virtual_num < $virtual_num ? $virtual_num : $classes_lib->virtual_num; $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(); $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){ public static function add_virtual_init($classes_lib_id){
//更新课程规格库存 //更新课程规格库存

View File

@ -2,10 +2,11 @@
namespace app\common\model\school\classes; namespace app\common\model\school\classes;
use app\common\model\BaseModel;
use think\Model; use think\Model;
use traits\model\SoftDelete; use traits\model\SoftDelete;
class ClassesSpec extends Model class ClassesSpec extends BaseModel
{ {
use SoftDelete; use SoftDelete;

View File

@ -25,10 +25,21 @@ class Collect extends Model
// 追加属性 // 追加属性
protected $append = [ 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() protected static function init()
{ {
self::afterInsert(function ($row) { self::afterInsert(function ($row) {

View File

@ -769,6 +769,28 @@ class Order extends BaseModel
if($check){ if($check){
//用户操作权限检测 //用户操作权限检测
self::checkOptionAuth($order['classes_order_id'],$user_id ?: $oper_id,$oper_type); 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."次,无法再取消!");
}
}
} }
//判断逻辑 //判断逻辑

View File

@ -441,12 +441,20 @@ class Order extends BaseModel
//判断订单是否已创建 //判断订单是否已创建
$order_info = self::where(['order_no'=>$order_no])->find(); $order_info = self::where(['order_no'=>$order_no])->find();
if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!"); if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!");
} }
//校验订单参数 //校验订单参数
//课程是否存在并上架 //课程是否存在并上架
$classes_lib_info = ClassesLib::where('id',$classes_lib_id)->find(); $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['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(); $user_info = User::where('id',$user_id)->find();
if(!$user_info) throw new \Exception("用户不存在!"); if(!$user_info) throw new \Exception("用户不存在!");
@ -1025,8 +1033,11 @@ class Order extends BaseModel
if($check){ if($check){
//用户操作权限检测 //用户操作权限检测
\app\common\model\school\classes\hourorder\Order::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type); \app\common\model\school\classes\hourorder\Order::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type);
} }
//
//判断逻辑 //判断逻辑
if($trans){ if($trans){
self::beginTrans(); self::beginTrans();

View File

@ -2,11 +2,12 @@
namespace app\common\model\school\classes\order; namespace app\common\model\school\classes\order;
use app\common\model\BaseModel;
use app\common\model\dyqc\ManystoreShop; use app\common\model\dyqc\ManystoreShop;
use think\Model; use think\Model;
use traits\model\SoftDelete; use traits\model\SoftDelete;
class OrderDetail extends Model class OrderDetail extends BaseModel
{ {
use SoftDelete; use SoftDelete;

View File

@ -67,6 +67,10 @@ class Profile extends ManystoreBase
$manystore->save($params); $manystore->save($params);
Session::set("manystore", $manystore->toArray()); Session::set("manystore", $manystore->toArray());
//调用事件
$data = ['shop' => $manystore];
\think\Hook::listen('shop_update_after', $data);
$this->success(); $this->success();
} }
$this->error(); $this->error();

View File

@ -238,6 +238,8 @@ class ClassesLib extends ManystoreBase
|| empty($params["city"]) || empty($params["city"])
|| empty($params["district"]) || empty($params["district"])
|| empty($params["longitude"]) || empty($params["longitude"])
|| empty($params["address"])
|| empty($params["address_detail"])
|| empty($params["latitude"])) $this->error("独立地点需传定位信息"); || empty($params["latitude"])) $this->error("独立地点需传定位信息");
}else{ }else{
@ -252,6 +254,8 @@ class ClassesLib extends ManystoreBase
|| empty($shop["city"]) || empty($shop["city"])
|| empty($shop["district"]) || empty($shop["district"])
|| empty($shop["longitude"]) || empty($shop["longitude"])
|| empty($shop["address"])
|| empty($shop["address_detail"])
|| empty($shop["latitude"])) $this->error("当前机构地址并未完善!请去【个人资料】完善信息"); || empty($shop["latitude"])) $this->error("当前机构地址并未完善!请去【个人资料】完善信息");
@ -266,6 +270,8 @@ class ClassesLib extends ManystoreBase
$params["district"] = $shop["district"]; $params["district"] = $shop["district"];
$params["longitude"] = $shop["longitude"]; $params["longitude"] = $shop["longitude"];
$params["latitude"] = $shop["latitude"]; $params["latitude"] = $shop["latitude"];
$params["address"] = $shop["address"];
$params["address_detail"] = $shop["address_detail"];
} }
//收费免费判断 //收费免费判断

View File

@ -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("<li class='list-group-item'>" + j + "<span class='badge'>" + (i + 1) + "</span></li>");
});
$("#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;
});

View File

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