<?php
require_once dirname(__DIR__,2).'/core/lib/redirect_manager.php';cmsEnsureRedirectSchema($db);$ruleTable=$db->table('redirect_rule');$notFoundTable=$db->table('not_found_log');$action=trim((string)($_GET['action']??'list'));
if($_SERVER['REQUEST_METHOD']==='POST'){requireAdminCsrfToken(in_array($action,['delete','toggle','clear404'],true));
if($action==='save'){$id=intval($_POST['id']??0);$source=cmsNormalizeRedirectSource($_POST['source_path']??'');$target=cmsNormalizeRedirectTarget($_POST['target_path']??'');$code=intval($_POST['status_code']??301)===302?302:301;$enabled=isset($_POST['is_enabled'])?1:0;
if($source===''||$source==='/'||$target===''||cmsNormalizeRedirectSource($target)===$source){echo '<script>alert("来源或目标路径无效，且不能跳转到自身");history.back();</script>';exit;}
$duplicate=$db->fetch("SELECT id FROM {$ruleTable} WHERE source_path=? AND id<>? LIMIT 1",[$source,$id]);if($duplicate){echo '<script>alert("该来源路径已存在规则");history.back();</script>';exit;}
$rules=$db->fetchAll("SELECT id,source_path,target_path,is_enabled FROM {$ruleTable} WHERE id<>?",[$id]);if($enabled&&cmsRedirectGraphHasCycle($rules,$source,$target)){echo '<script>alert("检测到循环跳转，请调整目标路径");history.back();</script>';exit;}
$data=['source_path'=>$source,'target_path'=>$target,'status_code'=>$code,'is_enabled'=>$enabled,'created_by'=>intval($_SESSION['admin_id']??0),'update_time'=>time()];if($id){$db->update('redirect_rule',$data,'id=?',[$id]);}else{$data['create_time']=time();$db->insert('redirect_rule',$data);}addLog($id?'修改':'添加','跳转管理',($id?'修改':'添加').'跳转 '.$source.' → '.$target);echo '<script>alert("保存成功");location.href="?act=redirect";</script>';exit;}
if($action==='delete'||$action==='toggle'){while(ob_get_level()>0)ob_end_clean();header('Content-Type: application/json; charset=utf-8');$id=intval($_POST['id']??0);$rule=$db->fetch("SELECT * FROM {$ruleTable} WHERE id=?",[$id]);if(!$rule){echo json_encode(['code'=>404,'msg'=>'规则不存在']);exit;}if($action==='delete'){$db->delete('redirect_rule','id=?',[$id]);$msg='规则已删除';addLog('删除','跳转管理','删除跳转 '.$rule['source_path']);}else{$enabled=intval($rule['is_enabled'])?0:1;if($enabled){$others=$db->fetchAll("SELECT id,source_path,target_path,is_enabled FROM {$ruleTable} WHERE id<>?",[$id]);if(cmsRedirectGraphHasCycle($others,$rule['source_path'],$rule['target_path'])){echo json_encode(['code'=>409,'msg'=>'启用后会形成循环跳转']);exit;}}$db->update('redirect_rule',['is_enabled'=>$enabled,'update_time'=>time()],'id=?',[$id]);$msg=$enabled?'规则已启用':'规则已停用';addLog('设置','跳转管理',$msg.' '.$rule['source_path']);}echo json_encode(['code'=>200,'msg'=>$msg],JSON_UNESCAPED_UNICODE);exit;}
if($action==='clear404'){while(ob_get_level()>0)ob_end_clean();header('Content-Type: application/json; charset=utf-8');$id=intval($_POST['id']??0);$db->delete('not_found_log','id=?',[$id]);addLog('删除','跳转管理','清除 404 记录 ID '.$id);echo json_encode(['code'=>200,'msg'=>'记录已清除'],JSON_UNESCAPED_UNICODE);exit;}}
if($action==='add'||$action==='edit'){$rule=['id'=>0,'source_path'=>trim((string)($_GET['source']??'')),'target_path'=>'','status_code'=>301,'is_enabled'=>1];if($action==='edit'){$rule=$db->fetch("SELECT * FROM {$ruleTable} WHERE id=?",[intval($_GET['id']??0)]);if(!$rule){echo '<script>alert("规则不存在");location.href="?act=redirect";</script>';exit;}}?>
<div class="page-container fade-in redirect-form-page"><div class="page-header"><div class="page-title"><div class="page-icon"><i class="fa fa-random"></i></div><div><h1><?php echo $action==='edit'?'编辑跳转':'新增跳转'; ?></h1><p>来源仅允许站内路径，目标禁止外部域名</p></div></div><a class="btn btn-secondary" href="?act=redirect">返回</a></div><form method="post" action="?act=redirect&action=save"><input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(getAdminCsrfToken()); ?>"><input type="hidden" name="id" value="<?php echo intval($rule['id']); ?>"><div class="form-section"><div class="form-section-header">跳转规则</div><div class="form-section-body"><div class="form-group"><label>来源路径</label><input class="form-control" name="source_path" required value="<?php echo htmlspecialchars($rule['source_path']); ?>" placeholder="/old-page.html"><small>不含域名，例如 /old-page.html</small></div><div class="form-group"><label>目标路径</label><input class="form-control" name="target_path" required value="<?php echo htmlspecialchars($rule['target_path']); ?>" placeholder="/article/1.html"><small>仅允许本站相对路径，可包含查询参数</small></div><div class="form-row"><div class="form-group"><label>状态码</label><select class="form-control" name="status_code"><option value="301" <?php echo intval($rule['status_code'])===301?'selected':''; ?>>301 永久跳转</option><option value="302" <?php echo intval($rule['status_code'])===302?'selected':''; ?>>302 临时跳转</option></select></div><div class="form-group"><label><input type="checkbox" name="is_enabled" value="1" <?php echo intval($rule['is_enabled'])?'checked':''; ?>> 保存后立即启用</label></div></div></div></div><div class="form-actions"><button class="btn btn-success btn-lg">保存规则</button><a class="btn btn-secondary btn-lg" href="?act=redirect">取消</a></div></form></div><?php return;}
$rules=$db->fetchAll("SELECT * FROM {$ruleTable} ORDER BY id DESC");$notFound=$db->fetchAll("SELECT * FROM {$notFoundTable} ORDER BY hit_count DESC,last_seen_at DESC LIMIT 100");$enabledCount=0;$hits=0;foreach($rules as $r){$enabledCount+=intval($r['is_enabled']);$hits+=intval($r['hit_count']);}$notFoundHits=0;foreach($notFound as $n)$notFoundHits+=intval($n['hit_count']);?>
<div class="page-container fade-in redirect-page"><div class="page-header"><div class="page-title"><div class="page-icon"><i class="fa fa-random"></i></div><div><h1>404 与跳转</h1><p>维护旧链接跳转规则并发现高频未命中路径</p></div></div><a class="btn btn-primary" href="?act=redirect&action=add">新增规则</a></div><div class="redirect-summary"><div><small>跳转规则</small><strong><?php echo count($rules); ?></strong><span>条</span></div><div><small>已启用</small><strong><?php echo $enabledCount; ?></strong><span>条</span></div><div><small>跳转命中</small><strong><?php echo $hits; ?></strong><span>次</span></div><div><small>404 访问</small><strong><?php echo $notFoundHits; ?></strong><span>最近 100 个路径</span></div></div>
<section class="redirect-card"><div class="redirect-card-head"><div><small>REDIRECT RULES</small><h2>跳转规则</h2></div></div><div class="redirect-table-wrap"><table class="data-table"><thead><tr><th>来源路径</th><th>目标路径</th><th>类型</th><th>状态</th><th>命中</th><th>最后命中</th><th>操作</th></tr></thead><tbody><?php if(!$rules): ?><tr><td colspan="7" class="redirect-empty">暂无跳转规则</td></tr><?php endif; ?><?php foreach($rules as $r): ?><tr><td><code><?php echo htmlspecialchars($r['source_path']); ?></code></td><td><code><?php echo htmlspecialchars($r['target_path']); ?></code></td><td><?php echo intval($r['status_code'])===302?'302 临时':'301 永久'; ?></td><td><span class="redirect-state is-<?php echo intval($r['is_enabled'])?'on':'off'; ?>"><?php echo intval($r['is_enabled'])?'启用':'停用'; ?></span></td><td><?php echo intval($r['hit_count']); ?></td><td><?php echo intval($r['last_hit_at'])?date('Y-m-d H:i',$r['last_hit_at']):'-'; ?></td><td><a class="btn btn-primary" href="?act=redirect&action=edit&id=<?php echo $r['id']; ?>">编辑</a><button class="btn btn-secondary" onclick="ruleAction('toggle',<?php echo $r['id']; ?>)"><?php echo intval($r['is_enabled'])?'停用':'启用'; ?></button><button class="btn btn-ghost" onclick="ruleAction('delete',<?php echo $r['id']; ?>)">删除</button></td></tr><?php endforeach; ?></tbody></table></div></section>
<section class="redirect-card"><div class="redirect-card-head"><div><small>NOT FOUND</small><h2>高频 404 路径</h2></div><span>最多显示 100 个路径</span></div><div class="redirect-table-wrap"><table class="data-table"><thead><tr><th>请求路径</th><th>次数</th><th>首次出现</th><th>最近出现</th><th>来源页面</th><th>操作</th></tr></thead><tbody><?php if(!$notFound): ?><tr><td colspan="6" class="redirect-empty">尚无 404 记录</td></tr><?php endif; ?><?php foreach($notFound as $n): ?><tr><td><code><?php echo htmlspecialchars($n['request_path']); ?></code></td><td><strong><?php echo intval($n['hit_count']); ?></strong></td><td><?php echo date('Y-m-d H:i',$n['first_seen_at']); ?></td><td><?php echo date('Y-m-d H:i',$n['last_seen_at']); ?></td><td class="redirect-ref"><?php echo htmlspecialchars($n['last_referrer']?:'-'); ?></td><td><a class="btn btn-primary" href="?act=redirect&action=add&source=<?php echo urlencode($n['request_path']); ?>">创建跳转</a><button class="btn btn-secondary" onclick="clear404(<?php echo $n['id']; ?>)">清除</button></td></tr><?php endforeach; ?></tbody></table></div></section></div>
<style>.redirect-page{max-width:1500px}.redirect-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.redirect-summary>div{padding:16px 18px;border:1px solid #e2e8f0;border-radius:14px;background:#fff}.redirect-summary small,.redirect-summary span{display:block;color:#94a3b8;font-size:11px}.redirect-summary strong{display:inline-block;margin:5px 7px 2px 0;color:#1e293b;font-size:23px}.redirect-card{margin-bottom:18px;border:1px solid #dfe6ef;border-radius:16px;background:#fff;overflow:hidden}.redirect-card-head{display:flex;justify-content:space-between;align-items:center;padding:18px 20px;border-bottom:1px solid #edf2f7}.redirect-card-head small{color:#94a3b8;letter-spacing:.12em}.redirect-card-head h2{margin:3px 0 0;color:#1e293b;font-size:17px}.redirect-card-head>span{color:#94a3b8;font-size:11px}.redirect-table-wrap{overflow-x:auto}.redirect-table-wrap code{color:#334155;font-size:11px;word-break:break-all}.redirect-state{padding:5px 8px;border-radius:999px;font-size:10px;font-weight:800}.redirect-state.is-on{color:#047857;background:#ecfdf5}.redirect-state.is-off{color:#64748b;background:#f1f5f9}.redirect-ref{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#64748b;font-size:11px}.redirect-empty{text-align:center!important;padding:45px!important;color:#94a3b8}@media(max-width:800px){.redirect-summary{grid-template-columns:repeat(2,1fr)}}@media(max-width:500px){.redirect-summary{grid-template-columns:1fr}}</style>
<script>function redirectPost(action,id){return fetch('?act=redirect&action='+action,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'id='+id+'&csrf_token=<?php echo rawurlencode(getAdminCsrfToken()); ?>'}).then(function(r){return r.json();}).then(function(d){alert(d.msg);if(d.code===200)location.reload();});}function ruleAction(action,id){if(action==='delete'&&!confirm('确定删除这条跳转规则？'))return;redirectPost(action,id);}function clear404(id){if(confirm('确定清除这条 404 记录？'))redirectPost('clear404',id);}</script>
