refactor: remove action field, rules define kill conditions only
- WARN is auto-detected by RiskCheckEngine (riskPct > 80), not configured in rules - Each rule simply defines: type + metric + condition + threshold + unit -> 封号 - Card always shows red left border and red '封号' arrow, no action selector - Presets reduced to 7 kill-only templates, no WARN presets
This commit is contained in:
@@ -62,7 +62,7 @@
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="p in presets" :key="p.name" :command="p">
|
||||
<div style="font-size:12px">
|
||||
<span :style="{color:actionColor(p.action),fontWeight:600}">{{ p.name }}</span>
|
||||
<span style="font-weight:600">{{ p.name }}</span>
|
||||
<span style="color:#909399;margin-left:4px">{{ typeLabel(p.type) }}</span>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
@@ -75,7 +75,7 @@
|
||||
<!-- 规则卡片列表 -->
|
||||
<div v-if="parsed.length" style="display:flex;flex-direction:column;gap:10px">
|
||||
<div v-for="(rule, idx) in parsed" :key="idx"
|
||||
:class="['rule-card', rule.action.toLowerCase()]"
|
||||
class="rule-card"
|
||||
:draggable="true"
|
||||
@dragstart="onDragStart($event, idx)"
|
||||
@dragover.prevent="onDragOver($event, idx)"
|
||||
@@ -86,18 +86,12 @@
|
||||
<div class="card-header" @dblclick="rule._fold = !rule._fold">
|
||||
<div class="card-header-left">
|
||||
<el-icon class="drag-handle"><Rank /></el-icon>
|
||||
<!-- 操作:封号 / 告警 -->
|
||||
<el-select v-model="rule.action" size="small" @change="sync" style="width:80px">
|
||||
<el-option v-for="x in actionOptions" :key="x.value" :label="x.label" :value="x.value">
|
||||
<span :style="{color:actionColor(x.value),fontWeight:600}">{{ x.label }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<!-- 类型 -->
|
||||
<el-select v-model="rule.type" size="small" @change="sync" style="width:90px">
|
||||
<el-select v-model="rule.type" size="small" @change="sync" style="width:100px">
|
||||
<el-option v-for="x in typeOptions" :key="x.value" :label="x.label" :value="x.value" />
|
||||
</el-select>
|
||||
<!-- 规则名称 -->
|
||||
<el-input v-model="rule.name" size="small" placeholder="规则名称" @input="sync" style="width:200px" />
|
||||
<el-input v-model="rule.name" size="small" placeholder="规则名称" @input="sync" style="width:220px" />
|
||||
</div>
|
||||
<div class="card-header-right">
|
||||
<span class="rule-index">#{{ idx + 1 }}</span>
|
||||
@@ -120,7 +114,7 @@
|
||||
<span class="field-hint">{{ applicableHint(rule.applicable) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 触发条件 -->
|
||||
<!-- 触发条件:当 [指标] [条件] [阈值] [单位] → 封号 -->
|
||||
<div class="field-row">
|
||||
<span class="field-label">触发条件</span>
|
||||
<span class="condition-text">当</span>
|
||||
@@ -133,7 +127,7 @@
|
||||
<el-input v-model.number="rule.threshold" size="small" type="number" placeholder="阈值" @input="sync" style="width:90px" />
|
||||
<el-input v-model="rule.unit" size="small" placeholder="单位" @input="sync" style="width:70px" />
|
||||
<span class="then-arrow">→</span>
|
||||
<span :style="{color:actionColor(rule.action),fontWeight:600}">{{ actionText(rule.action) }}</span>
|
||||
<span style="color:#f56c6c;font-weight:600">封号</span>
|
||||
</div>
|
||||
|
||||
<!-- 规则描述 -->
|
||||
@@ -151,7 +145,7 @@
|
||||
<el-dialog v-model="showPresets" title="PT 站点管理规则模板" width="700px" :close-on-click-modal="false">
|
||||
<div style="margin-bottom:12px;padding:10px 14px;background:#f0f9ff;border-radius:6px;font-size:13px;color:#606266">
|
||||
<p style="margin:0 0 4px;font-weight:500">规则体系:等级豁免 → 账号存活 → 流量监控 → H&R 惩罚 → 分享率检查</p>
|
||||
<p style="margin:0;font-size:12px;color:#909399">满足豁免条件的用户不受任何规则影响。导入后可调整参数和操作类型(封号/告警)。</p>
|
||||
<p style="margin:0;font-size:12px;color:#909399">满足豁免条件的用户不受任何规则影响。系统会自动根据风险百分比触发告警(riskPct > 80%)。</p>
|
||||
</div>
|
||||
<div v-for="cat in presetCategories" :key="cat.title" style="margin-bottom:12px">
|
||||
<div style="font-weight:600;font-size:13px;margin-bottom:6px;color:#303133">{{ cat.title }}</div>
|
||||
@@ -161,7 +155,7 @@
|
||||
<el-checkbox :value="p.name" :label="p.name" style="margin-right:0;height:auto">
|
||||
<span style="font-weight:500;font-size:13px">{{ p.name }}</span>
|
||||
</el-checkbox>
|
||||
<span :style="{color:actionColor(p.action),fontSize:'11px',fontWeight:500}">{{ actionText(p.action) }}</span>
|
||||
<span style="color:#f56c6c;font-size:11px;font-weight:500">封号</span>
|
||||
<span style="color:#909399;font-size:11px;flex:1">{{ presetDesc(p) }}</span>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
@@ -180,10 +174,6 @@ import { Plus, ArrowDown, ArrowUp, Rank, Delete } from '@element-plus/icons-vue'
|
||||
import api from '../api'
|
||||
|
||||
// ============ 选项配置 ============
|
||||
const actionOptions = [
|
||||
{ value: 'KILL', label: '封号' },
|
||||
{ value: 'WARN', label: '告警' },
|
||||
]
|
||||
const typeOptions = [
|
||||
{ value: 'KILL', label: '封号规则' },
|
||||
{ value: 'TRAFFIC', label: '流量监控' },
|
||||
@@ -217,34 +207,24 @@ const metricOptions = [
|
||||
]
|
||||
|
||||
const typeLabel = (t) => typeOptions.find(x => x.value === t)?.label || t
|
||||
const actionColor = (a) => ({ KILL: '#f56c6c', WARN: '#e6a23c' }[a] || '#909399')
|
||||
const actionText = (a) => ({ KILL: '封号', WARN: '告警' }[a] || a)
|
||||
const applicableHint = (a) => {
|
||||
const m = { '!sealed':'未封存用户', 'sealed':'已封存用户', 'zero_traffic':'上传/下载均为 0', 'new_account_30d':'从未完成种子且无流量增量' }
|
||||
return a ? `— ${m[a] || ''}` : ''
|
||||
}
|
||||
|
||||
// ============ 预设规则模板 ============
|
||||
// 每条模板包含 type(监控类型)+ action(KILL/WARN 操作)+ 触发条件
|
||||
const presets = [
|
||||
// 四、账号存活策略
|
||||
{ name:'未封存不登录封号', type:'KILL', action:'KILL', metric:'days_since_last_login', threshold:150, unit:'天', condition:'GT', applicable:'!sealed', description:'未封存账号连续150天不登录即封号' },
|
||||
{ name:'封存账号不登录封号', type:'KILL', action:'KILL', metric:'days_since_last_login', threshold:365, unit:'天', condition:'GT', applicable:'sealed', description:'封存账号连续365天不登录即封号' },
|
||||
{ name:'零流量账号清理', type:'KILL', action:'KILL', metric:'days_since_last_login', threshold:30, unit:'天', condition:'GT', applicable:'zero_traffic', description:'零流量用户连续30天不登录即封号' },
|
||||
{ name:'未封存不登录封号', type:'KILL', metric:'days_since_last_login', threshold:150, unit:'天', condition:'GT', applicable:'!sealed', description:'未封存账号连续150天不登录即封号' },
|
||||
{ name:'封存账号不登录封号', type:'KILL', metric:'days_since_last_login', threshold:365, unit:'天', condition:'GT', applicable:'sealed', description:'封存账号连续365天不登录即封号' },
|
||||
{ name:'零流量账号清理', type:'KILL', metric:'days_since_last_login', threshold:30, unit:'天', condition:'GT', applicable:'zero_traffic', description:'零流量用户连续30天不登录即封号' },
|
||||
// 五、零活跃新用户清理
|
||||
{ name:'零活跃不登录封号', type:'KILL', action:'KILL', metric:'days_since_last_login', threshold:7, unit:'天', condition:'GT', applicable:'new_account_30d', description:'零活跃新用户连续7天不登录即封号' },
|
||||
{ name:'零活跃注册天数封号', type:'KILL', action:'KILL', metric:'days_since_registration', threshold:30, unit:'天', condition:'GT', applicable:'new_account_30d', description:'零活跃新用户注册满30天即封号' },
|
||||
{ name:'零活跃不登录封号', type:'KILL', metric:'days_since_last_login', threshold:7, unit:'天', condition:'GT', applicable:'new_account_30d', description:'零活跃新用户连续7天不登录即封号' },
|
||||
{ name:'零活跃注册天数封号', type:'KILL', metric:'days_since_registration', threshold:30, unit:'天', condition:'GT', applicable:'new_account_30d', description:'零活跃新用户注册满30天即封号' },
|
||||
// 流量监控
|
||||
{ name:'连续无流量封号', type:'TRAFFIC', action:'KILL', metric:'days_since_last_traffic', threshold:90, unit:'天', condition:'GT', description:'连续90天无上传/下载流量即封号' },
|
||||
{ name:'无流量告警', type:'TRAFFIC', action:'WARN', metric:'days_since_last_traffic', threshold:60, unit:'天', condition:'GT', description:'连续60天无流量时发出告警提醒' },
|
||||
// 登录监控
|
||||
{ name:'长期未登录告警', type:'LOGIN', action:'WARN', metric:'days_since_last_login', threshold:30, unit:'天', condition:'GT', description:'超过30天未登录时发出告警提醒' },
|
||||
{ name:'即将触发封号提醒', type:'LOGIN', action:'WARN', metric:'days_since_last_login', threshold:140, unit:'天', condition:'GT', applicable:'!sealed', description:'未封存用户即将达到150天封号线时提前告警' },
|
||||
{ name:'连续无流量封号', type:'TRAFFIC', metric:'days_since_last_traffic', threshold:90, unit:'天', condition:'GT', description:'连续90天无上传/下载流量即封号' },
|
||||
// H&R
|
||||
{ name:'H&R 封号', type:'HD', action:'KILL', metric:'hnr_count', threshold:5, unit:'次', condition:'GTE', description:'H&R违规次数达到5次即封号' },
|
||||
{ name:'H&R 告警', type:'HD', action:'WARN', metric:'hnr_count', threshold:3, unit:'次', condition:'GTE', description:'H&R违规次数达到3次发出告警' },
|
||||
// 分享率
|
||||
{ name:'分享率过低告警', type:'RATIO', action:'WARN', metric:'ratio', threshold:1.0, unit:'倍', condition:'LT', description:'分享率低于1.0时发出告警提醒' },
|
||||
{ name:'H&R 封号', type:'HD', metric:'hnr_count', threshold:5, unit:'次', condition:'GTE', description:'H&R违规次数达到5次即封号' },
|
||||
]
|
||||
|
||||
const presetCategories = [
|
||||
@@ -252,8 +232,6 @@ const presetCategories = [
|
||||
{ title:'五、零活跃新用户清理', items: presets.filter(p => p.name.includes('零活跃')) },
|
||||
{ title:'流量监控', items: presets.filter(p => p.type === 'TRAFFIC') },
|
||||
{ title:'H&R 惩罚', items: presets.filter(p => p.type === 'HD') },
|
||||
{ title:'登录提醒', items: presets.filter(p => p.type === 'LOGIN') },
|
||||
{ title:'分享率监控', items: presets.filter(p => p.type === 'RATIO') },
|
||||
]
|
||||
|
||||
const presetDesc = (p) => {
|
||||
@@ -312,7 +290,7 @@ const sync = () => { editorJson.value = JSON.stringify(parsed.value, null, 2) }
|
||||
|
||||
const addRule = () => {
|
||||
parsed.value = [...parsed.value, {
|
||||
type:'KILL', action:'KILL', name:'', metric:'days_since_last_login', threshold:0, unit:'天', condition:'GT',
|
||||
type:'KILL', name:'', metric:'days_since_last_login', threshold:0, unit:'天', condition:'GT',
|
||||
applicable:null, description:'', _fold:false
|
||||
}]
|
||||
}
|
||||
@@ -365,14 +343,13 @@ onMounted(fetchAll)
|
||||
<style scoped>
|
||||
.rule-card {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-left: 4px solid #f56c6c;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
transition: box-shadow .2s;
|
||||
cursor: default;
|
||||
}
|
||||
.rule-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,.08); }
|
||||
.rule-card.kill { border-left: 4px solid #f56c6c; }
|
||||
.rule-card.warn { border-left: 4px solid #e6a23c; }
|
||||
|
||||
.card-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
@@ -390,5 +367,5 @@ onMounted(fetchAll)
|
||||
.field-label { font-weight: 500; color: #606266; font-size: 12px; min-width: 56px; }
|
||||
.field-hint { color: #909399; font-size: 11px; }
|
||||
.condition-text { color: #909399; font-size: 12px; }
|
||||
.then-arrow { color: #409eff; font-size: 16px; font-weight: 700; margin: 0 2px; }
|
||||
.then-arrow { color: #f56c6c; font-size: 16px; font-weight: 700; margin: 0 2px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user