买家真实性验证 · 短链方案

591 生成短链 → 中介转发给买家 → 买家点击 → 采集设备信息,用于去重与轻过滤,把中介回报的「N 组带看」收敛成「M 个不同装置确认开启」
建立日期 2026-09-02 | 修订 2026-09-02(修正链路方向与目标定位)| 状态:方案设计完成,五端实测待做
问题
中介提交的买家信息无从核对
方案
短链埋点 → 设备去重 + 轻过滤
不做
不做身份认证,不索取买家电话
当前进展
待实测 五端采集能力
1. 问题与能力边界
先把这套方案能做什么、不能做什么钉死,避免后面拿它当证据用

591 在屋主委托链路上做媒合:屋主提交委托 → 平台分发给中介 → 中介带看、回报。买家只出现在中介的回报里,平台手上没有任何独立信息可以核对。中介回报「本周带看 5 组」,这个 5 目前是纯声明。

中介不会把买家电话交给平台 —— 那是他的客户资源,也涉及个资。所以方案的输入只有:中介提交的买家条目(自填标识)+ 一条平台生成的短链。

能力边界:链接经中介的手,就证明不了买家存在 短链由中介转发,中介随时可以自己点、找同事点、拿备用机点。因此这套方案不能证明「买家是真人」,也不能作为处罚依据。

它能做的是三件更小但有用的事:① 同一装置的重复条目合并;② 剔除中介自己的装置;③ 过滤脚本、模拟器、点开就关。 定位是「把水分挤掉一层」的过滤器,不是防伪证明。文档后面所有设计都按这个定位来。

产出的核心指标

claimed_count
中介回报数
clicked_count
有点击的
unique_device
去重后装置数
effective_count
过滤后有效数

条形为示意比例,实际分布待灰度数据。中介评级、线索分发若要用带看量,应该用 effective_count 而不是 claimed_count

2. 链路
591 生成 → 中介转发 → 买家点击 → 平台采集判定
STEP 1
中介提交买家
自填标识(代号/姓氏/
带看时间),无需电话
STEP 2
591 生成短链
token 绑定
中介ID + 买家ID + 房源ID
STEP 3
中介转发
LINE 为主
平台不接触买家
STEP 4
买家点击
加载即采集
不要求任何动作
STEP 5
去重 + 过滤
算出 effective_count
回写委托时间线
这个定位有一个很大的好处:落地页可以做到零打扰 因为不需要证明身份,就不需要 LINE 授权弹窗、不需要登录、不需要买家做任何动作。 页面加载完即完成全部采集,转化率损耗只剩「点不点开」这一层。 如果目标是身份认证,光授权弹窗就会掉掉一半以上,方案根本跑不起来。

买家为什么会点

中介转发的东西,买家点开率取决于对买家有没有用。落地页应该是一个买家本来就想看的页面,采集是副产品:

关键 让中介有动机转发,比让买家有动机点击更重要 —— 中介不转发,这条链路根本不启动。所以短链必须做成「中介本来就要做的事」的更好版本,不能是额外负担。

3. 短链设计
token 怎么构造、映射什么、采集分几段

3.1 URL 形态

https://591.tw/v/{token}          # 验证短链
https://591.tw/v/aK9x2Qm4f7Bd     # 示例,12 字符

3.2 token 构造:随机 + HMAC 校验位,不自解释

token = base62(random_48bit)  ||  base62(HMAC-SHA256(secret, random)[0:3])
         └── 8 字符,2.8e14 空间 ──┘     └──── 4 字符校验位 ────┘

长度 12。服务端先本地验校验位,不通过直接 404,不查库。
设计选择理由
不用自签名 payload(JWT 之类)URL 变长且不可撤销;一旦泄露等于泄露「中介ID + 买家ID + 房源ID」的映射,业务数据挂在网上
不用自增 ID base62可枚举。爬到别人的链接就能污染他人数据,也能反推平台的委托量
加 HMAC 校验位扫描者不掌握 secret,构造不出合法 token。绝大多数扫描流量在不查库的情况下被挡掉,同时保护 DB
随机 48 bit 而非 UUIDUUID base62 要 22 字符,转发到 LINE 里太长;48 bit + 速率限制足够
一条买家一个 token去重的前提。若一个中介一条通用链接,就分不出是哪个买家点的,整个方案失效

3.3 映射表

短链不携带任何语义,映射全放服务端,可随时撤销、改绑、加限制。

