新增修改医生
This commit is contained in:
+80
-2
@@ -4,13 +4,25 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"hospital-admin-api/config"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
ResultCode int `json:"result_code"`
|
||||
ResultMsg string `json:"result_msg"`
|
||||
Body interface{} `json:"body"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
// GenerateSignature 生成签名
|
||||
func GenerateSignature(paramMap map[string]string) string {
|
||||
func GenerateSignature(paramMap map[string]interface{}) string {
|
||||
keys := make([]string, 0, len(paramMap))
|
||||
for k := range paramMap {
|
||||
if k == "pdfFile" {
|
||||
@@ -22,7 +34,10 @@ func GenerateSignature(paramMap map[string]string) string {
|
||||
|
||||
var toSign string
|
||||
for _, k := range keys {
|
||||
v := paramMap[k]
|
||||
v, ok := paramMap[k].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
toSign += v + "&"
|
||||
}
|
||||
toSign = strings.TrimSuffix(toSign, "&")
|
||||
@@ -34,3 +49,66 @@ func GenerateSignature(paramMap map[string]string) string {
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// 统一请求
|
||||
func postRequest(requestUrl string, formData url.Values, signature string) (map[string]interface{}, error) {
|
||||
payload := strings.NewReader(formData.Encode())
|
||||
// 创建 POST 请求
|
||||
req, err := http.NewRequest("POST", requestUrl, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Add("app_id", config.C.CaOnline.CaOnlineAppId)
|
||||
req.Header.Add("signature", signature)
|
||||
|
||||
// 创建 HTTP 请求客户端
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("请求失败")
|
||||
}
|
||||
// 读取响应内容
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response Response
|
||||
err = json.Unmarshal([]byte(respBody), &response)
|
||||
if err != nil {
|
||||
// json解析失败
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.ResultCode != 0 {
|
||||
if response.ResultMsg != "" {
|
||||
return nil, errors.New(response.ResultMsg)
|
||||
} else {
|
||||
return nil, errors.New("请求ca失败")
|
||||
}
|
||||
}
|
||||
|
||||
body := make(map[string]interface{})
|
||||
if response.Body != nil || response.Body != "" {
|
||||
bodyMap, ok := response.Body.(map[string]interface{})
|
||||
if !ok {
|
||||
body = bodyMap
|
||||
}
|
||||
}
|
||||
|
||||
if len(body) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
+321
-3
@@ -1,5 +1,323 @@
|
||||
package ca
|
||||
|
||||
// func GenerateSi1gnature(paramMap map[string]string, secret string) string {
|
||||
//
|
||||
// }
|
||||
import (
|
||||
"errors"
|
||||
"hospital-admin-api/config"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// EditCloudCertRequestData 修改云证书请求数据
|
||||
type EditCloudCertRequestData struct {
|
||||
EntityId string `json:"entityId"` // 用户唯一标识,由业务系统定义
|
||||
EntityType string `json:"entityType"` // 用户类型,可选值[Personal/Organizational]
|
||||
PersonalPhone string `json:"personalPhone"` // 联系人电话
|
||||
PersonalName string `json:"personalName"` // 个人姓名,类型为Personal时必填
|
||||
PersonalIdNumber string `json:"personalIdNumber"` // 个人证件号,类型为Personal时必填
|
||||
OrgName string `json:"orgName"` // 组织机构名称,信用代码类型为Organizational时必填
|
||||
OrgNumber string `json:"orgNumber"` // 组织机构代码,信用代码类型为Organizational时必填
|
||||
Pin string `json:"pin"` // 证书PIN码
|
||||
OrgDept string `json:"orgDept"` // 卫生证书:医院部门
|
||||
Province string `json:"province"` // 卫生证书:省、州
|
||||
Locality string `json:"locality"` // 卫生证书:城市
|
||||
AuthType string `json:"authType"` // 委托鉴证方式[实人认证、线下认证、其它方式认证]
|
||||
AuthTime string `json:"authTime"` // 委托鉴证时间(鉴证完成的时间戳)单位:秒
|
||||
AuthResult string `json:"authResult"` // 委托鉴证结果[认证通过]
|
||||
AuthNoticeType string `json:"authNoticeType"` // 委托鉴证告知类型[数字证书申请告知]
|
||||
}
|
||||
|
||||
// AddCloudCertRequestData 新增云证书请求数据
|
||||
type AddCloudCertRequestData struct {
|
||||
EntityId string `json:"entityId"` // 用户唯一标识,由业务系统定义
|
||||
EntityType string `json:"entityType"` // 用户类型,可选值[Personal/Organizational]
|
||||
PersonalPhone string `json:"personalPhone"` // 联系人电话
|
||||
PersonalName string `json:"personalName"` // 个人姓名,类型为Personal时必填
|
||||
PersonalIdNumber string `json:"personalIdNumber"` // 个人证件号,类型为Personal时必填
|
||||
OrgName string `json:"orgName"` // 组织机构名称,信用代码类型为Organizational时必填
|
||||
OrgNumber string `json:"orgNumber"` // 组织机构代码,信用代码类型为Organizational时必填
|
||||
Pin string `json:"pin"` // 证书PIN码
|
||||
OrgDept string `json:"orgDept"` // 卫生证书:医院部门
|
||||
Province string `json:"province"` // 卫生证书:省、州
|
||||
Locality string `json:"locality"` // 卫生证书:城市
|
||||
AuthType string `json:"authType"` // 委托鉴证方式[实人认证、线下认证、其它方式认证]
|
||||
AuthTime string `json:"authTime"` // 委托鉴证时间(鉴证完成的时间戳)单位:秒
|
||||
AuthResult string `json:"authResult"` // 委托鉴证结果[认证通过]
|
||||
AuthNoticeType string `json:"authNoticeType"` // 委托鉴证告知类型[数字证书申请告知]
|
||||
}
|
||||
|
||||
// GetUserSignConfigRequestData 获取用户签章图片
|
||||
type GetUserSignConfigRequestData struct {
|
||||
UserId string `json:"userId"` // 用户标识信息
|
||||
}
|
||||
|
||||
// DeleteUserSignConfigRequestData 删除签章配置
|
||||
type DeleteUserSignConfigRequestData struct {
|
||||
UserId string `json:"userId"` // 用户标识信息
|
||||
ConfigKey string `json:"configKey"` // 签章配置唯一标识
|
||||
}
|
||||
|
||||
// EditCloudCertResponse 修改云证书返回数据
|
||||
type EditCloudCertResponse struct {
|
||||
CertBase64 string `json:"certBase64"` // 签名值证书
|
||||
CertP7 string `json:"certP7"` // 证书链
|
||||
CertSerialnumber string `json:"certSerialnumber"` // 证书序列号
|
||||
}
|
||||
|
||||
// AddCloudCertResponse 申请云证书返回数据
|
||||
type AddCloudCertResponse struct {
|
||||
CertBase64 string `json:"certBase64"` // 签名值证书
|
||||
CertP7 string `json:"certP7"` // 证书链
|
||||
CertSerialnumber string `json:"certSerialnumber"` // 证书序列号
|
||||
}
|
||||
|
||||
// GetUserSignConfigResponse 获取用户签章图片返回数据
|
||||
type GetUserSignConfigResponse struct {
|
||||
SealImg string `json:"sealImg"` // 印章图片
|
||||
SealType int `json:"sealType"` // 印章类型(1公章;2财务章;3个人章;4合同印章;5其他)
|
||||
AppId string `json:"appId"` // 应用appid
|
||||
Id string `json:"id"` // 印章唯一标识
|
||||
}
|
||||
|
||||
// EditCloudCert 修改云证书
|
||||
func EditCloudCert(d *EditCloudCertRequestData) (*EditCloudCertResponse, error) {
|
||||
if d == nil {
|
||||
return nil, errors.New("修改云证书失败")
|
||||
}
|
||||
|
||||
// 获取签名
|
||||
requestDataMap := make(map[string]interface{})
|
||||
requestDataMap["entityId"] = d.EntityId
|
||||
requestDataMap["entityType"] = d.EntityType
|
||||
requestDataMap["personalPhone"] = d.PersonalPhone
|
||||
requestDataMap["personalName"] = d.PersonalName
|
||||
requestDataMap["personalIdNumber"] = d.PersonalIdNumber
|
||||
requestDataMap["orgName"] = d.OrgName
|
||||
requestDataMap["orgNumber"] = d.OrgNumber
|
||||
requestDataMap["pin"] = d.Pin
|
||||
requestDataMap["province"] = d.Province
|
||||
requestDataMap["locality"] = d.Locality
|
||||
requestDataMap["authType"] = d.AuthType
|
||||
requestDataMap["authTime"] = d.AuthTime
|
||||
requestDataMap["authResult"] = d.AuthResult
|
||||
requestDataMap["authNoticeType"] = d.AuthNoticeType
|
||||
|
||||
signature := GenerateSignature(requestDataMap)
|
||||
if signature == "" {
|
||||
return nil, errors.New("云证书签名错误")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("entityId", d.EntityId)
|
||||
formData.Set("entityType", d.EntityType)
|
||||
formData.Set("personalPhone", d.PersonalPhone)
|
||||
formData.Set("personalName", d.PersonalName)
|
||||
formData.Set("personalIdNumber", d.PersonalIdNumber)
|
||||
formData.Set("orgName", d.OrgName)
|
||||
formData.Set("orgNumber", d.OrgNumber)
|
||||
formData.Set("pin", d.Pin)
|
||||
formData.Set("province", d.Province)
|
||||
formData.Set("locality", d.Locality)
|
||||
formData.Set("authType", d.AuthType)
|
||||
formData.Set("authTime", d.AuthTime)
|
||||
formData.Set("authResult", d.AuthResult)
|
||||
formData.Set("authNoticeType", d.AuthNoticeType)
|
||||
|
||||
// 构建请求 URL
|
||||
requestUrl := config.C.CaOnline.CaOnlineApiUrl + "/cloud-certificate-service/api/cloudCert/open/v2/cert/certChange"
|
||||
|
||||
response, err := postRequest(requestUrl, formData, signature)
|
||||
if err != nil {
|
||||
return nil, errors.New(err.Error())
|
||||
}
|
||||
|
||||
certBase64, ok := response["certBase64"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
certP7, ok := response["certP7"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
certSerialnumber, ok := response["certSerialnumber"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
result := &EditCloudCertResponse{
|
||||
CertBase64: certBase64.(string),
|
||||
CertP7: certP7.(string),
|
||||
CertSerialnumber: certSerialnumber.(string),
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// AddCloudCert 新增云证书
|
||||
func AddCloudCert(d *AddCloudCertRequestData) (*AddCloudCertResponse, error) {
|
||||
if d == nil {
|
||||
return nil, errors.New("修改云证书失败")
|
||||
}
|
||||
|
||||
// 获取签名
|
||||
requestDataMap := make(map[string]interface{})
|
||||
requestDataMap["entityId"] = d.EntityId
|
||||
requestDataMap["entityType"] = d.EntityType
|
||||
requestDataMap["personalPhone"] = d.PersonalPhone
|
||||
requestDataMap["personalName"] = d.PersonalName
|
||||
requestDataMap["personalIdNumber"] = d.PersonalIdNumber
|
||||
requestDataMap["orgName"] = d.OrgName
|
||||
requestDataMap["orgNumber"] = d.OrgNumber
|
||||
requestDataMap["pin"] = d.Pin
|
||||
requestDataMap["province"] = d.Province
|
||||
requestDataMap["locality"] = d.Locality
|
||||
requestDataMap["authType"] = d.AuthType
|
||||
requestDataMap["authTime"] = d.AuthTime
|
||||
requestDataMap["authResult"] = d.AuthResult
|
||||
requestDataMap["authNoticeType"] = d.AuthNoticeType
|
||||
|
||||
signature := GenerateSignature(requestDataMap)
|
||||
if signature == "" {
|
||||
return nil, errors.New("云证书签名错误")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("entityId", d.EntityId)
|
||||
formData.Set("entityType", d.EntityType)
|
||||
formData.Set("personalPhone", d.PersonalPhone)
|
||||
formData.Set("personalName", d.PersonalName)
|
||||
formData.Set("personalIdNumber", d.PersonalIdNumber)
|
||||
formData.Set("orgName", d.OrgName)
|
||||
formData.Set("orgNumber", d.OrgNumber)
|
||||
formData.Set("pin", d.Pin)
|
||||
formData.Set("province", d.Province)
|
||||
formData.Set("locality", d.Locality)
|
||||
formData.Set("authType", d.AuthType)
|
||||
formData.Set("authTime", d.AuthTime)
|
||||
formData.Set("authResult", d.AuthResult)
|
||||
formData.Set("authNoticeType", d.AuthNoticeType)
|
||||
|
||||
// 构建请求 URL
|
||||
requestUrl := config.C.CaOnline.CaOnlineApiUrl + "/cloud-certificate-service/api/cloudCert/open/v2/cert/certEnroll"
|
||||
|
||||
response, err := postRequest(requestUrl, formData, signature)
|
||||
if err != nil {
|
||||
return nil, errors.New(err.Error())
|
||||
}
|
||||
|
||||
certBase64, ok := response["certBase64"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
certP7, ok := response["certP7"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
certSerialnumber, ok := response["certSerialnumber"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
result := &AddCloudCertResponse{
|
||||
CertBase64: certBase64.(string),
|
||||
CertP7: certP7.(string),
|
||||
CertSerialnumber: certSerialnumber.(string),
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetUserSignConfig 获取用户签章图片
|
||||
func GetUserSignConfig(d *GetUserSignConfigRequestData) (*GetUserSignConfigResponse, error) {
|
||||
if d == nil {
|
||||
return nil, errors.New("修改云证书失败")
|
||||
}
|
||||
|
||||
// 获取签名
|
||||
requestDataMap := make(map[string]interface{})
|
||||
requestDataMap["userId"] = d.UserId
|
||||
|
||||
signature := GenerateSignature(requestDataMap)
|
||||
if signature == "" {
|
||||
return nil, errors.New("云证书签名错误")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("userId", d.UserId)
|
||||
|
||||
// 构建请求 URL
|
||||
requestUrl := config.C.CaOnline.CaOnlineApiUrl + "/signature-server/api/open/signature/fetchUserSeal"
|
||||
|
||||
response, err := postRequest(requestUrl, formData, signature)
|
||||
if err != nil {
|
||||
return nil, errors.New(err.Error())
|
||||
}
|
||||
|
||||
// 返回内容为空,未设置签章图片
|
||||
if response == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sealImg, ok := response["sealImg"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
sealType, ok := response["sealType"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
appId, ok := response["appId"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
id, ok := response["id"]
|
||||
if !ok {
|
||||
return nil, errors.New("返回数据错误")
|
||||
}
|
||||
|
||||
result := &GetUserSignConfigResponse{
|
||||
SealImg: sealImg.(string),
|
||||
SealType: sealType.(int),
|
||||
AppId: appId.(string),
|
||||
Id: id.(string),
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DeleteUserSignConfig 删除签章配置
|
||||
func DeleteUserSignConfig(d *DeleteUserSignConfigRequestData) (bool, error) {
|
||||
if d == nil {
|
||||
return false, errors.New("修改云证书失败")
|
||||
}
|
||||
|
||||
// 获取签名
|
||||
requestDataMap := make(map[string]interface{})
|
||||
requestDataMap["userId"] = d.UserId
|
||||
|
||||
signature := GenerateSignature(requestDataMap)
|
||||
if signature == "" {
|
||||
return false, errors.New("云证书签名错误")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("userId", d.UserId)
|
||||
formData.Set("configKey", d.ConfigKey)
|
||||
|
||||
// 构建请求 URL
|
||||
requestUrl := config.C.CaOnline.CaOnlineApiUrl + "/signature-server/api/open/signature/delSignConfig"
|
||||
|
||||
response, err := postRequest(requestUrl, formData, signature)
|
||||
if err != nil {
|
||||
return false, errors.New(err.Error())
|
||||
}
|
||||
|
||||
// 返回内容为空
|
||||
if response == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
package tencentIm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
/**
|
||||
*【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据
|
||||
*
|
||||
*【参数说明】
|
||||
* sdkappid - 应用id
|
||||
* key - 计算 usersig 用的加密密钥,控制台可获取
|
||||
* userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
|
||||
* expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。
|
||||
*/
|
||||
|
||||
// GenUserSig /**
|
||||
func GenUserSig(sdkappid int, key string, userid string, expire int) (string, error) {
|
||||
return genSig(sdkappid, key, userid, expire, nil)
|
||||
}
|
||||
|
||||
func GenUserSigWithBuf(sdkappid int, key string, userid string, expire int, buf []byte) (string, error) {
|
||||
return genSig(sdkappid, key, userid, expire, buf)
|
||||
}
|
||||
|
||||
/**
|
||||
*【功能说明】
|
||||
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
|
||||
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
|
||||
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
|
||||
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
|
||||
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】=>【应用管理】=>【应用信息】中打开“启动权限密钥”开关。
|
||||
*
|
||||
*【参数说明】
|
||||
* sdkappid - 应用id。
|
||||
* key - 计算 usersig 用的加密密钥,控制台可获取。
|
||||
* userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
|
||||
* expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
|
||||
* roomid - 房间号,用于指定该 userid 可以进入的房间号
|
||||
* privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
|
||||
* - 第 1 位:0000 0001 = 1,创建房间的权限
|
||||
* - 第 2 位:0000 0010 = 2,加入房间的权限
|
||||
* - 第 3 位:0000 0100 = 4,发送语音的权限
|
||||
* - 第 4 位:0000 1000 = 8,接收语音的权限
|
||||
* - 第 5 位:0001 0000 = 16,发送视频的权限
|
||||
* - 第 6 位:0010 0000 = 32,接收视频的权限
|
||||
* - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限
|
||||
* - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限
|
||||
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
|
||||
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function:
|
||||
* Used to issue PrivateMapKey that is optional for room entry.
|
||||
* PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities.
|
||||
* - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room.
|
||||
* - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room.
|
||||
* To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info.
|
||||
*
|
||||
* Parameter description:
|
||||
* sdkappid - Application ID
|
||||
* userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-).
|
||||
* key - The encryption key used to calculate usersig can be obtained from the console.
|
||||
* roomid - ID of the room to which the specified UserID can enter.
|
||||
* expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated.
|
||||
* privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features:
|
||||
* - Bit 1: 0000 0001 = 1, permission for room creation
|
||||
* - Bit 2: 0000 0010 = 2, permission for room entry
|
||||
* - Bit 3: 0000 0100 = 4, permission for audio sending
|
||||
* - Bit 4: 0000 1000 = 8, permission for audio receiving
|
||||
* - Bit 5: 0001 0000 = 16, permission for video sending
|
||||
* - Bit 6: 0010 0000 = 32, permission for video receiving
|
||||
* - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing)
|
||||
* - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing)
|
||||
* - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid.
|
||||
* - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data.
|
||||
*/
|
||||
|
||||
func GenPrivateMapKey(sdkappid int, key string, userid string, expire int, roomid uint32, privilegeMap uint32) (string, error) {
|
||||
var userbuf []byte = genUserBuf(userid, sdkappid, roomid, expire, privilegeMap, 0, "")
|
||||
return genSig(sdkappid, key, userid, expire, userbuf)
|
||||
}
|
||||
|
||||
/**
|
||||
*【功能说明】
|
||||
* 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。
|
||||
* PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力:
|
||||
* - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。
|
||||
* - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。
|
||||
* 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】=>【应用管理】=>【应用信息】中打开“启动权限密钥”开关。
|
||||
*
|
||||
*【参数说明】
|
||||
* sdkappid - 应用id。
|
||||
* key - 计算 usersig 用的加密密钥,控制台可获取。
|
||||
* userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。
|
||||
* expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。
|
||||
* roomStr - 字符串房间号,用于指定该 userid 可以进入的房间号
|
||||
* privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关:
|
||||
* - 第 1 位:0000 0001 = 1,创建房间的权限
|
||||
* - 第 2 位:0000 0010 = 2,加入房间的权限
|
||||
* - 第 3 位:0000 0100 = 4,发送语音的权限
|
||||
* - 第 4 位:0000 1000 = 8,接收语音的权限
|
||||
* - 第 5 位:0001 0000 = 16,发送视频的权限
|
||||
* - 第 6 位:0010 0000 = 32,接收视频的权限
|
||||
* - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限
|
||||
* - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限
|
||||
* - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。
|
||||
* - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function:
|
||||
* Used to issue PrivateMapKey that is optional for room entry.
|
||||
* PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities.
|
||||
* - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room.
|
||||
* - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room.
|
||||
* To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info.
|
||||
*
|
||||
* Parameter description:
|
||||
* sdkappid - Application ID
|
||||
* userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-).
|
||||
* key - The encryption key used to calculate usersig can be obtained from the console.
|
||||
* roomstr - ID of the room to which the specified UserID can enter.
|
||||
* expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated.
|
||||
* privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features:
|
||||
* - Bit 1: 0000 0001 = 1, permission for room creation
|
||||
* - Bit 2: 0000 0010 = 2, permission for room entry
|
||||
* - Bit 3: 0000 0100 = 4, permission for audio sending
|
||||
* - Bit 4: 0000 1000 = 8, permission for audio receiving
|
||||
* - Bit 5: 0001 0000 = 16, permission for video sending
|
||||
* - Bit 6: 0010 0000 = 32, permission for video receiving
|
||||
* - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing)
|
||||
* - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing)
|
||||
* - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid.
|
||||
* - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data.
|
||||
*/
|
||||
func GenPrivateMapKeyWithStringRoomID(sdkappid int, key string, userid string, expire int, roomStr string, privilegeMap uint32) (string, error) {
|
||||
var userbuf []byte = genUserBuf(userid, sdkappid, 0, expire, privilegeMap, 0, roomStr)
|
||||
return genSig(sdkappid, key, userid, expire, userbuf)
|
||||
}
|
||||
|
||||
func genUserBuf(account string, dwSdkappid int, dwAuthID uint32,
|
||||
dwExpTime int, dwPrivilegeMap uint32, dwAccountType uint32, roomStr string) []byte {
|
||||
|
||||
offset := 0
|
||||
length := 1 + 2 + len(account) + 20 + len(roomStr)
|
||||
if len(roomStr) > 0 {
|
||||
length = length + 2
|
||||
}
|
||||
|
||||
userBuf := make([]byte, length)
|
||||
|
||||
// ver
|
||||
if len(roomStr) > 0 {
|
||||
userBuf[offset] = 1
|
||||
} else {
|
||||
userBuf[offset] = 0
|
||||
}
|
||||
|
||||
offset++
|
||||
userBuf[offset] = (byte)((len(account) & 0xFF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(len(account) & 0x00FF)
|
||||
offset++
|
||||
|
||||
for ; offset < len(account)+3; offset++ {
|
||||
userBuf[offset] = account[offset-3]
|
||||
}
|
||||
|
||||
// dwSdkAppid
|
||||
userBuf[offset] = (byte)((dwSdkappid & 0xFF000000) >> 24)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwSdkappid & 0x00FF0000) >> 16)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwSdkappid & 0x0000FF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(dwSdkappid & 0x000000FF)
|
||||
offset++
|
||||
|
||||
// dwAuthId
|
||||
userBuf[offset] = (byte)((dwAuthID & 0xFF000000) >> 24)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwAuthID & 0x00FF0000) >> 16)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwAuthID & 0x0000FF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(dwAuthID & 0x000000FF)
|
||||
offset++
|
||||
|
||||
// dwExpTime now+300;
|
||||
currTime := time.Now().Unix()
|
||||
var expire = currTime + int64(dwExpTime)
|
||||
userBuf[offset] = (byte)((expire & 0xFF000000) >> 24)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((expire & 0x00FF0000) >> 16)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((expire & 0x0000FF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(expire & 0x000000FF)
|
||||
offset++
|
||||
|
||||
// dwPrivilegeMap
|
||||
userBuf[offset] = (byte)((dwPrivilegeMap & 0xFF000000) >> 24)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwPrivilegeMap & 0x00FF0000) >> 16)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwPrivilegeMap & 0x0000FF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(dwPrivilegeMap & 0x000000FF)
|
||||
offset++
|
||||
|
||||
// dwAccountType
|
||||
userBuf[offset] = (byte)((dwAccountType & 0xFF000000) >> 24)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwAccountType & 0x00FF0000) >> 16)
|
||||
offset++
|
||||
userBuf[offset] = (byte)((dwAccountType & 0x0000FF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(dwAccountType & 0x000000FF)
|
||||
offset++
|
||||
|
||||
if len(roomStr) > 0 {
|
||||
userBuf[offset] = (byte)((len(roomStr) & 0xFF00) >> 8)
|
||||
offset++
|
||||
userBuf[offset] = (byte)(len(roomStr) & 0x00FF)
|
||||
offset++
|
||||
|
||||
for ; offset < length; offset++ {
|
||||
userBuf[offset] = roomStr[offset-(length-len(roomStr))]
|
||||
}
|
||||
}
|
||||
|
||||
return userBuf
|
||||
}
|
||||
|
||||
func hmacsha256(sdkappid int, key string, identifier string, currTime int64, expire int, base64UserBuf *string) string {
|
||||
var contentToBeSigned string
|
||||
contentToBeSigned = "TLS.identifier:" + identifier + "\n"
|
||||
contentToBeSigned += "TLS.sdkappid:" + strconv.Itoa(sdkappid) + "\n"
|
||||
contentToBeSigned += "TLS.time:" + strconv.FormatInt(currTime, 10) + "\n"
|
||||
contentToBeSigned += "TLS.expire:" + strconv.Itoa(expire) + "\n"
|
||||
if nil != base64UserBuf {
|
||||
contentToBeSigned += "TLS.userbuf:" + *base64UserBuf + "\n"
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write([]byte(contentToBeSigned))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func genSig(sdkappid int, key string, identifier string, expire int, userbuf []byte) (string, error) {
|
||||
currTime := time.Now().Unix()
|
||||
sigDoc := make(map[string]interface{})
|
||||
sigDoc["TLS.ver"] = "2.0"
|
||||
sigDoc["TLS.identifier"] = identifier
|
||||
sigDoc["TLS.sdkappid"] = sdkappid
|
||||
sigDoc["TLS.expire"] = expire
|
||||
sigDoc["TLS.time"] = currTime
|
||||
var base64UserBuf string
|
||||
if nil != userbuf {
|
||||
base64UserBuf = base64.StdEncoding.EncodeToString(userbuf)
|
||||
sigDoc["TLS.userbuf"] = base64UserBuf
|
||||
sigDoc["TLS.sig"] = hmacsha256(sdkappid, key, identifier, currTime, expire, &base64UserBuf)
|
||||
} else {
|
||||
sigDoc["TLS.sig"] = hmacsha256(sdkappid, key, identifier, currTime, expire, nil)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(sigDoc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
w := zlib.NewWriter(&b)
|
||||
if _, err = w.Write(data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = w.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64urlEncode(b.Bytes()), nil
|
||||
}
|
||||
|
||||
// VerifyUserSig 检验UserSig在now时间点时是否有效
|
||||
// VerifyUserSig Check if UserSig is valid at now time
|
||||
func VerifyUserSig(sdkappid uint64, key string, userid string, usersig string, now time.Time) error {
|
||||
sig, err := newUserSig(usersig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.verify(sdkappid, key, userid, now, nil)
|
||||
}
|
||||
|
||||
// VerifyUserSigWithBuf 检验带UserBuf的UserSig在now时间点是否有效
|
||||
// VerifyUserSigWithBuf Check if UserSig with UserBuf is valid at now
|
||||
func VerifyUserSigWithBuf(sdkappid uint64, key string, userid string, usersig string, now time.Time, userbuf []byte) error {
|
||||
sig, err := newUserSig(usersig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sig.verify(sdkappid, key, userid, now, userbuf)
|
||||
}
|
||||
|
||||
type userSig struct {
|
||||
Version string `json:"TLS.ver,omitempty"`
|
||||
Identifier string `json:"TLS.identifier,omitempty"`
|
||||
SdkAppID uint64 `json:"TLS.sdkappid,omitempty"`
|
||||
Expire int64 `json:"TLS.expire,omitempty"`
|
||||
Time int64 `json:"TLS.time,omitempty"`
|
||||
UserBuf []byte `json:"TLS.userbuf,omitempty"`
|
||||
Sig string `json:"TLS.sig,omitempty"`
|
||||
}
|
||||
|
||||
func newUserSig(usersig string) (userSig, error) {
|
||||
b, err := base64urlDecode(usersig)
|
||||
if err != nil {
|
||||
return userSig{}, err
|
||||
}
|
||||
r, err := zlib.NewReader(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return userSig{}, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return userSig{}, err
|
||||
}
|
||||
if err = r.Close(); err != nil {
|
||||
return userSig{}, err
|
||||
}
|
||||
var sig userSig
|
||||
if err = json.Unmarshal(data, &sig); err != nil {
|
||||
return userSig{}, nil
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func (u userSig) verify(sdkappid uint64, key string, userid string, now time.Time, userbuf []byte) error {
|
||||
if sdkappid != u.SdkAppID {
|
||||
return ErrSdkAppIDNotMatch
|
||||
}
|
||||
if userid != u.Identifier {
|
||||
return ErrIdentifierNotMatch
|
||||
}
|
||||
if now.Unix() > u.Time+u.Expire {
|
||||
return ErrExpired
|
||||
}
|
||||
if userbuf != nil {
|
||||
if u.UserBuf == nil {
|
||||
return ErrUserBufTypeNotMatch
|
||||
}
|
||||
if !bytes.Equal(userbuf, u.UserBuf) {
|
||||
return ErrUserBufNotMatch
|
||||
}
|
||||
} else if u.UserBuf != nil {
|
||||
return ErrUserBufTypeNotMatch
|
||||
}
|
||||
if u.sign(key) != u.Sig {
|
||||
return ErrSigNotMatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u userSig) sign(key string) string {
|
||||
var sb bytes.Buffer
|
||||
sb.WriteString("TLS.identifier:")
|
||||
sb.WriteString(u.Identifier)
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString("TLS.sdkappid:")
|
||||
sb.WriteString(strconv.FormatUint(u.SdkAppID, 10))
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString("TLS.time:")
|
||||
sb.WriteString(strconv.FormatInt(u.Time, 10))
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString("TLS.expire:")
|
||||
sb.WriteString(strconv.FormatInt(u.Expire, 10))
|
||||
sb.WriteString("\n")
|
||||
if u.UserBuf != nil {
|
||||
sb.WriteString("TLS.userbuf:")
|
||||
sb.WriteString(base64.StdEncoding.EncodeToString(u.UserBuf))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write(sb.Bytes())
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func base64urlEncode(data []byte) string {
|
||||
str := base64.StdEncoding.EncodeToString(data)
|
||||
str = strings.Replace(str, "+", "*", -1)
|
||||
str = strings.Replace(str, "/", "-", -1)
|
||||
str = strings.Replace(str, "=", "_", -1)
|
||||
return str
|
||||
}
|
||||
|
||||
func base64urlDecode(str string) ([]byte, error) {
|
||||
str = strings.Replace(str, "_", "=", -1)
|
||||
str = strings.Replace(str, "-", "/", -1)
|
||||
str = strings.Replace(str, "*", "+", -1)
|
||||
return base64.StdEncoding.DecodeString(str)
|
||||
}
|
||||
|
||||
// 错误类型
|
||||
var (
|
||||
ErrSdkAppIDNotMatch = errors.New("sdk appid not match")
|
||||
ErrIdentifierNotMatch = errors.New("identifier not match")
|
||||
ErrExpired = errors.New("expired")
|
||||
ErrUserBufTypeNotMatch = errors.New("userbuf type not match")
|
||||
ErrUserBufNotMatch = errors.New("userbuf not match")
|
||||
ErrSigNotMatch = errors.New("sig not match")
|
||||
)
|
||||
@@ -0,0 +1,94 @@
|
||||
package tencentIm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"hospital-admin-api/config"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetUserSign 获取签名
|
||||
func GetUserSign(userId string) (string, error) {
|
||||
if userId == "" {
|
||||
userId = "administrator"
|
||||
}
|
||||
ImAppID := config.C.Im.ImAppID
|
||||
ImSecret := config.C.Im.ImSecret
|
||||
sign, err := GenUserSig(ImAppID, ImSecret, userId, 86400*180)
|
||||
if err != nil || sign == "" {
|
||||
return "", errors.New("签名获取失败")
|
||||
}
|
||||
|
||||
return sign, err
|
||||
}
|
||||
|
||||
// 获取请求链接
|
||||
func getRequestUrlParams(userId string) (bool, string) {
|
||||
// 获取签名
|
||||
// 获取签名
|
||||
sign, err := GetUserSign(userId)
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("sdkappid", strconv.Itoa(config.C.Im.ImAppID))
|
||||
params.Set("identifier", "administrator")
|
||||
params.Set("usersig", sign)
|
||||
params.Set("random", strconv.Itoa(rand.Intn(4294967296)))
|
||||
params.Set("contenttype", "json")
|
||||
|
||||
queryString := params.Encode()
|
||||
|
||||
return true, queryString
|
||||
}
|
||||
|
||||
// 统一请求
|
||||
func postRequest(url string, requestBody []byte) (map[string]interface{}, error) {
|
||||
responseMap := make(map[string]interface{})
|
||||
|
||||
// 发起 POST 请求
|
||||
resp, err := http.Post(url, "application/json", bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(string(body)), &responseMap)
|
||||
if err != nil {
|
||||
// json解析失败
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if responseMap == nil {
|
||||
return nil, errors.New("请求im失败")
|
||||
}
|
||||
|
||||
if errorCode, ok := responseMap["ErrorCode"]; ok {
|
||||
if errorCode != 0 {
|
||||
if errorInfo, ok := responseMap["ErrorInfo"].(string); ok {
|
||||
return nil, errors.New(errorInfo)
|
||||
} else {
|
||||
return nil, errors.New("请求im失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return responseMap, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Package tencentIm im资料
|
||||
package tencentIm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"hospital-admin-api/config"
|
||||
)
|
||||
|
||||
// ProfileItem 资料对象数组
|
||||
type ProfileItem struct {
|
||||
Tag string `json:"Tag"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
// PortraitSetRequest 请求格式
|
||||
type PortraitSetRequest struct {
|
||||
FromAccount string `json:"From_Account"`
|
||||
ProfileItems []ProfileItem `json:"ProfileItem"`
|
||||
}
|
||||
|
||||
// SetProfile 设置账户资料
|
||||
func SetProfile(userId string, profileItem []ProfileItem) (bool, error) {
|
||||
if len(profileItem) == 0 {
|
||||
return false, errors.New("未设置资料")
|
||||
}
|
||||
|
||||
// 构建请求数据
|
||||
requestData := &PortraitSetRequest{
|
||||
FromAccount: userId,
|
||||
ProfileItems: profileItem,
|
||||
}
|
||||
|
||||
// 将请求数据转换为 JSON
|
||||
requestBody, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return false, errors.New("设置im资料失败")
|
||||
}
|
||||
|
||||
// 构建请求 URL
|
||||
res, result := getRequestUrlParams("administrator")
|
||||
if res != true {
|
||||
return false, errors.New(result)
|
||||
}
|
||||
url := config.C.Im.ImBaseUrl + "v4/profile/portrait_set?" + result
|
||||
|
||||
_, err = postRequest(url, requestBody)
|
||||
if err != nil {
|
||||
return false, errors.New(err.Error())
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user