Skip to content

人行系统OpenApi接口文档 v1.0

1.概述

本文档面向第三方系统集成开发者,提供人行系统(门禁通行管理系统)的 OpenAPI 接口规范说明,帮助开发者快速对接以下核心功能:

  • 访客模块:支持访客邀请,访客登记,访客信息维护等功能
  • 门禁模块:提供员工及访客通行记录查询,通行状态管理等功能
  • 黑名单模块:支持黑名单人员管理与通行限制控制
  • 通行组模块:支持通行权限分组及权限分配管理
  • 企业模块:支持企业信息及员工归属关系管理

通过本文档,开发者可以了解接口调用方式、参数说明、返回格式及常见问题解决方案。


2.前置准备

2.1 申请应用接入权限

为保障接口安全,所有请求需进行身份认证。具体流程如下:

向基础数据组提交应用接入申请,提供三方系统的名称。审核通过后,将获得以下凭证:

  • clientId:应用唯一标识

  • clientSecret:应用密钥(请妥善保管,切勿泄露)

  • 请求签名(sign)生成: 在发起接口请求前,需使用 clientSecret 对请求参数进行加密,生成签名 sign,并随请求一同传递。 服务端将根据传入的 clientId 查找对应的 clientSecret,对 sign 进行校验,验证通过后方可提供服务。

⚠️ 注意:未携带有效 sign 或签名验证失败的请求将被拒绝。

2.2 统一认证机制

所有请求必须携带以下 Header。其中 sign 由 HMAC-SHA1 算法生成。具体步骤请查看 2.3.签名机制。本文附录一:签名生成包含Java完整实现代码

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

2.3 签名机制

3.名词解释

术语解释
访客单员工邀请访客的流程单
通行组具有相同通行权限的人员分组
人员编码人员入驻项目后生成唯一标识
访客单号创建访客邀请后生成的唯一单号
项目编码项目的唯一标识,由基础数据创建提供

系统交互流程

4.1 创建访客邀请

4.2 访客自助登记


通行组模块

5.1 通行组员工列表

概述:用于获取指定通行组内人员信息

接口定义:POST https://[host]/api/openApi/pass-team/v1.0.0/businessResident/page

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
pageNum页码,从 1 开始。默认值1Integer
pageSize每页条数 默认值10Integer
projectCode项目编号String
name姓名String
mobile手机号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
data响应数据Object
+ pageNum页数Integer
+ pageSize每页数量Integer
+ totalPages总页数Integer
+ total总记录数Integer
+ list人员列表List
++ personCode人员编码String
++ name真实姓名String
++ gender性别(0: 女, 1: 男,2:未知)Integer
++ mobile手机号String
++ isVisitorInvite是否有访客邀约权限(1:是,0:否)Integer
msg提示信息String

企业模块

6.1 员工企业列表

概述:用于通过员工手机号在项目中已入驻的企业列表

接口定义:GET https://[host]/api/openApi/pass-team/v1.0.0/invite/tenantListByUser

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
mobile员工手机号String
projectCode项目编号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据List
+ companyFlag是否公司Boolean
+ companyId企业IDLong
+ spaceId空间IDLong
+ name名称String
+ children子级List
+ invitationStartTime邀请开始时间,不限制为365Integer
+ invitationEndTime邀请结束时间,当天内有效为0Integer
+ visitorInviteCCFlag访客邀约抄送服务,0关闭,1开启Integer
+ visitorRegisterBAApproveFlag企业管理员审批,0关闭,1开启Integer
+ visitorRegisterCarNumFlag访客登记车牌,0关闭,1开启Integer

6.2 员工楼层列表

定义:用于通过员工手机号在项目中已入驻企业中楼层信息

