跳到主要内容

Blacklist:执行反馈、卸载历史与查询

黑名单设备执行反馈与时序记录​

设备拿到策略并不代表已经执行。卸载操作与杀进程操作有不同反馈粒度:卸载保存一次事件明细,杀进程保存设备按日期上报的次数。前者在 MySQL,后者在 TDengine;后台根据策略类型选择查询来源,再适配成统一的结果展示。

需要实际查找某策略、某设备或某时间窗口的数据时,见分表、索引与查询指引。该页区分当前 Mapper 条件与建议 SQL,特别说明 MySQL 入库时间筛选不等于设备时间筛选。

卸载反馈:事件级 MySQL 记录​

task 的设备事件服务收到 BLACK_OP_CALL_BACK,将黑名单 ID 列表、MAC、CPU、事件 ID 和设备时间发到 kafka.topic.black-call-back。blacklist 的 BlackCallbackConsumer 拆成每个策略一条 flow_blacklisted_device,然后发送 ApkUninstallUpdateEvent。这个后续事件默认主题为 apk-uninstall-update,可由 kafka.topic.apk-uninstall-update 覆盖。

已核对的下游是 report 的 ApkUninstallUpdateConsumer:它调用推送历史服务补充 CPU 与卸载时间,不应写成 device 服务已经更新安装清单。blacklist 构造该事件时使用当前服务时间作为卸载时间;明细里的 device_time 则来自设备上报。这两种时间不同,跨模块对账时要保留来源。

消费者对每条策略单独捕获异常并继续,MySQL 写入后才发送后续事件。源码没有在这里建立 MySQL 与 Kafka 的原子事务,因此“有明细”不能证明 report 已更新,“无明细”也不能反推设备未执行。本次只证实源码链路与物理结构,未发送消息或验证消费结果。

Java 字段映射列Java 类型业务含义
ididLong明细主键;实库 int、自增
blacklistedIdblacklisted_idLong本次执行对应的策略 ID
macmacString设备 MAC
cpucpuString设备 CPU 标识,与 MAC 共同定位设备
eventIdevent_idInteger设备上报事件标识;消费者从消息 ID 转换
deviceTimedevice_timeDate设备侧发生时间;上报时间不小于 0 才赋值
createTimecreate_timeDate插入时填充的服务侧记录时间

该 DO 不继承 BaseDO,无软删除、租户和额外审计字段。实测索引为 PRIMARY(id)、唯一索引 blacklisted_id(blacklisted_id, mac, cpu, event_id, device_time);不能将它缩写成“每设备每策略只有一条”。两个末尾字段可空,空值情况下也不能据此宣称严格的全量去重保证。常见查询按策略、MAC、CPU 和创建时间过滤;后台禁止直接编辑反馈数据。

杀进程反馈:按天的 TDengine 记录​

task 将 BLACK_KILL_CALL_BACK 发到 kafka.topic.black-kill-call-back。blacklist 消费者逐项读取上报日期、策略 ID、frequency,补充设备身份与接收时间,经 AppKillRecordMapper 写入 blacklisted.app_kill_record。frequency 是上报的计数值,消费者直接赋值写入,没有在这里做 旧值 + 新值 的累加。

业务粒度是“一个策略下,一台设备在某天的杀进程反馈”。ts 由上报的 yyyy-MM-dd 解析;create_time 是服务器消费时间。时区没有从本次结构采集中运行验证,不应把两者换算成未经核实的 UTC 日界线。

Java 字段物理字段或定位角色Java 类型实测类型 / 含义
tstsDateTIMESTAMP;上报日期
dataKeydata_keyStringVARCHAR(1000),实测标注 COMPOSITE KEY;getter 返回 mac + cpuId
macmacStringVARCHAR(20);设备 MAC
cpuIdcpu_idStringVARCHAR(255);设备 CPU 标识
frequencyfrequencyIntegerINT;该设备上报的杀进程次数
createTimecreate_timeDateTIMESTAMP;接收时间
blackListIdblack_list_idIntegerINT TAG;策略 ID 的标签
tbname子表定位信息,不是 DESCRIBE 的普通列Stringgetter 返回 app_kill_record_{blackListId}

