创建自定义集成以将劳动力管理系统与排班同步

概述

将排班(Microsoft Teams 中的计划管理应用)与劳动力管理 (WFM) 系统集成。 此集成允许一线员工直接在排班中查看和管理其日程安排。

本文介绍如何使用Microsoft图形 API创建连接器,以便实现此集成。

可以为单向数据同步或双向数据同步设置集成。

  • 单向同步 (WFM 系统到排班) :在此设置中,WFM系统中的计划数据将同步到排班。 连接器读取WFM系统中的数据,并将其写入 Shifts。 但是,用户在排班中所做的任何更改不会反映在WFM系统中。

  • 双向同步 (WFM 系统和排班) :此设置允许双向同步。WFM系统中的计划数据将同步到排班,用户对排班所做的任何更改将同步回WFM系统。 在将更改写入排班之前,连接器会根据WFM系统强制实施的业务规则验证和批准用户在排班中所做的更改。

注意

如果使用 UKG Pro WFM、Blue Yonder WFM 或 Reflexis WFM,还可以使用托管连接器将 Shifts 与 WFM 系统集成。 若要了解详细信息,请参阅 Shifts 连接器

本文中使用的术语

术语 描述
连接器 在WFM系统和排班之间同步计划数据的应用。
劳动力集成 一个实体,用于定义通信的加密方法、连接器的回调 URL 和要同步的 Shifts 实体。

开始之前

先决条件

  • 根据业务需求确定要同步的数据。
  • 了解Microsoft 标识平台中的身份验证和授权概念。 请参阅 身份验证和授权基础知识
  • 管理员所需的角色:
    • 至少由云应用程序管理员在 Microsoft Entra 管理中心 中注册应用
    • 用于注册劳动力集成的全局管理员

熟悉集成过程

下面是集成步骤的概述。 查看此信息以了解整个过程,包括执行每个步骤的人员。

步骤 单向同步 双向同步 执行此步骤的人员
1 创建连接器: 创建连接器: 开发人员版
2 在Microsoft Entra 管理中心中注册应用 在Microsoft Entra 管理中心中注册应用 至少是云应用程序管理员的帐户
3 创建用于同步的团队和计划 创建用于同步的团队和计划 开发人员或 Teams 管理员
4 注册并启用员工集成: 注册并启用员工集成: 步骤 4a:全局管理员
步骤 4b:开发人员

步骤 1:创建连接器

若要创建连接器,请完成以下步骤:

步骤 1a:将排班中所做的更改同步到WFM系统

若要将连接器设置为接收和处理来自排班的请求,需要实现以下终结点:

确定基 URL 和终结点 URL

webhook) (基 URL 为 {url}/v{apiVersion},其中 urlapiVersion注册劳动力集成时在 workforceIntegration 对象中设置的属性。

终结点 URL 的相对路径如下所示:

  • /连接: /connect
  • /更新: /teams/{teamid}/update
  • /读: /teams/{teamid}/read

例如,如果 urlhttps://contosoconnector.com/wfiapiVersion1

  • 基 URL 为 https://contosoconnector/com/wfi/v1
  • /connect 终结点为 https://contosoconnector/wfi/v1/connect
  • /update 终结点为 https://contosoconnector/wfi/v1/teams/{teamid}/update
  • /read 终结点为 https://contosoconnector/wfi/v1/teams/{teamid}/read

加密

所有请求都使用 AES-256-CBC-HMAC-SHA256 进行加密。 注册劳动力集成时,可以指定共享密钥。 发回班次的响应不应加密。

终结点

POST /connect

Shifts 会在 注册劳动力集成时调用此终结点来测试连接。 仅当此终结点返回 HTTP 200 OK 响应时,才会返回成功响应。

示例

请求
ConnectRequest

{
   "tenantId": "a1s2s355-a2s3-j7h6-f4d3-k2h9j4mqpz",
   "userId": "4fbc12d7-1234-56ef-8a90-bc123d45678f"
}

响应
返回 HTTP 200 OK

POST /teams/{teamid}/update

班次调用此终结点,以便在为劳动力集成启用的计划中对排班实体进行更改时获得批准。 如果此终结点批准请求,则更改将保存在排班中。

由于WFM系统是记录系统,因此当连接器收到对此终结点的请求时,它应首先尝试在WFM系统中进行更改。 如果更改成功,则返回成功。 否则返回失败。

班次针对每个更改 (包括从连接器/WFM系统) 发起的更改)调用此终结点。 如果连接器使用 图形 API 向 Shifts 发送了更新并添加了X-MS-WFMPassthrough: workforceIntegratonId标头,则传入此终结点的请求将具有相同的标头,这允许你适当地识别和处理这些请求。 例如,返回成功,但不在WFM系统中进行与冗余相同的更改,并可能导致连接器停滞在无限循环中。