接口定义:POST https://[host]/api/openApi/pass-team/v1.0.0/invite/spaceFloorListByUser

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
mobile员工手机号String
projectCode项目编号String
companyId企业IDLong

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ projectName项目名称String
+ spaceNameList空间名称列表List
+ spaceDtoList空间列表List
++ spaceCode空间编码String
++ spaceName空间名称String
++ alias空间别名String
++ level空间层级Integer
++ createTime创建时间String
++ sort排序值Integer
++ parentSort上级排序值Integer
+ projectCode项目编码String

访客管理模块

7.1 创建访客邀请

定义:用于员工发起访客邀请

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/addFlow

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
companyId企业idLong
interviewFloorId楼层IdLong
projectCode项目编码String
visitReason来访事由(见附录)Integer
intervieweeTelephone被访人手机号String
startTime来访开始时间(yyyy-MM-dd HH:mm)String
endTime来访结束时间(yyyy-MM-dd HH:mm)String
followVisitorList访客列表List
> + visitorName访客姓名String
> + visitorTelephone访客手机号String
> + isFollower是否随访人(默认否,必须有一个主访客。true:主访人/false:随访人)Boolean

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ flowUuid访客单号String

7.2 访客单列表查询

定义:用于系统中访客单列表

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/front/page

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
pageNum当前页码,默认 1Integer
pageSize每页条数,默认 10Integer
projectCode项目编码String
visitName访客姓名String
startTime来访开始时间(起始)String
endTime来访结束时间(截止)String
applyStatus申请状态(见附录)Integer
visitStatus到访状态(见附录)Integer
visitPhone访客手机号String
tenantId企业IdLong

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ pageNum当前页Integer
+ pageSize每页显示条数Integer
+ totalPages总页数Integer
+ total总记录数Integer
+ list结果集List
++ visitSourceType来源类型(见附录)Integer
++ startTime来访开始时间(yyyy-MM-dd HH:mm)String
++ endTime来访结束时间(yyyy-MM-dd HH:mm)String
++ visitStatus来访状态(见附录)Integer
++ applyStatus审核状态(见附录)Integer
++ visitReason来访事由(见附录)Integer
++ intervieweeTelephone被访人手机号String
++ intervieweeName被访人姓名String
++ intervieweeEnterprise被访人企业String
++ flowUuid流程UUIDString
++ visitorId访客IDLong
++ visitorName来访人姓名String
++ visitorTelephone来访人电话String
++ visitorType访客属性String
++ linkName被访地址String
++ interviewSpaceId被访地址空间IDLong
++ showDownloadBtn是否显示下载通行码按钮Boolean
++ qrcodeStaticContent二维码静态内容String
++ visitIdentityName访客身份名称String
++ plateNumber车牌号String
++ interviewFloorId被访楼层IDLong
++ approvalUserId审核人IDString
++ interviewFloorName到访楼层名称String

7.3 上传人脸图片

定义:用于上传人脸图片。注意:该接口单纯上传图片到服务器,并不是上传某个访客的人脸,接口返回图片地址。配合上传访客人脸接口使用。图片上传大小限制20KB

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/face/add

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
face图片base64编码String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ faceId图片地址String

7.4 上传访客人脸图片

定义:用于上传访客人脸图片。

接口定义:GET https://[host]/api/openApi/invite/v1.0.0/uploadVisitorFace

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
flowUuid访客单UUIDString
faceId图片地址String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object

7.5 查询访客单详情

定义:用于查询访客单详情。

接口定义:GET https://[host]/api/openApi/invite/v1.0.0/visitFlow/detail

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
flowUuid访客单编号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ visitSourceType来源类型(见附录)Integer
+ visitReason来访事由。备注:访客邀请单或线上预约时填写的来访理由。若用户未进行前台二次登记,此字段为最终展示值。Integer
+ visitCause来访事由。备注:前台现场登记时,安保人员或访客重新确认/修改的来访理由

1. 若用户经过前台登记,此字段有值,且优先级高于 visitReason。