实测共有 6 个数据字段加 1 个标签,46 张子表,无普通表。data_key 的拼接顺序以 getter 为准:MAC 在前、CPU 在后,中间没有分隔符,不能照旧注释写成 CPU+MAC。复合键中的日期/设备维度和策略子表一起说明数据身份;本轮没有通过重复写入试验验证数据库更新语义。

子表数量来自统一元数据统计,不等于有效策略数量:配置可删除,已有 TDengine 数据不会因此删除。采集没有逐张输出子表名称和 tag 值,因此 46 张子表是否全部符合当前命名规则尚未逐项比对;命名规则是源码预期,超级表结构与数量是实测事实。

查询和统计不要混用口径​

后台结果页在 type=1 时查 TDengine,否则查 MySQL。MySQL 的设备发生时间在响应中适配为 ts;TDengine 排序适配会把 cpu 改为 cpuId,移除不存在的 eventId。这些响应兼容字段不是要求两个存储具有完全相同的列。

页面 opCount 在卸载策略上取 MySQL COUNT,在杀进程策略上取 TDengine COUNT。后者是记录条数,不是 SUM(frequency),也不是去重设备数。应将“配置覆盖设备数、反馈记录数、杀进程次数”分别解释,避免用同一指标描述业务效果。

删除策略时会清理其 MySQL 反馈;审批处理明确不清理 TDengine 反馈。因此历史统计不能假设两个引擎有相同的生命周期或完整保留策略主表关联。

核验证据​

blacklist 源码基准:ik_project/yudao-cloud/yudao-module-blacklist/yudao-module-blacklist-biz/src/main/java/cn/iocoder/yudao/module/blacklist/。

  • kafka/consumer/BlackCallbackConsumer.java:34、:41、:62:卸载入库与后续事件。
  • kafka/consumer/BlackKillCallbackConsumer.java:39、:62:日期和频次赋值。
  • dal/dataobject/blacklisted/FlowBlacklistedDeviceDO.java:21、AppKillRecordDO.java:19、:69:全部字段及计算 getter。
  • dal/tdengine/AppKillRecordMapper.java:22:数据源与过滤条件;service/blacklisted/AppKillRecordServiceImpl.java:28、:61:写入及计数。
  • service/blacklisted/AppBlacklistedServiceImpl.java:912、:938;controller/admin/blacklisted/AppBlacklistedController.java:148:结果页路由及统计口径。
  • service/blacklisted/AppBlackListedBpmServiceImpl.java:471:删除配置时处理 MySQL 反馈,不清理 TDengine。
  • ik_project/yudao-cloud/yudao-module-task/yudao-module-task-biz/src/main/java/cn/iocoder/yudao/module/task/service/deviceevent/DeviceEventServiceImpl.java:184、:194:两类事件生产者。
  • ik_project/yudao-cloud/yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/kafka/consumer/ApkUninstallUpdateConsumer.java:47、:71:后续卸载更新的接收方。

物理结构见 MySQL 实测字典和 TDengine 实测字典。核验日期:2026-09-18;未验证消息运行状态、设备实际结果或跨服务最终一致性。

黑名单分表、索引与查询指引​

先确定要查的是“策略配置”“卸载事件”还是“按天杀进程次数”,再选存储与时间口径。卸载事件用 MySQL ik_apk.flow_blacklisted_device;杀进程反馈用 TDengine blacklisted.app_kill_record。两者字段相似,但时间过滤、数据粒度和索引组织不同,不能直接套用同一条查询。

本页索引名称与字段顺序来自 2026-09-18 的结构快照,查询条件来自当前 Mapper。没有执行查询或 EXPLAIN;下述索引可用前缀是结构分析,不是已观测的执行计划或耗时承诺。示例 SQL 是独立分析建议,不代表当前接口已经使用这些 SQL。

MySQL:按业务场景选索引前缀​

以下索引均为实测 BTREE;组合索引只有从首列开始连续约束,才具备通常的连续前缀定位条件。范围条件后的列仍可能参与过滤,但不能笼统视为同样有效的前缀范围定位;最终是否采用索引由执行计划决定。