-- 短链主表:token → (中介, 买家条目, 房源, 委托)
CREATE TABLE verify_link (
  token         CHAR(12)     PRIMARY KEY,   -- 随机 + HMAC,不可枚举
  entrust_id    BIGINT       NOT NULL,      -- 委托单
  property_id   BIGINT       NOT NULL,      -- 房源
  agent_id      BIGINT       NOT NULL,      -- 中介:结果归因到他
  buyer_ref_id  BIGINT       NOT NULL,      -- 中介提交的买家条目
  purpose       ENUM('viewing','offer','lead') NOT NULL,
  status        ENUM('created','sent','clicked','judged','expired','revoked'),
  max_clicks    SMALLINT     DEFAULT 10,    -- 允许重复点;超额是风险信号,不阻断
  created_at    DATETIME     NOT NULL,
  expires_at    DATETIME     NOT NULL,      -- 建议 14 天,给中介转发留余裕
  KEY idx_agent (agent_id, created_at),
  KEY idx_entrust (entrust_id)
);

-- 中介提交的买家条目:不含电话,中介不给我们也不要
CREATE TABLE buyer_ref (
  buyer_ref_id  BIGINT PRIMARY KEY,
  agent_id      BIGINT NOT NULL,
  label         VARCHAR(64),                -- 中介自填:代号 / 姓氏 / "王先生"
  claimed_at    DATETIME,                   -- 中介声称的带看时间
  claimed_type  VARCHAR(20),
  created_at    DATETIME
);

-- 点击事件:一次点击一行,不覆盖,全部留痕
CREATE TABLE verify_click (
  click_id      BIGINT PRIMARY KEY,
  token         CHAR(12) NOT NULL,
  ts            DATETIME(3) NOT NULL,
  -- 网络层(302 阶段即可得,无需 JS)
  ip            VARBINARY(16),
  ip_asn        INT,
  ip_geo        VARCHAR(32),
  ua_raw        VARCHAR(512),
  ja4           CHAR(36),                   -- TLS 指纹,抗 UA 伪造
  referer       VARCHAR(255),
  -- 指纹层(落地页 JS 回传,加载即采集,无需用户动作)
  fp_hash       CHAR(64),                   -- Canvas+WebGL+Audio+字体+屏幕 综合
  cookie_id     CHAR(32),                   -- first-party,1 年
  ls_id         CHAR(32),                   -- localStorage 备份,抗 cookie 清除
  screen        VARCHAR(24),
  tz            VARCHAR(40),
  ua_ch         JSON,                       -- User-Agent Client Hints
  channel       ENUM('pc','touch','ios','android','line'),
  -- 身份层(有就记,不主动索取、不弹窗)
  x591_user_id  BIGINT,                     -- 恰好登录着就能拿到
  line_user_id  VARCHAR(48),                -- 仅 LIFF 形态,本方案不强求
  app_device_id VARCHAR(64),                -- 恰好装了 App 且被唤起
  -- 行为
  dwell_ms      INT,
  interacted    TINYINT,
  KEY idx_token (token, ts),
  KEY idx_fp (fp_hash),
  KEY idx_cookie (cookie_id)
);

-- 去重与过滤结果:一条 buyer_ref 一行
CREATE TABLE verify_result (
  buyer_ref_id  BIGINT PRIMARY KEY,
  device_key    CHAR(64),                   -- 归一化后的装置标识,去重就靠它
  clicked       TINYINT,
  is_duplicate  TINYINT,                    -- 与同委托其他条目撞装置
  dup_of        BIGINT,                     -- 合并到哪条
  is_filtered   TINYINT,                    -- 命中过滤规则
  filter_reason JSON,                       -- 命中了哪些规则,供中介申诉时解释
  judged_at     DATETIME
);

3.4 device_key:去重的基准

去重的准确度全看 device_key 取得稳不稳。按可靠性降序取第一个非空值:

优先取值稳定性可得率
1app_device_id最稳。装了 591 App 且被 Universal Link / App Links 唤起才有
2x591_user_id稳,且是「人」不是「装置」。买家恰好登录着 591 才有中低
3cookie_id / ls_id较稳。同浏览器内长期有效;跨浏览器、跨 App WebView 失效;iOS 受 ITP 7 天限制
4fp_hash + IP /24弱。同型号手机会撞;换网络会变。只作兜底最高
误差方向是「去重不足」,这反而是好事 上面几种标识的失效方式都是「同一个人被算成两个装置」(清了 cookie、换了浏览器、iOS 满 7 天), 极少出现「两个人被算成同一装置」。所以 unique_device系统性偏高, 也就是偏向对中介有利

这个性质要保住:宁可漏杀不可错杀。中介被少算带看量会立刻投诉,被多算不会有人来说。

3.5 两段式采集,全程无感