2. 若用户直接通行(无前台登记),此字段为空。
Integer
+ visitCauseReasonName来访事由(文字)String
+ startTime来访开始时间(yyyy-MM-dd HH:mm)String
+ endTime来访结束时间(yyyy-MM-dd HH:mm)String
+ limitedTime限时通行时间段(00:00-23:59)String
+ visitStatus来访状态(见附录)Integer
+ applyStatus审核状态(见附录)Integer
+ intervieweeId被访人IDLong
+ intervieweeTelephone被访人手机号String
+ intervieweeName被访人姓名String
+ interviewSpaceId被访空间IDLong
+ companyId企业IDLong
+ flowUuid流程UUIDString
+ qrcodeStaticContent二维码静态内容String
+ qrcodeContent二维码内容String
+ visitorList访客列表List
++ visitorName访客姓名String
++ visitorCode访客编号String
++ visitorPhoto访客照片String
++ visitorTelephone手机号String
++ visitorIdcard身份证号String
++ visitorType访客类型Integer
+ interviewSpaceName被访空间名称String
+ companyName企业名称String
+ visitIdentity访客身份Integer
+ visitIdentityName访客身份名称String
+ thirdVisitId第三方访客IDString
+ approvalUserId审核人IDString
+ plateNumber车牌号String
+ interviewFloorId被访楼层IDLong
+ interviewFloorName被访楼层名称String
+ createTime创建时间(yyyy-MM-dd HH:mm:ss)String
+ isExpire是否过期Integer

7.6 查询被访人是否公司员工

定义:访客自助填写访问信息时,查询被访人是否属于公司员工。

接口定义:GET https://[host]/api/openApi/invite/v1.0.0/register/checkMobile

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
mobile被访人手机号String
projectCode项目编码String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Boolean

7.7 访客自助登记

定义:访客自助填写访问信息。

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/register/add

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
intervieweeTelephone被访人手机号String
intervieweeName被访人姓名String
startTime开始时间String
endTime结束时间String
visitReason来访事由(见附录)Integer
followVisitorList访客列表List
> + visitorName访客姓名String
> + visitorTelephone访客手机号String
> + isFollower是否随访人(必须有一个人为主访人。true:主访人/false:随访人)Boolean
> projectCode项目编码String
> companyId企业IdLong

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object

7.8 取消访客邀请

定义:取消访客邀请。

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/visitor/cancel

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
flowUuid访客单单号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object

7.9 访客二维码详情

定义:访客二维码,刷闸机使用

接口定义:GET https://[host]/api/openApi/invite/v1.0.0/visitor/qrcode/info/getByMobile

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
mobile手机号String
projectCode项目编码String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ projectName项目名称String
+ projectCode项目编码String
+ qrcodeStaticContent二维码图片静态内容String
+ qrcodeContent二维码内容String
+ visitorName访客姓名String
+ startTime来访开始时间(yyyy-MM-dd HH:mm)String
+ endTime来访结束时间(yyyy-MM-dd HH:mm)String
+ limitedTime限时通行时间段(00:00-23:59)String
+ addressName访问区域String
+ visitSourceType来源类型(见附录)Integer
+ visitorPhoto访客照片String
+ interviewFloorName访问楼层String
+ effective是否在有效期内Boolean
+ flowUuid访客单UUIDString
+ visitReason来访事由(见附录)Integer
+ applyStatus审核状态(见附录)Integer
+ visitPhone访客手机号String
+ visitorNumber来访人数Integer

黑名单模块

8.1 新增访客黑名单

定义:新增访客黑名单,禁止邀请黑名单中的访客。

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/blacklisted

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
name姓名String
mobile手机号String
projectCode项目编码String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object

8.2 查询访客黑名单列表

定义:查询已添加的黑名单列表