业务场景表与当前条件可对应的实测索引查询边界
查看单项策略app_blacklisted.idPRIMARY(id)可按主键定位;包名、status、valid、bpm_status、create_time 没有额外实测索引
某策略全部卸载反馈或记录数flow_blacklisted_device.blacklisted_idUNIQUE blacklisted_id(blacklisted_id, mac, cpu, event_id, device_time) 的首列可从策略维度缩小候选;统计是反馈条数,不是去重设备数
某策略下某台设备的卸载反馈blacklisted_id、mac、cpu上述组合索引的前三列即使没有 event_id,也可形成前三列等值前缀;create_time 需另外过滤
精确定位某次卸载事件blacklisted_id、mac、cpu、event_id、device_time上述组合索引的全部五列最后两列可空,不能把唯一键理解成所有空值组合也严格去重
某策略下某设备的设备发生时间范围blacklisted_id、mac、cpu、device_time 范围最多先用前三列;若再提供 event_id 等值,才形成 device_time 之前的连续前缀event_id 缺失时,不可跳过第四列并宣称第五列完成连续范围定位;当前 Mapper 没有此设备时间过滤
某时间段入库的卸载反馈create_time 范围,可附策略/设备create_time 不在任何实测索引中若有策略 ID,可先利用策略前缀;只有时间时没有对应时间起始索引
跨策略查某台设备所有卸载mac、cpu,可附时间无 mac 或 cpu 起始的实测索引不能借用首列 blacklisted_id 的组合索引保证按设备直达;先明确是否确需跨策略全量
查某策略绑定的兼容 MACflow_blacklisted_mac.blacklisted_id,可附 mac 或 validUNIQUE blacklistId_mac(blacklisted_id, mac);INDEX blacklistId_vaild(blacklisted_id, valid)根据实际条件区分两条索引;不要把名字中的 vaild 当成物理列名
由 MAC 反查兼容策略flow_blacklisted_mac.mac,可附 validINDEX mac_vaild(mac, valid)当前回源使用 valid != 0;首列 MAC 可定位,后续不等条件的利用由优化器决定
查渠道/地区绑定flow_blacklisted_channel / region 的 blacklisted_id、channel_id 或 region_id两表均仅 PRIMARY(id)当前业务按关联字段查,但没有对应组合索引;不能说关系字段天然有索引

app_uninstall_detail 与 flow_blacklisted_flow 实测也仅有 PRIMARY(id),当前 blacklist Java/XML 未发现其查询入口,不能照当前反馈路径给历史表虚构索引使用场景。完整索引见 MySQL 实测字典。

当前接口实际过滤什么时间​

FlowBlacklistedDeviceMapper.selectPage 接收 blacklistedId/mac/cpu 等值条件,以及 createTime 对 create_time 的 BETWEEN 过滤。它把排序字段 ts 改成 device_time,响应又把 device_time 显示为 ts;但 WHERE 中没有使用 pageReqVO.ts。因此“按设备时间排序”不等于“按设备时间筛选”,按当前接口的 createTime 查到的是入库时间窗口。

下面是按“服务侧入库窗口”查询某策略某设备的分析示例。? 为绑定参数,占位值需由调用方提供,未执行;示例用半开区间避免相邻窗口重复计数,不同于当前 Mapper 的 BETWEEN 两端包含语义。

SELECT id, blacklisted_id, mac, cpu, event_id, device_time, create_time
FROM ik_apk.flow_blacklisted_device
WHERE blacklisted_id = ? AND mac = ? AND cpu = ?
AND create_time >= ? AND create_time < ?
ORDER BY create_time, id;

该示例可以借助组合索引的前三列缩小设备候选;create_time 的过滤与排序不因此变成有时间索引支持。若业务要问“设备在哪天执行”,应改为设备时间口径并另行评估查询/索引,不能只换展示标题。本次没有修改接口或增加索引。

TDengine:策略子表、TAG 与时间窗口​

维度当前结构 / 源码应如何查询
超级表blacklisted.app_kill_record;实测 46 张子表多策略或通用入口查询超级表,不把子表数当成当前有效策略数
策略black_list_id 是唯一实测 TAG,INT已知策略时显式带 black_list_id;当前 Mapper 就使用此条件
子表命名getter 为 app_kill_record_{blackListId}单策略直查是可选分析方式;先核实子表真实存在且标签匹配,不能把未经确认的拼接名当实测表名
业务日期ts,首个 TIMESTAMP,由设备上报日期解析按“哪一天执行”优先明确 ts 窗口;并先确认业务时区及日期边界
接收时间create_time,另一 TIMESTAMP排查迟报/补报用接收窗口;它不是业务日期,也不能等同于首时间列的物理组织
设备mac、cpu_id 是普通数据列两项共同过滤设备;本次没有采集到它们存在额外二级索引的证据
记录身份data_key VARCHAR(1000),实测 COMPOSITE KEYgetter 为 mac+cpuId;它不是 TAG,不要把“复合键”推广成任意 mac/cpu 条件都有索引