下图显示了数据流。

显示从排班到WFM系统的更新流的关系图。

注意

有关请求和响应模型的详细信息,请参阅本文终结点参考部分中的 WfiRequest

返回响应代码
来自集成的任何响应(包括错误)都必须具有 HTTP 响应代码 200 OK。 响应正文必须具有反映相应子调用错误状态的状态和错误消息。 来自集成以外的 200 OK 任何响应都被视为错误,并返回到调用方 (客户端或Microsoft Graph) 。

如果要设置单向同步,请将 Shifts 设置为只读

对于单向同步,必须将排班设置为只读,以便用户无法在排班中进行更改。 若要使班次为只读,请返回来自排班的所有请求的失败响应。

例如,若要阻止用户对计划中的班次进行更改,此终结点必须在收到有关 shift 实体的请求时返回失败响应。

示例

请求
WfiRequestContainer

以下示例显示了来自班次的请求,该请求询问是否可以在班次中保存其 ID 为 SHFT_12345678-1234-1234-1234-1234567890ab 且具有 正文中列出的属性的班次。 当用户在排班中创建班次时,会触发此请求。

{
  "requests": [
    {
      "id": "SHFT_12345678-1234-1234-1234-1234567890ab",
      "method": "POST",
      "url": "/shifts/SHFT_12345678-1234-1234-1234-1234567890ab",
      "headers": {
        "X-MS-Transaction-ID": "1",
        "X-MS-Expires": "2024-10-11T21:27:59.0134605Z"
      },
      "body": {
        "draftShift": {
          "activities": [],
          "isActive": true,
          "startDateTime": "2024-10-12T15:00:00.000Z",
          "endDateTime": "2024-10-12T17:00:00.000Z",
          "theme": "Blue"
        },
        "isStagedForDeletion": false,
        "schedulingGroupId": "TAG_a3e0b3f1-4a5c-4c2e-8eeb-5b8c3d1e3f8b",
        "userId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "createdDateTime": "2024-10-11T21:27:28.762Z",
        "lastModifiedDateTime": "2024-10-11T21:27:28.762Z",
        "lastModifiedBy": {
          "user": {
            "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
            "displayName": "Adele Vance"
          }
        },
        "id": "SHFT_12345678-1234-1234-1234-1234567890ab"
      }
    }
  ]
}

响应
WfiResponse

成功:返回 HTTP 200 OK

此示例显示终结点批准请求时返回的响应。 在此方案中,班次保存在排班中,用户可以在计划中查看班次。

{
  "responses": [
    {
      "id": "SHFT_12345678-1234-1234-1234-1234567890ab",
      "status": 200,
      "body": {
        "eTag": "3f4e5d6c-7a8b-9c0d-1e2f-3g4h5i6j7k8l",
        "error": null,
        "data": null
      }
    }
  ]
}

失败:返回 HTTP 200 OK

此示例显示终结点拒绝请求时返回的响应。 在此方案中,用户在排班中收到“无法添加班次”错误消息。

{
    "responses": [
        {
            "id": "SHFT_12345678-1234-1234-1234-1234567890ab",
            "status": 500,
            "body": {
                "error": {
                    "code": "500",
                    "message": “Could not add the shift”
                },
                "data": null
            }
        }
    ]
}
POST /teams/{teamid}/read

此终结点处理来自排班的请求,以便为用户的交换请求提取符合条件的休假原因或符合条件的班次。

注意

自 2024 年 10 月起,此终结点仅在Microsoft图形 API的 beta 版本中受支持。 注册劳动力集成时,还必须指定 eligibilityFilteringEnabledEntities 属性的值。