接口定义:POST https://[host]/api/openApi/invite/v1.0.0/blacklistedList

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
pageNum页码,从 1 开始.默认值1Integer
pageSize每页条数。默认值10Integer
name姓名String
mobile手机号String
startTime开始时间String
endTime结束时间String
projectCode项目编码String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ pageNum当前页Integer
+ pageSize每页显示条数Integer
+ totalPages总页数Integer
+ total总记录数Integer
+ list结果集List
++ name姓名String
++ mobile手机号String
++ entryDate黑名单登记日期String
++ creator记录创建者String

8.3 删除访客黑名单

定义:删除指定访客黑名单。

接口定义:DELETE https://[host]/api/openApi/invite/v1.0.0/blacklisted/del/

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

路径参数:

参数名称参数说明参数类型是否必填
mobile手机号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object

门禁模块

9.1 通行记录列表

定义:查询访客进出闸机的通行记录

接口定义:POST https://[host]/api/openApi/pass-records/v1.0.0/page

请求头:

参数名示例值参数类型是否必填参数描述
AuthorizationclientId:signString权限认证(调用方加密签名)
DateTue, 18 Nov 2025 02:09:56 GMTString请求时间(EEE, dd MMM yyyy HH:mm:ss zzz)服务端校验时差不得超过五分钟
Content-Typeapplication/json;charset=UTF-8String请求内容类型。固定格式:application/json;charset=UTF-8

请求参数:

参数名称参数说明参数类型是否必填
pageNum页码,从 1 开始。默认值1Integer
pageSize每页条数。默认值10Integer
passTimeStart通行开始时间String
passTimeEnd通行结束时间String
projectCode项目编码String
name姓名String
mobile手机号String

返回参数:

参数名称参数说明参数类型
code业务状态码,200 表示成功Integer
success操作是否成功Boolean
msg提示信息String
data响应数据Object
+ pageNum当前页Integer
+ pageSize每页显示条数Integer
+ totalPages总页数Integer
+ total总记录数Integer
+ list结果集List
++ projectName项目名称String
++ projectCode项目编码String
++ personCode人员编码String
++ personName人员姓名String
++ personPhone人员手机号String
++ passType通行验证方式(10刷卡、20二维码、30人脸、40蓝牙、50远程、60对讲、70密码、80身份证号)Integer
++ deviceDirection设备方向(0不限、1入口、2出口)Integer
++ passTypeName通行验证方式名称String
++ employeeType通行人员属性Integer
++ employeeTypeName通行人员属性名称String
++ personTypeName人类型名称String
++ deviceName设备名称String
++ deviceCode设备编码String
++ modelName设备型号名称String
++ passTime通行时间(yyyy-MM-dd HH:mm:ss)String
++ passPhoto通行照片全路径String
++ passSnapshotPhoto通行抓拍头像照片String
++ linkName空间名称String
++ interviewSpace被访地址空间String
++ tenantName企业名称String

附录

附录A 签名生成

java
@Slf4j
public class OpenApiClientAuth {