阶段时机拿到什么买家感知
① 网络层 短链 302 之前,服务端记录 IP、ASN、粗地理、UA、Accept-Language、JA4 TLS 指纹、Referer、点击时刻 无感
② 指纹层 落地页加载后 JS 异步 POST /v/collect Canvas/WebGL/AudioContext 哈希、字体、屏幕、时区、UA-CH、first-party cookie、localStorage ID、停留与交互;顺带读到的 591 登录态 无感(隐私政策须告知)

没有第三段。不做 LIFF 授权弹窗、不做登录引导、不做 App 强制唤起 —— 那些属于身份认证方案,不在本次目标内,且会显著压低点击转化。

3.6 状态机

created ──(中介转发)──> sent ──(首次点击)──> clicked ──┐
   │                                                      │
   │                                    (T+14d 或批量任务) ↓
   │                                                    judged ──> 回写 effective_count
   └──(中介撤回/争议/风控)──> revoked      expired ──> clicked = 0
4. 五端实测矩阵
前置条件 / 预期可采集信息 / 实测结果 —— 预期列为设计推断,实测列待填

4.1 全端共通(无前置条件,302 阶段即可得)

IP + ASN + 粗地理、User-Agent、Accept-Language、JA4 TLS 指纹、Referer、点击时间戳。任何端都拿得到,也任何端都能伪造(改 UA、挂 VPN),只当辅助信号。

4.2 分端明细

接收端 前置条件 预期可采集信息 对去重的影响 实测
PC 端
桌面浏览器
· 买家在桌面点开
· JS 启用
· 非隐私窗口(否则 cookie 不持久)
共通层 +
· first-party cookie(1 年)+ localStorage ID
· Canvas / WebGL / AudioContext 指纹
· 字体列表、screen、devicePixelRatio、时区
· hardwareConcurrency、deviceMemory
· UA-CH:platform / arch / model
· 恰好登录时的 591 UserID
去重可靠 cookie 持久、指纹熵高。
macOS Safari 受 ITP,JS 写的 cookie 上限 7 天 → 跨周去重会漏
待测
触屏端
平板 / 触屏笔电
· 同 PC
· 走系统浏览器而非 App WebView
PC 端全部,外加:
· maxTouchPoints、pointer: coarse
· orientation、devicePixelRatio
· 虚拟键盘引起的 viewport 变化
指纹熵最低 同型号 iPad 之间几乎无差异, fp_hash 会撞 → 必须靠 cookie,兜底层不可用 待测
iOS 端
Safari / WKWebView
· 点开链接即可
· App device_id 需已装 591 App 且 Universal Link 生效(本方案不强求)
共通层 +
· 基础指纹(熵低)
· first-party cookie(受限)
· 恰好登录时的 591 UserID
· 恰好唤起 App 时的 IDFV
去重最弱
· ITP:document.cookie ≤ 7 天;无交互的 localStorage 7 天清除
· UA 不报机型,只报 iPhone
· Canvas 加噪 / 锁定模式更严
· iCloud 私人转送 → IP 变 Apple 中继,ASN 无参考价值
待测
安卓端
Chrome / WebView
· 点开链接即可
· App device_id 需已装 591 App 且 App Links 校验通过(本方案不强求)
共通层 +
· 指纹熵最高(GPU 型号分散、UA-CH 报机型)
· cookie 无 ITP 限制,持久性最好
· 恰好唤起 App 时的 SSAID / OAID
去重最可靠
但也是模拟器 / 群控最集中的一端 → 过滤规则要在这端最严
待测
LINE 端
主场景
中介转发就是发 LINE,这一端占比会最高

· 无额外前置条件
· 买家在 LINE 内建浏览器直接打开
· 不做 LIFF 授权(本方案不需要身份)
共通层 + 基础指纹
· UA 含 Line/x.y.z → 可识别来源渠道
· LINE WebView 内的 first-party cookie
· 底层仍是 iOS WKWebView / Android WebView, 能力随宿主系统

备选 若后续要拿 LINE userId,需建 LINE Login channel + LIFF app + profile 授权,属于身份认证方案,不在本期
跨端去重会漏
LINE 内建浏览器的 cookie 存在独立容器,与系统浏览器不互通: 同一个人在 LINE 里点一次、又在 Safari 里点一次,会被算成两个装置
待测
实测要回答的三个问题,按重要性排序 1)五端的 fp_hash 碰撞率是多少? 拿真实设备测。撞得多,兜底层就只能当负向信号用(没撞 → 确实是不同装置;撞了 → 不一定是同一人)。
2)LINE 内建浏览器的 cookie 存活多久? 这一端流量占比最高,cookie 活不久,去重就基本只剩指纹。
3)iOS 的 cookie 7 天后还剩多少? 决定跨周去重能不能做,也决定「同装置跨中介」这条过滤规则的覆盖面。

4.3 测试链接与覆盖率指标