下图显示了数据流。

显示资格筛选请求流的关系图。

返回响应代码
来自集成的任何响应(包括错误)都必须具有 HTTP 响应代码 200 OK。 响应正文必须包含反映相应子调用错误状态的状态和错误消息。 来自集成以外的 200 OK 任何响应都被视为错误,并返回到调用方 (客户端或Microsoft Graph) 。

示例:TimeOffReason

请求

以下示例显示了来自班次的请求,该请求询问用户 (用户 ID aa162a04-bec6-4b81-ba99-96caa7b2b24d) 的休假原因。 当用户在排班中请求休假时,会触发此请求。

 { 
  "requests": [ 
    { 
      "id": "aa162a04-bec6-4b81-ba99-96caa7b2b24d", 
      "method": "GET", 
      "url": "/users/aa162a04-bec6-4b81-ba99-96caa7b2b24d/timeOffReasons?requestType=TimeOffReason"
    } 
  ] 
}

响应
成功:返回 HTTP 200 OK

以下响应显示用户的合格休假原因 ID 为“TOR_29f4a110-ae53-458b-83d6-00c910fe2fbc”和“TOR_8c0e8d07-ac1a-48dc-b3af-7bc71a62ff7d”。 在此方案中,用户在排班中看到相应的休假原因可供选择。

{
    "responses": [ 
      { 
        "id": "aa162a04-bec6-4b81-ba99-96caa7b2b24d", 
        "status": 200, 
        "body": { 
          "data": [ 
            "TOR_29f4a110-ae53-458b-83d6-00c910fe2fbc", 
            "TOR_8c0e8d07-ac1a-48dc-b3af-7bc71a62ff7d" 
          ], 
          "error": null 
          } 
        }
    ]
}

失败:返回 HTTP 200 OK

在此示例中,返回错误响应是因为连接器无法访问WFM系统来检索用户的休假原因。

 {
  "responses": [
    {
      "id": "aa162a04-bec6-4b81-ba99-96caa7b2b24d",
      "status": 503,
      "body": {
        "data": null,
        "error": {
          "code": "503",
          "message": "Could not reach WFM"
        }
      }
    }
  ]
}
示例:SwapRequest

请求

以下示例显示了来自班次的请求,该请求询问哪些班次符合交换条件,其 ID 为 2024-4a66-83ea-1bbbf81ac029(该班次的 ID 为 SHFT_5e2b51ac-dc47-4a66-83ea-1bbbf81ac029)的班次交换 10-01T04:00:00.0000000Z 和 2024-11-01T03:59:59.9990000Z。

{
  "requests": [
    {
      "id": "SHFT_5e2b51ac-dc47-4a66-83ea-1bbbf81ac029",
      "method": "GET",
      "url": "/shifts/SHFT_5e2b51ac-dc47-4a66-83ea-1bbbf81ac029/requestableShifts?requestType=SwapRequest&startTime=2024-10-01T04:00:00.0000000Z&endTime=2024-11-01T03:59:59.9990000Z"
    }
  ]
}

响应
成功:返回 HTTP 200 OK

以下响应显示,该班次可与 ID 为 SHFT_98e96e23-966b-43be-b90d-4697037b67af 的班次交换。

{ 
  "responses": [ 
    { 
      "id": "SHFT_5e2b51ac-dc47-4a66-83ea-1bbbf81ac029", 
      "status": 200, 
      "body": { 
        "data": ["SHFT_98e96e23-966b-43be-b90d-4697037b67af"],
        "error": null, 
      } 
    }
  ]
}

失败:返回 HTTP 200 OK

在此示例中,返回错误响应是因为连接器无法访问WFM系统来检索用户的交换请求的合格班次。

{
  "responses": [
    {
      "id": "SHFT_5e2b51ac-dc47-4a66-83ea-1bbbf81ac029",
      "status": 503,
      "body": {
        "data": null,
        "error": {
          "code": "503",
          "message": "could not reach WFM"
        }
      }
    }
  ]
}

步骤 1b:将数据从WFM系统同步到排班

使用 Microsoft Graph 中的排班 API 从WFM系统读取计划数据,并将数据写入排班。

