初始化提交

This commit is contained in:
2023-08-31 17:32:45 +08:00
parent 88dc1bbe74
commit 7826652e11
101 changed files with 6127 additions and 2 deletions
+120
View File
@@ -0,0 +1,120 @@
// Package aliyun 短信
package aliyun
import (
"encoding/json"
"errors"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"hospital-open-api/api/dao"
"hospital-open-api/api/model"
"hospital-open-api/config"
)
func createClient() (_result *dysmsapi20170525.Client, _err error) {
accessKeyId := config.C.Dysms.DysmsAccessKey
accessKeySecret := config.C.Dysms.DysmsAccessSecret
openapiConfig := &openapi.Config{
// 必填,您的 AccessKey ID
AccessKeyId: &accessKeyId,
// 必填,您的 AccessKey Secret
AccessKeySecret: &accessKeySecret,
}
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
openapiConfig.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{}
_result, _err = dysmsapi20170525.NewClient(openapiConfig)
return _result, _err
}
// SendSms 发送短信
func SendSms(phoneNumber, templateCode, sceneDesc string, templateParam map[string]interface{}) error {
client, err := createClient()
if err != nil {
return err
}
params, err := json.Marshal(templateParam)
if err != nil {
return err
}
sendSmsRequest := &dysmsapi20170525.SendSmsRequest{
PhoneNumbers: tea.String(phoneNumber),
SignName: tea.String("肝胆相照"),
TemplateCode: tea.String(templateCode),
TemplateParam: tea.String(string(params)),
}
tryErr := func() (e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
e = r
}
}()
// 初始化运行时配置。
runtime := &util.RuntimeOptions{}
// 读取超时
runtime.SetReadTimeout(10000)
// 连接超时
runtime.SetConnectTimeout(5000)
// 复制代码运行请自行打印 API 的返回值
response, err := client.SendSms(sendSmsRequest)
if err != nil {
return err
}
if response.Body == nil {
return errors.New("短信发送失败")
}
if response.Body.Code != nil && *response.Body.Code != "OK" {
if response.Body.Message != nil {
return errors.New(*response.Body.Message)
}
}
// 检测唯一值返回
if response.Body.RequestId == nil {
if response.Body.Message != nil {
return errors.New(*response.Body.Message)
}
}
// 记录log
logSms := &model.LogSms{
Type: 1,
Status: 1,
Phone: phoneNumber,
TemplateCode: templateCode,
ThirdCode: *response.Body.RequestId,
SceneDesc: sceneDesc,
Remarks: string(params),
}
logSmsDao := dao.LogSms{}
_, _ = logSmsDao.AddLogSmsUnTransaction(logSms)
return nil
}()
if tryErr != nil {
var sdkError = &tea.SDKError{}
if t, ok := tryErr.(*tea.SDKError); ok {
sdkError = t
} else {
sdkError.Message = tea.String(tryErr.Error())
}
// 如有需要,请打印 error
_, err = util.AssertAsString(sdkError.Message)
if err != nil {
return err
}
}
return err
}
+69
View File
@@ -0,0 +1,69 @@
package aliyun
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"hospital-open-api/config"
"strings"
"time"
)
// GetOssSignResponse 获取oss签名返回值
type GetOssSignResponse struct {
AccessId string `json:"access_id"` // 主键id
Host string `json:"host"`
Policy string `json:"policy"`
Signature string `json:"signature"`
Expire int64 `json:"expire"`
Callback string `json:"callback"`
Dir string `json:"dir"`
}
// GetOssSign 获取oss签名
func GetOssSign(dir string) (*GetOssSignResponse, error) {
// Endpoint := config.C.Oss.OssEndpoint
// accessKey := config.C.Oss.OssAccessKey
// accessSecret := config.C.Oss.OssAccessKeySecret
// client, err := oss.New(Endpoint, accessKey, accessSecret)
// if err != nil {
// return nil, err
// }
//
// bucket, err := client.Bucket(viper.GetString("aliyun.Bucket"))
// if err != nil {
// return "", err
// }
now := time.Now()
expire := 30 // 设置该policy超时时间是30s,即这个policy过了这个有效时间,将不能访问。
end := now.Add(time.Second * time.Duration(expire))
expiration := strings.Replace(end.Format("2006-01-02T15:04:05.000Z"), "+00:00", ".000Z", 1)
start := []interface{}{"starts-with", "$key", dir}
conditions := [][]interface{}{start}
arr := map[string]interface{}{
"expiration": expiration,
"conditions": conditions,
}
policy, _ := json.Marshal(arr)
base64Policy := base64.StdEncoding.EncodeToString(policy)
stringToSign := base64Policy
h := hmac.New(sha1.New, []byte(config.C.Oss.OssAccessKeySecret))
h.Write([]byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
response := &GetOssSignResponse{
AccessId: config.C.Oss.OssAccessKey,
Host: config.C.Oss.OssCustomDomainName,
Policy: base64Policy,
Signature: signature,
Expire: end.Unix(),
Callback: "",
Dir: dir,
}
return response, nil
}
+116
View File
@@ -0,0 +1,116 @@
package ca
import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hospital-open-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]interface{}) string {
keys := make([]string, 0, len(paramMap))
for k := range paramMap {
if k == "pdfFile" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var toSign string
for _, k := range keys {
v, ok := paramMap[k].(string)
if !ok {
return ""
}
toSign += v + "&"
}
toSign = strings.TrimSuffix(toSign, "&")
// Step 3: Calculate HMAC-SHA1 and convert to hex format
h := hmac.New(sha1.New, []byte(config.C.CaOnline.CaOnlineAppSecret))
h.Write([]byte(toSign))
signature := hex.EncodeToString(h.Sum(nil))
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
}
}
fmt.Println(body)
if len(body) == 0 {
return nil, nil
}
return body, nil
}
+327
View File
@@ -0,0 +1,327 @@
package ca
import (
"errors"
"hospital-open-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"` // 委托鉴证告知类型[数字证书申请告知]
}
// AddCloudCertRequest 新增云证书请求数据
type AddCloudCertRequest 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["orgDept"] = d.OrgDept
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("orgDept", d.OrgDept)
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("返回数据错误1")
}
certSerialnumber, ok := response["certSerialnumber"]
if !ok {
return nil, errors.New("返回数据错误2")
}
result := &EditCloudCertResponse{
CertBase64: certBase64.(string),
CertP7: certP7.(string),
CertSerialnumber: certSerialnumber.(string),
}
return result, nil
}
// AddCloudCert 新增云证书
func AddCloudCert(d *AddCloudCertRequest) (*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["orgDept"] = d.OrgDept
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("orgDept", d.OrgDept)
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
}
+422
View File
@@ -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")
)
+37
View File
@@ -0,0 +1,37 @@
// Package tencentIm 账号
package tencentIm
import (
"encoding/json"
"errors"
"hospital-open-api/config"
)
// CreateAccount 创建账号
func CreateAccount(userId, nickName, avatar string) (bool, error) {
// 构建请求数据
requestData := make(map[string]interface{})
requestData["UserID"] = userId
requestData["Nick"] = nickName
requestData["FaceUrl"] = avatar
// 将请求数据转换为 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/im_open_login_svc/account_import?" + result
_, err = postRequest(url, requestBody)
if err != nil {
return false, errors.New(err.Error())
}
return true, nil
}
+141
View File
@@ -0,0 +1,141 @@
package tencentIm
import (
"bytes"
"encoding/json"
"errors"
"hospital-open-api/config"
"io"
"math/rand"
"net/http"
"net/url"
"strconv"
"time"
)
// 请求返回值
type responseData struct {
ActionStatus string `json:"actionStatus"` // 请求处理的结果,“OK” 表示处理成功,“FAIL” 表示失败
ErrorCode int `json:"errorCode"` // 错误码,0表示成功,非0表示失败
ErrorInfo string `json:"errorInfo"` // 详细错误信息
}
// 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 _, ok := responseMap["ErrorCode"]; ok {
// errorCode := responseMap["ErrorCode"].(int)
// if errorCode != 0 {
// if errorInfo, ok := responseMap["ErrorInfo"].(string); ok {
// return nil, errors.New(errorInfo)
// } else {
// return nil, errors.New("请求im失败")
// }
// }
// } else {
// return nil, errors.New("请求im失败")
// }
//
// return responseMap, nil
// }
// 统一请求
func postRequest(url string, requestBody []byte) (*responseData, error) {
var responseData responseData
// 发起 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(body, &responseData)
if err != nil {
// json解析失败
return nil, err
}
if responseData.ErrorCode != 0 {
if responseData.ErrorInfo != "" {
return nil, errors.New(responseData.ErrorInfo)
} else {
return nil, errors.New("请求im失败")
}
}
return &responseData, nil
}
+53
View File
@@ -0,0 +1,53 @@
// Package tencentIm im资料
package tencentIm
import (
"encoding/json"
"errors"
"hospital-open-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
}
+89
View File
@@ -0,0 +1,89 @@
package verifyDun
import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// 请求返回值
type bankCardResultResponseData struct {
Code int `json:"code"` // 接口调用状态。200:正常;其它值:调用出错
Msg string `json:"msg"` // 结果说明。如果接口调用出错,那么返回错误描述。成功则返回 ok
Result bankCardResult `json:"result"` // 接口返回结果,各个接口自定义,数据结构参考具体文档说明
}
type bankCardResult struct {
Status int `json:"status"` // 认证结果,1-通过 2-不通过(原因见reasonType) 0-待定
ReasonType int `json:"reasonType"` // 原因详情
TaskId string `json:"taskId"` // 本次请求数据标识,可以根据该标识在控制台进行数据查询
IsPayed int `json:"isPayed"` // 本次请求是否收费标识,1代表收费,0代表不收费
}
// CheckBankCard 银行卡三/四要素认证
func CheckBankCard(name, bankCardNo, idCardNo string) (bool, error) {
formData := url.Values{}
formData.Set("name", name)
formData.Set("bankCardNo", bankCardNo)
formData.Set("idCardNo", idCardNo)
formData.Set("secretId", secretId)
formData.Set("businessId", "3cb726bd85104161b25613153c4fba7c")
formData.Set("version", "v1")
formData.Set("timestamp", strconv.FormatInt(time.Now().UnixNano()/1000000, 10))
formData.Set("nonce", string(make([]byte, 32)))
formData.Set("signature", GenSignature(formData))
resp, err := http.Post(apiUrl+"/"+version+"/bankcard/check", "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
if err != nil {
return false, errors.New("调用API接口失败:" + err.Error())
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
var responseData idCardResponseData
err = json.Unmarshal(body, &responseData)
if err != nil {
// json解析失败
return false, err
}
if responseData.Code != 200 {
if responseData.Msg != "" {
return false, errors.New(responseData.Msg)
} else {
return false, errors.New("身份证认证失败")
}
}
if responseData.Result.Status == 2 {
if responseData.Result.ReasonType == 2 {
return false, errors.New("持卡人信息与输入信息不一致")
} else if responseData.Result.ReasonType == 3 {
return false, errors.New("查无此银行卡")
} else if responseData.Result.ReasonType == 4 {
return false, errors.New("查无此身份证")
} else if responseData.Result.ReasonType == 5 {
return false, errors.New("手机号码格式不正确")
} else if responseData.Result.ReasonType == 6 {
return false, errors.New("银行卡号不正确")
} else if responseData.Result.ReasonType == 7 {
return false, errors.New("其他出错,请联系客服")
} else {
return false, errors.New("银行卡认证失败")
}
}
return true, nil
}
+32
View File
@@ -0,0 +1,32 @@
package verifyDun
import (
"crypto/md5"
"encoding/hex"
"net/url"
"sort"
)
const (
apiUrl = "https://verify.dun.163.com" // 本机认证服务身份证实人认证在线检测接口地址
version = "v1"
secretId = "0bcf9a5633eb9ca9d196583e67c3762b" // 产品密钥ID,产品标识
secretKey = "b31e8220d115b6531a22ee71d1e89936" // 产品私有密钥,服务端生成签名信息使用,请严格保管,避免泄露
)
// GenSignature 生成签名信息
func GenSignature(params url.Values) string {
var paramStr string
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
paramStr += key + params[key][0]
}
paramStr += secretKey
md5Reader := md5.New()
md5Reader.Write([]byte(paramStr))
return hex.EncodeToString(md5Reader.Sum(nil))
}
+82
View File
@@ -0,0 +1,82 @@
package verifyDun
import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// 请求返回值
type idCardResponseData struct {
Code int `json:"code"` // 接口调用状态。200:正常;其它值:调用出错
Msg string `json:"msg"` // 结果说明。如果接口调用出错,那么返回错误描述。成功则返回 ok
Result idCardResult `json:"result"` // 接口返回结果,各个接口自定义,数据结构参考具体文档说明
}
type idCardResult struct {
Status int `json:"status"` // 认证结果,1-通过 2-不通过(原因见reasonType) 0-待定
ReasonType int `json:"reasonType"` // 原因详情
TaskId string `json:"taskId"` // 本次请求数据标识,可以根据该标识在控制台进行数据查询
IsPayed int `json:"isPayed"` // 本次请求是否收费标识,1代表收费,0代表不收费
}
// CheckIdCard 实证认证
func CheckIdCard(name, cardNo string) (bool, error) {
formData := url.Values{}
formData.Set("name", name)
formData.Set("cardNo", cardNo)
formData.Set("secretId", secretId)
formData.Set("businessId", "45a8fd254b4649e9bd25d773ac7ab666")
formData.Set("version", "v1")
formData.Set("timestamp", strconv.FormatInt(time.Now().UnixNano()/1000000, 10))
formData.Set("nonce", string(make([]byte, 32)))
formData.Set("signature", GenSignature(formData))
resp, err := http.Post(apiUrl+"/"+version+"/idcard/check", "application/x-www-form-urlencoded", strings.NewReader(formData.Encode()))
if err != nil {
return false, errors.New("调用API接口失败:" + err.Error())
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
var responseData idCardResponseData
err = json.Unmarshal(body, &responseData)
if err != nil {
// json解析失败
return false, err
}
if responseData.Code != 200 {
if responseData.Msg != "" {
return false, errors.New(responseData.Msg)
} else {
return false, errors.New("身份证认证失败")
}
}
if responseData.Result.Status == 2 {
if responseData.Result.ReasonType == 2 {
return false, errors.New("输入姓名和身份证号不一致")
} else if responseData.Result.ReasonType == 3 {
return false, errors.New("查无此身份证")
} else if responseData.Result.ReasonType == 4 {
return false, errors.New("身份证照片信息与输入信息不一致")
} else {
return false, errors.New("身份证认证失败")
}
}
return true, nil
}
Binary file not shown.
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIELjCCAxagAwIBAgIUfewObFfg3HHwd/AvUkBlZq85vrswDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwMzAxMDExNzE3WhcNMjgwMjI4MDExNzE3WjCBhzETMBEGA1UEAwwK
MTYzNjY0NDI0ODEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL
DCrljJfkuqzmrKPmrKPnm7jnhaflgaXlurfnp5HmioDmnInpmZDlhazlj7gxCzAJ
BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAK7x9ywApasrs+xIt0XxHhYOK4xZeuNEj5o7pzTcZMRkd6CB
1eX2ydlUfDe+FcNS03cwLNtUeAfrZXlhA807b4HuReaFqCrbt87hfIF/lOBpePdN
sSr8Wi6OKfPakuLNZ4RCOdlvgxPMhOf2b9VFQwns8h72H6JrFz7xR+Heundy3KGH
kEoG2qcl7nKyhkUVSRSH4/yzsxsDIwkpXjEiSL87E+A6GKik7jphYc+vfV9NURiA
JwbVWbQMhpj3YLRxgXadLS4xMryB59fYKm+VFMNg/jSZG55Jz2DW02aR2h3KjegY
SGA9qMKojkFVOrZvxKWOvtEWw2JQ/1dRvzYpgw0CAwEAAaOBuTCBtjAJBgNVHRME
AjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDov
L2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUw
REJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJF
MTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IB
AQCBjjBeJTRtnsIISCfoQ7pj70dil5LTBNjpzV9swGOG7sY9dTMbHV0K2BUyRvbD
eY2fuCcr3Qt3L1RykyomKbxD4O6b/ZPLTkiq2m+hq34g2Ig7zUx0a0sNKf/NPAq6
6pVu5/XdIKYXz2OsByBI8aN2hkcUApmM1qlm+gb4FSmTzkdHaFRblhDlKfiRdAw8
T4yliaOFovqhf/S8o5Xa76kQMOb3uJ/oE4KX02kp/ig5wZKj9nay46AyovBXOj+Z
0oaeIPFDXCPzZV7LA4E45zDl43fL8r+9OzaVprRZ9ASO0cgnPsHyS+o/mpMEBOR4
NIwdIaqYRQ/Rh6eZQvrqDZ4r
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCu8fcsAKWrK7Ps
SLdF8R4WDiuMWXrjRI+aO6c03GTEZHeggdXl9snZVHw3vhXDUtN3MCzbVHgH62V5
YQPNO2+B7kXmhagq27fO4XyBf5TgaXj3TbEq/Foujinz2pLizWeEQjnZb4MTzITn
9m/VRUMJ7PIe9h+iaxc+8Ufh3rp3ctyhh5BKBtqnJe5ysoZFFUkUh+P8s7MbAyMJ
KV4xIki/OxPgOhiopO46YWHPr31fTVEYgCcG1Vm0DIaY92C0cYF2nS0uMTK8gefX
2CpvlRTDYP40mRueSc9g1tNmkdodyo3oGEhgPajCqI5BVTq2b8Sljr7RFsNiUP9X
Ub82KYMNAgMBAAECggEAT7uNyGs/FkVjykPV67WZ3bl1lZDOljgQLt4TNd9gubWE
ZA3om9efZULBHnKu3oeoQ0EcoJXd4tYhOHHD1szI5HHhP9AYtffPzSUtpqOsCZ9o
d2XcYlgDDgbTDgXHPkEZdcjtLrFJD0P+Ku5BR/U6OZLZQs0v28ltHc2/0iy91WQt
iB/NSf88dY1L5RVZc7dZfb8nFB2/BsITUXDFJpzdim3AXKvNYzDajs/yuoTp5r/b
2EOjUIGPP55W/8iPwbezgDToF79toum6V7yUpCq5P+QQvnnbJfoc1yXB48zHjmgP
I2P1cDVtFs+V9Edb6qsU3e4CMyBQg5Uykk3TUqZRIQKBgQDnXgGk8XnI1kG9jZMR
wMkra87Dx76vrBdM47n+c2I1xubM9WSeSUxtXGyryTwJ9i6/BUyc3Eyb2wi4Q8AK
1ZIEFhvIUkBJpUgevAksj4eX6z0qDwDc3ZhgAAWOplnOCCyJuOBKmZks4GaQb4Gv
/aSFVQ9jefOc5e9RIDNzkHFkaQKBgQDBkigvmusjad95Jt34TwyE4Dr9z2LzBker
6ebcyQmv4YdKvq0KaaVMrroH7pNu7CSFrj4CGqdS/Gg1BtK8Xaxn+gEWT6RPcsi1
mPwy/7oiK0GodzNI6RW5HDZEHKG2icEb4ycDnHeLfThKmP/gckPifjcJHrP5OX1A
V6qrq4iFBQKBgGdgB1gNVJ65rJHnCckq3Dd8advsCXUwbRC7x0S7hSwF/OWi1xwq
H+3VF/EBbsP8rRJIadzESa5xhUnfa5Trq9wLjMpKhdLh+IFS/r5cOvdT8fYy0e3d
TNHH8LO1+/YkjNHUOtLaIih88xah284ohDPWt5N4z7JQwkb7HkIKTb/RAoGARt51
7Afx8sM+WCLMva5jTPqzXl1hQsyXzO8T4N2RuFz/pXPt8pP/OvX1khXc0I2QSYkj
lq2feRiEJnXbDa/WATNc1ohOBfBmX2YlX56UzRG9Nip+EkGT/HPBwmohIq2Ij+c4
T3AnrGAqDdW6SLhM9k1zZNli1uofW0E9cSCaGOkCgYAI29eelAwUxpbDXJB6vSyr
LBvDV+C+/3OW4AjlJ5lWSzY8oMn21Xzp03MwXOXOSFuR6vTMkJDKJ3zIDHBt9vJ2
evNmfMKzqNdsvNjORb79GAZ0paBF1XGzXvPO9JSUi6oZxNg+pW8oxIzx0xFIgtZL
PxnzFj2IbraxYwuR1CI6rQ==
-----END PRIVATE KEY-----
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEFDCCAvygAwIBAgIUES/M0bnsyCknA6tzY8c9dLav3BowDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwMzAxMDExNzE2WhcNMjgwMjI4MDExNzE2WjBuMRgwFgYDVQQDDA9U
ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl
bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo
ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvM2YfnzwBvd+B5Ro1
z+LaCVYhxia9/hQwRhi5ag2HmeZZNgMNf4+bC75Nv+t3RW0lklbM9qJSBk03eEBG
75Q33VPGy//WCJZgXlHV+rZ7ejzWtwh9Muhd60HFXhIsM3pwlfWpPo6bkJie9Na0
wMsgg/8UxsNydhEZF6HFLdqY+zqGOjRRCduIyJcFhtrkjNUMIFAOSkHBaGJjmGrm
OXigAnYAsaD1VWLOoblA0HlOs144KQ/5Shj74Ggk2pFo/YDN+i5hazHZo9hZnjmX
G5BV3KDJD0k3Gn/qymhiLlzGsYW79P+BbsmE/M6jKIP2jxcDDdpek0Z6Lk0Cz/au
02UPAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB
kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv
aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4
RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0
MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAJX6C/QMYF0F3IiK9P8tW2DKN8y9vCU20
6Ws8u4bcO3AaiSmsdAJ6I1MyZkUg3dKijtnDbieY2P364IEp48TmI4k6UJwP4+/f
i0NseOm3BmAJ3mBoNmuFul+61opKpeV67AYQuhbehVA4cDPqKb/hP0tEcb6nefIO
mnys5GReLWLFV2XFR1h9QtsohPOEYlpBl6lmKHNoQZdAPtSHq2iVFLbuD7GMKoj7
Br2puJEQgxla1AsNxAYjGttEIO9I30+L5av7SKesSGXvL6G5eOvFrZl0S4EsNVz4
QeL4qmtFEKDi2eaxeyLuWe9TMI/IiI3ngekoDyTwdnUzadumbio+dg==
-----END CERTIFICATE-----
+63
View File
@@ -0,0 +1,63 @@
package weChat
import (
"context"
"errors"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"hospital-open-api/config"
"path/filepath"
)
type RefundRequest struct {
TransactionId string `json:"transaction_id" comment:"微信订单号"`
OutTradeNo string `json:"out_trade_no" comment:"商户订单号"`
OutRefundNo string `json:"out_refund_no" comment:"退款订单号"`
Reason string `json:"reason" comment:"退款原因"`
PaymentAmountTotal int64 `json:"payment_amount_total" comment:"退款金额"`
NotifyUrl string `json:"notify_url" comment:"回调地址"`
}
// Refund 退款
func (r RefundRequest) Refund() (*refunddomestic.Refund, error) {
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
certsDir := filepath.Join("extend/weChat/certs", "/1636644248/apiclient_key.pem")
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(certsDir)
if err != nil {
return nil, errors.New("微信签名生成失败")
}
ctx := context.Background()
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(config.C.Wechat.MchId, config.C.Wechat.MchCertificateSerialNumber, mchPrivateKey, config.C.Wechat.V3ApiSecret),
}
client, err := core.NewClient(ctx, opts...)
if err != nil {
return nil, errors.New(err.Error())
}
refundRequest := refunddomestic.CreateRequest{
TransactionId: core.String(r.TransactionId),
OutTradeNo: core.String(r.OutTradeNo),
OutRefundNo: core.String(r.OutRefundNo),
Reason: core.String(r.Reason),
NotifyUrl: core.String(r.NotifyUrl),
Amount: &refunddomestic.AmountReq{
Currency: core.String("CNY"),
Refund: core.Int64(r.PaymentAmountTotal),
Total: core.Int64(r.PaymentAmountTotal),
},
}
svc := refunddomestic.RefundsApiService{Client: client}
resp, _, err := svc.Create(ctx, refundRequest)
if err != nil {
return nil, errors.New(err.Error())
}
return resp, nil
}