<?php
require_once dirname(__DIR__, 2) . '/core/lib/task_center.php';

$rootPath = dirname(__DIR__, 2);
$definitions = cmsTaskDefinitions();
$visibleTasks = array_filter($definitions, function ($task) use ($adminAccess) { return cmsTaskCanRun($task, (array)$adminAccess) || cmsAdminCan($adminAccess, 'task_center', 'view'); });

if (($_GET['action'] ?? '') === 'detail') {
    $run=cmsTaskRunDetail($db,intval($_GET['id']??0));while(ob_get_level()>0){ob_end_clean();}header('Content-Type: application/json; charset=utf-8');
    if(!$run){echo json_encode(['code'=>404,'msg'=>'运行记录不存在'],JSON_UNESCAPED_UNICODE);exit;}
    $result=json_decode((string)($run['result_json']??''),true);echo json_encode(['code'=>200,'data'=>['id'=>intval($run['id']),'task_key'=>$run['task_key'],'status'=>$run['status'],'trigger_source'=>$run['trigger_source'],'admin_name'=>$run['admin_name']?:'系统','started_at'=>intval($run['started_at']),'finished_at'=>intval($run['finished_at']),'duration_ms'=>intval($run['duration_ms']),'summary'=>$run['summary'],'error_message'=>$run['error_message'],'result'=>is_array($result)?$result:[]]],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    requireAdminCsrfToken(true);
    while (ob_get_level() > 0) { ob_end_clean(); }
    header('Content-Type: application/json; charset=utf-8');
    if(($_POST['task_action']??'')==='cleanup'){$deleted=cmsCleanupTaskRuns($db,intval($_POST['keep_days']??90),intval($_POST['keep_minimum']??200));addLog('清理','后台任务','清理历史运行记录 '.$deleted.' 条');echo json_encode(['code'=>200,'msg'=>'已清理 '.$deleted.' 条过期任务记录'],JSON_UNESCAPED_UNICODE);exit;}
    $taskKey = trim((string)($_POST['task_key'] ?? ''));
    if (!isset($definitions[$taskKey])) { echo json_encode(['code'=>404,'msg'=>'任务不存在'],JSON_UNESCAPED_UNICODE); exit; }
    if (!cmsTaskCanRun($definitions[$taskKey], (array)$adminAccess)) { echo json_encode(['code'=>403,'msg'=>'当前账号没有执行此任务的权限'],JSON_UNESCAPED_UNICODE); exit; }
    $result = cmsRunTask($db, $taskKey, $rootPath, ['id'=>intval($_SESSION['admin_id'] ?? 0),'name'=>(string)($_SESSION['admin_name'] ?? '')], 'manual');
    if ($result['success']) { addLog('手动执行', '后台任务', $definitions[$taskKey]['name'].'：'.$result['message']); }
    echo json_encode(['code'=>$result['success']?200:($result['busy']?409:500),'msg'=>$result['message'],'duration_ms'=>$result['duration_ms']??0],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); exit;
}

cmsEnsureTaskCenterSchema($db);
$db->query("UPDATE {$db->table('task_run')} SET status='error', finished_at=?, error_message='任务进程异常中断或超时' WHERE status='running' AND started_at<?", [time(),time()-21600]);
$taskFilter = trim((string)($_GET['task'] ?? '')); $statusFilter = trim((string)($_GET['status'] ?? ''));$page=max(1,intval($_GET['page']??1));$pageSize=20;
$allRecent = cmsTaskRecentRuns($db, 200); $latest = cmsTaskLatestByKey($allRecent);
$total=cmsTaskRunCount($db,$taskFilter,$statusFilter);$totalPages=max(1,intval(ceil($total/$pageSize)));if($page>$totalPages)$page=$totalPages;$runs=cmsTaskRunsPage($db,$page,$pageSize,$taskFilter,$statusFilter);$stats=cmsTaskStats($db,30);
$canMap=[]; foreach($definitions as$key=>$definition){$canMap[$key]=cmsTaskCanRun($definition,(array)$adminAccess);}
$statusLabels=['running'=>'运行中','success'=>'成功','error'=>'失败'];
?>
<div class="page-container fade-in task-page">
<div class="page-header"><div class="page-title"><div class="page-icon"><i class="fa fa-tasks"></i></div><div><h1>后台任务中心</h1><p>统一执行、追踪和诊断系统维护任务</p></div></div><div class="page-actions"><button class="btn btn-secondary" onclick="CMSAdminUI.reload(this,'刷新中')"><i class="fa fa-refresh"></i> 刷新状态</button></div></div>
<div class="task-summary"><div><small>任务类型</small><strong><?php echo count($definitions); ?></strong><span>项</span></div><div><small>近 30 天运行</small><strong><?php echo $stats['total']; ?></strong><span>次</span></div><div class="is-success"><small>成功率</small><strong><?php echo $stats['success_rate']; ?>%</strong><span><?php echo $stats['success']; ?> 次成功</span></div><div class="is-error"><small>执行失败</small><strong><?php echo $stats['error']; ?></strong><span>次</span></div><div><small>平均耗时</small><strong><?php echo $stats['avg_ms']; ?></strong><span>毫秒</span></div></div>
<div class="task-grid">
<?php foreach($definitions as$key=>$task): $last=$latest[$key]??null; ?>
<article class="task-card"><div class="task-card-head"><span><i class="fa <?php echo $task['icon']; ?>"></i></span><div><h2><?php echo htmlspecialchars($task['name']); ?></h2><p><?php echo htmlspecialchars($task['description']); ?></p></div></div>
<div class="task-last"><?php if($last): ?><div><small>最近状态</small><b class="is-<?php echo $last['status']; ?>"><?php echo $statusLabels[$last['status']]??$last['status']; ?></b></div><div><small>运行时间</small><b><?php echo date('m-d H:i:s',intval($last['started_at'])); ?></b></div><div><small>耗时</small><b><?php echo intval($last['duration_ms']); ?> ms</b></div><p><?php echo htmlspecialchars($last['status']==='error'?$last['error_message']:$last['summary']); ?></p><?php else: ?><div class="task-never">尚未运行</div><?php endif; ?></div>
<button class="btn btn-primary task-run" data-task="<?php echo $key; ?>" <?php echo !$canMap[$key]?'disabled title="没有此任务的执行权限"':''; ?>><i class="fa fa-play"></i> <?php echo $last&&$last['status']==='error'?'重新执行':'立即执行'; ?></button></article>
<?php endforeach; ?>
</div>
<div class="task-history-head"><div><h2>运行记录</h2><p>保留执行来源、操作者、耗时、摘要和失败原因</p></div><div class="task-history-actions"><form method="get"><input type="hidden" name="act" value="task_center"><select name="task"><option value="">全部任务</option><?php foreach($definitions as$key=>$task): ?><option value="<?php echo$key; ?>" <?php echo$taskFilter===$key?'selected':''; ?>><?php echo htmlspecialchars($task['name']); ?></option><?php endforeach; ?></select><select name="status"><option value="">全部状态</option><?php foreach($statusLabels as$key=>$label): ?><option value="<?php echo$key; ?>" <?php echo$statusFilter===$key?'selected':''; ?>><?php echo$label; ?></option><?php endforeach; ?></select><button class="btn btn-secondary">筛选</button></form><?php if(cmsAdminCan($adminAccess,'task_center','manage')): ?><button class="btn btn-secondary" onclick="cleanupRuns(this)"><i class="fa fa-trash-o"></i> 清理记录</button><?php endif; ?></div></div>
<div class="content-card task-table-wrap"><table class="data-table task-table"><thead><tr><th>任务</th><th>状态</th><th>触发方式</th><th>操作者</th><th>开始时间</th><th>耗时</th><th>执行结果</th><th>操作</th></tr></thead><tbody><?php if(!$runs): ?><tr><td colspan="8" class="task-empty">暂无运行记录</td></tr><?php endif; ?><?php foreach($runs as$run): $task=$definitions[$run['task_key']]??['name'=>$run['task_key']]; ?><tr><td><strong><?php echo htmlspecialchars($task['name']); ?></strong><small>#<?php echo intval($run['id']); ?></small></td><td><span class="task-status is-<?php echo$run['status']; ?>"><?php echo$statusLabels[$run['status']]??$run['status']; ?></span></td><td><?php echo$run['trigger_source']==='manual'?'手动':'计划任务'; ?></td><td><?php echo htmlspecialchars($run['admin_name']?:'系统'); ?></td><td><?php echo date('Y-m-d H:i:s',intval($run['started_at'])); ?></td><td><?php echo intval($run['duration_ms']); ?> ms</td><td class="task-result" title="<?php echo htmlspecialchars($run['error_message']?:$run['summary']); ?>"><?php echo htmlspecialchars($run['error_message']?:$run['summary']); ?></td><td><button class="btn btn-secondary btn-sm" onclick="showRun(<?php echo intval($run['id']); ?>)">详情</button> <?php if($run['status']==='error'&&!empty($canMap[$run['task_key']])): ?><button class="btn btn-secondary btn-sm task-run" data-task="<?php echo htmlspecialchars($run['task_key']); ?>">重试</button><?php endif; ?></td></tr><?php endforeach; ?></tbody></table><?php if($totalPages>1): ?><div class="task-pagination"><?php for($i=max(1,$page-2);$i<=min($totalPages,$page+2);$i++): $query=$_GET;$query['page']=$i; ?><a class="<?php echo$i===$page?'active':''; ?>" href="?<?php echo htmlspecialchars(http_build_query($query)); ?>"><?php echo$i; ?></a><?php endfor; ?></div><?php endif; ?></div>
</div>
<div class="task-detail-modal" id="taskDetailModal" hidden><div><button type="button" onclick="closeRun()" aria-label="关闭">×</button><small>TASK RUN DETAIL</small><h2 id="runTitle">运行详情</h2><dl id="runMeta"></dl><h3>结果数据</h3><pre id="runResult"></pre></div></div>
<style>
.task-page{max-width:1500px}.task-summary{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:16px}.task-summary>div{padding:16px 18px;border:1px solid #e2e8f0;border-radius:14px;background:#fff}.task-summary small,.task-summary span{display:block;color:#94a3b8;font-size:11px}.task-summary strong{display:inline-block;margin:5px 7px 2px 0;color:#1e293b;font-size:23px}.task-summary .is-success strong{color:#059669}.task-summary .is-error strong{color:#dc2626}.task-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:22px}.task-card{display:flex;flex-direction:column;padding:19px;border:1px solid #dfe6ef;border-radius:17px;background:#fff;box-shadow:0 8px 24px rgba(15,23,42,.04)}.task-card-head{display:flex;gap:12px}.task-card-head>span{display:grid;flex:0 0 42px;height:42px;place-items:center;border-radius:12px;background:#eff6ff;color:#2563eb;font-size:17px}.task-card h2{margin:1px 0 5px;color:#1e293b;font-size:15px}.task-card p{margin:0;color:#64748b;font-size:11px;line-height:1.6}.task-last{min-height:112px;margin:17px 0;padding:12px;border-radius:12px;background:#f8fafc}.task-last>div{display:flex;justify-content:space-between;margin-bottom:7px}.task-last small{color:#94a3b8}.task-last b{color:#475569;font-size:11px}.task-last b.is-success{color:#059669}.task-last b.is-error{color:#dc2626}.task-last b.is-running{color:#2563eb}.task-last p{overflow:hidden;margin-top:9px;padding-top:9px;border-top:1px solid #e2e8f0;text-overflow:ellipsis;white-space:nowrap}.task-never{align-items:center;height:88px;color:#94a3b8}.task-card>.btn{margin-top:auto}.task-card>.task-run:not(:disabled){border-color:#684198!important;background:linear-gradient(180deg,#7b58ad,#684198)!important;color:#fff!important;box-shadow:0 3px 9px rgba(104,65,152,.2)!important}.task-card>.task-run:not(:disabled):hover{border-color:#56347f!important;background:linear-gradient(180deg,#6d499f,#56347f)!important;color:#fff!important;box-shadow:0 5px 13px rgba(104,65,152,.25)!important}.task-card>.task-run:not(:disabled):active{background:#56347f!important;box-shadow:0 1px 3px rgba(104,65,152,.18)!important}.task-history-head,.task-history-actions{display:flex;align-items:end;justify-content:space-between;gap:9px}.task-history-head{margin:0 2px 12px}.task-history-head h2{margin:0;color:#1e293b;font-size:18px}.task-history-head p{margin:4px 0 0;color:#94a3b8;font-size:11px}.task-history-head form{display:flex;gap:8px}.task-history-head select{height:38px;padding:0 10px;border:1px solid #dbe3ee;border-radius:9px;background:#fff}.task-table-wrap{overflow-x:auto}.task-table{min-width:1050px}.task-table td strong,.task-table td small{display:block}.task-table td small{margin-top:3px;color:#94a3b8}.task-status{display:inline-block;padding:5px 8px;border-radius:8px;background:#f1f5f9;color:#64748b;font-size:10px;font-weight:800}.task-status.is-success{background:#ecfdf5;color:#047857}.task-status.is-error{background:#fef2f2;color:#b91c1c}.task-status.is-running{background:#eff6ff;color:#1d4ed8}.task-result{max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.task-empty{padding:45px!important;text-align:center;color:#94a3b8}.task-pagination{display:flex;justify-content:center;gap:7px;padding:15px}.task-pagination a{display:grid;width:34px;height:34px;place-items:center;border:1px solid #e2e8f0;border-radius:9px;color:#64748b}.task-pagination a.active{border-color:#2563eb;background:#2563eb;color:#fff}.task-detail-modal{position:fixed;inset:0;z-index:9999;background:rgba(15,23,42,.5);backdrop-filter:blur(4px)}.task-detail-modal:not([hidden]){display:flex;align-items:center;justify-content:center;padding:24px}.task-detail-modal>div{position:relative;width:min(680px,100%);max-height:85vh;overflow:auto;padding:26px;border-radius:20px;background:#fff}.task-detail-modal>div>button{position:absolute;right:17px;top:11px;border:0;background:none;color:#64748b;font-size:28px}.task-detail-modal small{color:#94a3b8;letter-spacing:.13em}.task-detail-modal h2{margin:7px 0 15px;color:#1e293b}.task-detail-modal h3{margin:18px 0 8px;color:#334155;font-size:13px}.task-detail-modal dl{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}.task-detail-modal dl div{padding:10px;border-radius:10px;background:#f8fafc}.task-detail-modal dt{color:#94a3b8;font-size:10px}.task-detail-modal dd{margin:4px 0 0;color:#334155;font-size:12px}.task-detail-modal pre{overflow:auto;max-height:300px;margin:0;padding:14px;border-radius:12px;background:#0f172a;color:#dbeafe;font-size:11px;white-space:pre-wrap;word-break:break-word}@media(max-width:1200px){.task-summary{grid-template-columns:repeat(3,1fr)}.task-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:700px){.task-summary,.task-grid{grid-template-columns:1fr}.task-history-head,.task-history-actions{align-items:stretch;flex-direction:column;gap:12px}.task-history-head form{flex-wrap:wrap}.task-detail-modal dl{grid-template-columns:1fr}}
</style>
<script>(function(){var busy=false;document.querySelectorAll('.task-run').forEach(function(button){button.onclick=function(){if(busy||this.disabled)return;var key=this.getAttribute('data-task');CMSAdminUI.confirm({title:'执行后台任务',message:'任务会立即执行并写入运行记录。同一任务正在运行时不会重复启动。',confirmText:'立即执行'}).then(function(ok){if(!ok)return;busy=true;CMSAdminUI.setLoading(button,true,'执行中');fetch('?act=task_center',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded','Accept':'application/json'},body:'task_key='+encodeURIComponent(key)+'&csrf_token=<?php echo rawurlencode(getAdminCsrfToken()); ?>'}).then(function(r){return r.json();}).then(function(d){if(d.code===200){CMSAdminUI.flash(d.msg,'success');location.reload();return;}CMSAdminUI.toast(d.msg,'error');busy=false;CMSAdminUI.setLoading(button,false);}).catch(function(){CMSAdminUI.toast('请求失败，请稍后重试','error');busy=false;CMSAdminUI.setLoading(button,false);});});};});})();</script>
<script>function showRun(id){fetch('?act=task_center&action=detail&id='+id,{headers:{Accept:'application/json'}}).then(function(r){return r.json();}).then(function(res){if(res.code!==200){CMSAdminUI.toast(res.msg,'error');return;}var d=res.data,defs=<?php echo json_encode(array_map(function($v){return$v['name'];},$definitions),JSON_UNESCAPED_UNICODE); ?>;document.getElementById('runTitle').textContent=(defs[d.task_key]||d.task_key)+' #'+d.id;var fields=[['状态',d.status],['触发方式',d.trigger_source],['操作者',d.admin_name],['耗时',d.duration_ms+' ms'],['开始时间',new Date(d.started_at*1000).toLocaleString()],['结束时间',d.finished_at?new Date(d.finished_at*1000).toLocaleString():'—'],['摘要',d.summary||'—'],['错误',d.error_message||'—']];var dl=document.getElementById('runMeta');dl.innerHTML='';fields.forEach(function(f){var box=document.createElement('div'),dt=document.createElement('dt'),dd=document.createElement('dd');dt.textContent=f[0];dd.textContent=f[1];box.appendChild(dt);box.appendChild(dd);dl.appendChild(box);});document.getElementById('runResult').textContent=JSON.stringify(d.result||{},null,2);document.getElementById('taskDetailModal').hidden=false;}).catch(function(){CMSAdminUI.toast('详情加载失败','error');});}function closeRun(){document.getElementById('taskDetailModal').hidden=true;}function cleanupRuns(button){CMSAdminUI.confirm({title:'清理历史记录',message:'仅清理 90 天前的记录，始终保留最近 200 条，运行中的记录不会删除。',tone:'danger',confirmText:'确认清理'}).then(function(ok){if(!ok)return;CMSAdminUI.setLoading(button,true,'清理中');fetch('?act=task_center',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded',Accept:'application/json'},body:'task_action=cleanup&keep_days=90&keep_minimum=200&csrf_token=<?php echo rawurlencode(getAdminCsrfToken()); ?>'}).then(function(r){return r.json();}).then(function(d){if(d.code===200){CMSAdminUI.flash(d.msg,'success');location.reload();return;}CMSAdminUI.toast(d.msg,'error');CMSAdminUI.setLoading(button,false);}).catch(function(){CMSAdminUI.toast('清理请求失败','error');CMSAdminUI.setLoading(button,false);});});}document.addEventListener('keydown',function(e){if(e.key==='Escape')closeRun();});</script>
