Initial commit

This commit is contained in:
2025-11-18 03:36:49 +08:00
commit d17c7efb3c
7078 changed files with 831480 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
{% extends 'base.html' %}
{% block title %}运营面板 - 统计与监控{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-3">
<h3>统计与监控</h3>
<div>
<a class="btn btn-outline-secondary" href="{% url 'admin_panel:dashboard' %}">返回概览</a>
</div>
</div>
<form method="get" class="row g-2 mb-3">
<div class="col-md-3">
<input type="number" name="days" value="{{ days }}" class="form-control" placeholder="展示天数默认14" />
</div>
<div class="col-md-2">
<button class="btn btn-primary" type="submit">刷新</button>
</div>
</form>
<div class="row g-3">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">平台每日总流量GB</h5>
<canvas id="platformDailyChart" height="120" class="mb-3" style="width: 100%;"></canvas>
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr><th>日期</th><th>GB</th></tr>
</thead>
<tbody>
{% for r in daily_rows %}
<tr><td>{{ r.day }}</td><td>{{ r.gb }}</td></tr>
{% empty %}
<tr><td colspan="2" class="text-center">暂无数据</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">本月域名流量 Top 20GB</h5>
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr><th>域名</th><th>GB</th></tr>
</thead>
<tbody>
{% for t in top_domains %}
<tr><td>{{ t.domain.name }}</td><td>{{ t.gb }}</td></tr>
{% empty %}
<tr><td colspan="2" class="text-center">暂无数据</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mt-1">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<h5 class="card-title mb-0">异常域名(根据阈值检测)</h5>
<div class="text-muted small">
阈值:倍率 {{ anomaly_config.multiplier }} ×,窗口 {{ anomaly_config.window_days }} 天,最低 {{ anomaly_config.min_gb }} GB
<a href="{% url 'admin_panel:settings' %}">调整参数</a>
</div>
</div>
{% if not anomaly_config.enabled %}
<div class="alert alert-secondary">异常检测已关闭,可在系统设置中开启。</div>
{% endif %}
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr><th>域名</th><th>今日GB</th><th>过去均值GB</th><th>倍率</th><th>查看</th><th>操作</th></tr>
</thead>
<tbody>
{% for a in anomalies %}
<tr class="table-warning">
<td>{{ a.domain.name }}</td>
<td>{{ a.today_gb }}</td>
<td>{{ a.past_avg_gb }}</td>
<td>{% if a.ratio %}×{{ a.ratio }}{% else %}-{% endif %}</td>
<td>
<a class="btn btn-sm btn-outline-primary" href="/domains/{{ a.domain.id }}/">用户侧详情</a>
</td>
<td>
<form method="post" action="{% url 'admin_panel:domain_toggle_suspend' a.domain.id %}" class="d-inline" onsubmit="return confirm('确定切换暂停/恢复该域名吗?');">
{% csrf_token %}
<button class="btn btn-sm btn-outline-warning" type="submit">暂停/恢复</button>
</form>
<a class="btn btn-sm btn-outline-secondary" href="{% url 'admin_panel:domain_switch_plan' a.domain.id %}">切换套餐</a>
<a class="btn btn-sm btn-outline-success" href="{% url 'admin_panel:domain_grant_traffic' a.domain.id %}">流量赠送</a>
</td>
</tr>
{% empty %}
<tr><td colspan="6" class="text-center">暂无异常域名</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function(){
var rows = [
{% for r in daily_rows %}
{ day: '{{ r.day|date:"Y-m-d" }}', gb: {{ r.gb|default:0 }} },
{% endfor %}
];
var el = document.getElementById('platformDailyChart');
if (!el || !rows.length || !window.Chart) return;
var ctx = el.getContext('2d');
var labels = rows.map(function(x){ return x.day; });
var dataGb = rows.map(function(x){ return x.gb || 0; });
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '平台总流量GB',
data: dataGb,
borderColor: '#198754',
backgroundColor: 'rgba(25,135,84,0.15)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: { position: 'bottom' },
tooltip: { callbacks: { label: function(ctx){ return ctx.dataset.label + ': ' + ctx.formattedValue + ' GB'; } } }
},
scales: {
y: { title: { display: true, text: 'GB' } }
}
}
});
});
</script>