例如,若要将班次添加到班次,请使用 创建班次 API。

请参阅排班管理下列出的排班 API 图形 API v1.0 Microsoft参考

注意

标头 MS-APP-ACTS-AS 在请求中是必需的,并且必须包含应用代表的用户的 ID (GUID) 。 建议在更新计划时使用团队所有者的用户 ID。

下图显示了数据流。

显示将数据从WFM系统同步到排班的流程的关系图。

初始同步

对于第一次同步,连接器应读取WFM系统中的数据,并将数据写入排班。 建议同步两周的未来数据。

初始同步后

首次同步后,可以选择:

  • 使用WFM系统中的更改同步更新班次:针对WFM系统中所做的每一项更改,向排班发送更新。

  • 使用WFM系统中的更改异步更新班次:通过写入特定时间范围内WFM系统中发生的所有更改来执行定期同步 (例如,) 10 分钟) 班次。

    对 Shifts 的所有写入操作(包括连接器发起的写入操作)都会触发对连接器的 /update 终结点的调用。 建议将所有写入调用的标头包括在内 X-MS-WFMPassthrough: workforceIntegratonId ,以便连接器可以适当地识别和处理它们。 例如,如果WFM系统启动了更改,请在不对WFM系统应用更新的情况下批准更改。

    注意

    如果要为WFM系统和排班之间的双向数据同步设置连接器,请排除定期同步中从排班发起的更改。这些更改已写入排班。

步骤 2:在Microsoft Entra 管理中心中注册应用

按照以下步骤在 Microsoft 标识平台中为连接器注册应用,配置应用访问 Microsoft Graph 的权限,并获取访问令牌。

  1. 至少以云应用程序管理员身份登录到Microsoft Entra 管理中心。

  2. 注册应用。 有关步骤,请参阅使用 Microsoft 标识平台注册应用程序

  3. Schedule.ReadWrite.All应用程序权限 分配给应用,以便进行仅限应用的访问权限,并获取访问令牌。

    有关分步指南,请参阅 在没有用户的情况下获取访问权限

    访问令牌验证你的应用是否有权使用 Schedule.ReadWrite.All 权限使用自己的标识调用 Microsoft Graph。 它必须包含在请求的 Authorization 标头中。

步骤 3:创建用于同步的团队和计划

在 Teams 中设置要同步的团队。可以使用现有团队或创建新团队。

  1. 在 Teams 中设置团队以与WFM系统中的团队和位置对应。 确保将以下人员添加到每个团队:

    • 作为团队所有者的一线经理。 请确保将标头中的 MS-APP-ACTS-AS 用户添加为每个各自团队的团队所有者。
    • 一线工作人员作为团队成员。
  2. 在每个团队的排班中创建计划。 若要了解详细信息,请参阅 创建或替换计划

  3. 将计划组添加到每个团队的计划。 计划组用于根据团队中的共同特征对员工进行分组。 例如,计划组可以是部门或作业类型。 若要了解详细信息,请参阅 schedulingGroup 资源类型

  4. 将员工添加到每个计划组。 若要了解详细信息,请参阅 替换 schedulingGroup

注意

还可以使用 Teams 管理中心设置团队并将排班部署到团队。 若要了解详细信息,请参阅:

步骤 4:注册并启用员工集成

劳动力集成定义了用于排班和连接器之间通信的加密设置、来自排班的回调的 URL 以及要同步的实体类型。

若要注册并启用劳动力集成,请完成以下步骤:

步骤 4a:在租户中注册劳动力集成

你必须是全局管理员才能执行此步骤。

使用 创建 workforceIntegration API 在租户中注册劳动力集成。

下面是请求的示例。

POST https://graph.microsoft.com/v1.0/teamwork/workforceIntegrations/
{ 
  "displayName": "Contoso integration", 
  "apiVersion": 1, 
  "encryption": { 
    "protocol": "sharedSecret", 
    "secret": "secret-value" 
  }, 
  "isActive": true, 
  "url": "https://contosoconnector.com/wfi", 
  "supportedEntities": "Shift,SwapRequest,UserShiftPreferences,Openshift,OpenShiftRequest,OfferShiftRequest”,
}