测试域名   https://591.xx/v/{token}
测试 token 每端预生成 20 条,token → (中介ID, 买家ID) 映射预置在 verify_link
落地页     /v/landing?t={token},加载后 POST /v/collect
回收       按 channel 分组,导出 verify_click 全字段做覆盖率统计

覆盖率指标(每端分别统计):
  click_rate      = 点击数 / 转发数
  fp_rate         = 拿到 fp_hash / 点击数
  cookie_rate     = 拿到 cookie_id / 点击数
  device_key_rate = 拿到任一级 device_key / 点击数        ← 决定去重覆盖面
  fp_collision    = 不同真人撞同一 fp_hash 的比例          ← 决定兜底层能不能用
  cookie_ttl      = cookie 实际存活天数(分端)            ← 决定跨周去重
5. 去重与过滤规则
采集只是原料,这一节才是产品

5.1 去重

维度规则处理
同委托内 同一 device_key 在同一 entrust_id 下被报为多个 buyer_ref 合并 保留最早一条,其余 is_duplicate=1。同一套房不会同一台手机来看两次还算两组
同中介跨房源 同一 device_key 在同一 agent_id 名下出现在 > 5 套不同房源 标记 不合并 —— 真买家找同一个中介看多套完全正常。超阈值只进人工抽查队列
跨中介 同一 device_key 出现在 ≥ 3 个不同 agent_id 标记 同样不合并。买家货比三家很正常;但若集中在同一房源的多个中介,是同行互点的信号

5.2 过滤

类别规则强度处理
中介自身 device_key 命中该中介自己的登录装置(591 中介后台 / 中介 App 的登录设备记录) 极高 剔除
点击 IP 与该中介近 30 天常用 IP 重合(含门店 WiFi 出口)标记
device_key 命中同门店其他中介的登录装置中高标记
环境异常 WebGL renderer = SwiftShader / llvmpipe(模拟器特征)剔除
Android UA 但 maxTouchPoints = 0;或 UA 与 UA-CH 自相矛盾剔除
数据中心 ASN / 已知 VPN 出口标记
行为异常 停留 < 2s 且无滚动无交互 —— 「点开就关」标记
同一中介名下多条 token 在极短时间窗内被集中点击(脚本特征)剔除
只有「极高 / 高」强度的规则做剔除,其余一律只标记 每条规则都有假阳性:夫妻共用一台平板会撞装置;一个真买家看十套房也会跨中介出现; 办公室 WiFi 会撞 IP;买家在捷运上点开随手关掉也很正常。

filter_reason 必须留下命中的全部规则,中介申诉时能逐条解释。 直接扣分而给不出理由,比不做更伤中介侧的信任,而中介是这条链路的启动方 —— 他不转发,数据就是零。

5.3 中介设备库:这套方案里最硬的一条信号

「点击装置 = 中介自己的装置」是唯一一条几乎没有假阳性的规则,也是这套方案能真正挤掉水分的地方。而它的数据来源是现成的 —— 中介登录 591 中介后台和中介 App 的设备记录本来就有。

建议 这一条优先于五端实测做技术验证。如果 agent_device 关联不上,方案的过滤能力就只剩模拟器和脚本检测,价值会打对折。

5.4 呈现

6. 隐私与合规
采集装置资讯,台湾《个人资料保护法》适用
7. 进展与下一步
事项状态说明 / 产出
1问题定义与能力边界完成本文 §1。定位为去重 + 轻过滤,不是防伪证明
2短链 token 设计与表结构完成本文 §3,含 device_key 优先级
3中介设备库关联验证待做优先级最高。验证 agent_device 能否与 verify_click 的 device_key 对上;跨域是主要风险,建议短链与主站同域
4五端采集能力实测待做填 §4 矩阵实测列。优先跑 LINE 端(流量占比最高)与 fp 碰撞率
5落地页产品形态待做做成「中介本来就要发给买家的房源资料页」,让中介有动机转发
6去重阈值与过滤规则待做依赖第 4 项的碰撞率与 cookie 存活数据才能定阈值
7灰度与评估待做指标:click_rate、device_key_rate、effective/claimed 比值分布、中介申诉率
最大的开放风险:中介不转发,方案就没有数据 这条链路的启动方是中介,而中介是被这套机制约束的人 —— 他没有动机主动去做一件会让自己带看量变少的事。

所以第 5 项(落地页形态)不是锦上添花,是方案能不能跑起来的前提: 短链必须做成「中介本来就要发资料给买家」这个动作的更好版本。 如果它只是一个多余的验证步骤,转发率会很低,click_rate 低到没有统计意义, 最后 effective_count 反映的是「哪个中介配合度高」,而不是「哪个中介带看真」。
591 屋主委托 · 买家真实性验证 | 2026-09-02