feat: 来源归因规则引擎(渠道/AI/搜索词/诚实状态/历史重算)
- constants 规则库 v1:AI/引擎/社交域名表、搜索词参数白名单、付费点击参数、 应用内 UA 标记、CHANNELS 枚举;page_view 白名单开 nav_type/prerendering - services/attribution.classify 纯函数:付费>AI>引擎>社交>referral 分层, UTM 证据叠加(combined/conflict/label_only),应用内 UA 叠加/兜底, 引擎 TLD 国家 variant,搜索词白名单提取(Google 恒空不虚构) - SourceClassification 派生表(迁移 0004):first_touch/session_entry 双 scope, rule_version 可追溯 - ingest 增量 best-effort:首访客/新会话时分类落库(savepoint+静默, 失败不影响采集);validation 对 page_view referrer 完整 URL 参数清洗+截500 - recompute_attribution 命令:全量/按站幂等重算 - API:访客列表归因五字段+channel 筛选、详情 attribution 块(含最近5会话入口)、 dashboard channels 聚合(sources 原口径保留);TS VisitorListParams 加 channel - 测试 97 → 124(引擎20 + 集成/接口7) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Generated by Django 5.2.17 on 2026-09-16 08:52
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tracker', '0003_rawbatch'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SourceClassification',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('scope', models.CharField(max_length=16)),
|
||||
('channel', models.CharField(blank=True, default='', max_length=32)),
|
||||
('platform', models.CharField(blank=True, default='', max_length=64)),
|
||||
('variant', models.CharField(blank=True, default='', max_length=64)),
|
||||
('search_term', models.CharField(blank=True, default='', max_length=200)),
|
||||
('evidence_kind', models.CharField(blank=True, default='', max_length=32)),
|
||||
('resolution', models.CharField(blank=True, default='', max_length=16)),
|
||||
('detail', models.JSONField(blank=True, default=dict)),
|
||||
('rule_version', models.CharField(default='v1', max_length=16)),
|
||||
('computed_at', models.DateTimeField(auto_now=True)),
|
||||
('session', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='source_classifications', to='tracker.visitsession')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='source_classifications', to='tracker.site')),
|
||||
('visitor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='source_classifications', to='tracker.visitor')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'source_classification',
|
||||
'indexes': [models.Index(fields=['site', 'visitor', 'scope'], name='source_clas_site_id_58ad9c_idx'), models.Index(fields=['site', 'channel'], name='source_clas_site_id_f08b23_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user