有关详细信息,请参阅下表。 若要了解详细信息,请参阅 workforceIntegration 资源类型

属性 更多信息
apiVersion 回调 URL 的 API 版本。 基 URLurl 属性和此属性组成。
加密 协议 设置为 sharedSecret机密值必须正好为 64 个字符。

使用机密解密从排班发送到连接器终结点的加密 JSON 有效负载。 有效负载使用 AES-256-CBC-HMAC-SHA256 进行加密。 你的应用应安全地保留此机密。 例如,在密钥保管库中。
supportedEntities 指定希望连接器支持同步的 Shifts 实体。 当这些实体中的任何一个发生更改时,Shifts 都会调用连接器的 /update 终结点,以便你可以批准或拒绝更改。 有关可能值的列表,请参阅 workforceIntegration 资源类型

注意 此列表是 一个可演变的枚举。 必须使用 Prefer: include-unknown-enum-members 请求标头来获取所有值。
eligibilityFilteringEnabledEntities 注意:从 2024 年 10 月开始,此终结点仅在Microsoft图形 API的 beta 版本中受支持。

指定要连接以支持资格筛选的 Shifts 实体。 可能的值是:
  • none:空列表
  • SwapRequests:排班调用连接器的 /read 终结点,以获取用户可为交换请求选择的已筛选的排班列表。
  • TimeOffReasons:Shifts 调用连接器的 /read 终结点,以获取用户请求休假时可以选择的休假原因的筛选列表。
注意 此列表是 一个可演变的枚举。 必须使用 Prefer: include-unknown-enum-members 请求标头来获取所有值。
url 班次回调的劳动力集成 URL。 基 URL 由此属性和 apiVerson 属性组成。

步骤 4b:为团队计划启用劳动力集成

根据要管理的计划启用员工集成。 为此,请使用 创建或替换计划 API 创建或更新团队的计划。

下面是请求的示例。

POST https://graph.microsoft.com/v1.0/teams/{teamId}/schedule
{
  enabled: true,
  timezone: “America/New_York”,
  workforceIntegrationIds: [ “workforceIntegrationId”]
}
  • 指定 注册劳动力集成时生成的 workforceIntegrationId。
  • 一个计划最多可以启用一次员工集成。 如果在请求中包含多个 workforceIntegrationId,则使用第一个。

疑难解答

Connector

当连接器响应来自 Shifts 的请求时,如果返回 200 以外的响应代码,会发生什么情况? 如果在响应正文中返回 200 以外的状态,是否会有所作为?

这两种方案之间存在差异。

  • 如果连接器返回 200 以外的响应代码,则 Shifts 会尝试多次重试 /read 和 /update 终结点。 最终,排班显示“出现问题。 团队中的劳动力集成设置响应了无效数据。“错误消息。
  • 如果连接器在响应正文中返回 200 以外的状态,则 Shifts 将显示“出错。 很抱歉,无法完成更改,“错误消息并停止重试终结点。

如果连接器在响应正文中返回无效数据,会发生什么情况?

Shifts 尝试多次重试 /read 和 /update 终结点。 最终,排班显示“出现问题。 团队上设置的劳动力集成响应了无效数据。“错误消息。

如何实现确定请求最初是在排班还是WFM系统中发出,以防止无限循环?

X-MS-WFMPassthrough: workforceIntegratonId 标头添加到所有写入和更新调用,以标识/忽略连接器触发的更改。 此标头用于指示由于连接器先前调用而发出请求,该调用图形 API将数据从WFM系统同步到排班。

劳动力集成注册

我注册了劳动力集成并指定了“eligibilityFilteringEnabledEntities”,包括“SwapRequest、OfferShiftRequest 和 TimeOffReason',但响应正文未显示”eligibilityFilteringEnabledEntities“列表。

当前支持通过 https://graph.microsoft.com/beta 终结点(而不是 https://graph.microsoft.com/v1 终结点)筛选资格。

我注册了劳动力集成并添加了“supportedEntities”,但收到 400 错误请求响应和“有效负载无效:请求的值 'shift, ....' 找不到。“消息

确保列表请求正文中的每个 supportedEntities Shifts 实体都以大写字母开头。 例如,"supportedEntities":"Shift,SwapRequest,OpenShift"

