Skip to main content
本指南是将 /v1 读取到 Power BI 中的端到端示例,涵盖 Power Query (M) 连接模式、基于 /v1/activities 构建的星型架构语义模型、增量刷新,以及何时使用 reporting 预览数据集获取按人员统计的网络钓鱼成效。 示例使用美国生产主机(https://api.us-east-1.livingsecurity.ai/v1)。对于欧盟组织,请在 Fn_FetchAll 和 Fn_GetJson 中均使用 https://api.eu-west-1.livingsecurity.ai/v1,然后将数据源凭据绑定到该主机。请参阅区域。 本指南使用带有 API 密钥的已存储 Basic 凭据,无需 OAuth 客户端。有关凭据选项,请参阅身份验证;有关访问和设置,请参阅开发者中心。

本示例的适用场景

在以下情况下可使用本示例:
  • 通过正确的游标分页和速率限制重试机制,将 /v1 列表端点加载到 Power BI 数据集中。
  • 构建语义模型,使度量值(如“活跃实体数”“每个活跃实体的活动数”)基于维度计算,而不是对扁平表进行计数。
  • 通过增量刷新,将 Power BI 的计划刷新从遍历全部历史数据缩减为较小的每日时间窗口。
  • 选择正确的网络钓鱼粒度:活动总计(运营)或按人员统计的结果和类型化事件(reporting 预览)。

先决条件

  • 具有查询所需权限的 API 密钥:
    • 运营示例:activity:read、entity:read 和 simulation:read。
    • 通过 reporting 数据集获取按人员统计的网络钓鱼数据:reporting:read。 请参阅 API 密钥。
  • Power BI Desktop,或可创建语义模型并安排刷新的 Power BI / Fabric 工作区。

选择正确的粒度

/v1 为网络钓鱼提供了两个相关但不可互换的接口。 下面的 Power BI 示例查询调用的是运营路由(/v1/simulations、/v1/simulations/{id}/results、/v1/activities、/v1/entities)。对于按人员统计的网络钓鱼报表,建议优先使用下文的 reporting 预览系列。仅被授予 reporting:read 的密钥无法调用运营 /v1/simulation_results(API 返回 403)。

难度等级

模拟难度是 /v1/simulations(difficulty_level)、/v1/simulation_results(difficulty_level)、/v1/activities 属性(difficulty_tier)以及 /v1/reporting/simulation_events(difficulty_tier)上的 1–5 整数。1 最简单,5 最难。自适应和渐进式模拟没有单一的活动级别取值,因此 /v1/simulations.difficulty_level 为 null,而结果中每位收件人的 difficulty_level 仍可设置。若要映射到旧版的低/中/高导出,请将 1–2 视为低,3 视为中,4–5 视为高。
请勿针对未记录的仪表板网络调用或旧版精选的 /api/v1 公共 API 概述构建新的 BI 集成。请使用开发者中心 API 密钥对组织所在区域的 /v1 主机进行身份验证。请参阅区域。

连接模式 (Power Query)

示例中的每个查询都遵循四条规则。每条规则都是由 Power BI 服务强制执行的,而非出于风格偏好。
将所有动态内容放入 RelativePath 和 Query。Web.Contents 的第一个参数必须是以组织所在区域的 /v1 根路径结尾的字面字符串:
  • 美国:https://api.us-east-1.livingsecurity.ai/v1
  • 欧盟:https://api.eu-west-1.livingsecurity.ai/v1
调用方随后仅传入资源("simulations",而非 "v1/simulations")。请勿为欧盟组织粘贴美国主机。如果第一个参数是通过字符串拼接构建的,Power BI 服务将无法静态识别数据源,计划刷新会失败并出现“动态数据源”错误,即使该查询在 Power BI Desktop 中可以正常运行。
当 Power BI 在 /v1 数据源上提示输入凭据时,请选择 Basic,并将 API 密钥粘贴为用户名。在密码字段中填入占位符,例如 x。尽管 API 会忽略该字段,但 Power BI 服务会拒绝空白密码。将密钥作为密码并使用占位符用户名同样可行。请勿在 M 中添加 Authorization 标头。它在您的桌面上可以运行,但会将密钥固化到查询文件中,并与计划刷新冲突。
GET /v1 需要身份验证:使用有效密钥时返回 200,否则返回 401。请将数据源指向 …/v1,并且不要跳过测试——有效密钥可以保存,已撤销或格式错误的密钥会立即失败。旧版示例使用裸主机作为数据源,并告知您跳过测试,因为 GET / 始终为 404。请将这些模型重新指向 …/v1。
每个 /v1 列表端点的默认页大小均为 25。保留默认值会使相同数据的请求数成倍增加。查询值必须是文本——在 M 中使用 limit = "500",而不是 limit = 500。

共享分页函数

示例中的每个查询都调用同一个 Fn_FetchAll,该函数会遍历 next_page_token,直到其为 null(请参阅分页)。其主要特性如下:
  • 静态 /v1 基础 URL,动态 RelativePath 和 Query。 这是计划刷新和凭据测试所必需的。
  • ManualStatusHandling = {429} 并带有有限次数的重试。 该函数在 Power Query 公开 Retry-After 时读取该值,否则等待 30 秒;在连续收到 5 次 429 响应后,会以明确的错误放弃,而不是一直循环直到刷新超时。5xx 特意不进行重试——服务器错误应当显现出来,而不是悄悄生成不完整的数据集。
  • 60 秒 Timeout。 页面通常在不到一秒内响应,因此一分钟没有响应就意味着请求已失效。
  • 在 Json.Document 之前使用 Binary.Buffer。 如果不进行缓冲,当混合引擎多次读取响应流时,可能会重新请求该响应流。
  • 每次迭代都原样传递 baseQuery。 页面令牌是不透明的已签名游标,绑定到组织、凭据、契约版本以及过滤器和排序的指纹。在遍历过程中更改过滤器会被作为无效令牌拒绝,而不会从第一页重新开始。
Fn_FetchAll.pq — base and call shape

每个端点的类型化查询

Fn_FetchAll 的每个使用方都遵循相同的结构:调用端点,投影固定的列列表,丢弃 object 鉴别字段,并将时间戳类型设置为 datetimezone,以免日期度量值因刷新容量的时区而悄然偏移。
在 Table.FromRecords 上使用显式的列列表,可在端点返回零行时保持架构稳定。单独使用 Table.FromRecords({}) 会生成零列的表,之后的每个步骤都会因缺少字段而失败。

Excel

相同的 Power Query (M) 可在 Excel 的 Get & Transform 中运行——它与 Power BI Desktop 使用相同的引擎。当您需要工作表表格而不是语义模型时,请使用此方式。 在 Excel for Windows(Microsoft 365、Excel 2024、2021、2019 或 2016)上:
  1. Data → Get Data → From Other Sources → Blank Query。
  2. 在 Power Query Editor 中,依次选择 首页 → 高级编辑器。先粘贴 Fn_FetchAll,然后将该查询重命名为 Fn_FetchAll(Query Settings → Name,或在 Queries 窗格中右键单击 → Rename)。
  3. 对 Fn_GetJson 重复上述操作,然后对每个报表查询重复。
  4. 首页 → Close & Load → Close & Load To… 会打开 Import Data 对话框。对 Fn_FetchAll 和 Fn_GetJson 选择 Only Create Connection(它们是函数,而不是表)。对于要放在工作表上的每个报表查询,选择 Table。
  5. 首次刷新时,选择 Basic:将 API 密钥作为 User name,将 x 等占位符作为密码。Excel 为每个数据源 URL 存储一份凭据;之后访问相同 /v1 主机的查询会重复使用该凭据。
在工作簿中首次创建查询时,Close & Load To… 可能呈灰色不可用状态。请使用 Close & Load,然后在 Excel 中依次选择 Data → Queries & Connections,右键单击该查询,并选择 Load To。Microsoft 针对这种情况记录了该路径。
在已进行身份验证的数据源上,您无法通过 Power Query 发送 Living-Security-Version——这与 Power BI 的限制相同。请固定服务账号,或在查询记录中传入 api_version。请参阅契约版本和已存储凭据。 现成的报表查询(列映射以及与先前提供商导出内容的差异):

按人员统计的合规情况

人员 × 分配粒度的培训注册情况。

电子邮件网络钓鱼日志

事件级网络钓鱼交互,涵盖最近 12 个月。

最易受攻击的用户

按人员汇总的 12 个月点击、凭据和报告情况。

Reporting 预览数据集(按人员统计的网络钓鱼)

Reporting 预览系列提供用于按人员统计网络钓鱼的数据集。它需要 reporting:read 以及贵组织的报表访问权限。请使用下面的数据集目录查看其架构和数据策略。 查看实时目录:
每个数据集都声明了粒度、主键、关联、水位线、更正和删除策略。

simulation_results

  • 路径: GET /v1/reporting/simulation_results
  • 粒度: 实体 × 模拟
  • 过滤器: simulation、entity、updated_after,以及 limit / page_token
  • 传输对象: reporting.simulation_result
  • 关联键: entity_id、simulation_id
ReportingSimulationResults.pq (sketch)
刷新近期分区时,请将 updated_after 用作包含边界的下限水位线。可变的结果维度会在相同的 id 下原地重述。

simulation_events

  • 路径: GET /v1/reporting/simulation_events
  • 粒度: 每次观察到的交互对应一条类型化事实
  • 过滤器: simulation、entity、event_type、occurred_after,以及 limit / page_token
  • 传输对象: reporting.simulation_event
  • 事件类型: 封闭的公共令牌,例如 simulation.delivered、simulation.opened、simulation.link_clicked、simulation.credentials_submitted、simulation.reported——而不是原始的 ClickHouse phishing.simulation.* 活动类型
  • 设备: device_type 是派生的类别(已观察到的令牌包括 desktop、mobile、tablet、unknown)。原始 user_agent 不在 /v1 上。原始 IP 绝不会出现在 /v1 上(从不持久化;仅保留加盐的 ip_hash)。
ReportingSimulationEvents.pq (sketch)
增量加载事件时,请使用 occurred_after。事实仅追加;延迟到达的数据会作为新行。

entities

  • 路径: GET /v1/reporting/entities
  • 粒度: 每个实体一个当前行
  • 过滤器: type、updated_after,以及 limit / page_token
  • 传输对象: reporting.entity
  • 关联键: id / entity_id(值相同)
使用 reporting.simulation_results.entity_id = reporting.entities.id 将网络钓鱼事实与人员关联。运营 GET /v1/entities 仍在 entity:read 上,并且属于不同的权限。
ReportingEntities.pq

entity_versions 和 cohort_memberships

这两个数据集带有一项明确的仅限当前数据的例外:
No authoritative version or membership-history table exists. This dataset projects the current row as one open-ended interval (valid_to is null, is_current is true). valid_from is created_at or else updated_at for entity versions, and the request observation time for cohort memberships. Closed historical intervals are not fabricated.
  • entity_versions: GET /v1/reporting/entity_versions。过滤器与 entities 相同。valid_from 为 created_at,否则为 updated_at。两个时间戳均为 null 的实体将被省略。
  • cohort_memberships: GET /v1/reporting/cohort_memberships。过滤器:cohort、entity、updated_after。updated_at 是群组的最后更新时间,使增量刷新保持准确。valid_from 是请求观察时间。将 entity_id 关联到 reporting.entities.id。
ReportingEntityVersions.pq
ReportingCohortMemberships.pq

基于 /v1/activities 的星型架构语义模型

/v1/activities 是组织带时间戳的事件流。它没有数值型度量值——报表中的每个数量都是行数——这使得星型架构成为天然的选择。
  • Activities 是真实粒度的事件事实表,每个活动一行。参与者在此处被移除,并重建为独立的表。
  • ActivityParticipants 之所以存在,是因为参与者确实是一对多的,具有 actor 和 target 等角色。将其展平到 Activities 上,要么会重复活动行(使所有计数虚高),要么会丢弃额外的参与者。与 Activities 的关系是该模型中唯一的双向关系,因此实体切片器可以作用于事实表。
  • Entities 是用于命名参与者的维度。活动上的参与者行带有 entity_id 和角色,但不带显示名称。
  • Dates 在 M 中生成(零次 API 调用),并被标记为模型的日期表。这使得 DAX 时间智能分析(DATESINPERIOD、DATEADD)得以合法使用。月份和星期名称列在其数字对应列上声明了 sortByColumn,以免视觉对象按字母顺序排列月份。
所有度量值均为显式定义(模型设置了 discourageImplicitMeasures):活动总数、活跃实体数、每个活跃实体的活动数、有活动的天数,以及 7 天滚动日均值。

桥接表结构

ActivityParticipants 遍历 /v1/activities,并将 participants 数组展开为每个参与者一行:
ActivityParticipants.pq
ActivityParticipants 独立遍历 /v1/activities,而不是引用 Activities 查询。在 Power BI 中,加载为两张表的两个查询会在刷新时各自独立获取数据。保持它们相互独立,可使服务并行刷新两张表的分区。

增量刷新

两个事实粒度的表(Activities 和 ActivityParticipants)都声明了 Power BI 增量刷新策略:保留 5 年的按年分区,并在每次刷新时重新导入最近 14 天的数据。 其机制是一对 DateTime 参数 RangeStart 和 RangeEnd,Power BI 服务会在刷新时为每个分区重新绑定它们。每个事实查询都会将这些参数折叠到 API 的 start_date 和 end_date 过滤器中,因此分区级别的请求量与分区大小成正比,而不是与全部历史数据量成正比。该查询会在两侧各将 API 时间窗口放宽一分钟,并在本地重新应用精确的左闭右开 [RangeStart, RangeEnd) 边界,因此分区的正确性不取决于服务器将其边界视为包含还是不包含。
Activities.pq — the fold that makes incremental refresh work
部署后的首次刷新会应用该策略:完整的历史加载,速度较慢。此后的每次刷新只会处理增量窗口。 在包含约 158,000 个活动和约 2,300 个实体的数据集上的测量结果:
Power BI 经典刷新 REST 端点(仅带有 notifyOption 的 POST .../refreshes)会悄然忽略刷新策略,并且每次都执行完整遍历。若要通过 REST API 应用该策略,请使用增强刷新:在请求正文中发送 type、commitMode 和 applyRefreshPolicy。由 Power BI 服务计划程序触发的刷新始终会应用策略——此特殊情况仅限于 REST 调用。
14 天的窗口是有意设置得较为宽裕的。活动摄取并非只在当前时间追加:连接器回填会以过去的时间戳写入事件。1–2 天的窗口将永久错过这些事件。

将刷新窗口扩展到 API

GET /v1/activities 接受 Power BI 参数折叠到的 start_date 和 end_date 过滤器,并在这两个边界之间过滤 event_ts。Entities 是一个维度,因此本示例每次都对其进行完整刷新,而不是增量刷新。 Reporting 数据集使用各自的水位线(updated_after、occurred_after),而不是活动日期窗口。

刷新期间的速率限制

/v1 分别强制执行每个密钥和每个 IP 的预算(请参阅速率限制)。请遵循 429 + Retry-After;不要在模型中硬编码请求预算。Power BI 刷新有两个重要特性:
  • 单个表的遍历是顺序进行的。 每个页面请求都需要上一页的令牌,因此无论页大小如何,单个表都受延迟限制。
  • 并发的表刷新会叠加。 多个大表并行刷新会触及上限,这就是 Fn_FetchAll 中的 429 重试路径非常重要的原因。与其假定并发不受限制,不如减少并行的表数量。
该重试机制已针对真正饱和的限流器进行过验证。当并行工作负载有意将密钥维持在其上限时,运行的刷新仍然完成,并生成了与无竞争基线相同的行数——重试返回的是完整的页面,而不是缺口。

契约版本和已存储凭据

/v1 接受 Living-Security-Version 请求标头,用于固定已发布的契约版本(请参阅 API 快速入门)。当数据源使用已存储凭据时,Power Query 无法发送该标头——只有匿名数据源才允许自定义标头。添加该标头会导致刷新失败,并出现:
请将密钥保存在 Power BI 的已存储凭据中,并省略自定义标头。如果请求中没有显式版本,API 会使用服务账号已固定的 contract_version;如果未设置固定版本,则使用最新发布的版本。在发布刷新之前,请向您的 API 管理员确认该账号的契约版本。有关受支持的版本,请参阅 API 变更日志。 如果刷新开始出现意外行为,请检查 API 的 Living-Security-Version 响应标头,以查看实际由哪个契约处理了请求。

刷新并验证您的模型

将语义模型发布到您的 Power BI 工作区,将其已存储凭据绑定到区域 /v1 数据源,并在启用计划之前运行一次刷新。请检查刷新历史记录中是否存在失败或读取不完整的情况。 使用相同的过滤器,将行数与独立的 API 读取结果进行核对。分页指南包含用于读取完整结果集的 Python 和 cURL 示例。仅读取第一页的刷新在小型数据集上可能看起来是成功的;在依赖报表之前,请先比较计数。

模型选择

  • 活动总计与按人员统计的结果粒度不同。 SimulationResults 调用 GET /v1/simulations/{id}/results(每次运行一个 GET)。对于按人员统计的网络钓鱼数据,请为 GET /v1/reporting/simulation_results 和 GET /v1/reporting/simulation_events 添加查询(如果您已持有 simulation:read,也可以使用运营 GET /v1/simulation_results)。
  • /v1/simulations 上的 vectors 被展平为以逗号连接的字符串。 列表类型的列无法作为真正的维度进行切片。如果报表页面需要按向量过滤,请添加模拟到向量的桥接表。

后续步骤

开发者中心

为 Power BI 创建服务账号和 API 密钥。

API 快速入门

使用 Basic 身份验证对 /v1 请求进行身份验证。

分页

列表信封、游标令牌,以及遍历过程中的过滤器稳定性。

速率限制

每个密钥和每个 IP 的预算、429 约定以及重试指南。