    public static final String AUTHORIZATION = "Authorization";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String DATE = "Date";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
            .ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);

    /**
     * 构建带签名的请求头
     *
     * @param httpMethod   HTTP 方法(GET/POST/PUT等)
     * @param url          完整请求 URL(含查询参数)
     * @param clientId     客户端 ID
     * @param clientSecret 客户端密钥
     * @param contentType  内容类型,若为 null 则默认为 application/json
     * @return 包含 Authorization、Date、Content-Type 的 Header Map
     */
    @SneakyThrows
    public static Map<String, String> getSignHeaders(final String httpMethod, final String url,
                                                     final String clientId, final String clientSecret, final String contentType) {
        final String pathResource = getPathResource(url);
        log.info("pathResource: {}", pathResource);
        final String contentTypeValue = contentType != null ? contentType : "application/json";
        final String dateValue = DATE_TIME_FORMATTER.format(ZonedDateTime.now(ZoneId.of("GMT")));
        final String sign = calculateSign(httpMethod, contentTypeValue, dateValue, pathResource, clientSecret);
        log.info("sign: {}", sign);
        final String authorizationString = clientId + ":" + sign;
        final Map<String, String> authHeaders = new HashMap<>(4);
        log.warn(
                "method={};contentTypeValue={};dateValue={};canonicalizedResource={};appSecret={}",
                httpMethod, contentTypeValue, dateValue, pathResource,
                clientSecret);
        authHeaders.put(DATE, dateValue);
        authHeaders.put(AUTHORIZATION, authorizationString);
        authHeaders.put(CONTENT_TYPE, contentTypeValue);
        return authHeaders;
    }

    protected static String getPathResource(final String url) {
        final String substring = url.substring(url.indexOf("://") + 3);
        return substring.substring(substring.indexOf('/'));
    }

    public static String calculateSign(final String httpMethodString,
                                       final String contentTypeValue, final String dateValue, final String pathResource,
                                       final String appSecret) throws InvalidKeyException, NoSuchAlgorithmException {
        final String signToString = builderStringToSign(httpMethodString, contentTypeValue, dateValue, pathResource);
        final String base64HashString = HMAC.hmacSha1Encrypt(signToString, appSecret);
        return base64HashString.substring(5, 15);
    }

    protected static String builderStringToSign(final String method,
                                                final String contentTypeValue, final String dateValue, final String pathResource) {
        return method
                + "\n"
                + handleNullString(contentTypeValue)
                + "\n"
                + handleNullString(dateValue)
                + "\n"
                + pathResource;
    }

    protected static String handleNullString(final String str) {
        return !StringUtils.hasText(str) ? "" : str;
    }

    /**
     * 执行带签名的 HTTP 请求
     *
     * @param url          请求地址
     * @param body         请求体(JSON 字符串),GET 请求可传 null
     * @param httpMethod   HTTP 方法
     * @param clientId     客户端 ID
     * @param clientSecret 客户端密钥
     * @return 响应体字符串
     */
    public static String openApiClientExecute(String url, String body, HttpMethod httpMethod, String clientId, String clientSecret) {
        log.info("request url:{},param:{},httpMethod:{},appKey:{},appSecret:{}", url, body, httpMethod, clientId, clientSecret);
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> signHeaders = getSignHeaders(httpMethod.toString(), url, clientId, clientSecret, MediaType.APPLICATION_JSON.toString());
        final HttpHeaders headers = new HttpHeaders();
        signHeaders.forEach(headers::set);
        log.info("request headers:{}", headers);
        if (!StringUtils.hasText(body)) {
            body = "";
        }
        final HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
        final ParameterizedTypeReference<String> parameterizedTypeReference = new ParameterizedTypeReference<String>() {
        };
        String r = null;
        try {
            ResponseEntity<String> exchange = restTemplate.exchange(url, httpMethod, httpEntity, parameterizedTypeReference);
            r = exchange.getBody();
        } catch (Exception e) {
            log.error("request error: ", e);
        }
        log.info("request url:{},param:{},httpMethod:{},appKey:{},appSecret:{},result:{} ", url, body, httpMethod, clientId, clientSecret, r);
        return r;
    }

    protected static class HMAC {
        private static final String KEY_MAC_SHA1 = "HmacSHA1";

        private HMAC() {
        }

        public static String hmacSha1Encrypt(final String encryptText, final String encryptKey)
                throws NoSuchAlgorithmException, InvalidKeyException {
            final byte[] text = encryptText.getBytes(StandardCharsets.UTF_8);
            final byte[] keyData = encryptKey.getBytes(StandardCharsets.UTF_8);
            final SecretKeySpec secretKey = new SecretKeySpec(keyData, HMAC.KEY_MAC_SHA1);
            final Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return new String(Base64.getEncoder().encode(mac.doFinal(text)), StandardCharsets.UTF_8);
        }
    }

    /**
     * 验签
     *
     * @return
     */
    public static boolean validate(ValidateParamVO validateParam, String appSecret) {
        String finalQueryString = validateParam.getQueryString();
        if (!StringUtils.hasText(appSecret)) {
            throw new IllegalArgumentException(
                    "AppId[" + validateParam.getAppKey() + "] 没有被授权,找不到对应的AppSecurity");
        }
        String contentTypeValue = validateParam.getContentType();
        final String dateValue = validateParam.getDate();
        final String pathResource = catUrl(validateParam.getRequestUrl(), finalQueryString);
        if (!StringUtils.hasText(dateValue)) {
            throw new IllegalArgumentException("Header[" + DATE + "]不能为空.");
        }
        try {
            if (!StringUtils.hasText(contentTypeValue)) {
                contentTypeValue = MediaType.APPLICATION_JSON_VALUE;
            }
            if (contentTypeValue.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
                contentTypeValue = MediaType.MULTIPART_FORM_DATA_VALUE;
            }
            final String realSign = calculateSign(validateParam.getMethod(),
                    contentTypeValue, dateValue,
                    pathResource,
                    appSecret);
            if (!validateParam.getSign().equals(realSign)) {
                log.warn(
                        "method={};contentTypeValue={};dateValue={};canonicalizedResource={};appSecret={}",
                        validateParam.getMethod(), contentTypeValue, dateValue, pathResource,
                        appSecret);
                log.warn("签名校验没有通过。sign={};realSign={}", validateParam.getSign(), realSign);
                return false;
            }
            return true;
        } catch (final Exception e) {
            final String msg = "鉴权异常" + e.getMessage();
            log.warn(msg, e);
            throw new IllegalArgumentException(msg);
        }
    }

    protected static String catUrl(final String uri, final String queryString) {
        return uri + ("".equals(handleNullString(queryString)) ? ""
                : "?" + queryString);
    }

    public static void main(String[] args) {
        String clientId = "testAppKey";
        String clientSecret = "testAppSecret";
        String postUrl2 = "https://sppams-uat.icloudcity.com/api/api/openApi/invite/v1.0.0/test";
        String format = String.format(postUrl2, clientId);
        System.out.println(format);
        JSONObject body2 = new JSONObject();
        // body2.put("clientId", "sppamsOpenApi");
        String result2 = openApiClientExecute(postUrl2, JSON.toJSONString(body2), HttpMethod.GET, clientId, clientSecret);
        System.out.println("Result: " + result2);
        final String dateValue = DATE_TIME_FORMATTER.format(ZonedDateTime.now(ZoneId.of("GMT")));
        boolean valid = isValid(dateValue);
        System.out.println(valid);
    }

    public static boolean isValid(String dateStr) {
        // 解析客户端时间
        ZonedDateTime requestTime = ZonedDateTime.parse(dateStr, DATE_TIME_FORMATTER);
        // 当前GMT时间
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of("GMT"));
        // 计算时间差(秒)
        long diffSeconds = Math.abs(Duration.between(requestTime, now).getSeconds());
        // 允许5分钟(300秒)
        return diffSeconds <= 300;
    }
}

附录B 返回码说明

错误码说明消除建议
200成功无需处理
400业务异常根据返回错误消息查看请求参数是否异常
40000业务异常根据返回错误消息查看请求参数是否异常
20003图片检测异常检查参数中是否携带图片
100检测人脸异常检查人脸图片是否正常

附录C 数据字典

附录C1 来访事由

参数含义
1拜访
2快递/外卖
3其他
4商务
5面试
6私人

附录C2 访客单审批状态

参数含义
1已审核(通过)
2已审核(不通过)
3未审核
4已取消
5已过期
6审核中
11免审核(通过)

附录C3 访客来访状态

参数含义
1已到访(有效中)
2已到访(失效)
3未来访

附录C4 访客来源

参数含义
1访客自助登记卡号
2前台登记
3访客邀请
4企微办公申请
5美团自助登记
6饿了么自助登记
7企微临时到访
8顺丰速递自助登记
9京东自助登记
10顺丰同城自助登记
11京东秒送自助登记