我注册了劳动力集成并实现了 /connect、/update 和 /read 终结点,但 Webhook 不起作用。

确保已针对团队计划启用员工集成。 此外,检查 urlapiVersion 属性正确。

终结点参考

请求

ConnectRequest

属性 类型 说明
tenantId String 劳动力集成的租户 ID
userId String 劳动力集成的用户的 ID
{
  "tenantId": "string",
  "userId": "string"
}

WfiRequestContainer

属性 类型 说明
请求 WfiRequest 集合 WfiRequests 列表
{
  "requests": [
    {
      "id": "string",
      "method": "string",
      "url": "string",
      "headers": {
        "X-MS-Transaction-ID": "string",
        "X-MS-Expires": "string (DateTime)"
      },
      "body": "ShiftsEntity"
    }
  ]
}

请求中的元素数:

  • 在大多数情况下,请求有一个元素。
  • 某些请求(如交换班次请求审批)有五个元素:一个 PUT 交换请求,两个 DELETE 班次 (现有班次) ,两个 POST 班次 (新班次) 。

WfiRequest

属性 类型 说明
id String 实体的 ID
方法 String 对项调用的方法。 例如、POSTPUTGETDELETE
url String 指示实体的类型和操作详细信息。
标头 WfiRequestHeader 标题
body ShiftsEntity 与请求相关的实体的正文。
对于 POST /teams/{teamId}/update
属性 类型 说明
id String 实体的 ID
方法 String POST 创建实体, PUT 更新实体, DELETE 删除实体。
url String 格式为 /{EntityType}/{EntityId}。 的{EntityType}shifts可能值为 、、swapRequeststimeoffReasonsopenshiftrequestsoffershiftrequestsopenshiftstimesoff、 。 timeOffRequests 例如,/shifts/SHFT_12345678-1234-1234-1234-1234567890ab
标题 WfiRequestHeader 标头
body ShiftsEntity 必须在 url 属性中匹配{EntityType}。 使用 shiftswapShiftsChangeRequesttimeOffReasonopenshiftopenShiftChangeRequestofferShiftRequeststimeOffRequest 例如,/shifts/SHFT_12345678-1234-1234-1234-1234567890ab
对于 POST /teams/{teamsId}/read
属性 类型 说明
id String 实体的 ID
方法 String 始终为 GET
url String
  • TimeOffReasons:格式为 /users/{userId}/timeOffReasons?requestType=TimeOffReason。 例如,/users/aa162a04-bec6-4b81-ba99-96caa7b2b24d/timeOffReasons?requestType=TimeOffReason
  • SwapRequest:格式为 /shifts/{ShiftsId}/requestableShifts?requestType=SwapRequest\u0026startTime={startTime}\u0026endTime={endTime}。 例如,shifts/SHFT_1132430e-365e-4dc5-b8b0-b800592a81a8/requestableShifts?requestType=SwapRequest\u0026startTime=2024-10-01T07:00:00.0000000Z\u0026endTime=2024-11-01T06:59:59.9990000Z
标题 WfiRequestHeader 标头
body ShiftsEntity 始终为 null

WfiRequestHeader

属性 类型 说明
X-MS-Transaction-ID String 事务 ID
X-MS-Expires String (DateTime) 事务过期日期时间

X-MS-WFMPassthrough: workforceIntegratonId 不会包含在 WfiRequestHeader 中。 它应从 HttpRequest 中提取。

响应

WfiResponseContainer

属性 类型 说明
反应 WfiResponse 集合 WfiResponses 列表
{
  "responses": [
    {
      "id": "string",
      "status": "string",
      "body": {
        "eTag": "string",
        "error": {
          "code": "string",
          "message": "string"
        },
        "data": ["string1", "string2"]
      }
    }
  ]
}

WfiResponse

属性 类型 说明
id String 实体的 ID
status String 操作的结果
body WfiResponseBody WfiResponseBody

WfiResponseBody

属性 类型 说明
eTag String eTag
error WfiResponseError 有关错误的详细信息
data String 读取请求的请求数据 ()

WfiResponseError

属性 类型 说明
code String 错误代码
message String 错误消息