当前 AppKillRecordMapper 查询超级表模型,支持 black_list_id/mac/cpu_id 等值过滤,且可分别对 ts 与 create_time 使用 BETWEEN。当前查询没有拼接子表名执行 SELECT;子表 getter 是写入定位模型的一部分。TAG 条件、时间条件可以表达更窄的业务范围,但本轮未采集 TDengine 索引清单、vgroup、文件时间分段参数或执行计划,不能写成已证明的“按某标签索引命中”“只读一个物理分区”。

按策略、设备和业务日期取反馈的分析示例:

SELECT ts, black_list_id, mac, cpu_id, frequency, create_time
FROM blacklisted.app_kill_record
WHERE black_list_id = ? AND mac = ? AND cpu_id = ?
AND ts >= ? AND ts < ?
ORDER BY ts;

例中选择半开日期窗口是分析建议;当前接口仍按 BETWEEN 处理范围。只给 create_time 表示“此时收到哪些反馈”,只给 ts 表示“这些业务日期有什么反馈”,两者同时给出则要求两种窗口同时满足,可能排除迟到上报。

统计和分表/分区不能混为一谈​

要回答的问题统计口径已实现还是建议
某卸载策略有多少反馈MySQL 记录 COUNT当前 opCount 按策略统计,未附时间范围
某杀进程策略有多少反馈记录TDengine 记录 COUNT当前 opCount 按 TAG 统计,未附时间范围
指定日期窗口上报的杀进程次数合计在明确策略/设备/ts 范围后 SUM(frequency)分析建议;不是当前 opCount。需确认上报频次与重复/更正语义,不能声称等于所有真实执行次数
有多少台设备反馈按 mac+cpu 设备组合去重独立统计需求;不等于上述 COUNT 或 SUM,也不是当前 opCount

MySQL 当前代码使用固定表名,未发现本服务按日期或设备拆 MySQL 表的规则。2026-09-18 21:32:41(Asia/Shanghai)补采 information_schema.PARTITIONS,按 TABLE_SCHEMA=ik_apk AND PARTITION_NAME IS NOT NULL 查询返回空,状态为成功:本次可见的 ik_apk 7 张表未发现 MySQL 声明式分区。所以不能把时间过滤描述成“按日期裁剪 MySQL 分区”,也不能从复合索引推断存在分区。

TDengine 是按策略 ID 组织子表、以 ts 保存时序数据,这与 MySQL 的 PARTITION BY 不是同一机制。46 张 TDengine 子表不代表 46 个 MySQL 分区,也不代表 46 个物理节点;其底层时间文件分段和节点分布本轮未采集。

证据位置​

源码基准为 ik_project/yudao-cloud/yudao-module-blacklist/yudao-module-blacklist-biz/src/main/java/cn/iocoder/yudao/module/blacklist/。

  • dal/mysql/blacklisted/FlowBlacklistedDeviceMapper.java:18:分页条件与 ts 排序转换。
  • dal/mysql/blacklisted/FlowBlacklistedMacMapper.java:35:MAC 范围查询条件。
  • dal/tdengine/AppKillRecordMapper.java:33:TAG、设备、两类时间条件。
  • dal/dataobject/blacklisted/AppKillRecordDO.java:69:子表命名与 data_key 拼接。
  • service/blacklisted/AppKillRecordServiceImpl.java:61、AppBlacklistedServiceImpl.java:1174:当前两类 COUNT。
  • 物理结构:MySQL 字典、TDengine 字典。
  • MySQL 分区补采:query-layout/mysql-layout-ik_apk.json;查询定义为 doc_code/docs-develop/database-design/tools/MysqlMetadata.java:60,只记录非空 PARTITION_NAME 的声明式分区。

核验日期:2026-09-18,仅源码和已采集元数据;未连接数据库、运行示例 SQL、修改索引或调整分表规则。

用户文档
AI 助手
Agent 列表
请选择一个 Agent 开始对话
AI 问答