package xiangyu import ( "encoding/json" "errors" "eta/eta_bridge/global" "eta/eta_bridge/utils" "fmt" "io" "net/http" "strings" "time" ) // LoginReq // @Description: 登录请求参数 type LoginReq struct { SystemCode string `json:"systemCode" description:"来源系统"` In0 In0 `json:"in0"` IntegrationKey string `json:"integrationKey" description:"集成密钥"` } // In0 // @Description: 通用参数 type In0 struct { PageTotal string `json:"pageTotal" description:"总页数"` PageNo string `json:"pageNo" description:"当前请求页"` DocType string `json:"docType" description:"单据类型"` Property string `json:"property" description:"备用字段"` DocCode string `json:"docCode" description:"单据号码"` Source string `json:"source" description:"来源系统"` Target string `json:"target" description:"目标系统"` } // BaseResponse // @Description: 基础返回数据返回 type BaseResponse struct { ReturnCode string `json:"returnCode" description:"响应代码,成功--S,失败--F"` ReturnMsg string `json:"returnMsg" description:"响应消息"` Success bool `description:"true 执行成功,false 执行失败"` Message string `description:"消息"` } // LoginResp // @Description: 登录响应参数 type LoginResp struct { Out struct { ReturnCode string `json:"returnCode" description:"响应代码,成功--S,失败--F"` ReturnMsg string `json:"returnMsg" description:"响应消息"` TokenId string `json:"tokenId" description:"令牌id,login接口生成"` Success bool `description:"true 执行成功,false 执行失败"` Message string `description:"消息"` } `json:"out"` } // Login // @Description: 第三方应用系统集成统一身份管理平台,通过login接口进行身份验证,获取token,用于后续调用身份信息同步接口 // @author: Roc // @datetime 2024-01-16 16:20:10 // @return tokenId string // @return err error func Login() (tokenId string, err error) { urlPath := `/IAM/XYGIntegrationService/login/ProxyServices/loginPS` req := LoginReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "login", Property: "", //DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, IntegrationKey: global.CONFIG.Xiangyu.UserKey, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } // 解析响应结果 var resp *LoginResp err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", resp.Out.ReturnCode, resp.Out.ReturnMsg, resp.Out.Message)) return } if resp.Out.TokenId == "" { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", resp.Out.ReturnCode, resp.Out.ReturnMsg, resp.Out.Message)) return } tokenId = resp.Out.TokenId return } // LogoutReq // @Description: 清除tokenId的请求参数 type LogoutReq struct { SystemCode string `json:"systemCode" description:"来源系统"` In0 In0 `json:"in0"` TokenId string `json:"tokenId" description:"令牌id,login接口生成"` } // Logout // @Description: 第三方应用系统集成统一身份管理平台,身份信息同步完成后,调用logout接口清除tokenId,结束整个身份信息同步流程 // @author: Roc // @datetime 2024-01-16 13:53:12 // @param tokenId string // @return err error func Logout(tokenId string) (err error) { urlPath := `/IAM/XYGIntegrationService/logout/ProxyServices/logoutPS` req := LogoutReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "logout", Property: "", DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, TokenId: tokenId, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } // 解析响应结果 var resp *LoginResp err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", resp.Out.ReturnCode, resp.Out.ReturnMsg, resp.Out.Message)) return } return } // PullTaskResp // @Description: 开始同步用户的接口返回数据 type PullTaskResp struct { BaseResponse ObjectCode string `json:"objectCode" description:"对象代码,BIM中注册对象代码:账号:T_ACC;机构:T_ORC;岗位:T_POS"` Id string `json:"id" description:"身份管理平台内部对象ID"` TaskId string `json:"taskId" description:"身份管理平台下拉任务ID"` EffectOn string `json:"effectOn" description:"任务操作类型;创建:CREATED、更新:UPDATED、禁用:DISABLED、启用:ENABLED"` Timestamp int64 `json:"timestamp" description:"BIM当前时间戳"` ObjectType string `json:"objectType" description:"对象类型:账号:TARGET_ACCOUNT、机构;TARGET_ORGANIZATION、资源:TARGET_RESOURCE、角色:TARGET_ROLE"` Data string `json:"data" description:"任务数据,JSON格式,具体数据结构根据任务类型不同而不同"` } // AccountData // @Description: 账号对象信息 type AccountData struct { Username string `json:"username"` Password interface{} `json:"password"` Fullname string `json:"fullname"` IsDisabled bool `json:"isDisabled"` StartDate interface{} `json:"startDate"` EndDate interface{} `json:"endDate"` IsAdmin bool `json:"isAdmin"` CompanyEmail interface{} `json:"companyEmail"` CompanyPlate string `json:"companyPlate"` DepartName string `json:"departName"` Departld string `json:"departld"` CompanyName string `json:"companyName"` Companyld string `json:"companyld"` Sex string `json:"sex"` PyName string `json:"pyName"` Personld string `json:"personld"` IdCard interface{} `json:"idCard"` EmployeeNo string `json:"employeeNo"` Mobile interface{} `json:"mobile"` ScEmail interface{} `json:"ScEmail"` Email interface{} `json:"email"` } // SyncTaskResp // @Description: 全量同步的响应数据 // 1.objectType 为TARGET_ACCOUNT 时,表示下拉数据为账号信息。 // 2.objectType 为TARGET_ORGANIZATION 时,表示下拉数据为机构(公司和部门)信息。 // 3.objectType 为TARGET_RESOURCE并且objectCode为T_USR时,表示下拉数据为HR人员信息。 // 4.objectType为TARGET_RESOURCE 并且objectCode为T_POS 时,表示下拉数据为岗位信息。 // 5.objectType 为TARGET_RESOURCE并且objectCode为T_SEQ时,表示下拉数据为岗位序列信息 type SyncTaskResp struct { Out struct { ReturnCode string `json:"returnCode" description:"响应代码,成功--S,失败--F"` ReturnMsg string `json:"returnMsg" description:"响应消息"` ObjectCode string `json:"objectCode" description:"BIM中注册对象代码:账号:T_ACC;机构:T_ORC;岗位:T_POS"` Success bool `description:"true 执行成功,false 执行失败"` Message string `description:"消息"` Id string `json:"id"` Timestamp int64 `json:"timestamp"` ObjectType string `json:"objectType" description:"对象类型:账号:TARGET_ACCOUNT、机构;TARGET_ORGANIZATION、资源:TARGET_RESOURCE、角色:TARGET_ROLE"` } `json:"out"` } // SyncTaskUserResp // @Description: 全量同步的账号响应数据 type SyncTaskUserResp struct { Out struct { ReturnCode string `json:"returnCode" description:"响应代码,成功--S,失败--F"` ReturnMsg string `json:"returnMsg" description:"响应消息"` ObjectCode string `json:"objectCode" description:"BIM中注册对象代码:账号:T_ACC;机构:T_ORC;岗位:T_POS"` Success bool `description:"true 执行成功,false 执行失败"` Message string `description:"消息"` Id string `json:"id"` Timestamp int64 `json:"timestamp"` ObjectType string `json:"objectType" description:"对象类型:账号:TARGET_ACCOUNT、机构;TARGET_ORGANIZATION、资源:TARGET_RESOURCE、角色:TARGET_ROLE"` Data UserData `json:"data"` } `json:"out"` } // UserData // @Description: 用户账户信息 type UserData struct { Username string `json:"username" description:"统一登录名,和AD账号一致"` Password string `json:"password" description:"密码"` FullName string `json:"fullname" description:"姓名"` IsDisabled bool `json:"isDisabled" description:"状态(true禁用,false启用)"` StartDate interface{} `json:"startDate" description:""` EndDate interface{} `json:"endDate" description:""` PositionId string `json:"positionId" description:"HR人员中所属岗位ID(主岗职位记录中的)"` DepartId string `json:"departId" description:"HR人员中所属部门ID(主岗职位记录中的)"` CompanyId string `json:"companyId" description:"HR人员中所属公司ID(主岗职位记录中的)"` RefOrgid interface{} `json:"ref_orgid" description:""` PositionName string `json:"positionName" description:"HR人员中所属岗位名称(主岗职位记录中的)"` CompanyName string `json:"companyName" description:"HR人员中所属公司名称(主岗职位记录中的)"` DepartmentName string `json:"departmentName" description:"HR人员中所属部门名称(主岗职位记录中的)"` Mobile string `json:"mobile" description:"手机号"` OfficePhone string `json:"officePhone" description:""` Sex string `json:"sex" description:"性别(1男,2女)"` EmployeeNo string `json:"employeeNo" description:"员工号。"` } // SyncTask // @Description: 第三方应用系统集成统一身份管理平台,通过调用syncTask同步接口,从统一身份管理平台全量同步数据(账号、机构、岗位 // @author: Roc // @datetime 2024-01-18 13:30:38 // @param tokenId string // @return resp *BaseResponse // @return err error func SyncTask(tokenId string) (userResp *SyncTaskUserResp, err error) { urlPath := `/IAM/XYGIntegrationService/syncTask/ProxyServices/syncTaskPS` req := LogoutReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "syncTask", Property: "", DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, TokenId: tokenId, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } var baseResp *SyncTaskResp err = json.Unmarshal(result, &baseResp) if err != nil { return } if baseResp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", baseResp.Out.ReturnCode, baseResp.Out.ReturnMsg, baseResp.Out.Message)) return } // 如果不是账号信息的话,那么就过滤,不做处理 if baseResp.Out.ObjectType != "TARGET_ACCOUNT" { return } // 解析数据 获取账号信息 err = json.Unmarshal(result, &userResp) if err != nil { return } return } // SyncFinishReq // @Description: 结束同步的接口请求 type SyncFinishReq struct { SystemCode string `json:"systemCode" description:"来源系统"` In0 In0 `json:"in0"` TokenId string `json:"tokenId" description:"令牌id,login接口生成"` ObjectCode string `json:"objectCode" description:"对象代码,BIM中注册对象代码"` ObjectType string `json:"objectType" description:"对象类型:账号:TARGET__ACCOUNT、机构:TARGET_ORGANIZATION、资源:TARGET__RESOURCE、角色:TARGET_ROLE"` Id string `json:"id" description:"BIM中对象ID,从syncTask响应中获取"` Success bool `json:"success" description:"成功--true,失败--false"` Data interface{} `json:"data" description:"默认给空Map对象{)不能为null"` Guid string `json:"guid" description:"下游系统中对象主键字段success=true时必须"` Message string `json:"message" description:"success=false时必须"` } // SyncFinish // @Description: syncFinish 接口需要配合syncTask 接 口 同 时 使 用 ,syncFinish 接口作用是:应用系统调用syncTask 接口同步数据,并对数据做处理后,将结果通过syncFinish接口回写到统一身份管理平台 // @author: Roc // @datetime 2024-01-18 13:30:38 // @param tokenId string // @return resp *BaseResponse // @return err error func SyncFinish(tokenId, objectCode, objectType, id, guid, message string, success bool) (err error) { urlPath := `/IAM/XYGIntegrationService/syncFinish/ProxyServices/syncFinishPS` req := SyncFinishReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "syncFinish", Property: "", DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, TokenId: tokenId, ObjectCode: objectCode, ObjectType: objectType, Id: id, Success: success, Data: struct{}{}, Guid: guid, Message: message, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } // 解析响应结果 var resp *LoginResp err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", resp.Out.ReturnCode, resp.Out.ReturnMsg, resp.Out.Message)) return } return } // PullTaskUserResp // @Description: 增量同步的账号响应数据 type PullTaskUserResp struct { Out struct { ReturnCode string `json:"returnCode" description:"响应代码,成功--S,失败--F"` ReturnMsg string `json:"returnMsg" description:"响应消息"` ObjectCode string `json:"objectCode" description:"BIM中注册对象代码:账号:T_ACC;机构:T_ORC;岗位:T_POS"` Success bool `description:"true 执行成功,false 执行失败"` Message string `description:"消息"` Id string `json:"id" description:"身份管理平台内部对象ID"` Timestamp int64 `json:"timestamp"` ObjectType string `json:"objectType" description:"对象类型:账号:TARGET_ACCOUNT、机构;TARGET_ORGANIZATION、资源:TARGET_RESOURCE、角色:TARGET_ROLE"` Data UserData `json:"data"` GUid string `json:"guid" description:"下游数据主键,下游系统下拉到的数据后,若要做更新、启 用、禁用操作,可根据此字段查询到对应记录。"` EffectOn string `json:"effectOn" description:"作用点,任务操作类型;创建:CREATED、更新:UPDATED、禁用:DISABLED、启用:ENABLED。"` TaskId string `json:"Taskid" description:"任务id,身份管理平台下拉任务ID"` } `json:"out"` } // PullTask // @Description: 第三方应用系统集成统一身份管理平台,通过调用pulITask 下拉接口,从统一身份管理平台获取身份信息(机构、账号、岗位)。 注:下拉接口只会从统一身份管理平台下拉变更(新增、更新、启用、禁用)的记录,并且每一条变更记录在统一身份管理平台,都是 以待处理任务形式存在。 pullTask 接口下拉逻辑为:每调用一次下拉接口,获取一条待处理任务(任务中有详细身份数据)。当接口响应中 "objectCode" 为N U L L 时 , 表 示 统 一 身 份 管 理 平 台 待 处 理 任 务 全 部 下 拉 完 成 。 // @Description: 这个接口只会返回一条数据,所以同步账号的时候,需要循环调用 // @author: Roc // @datetime 2024-01-16 14:04:47 // @param tokenId string // @return resp *BaseResponse // @return err error func PullTask(tokenId string) (userResp *PullTaskUserResp, err error) { urlPath := `/IAM/XYGIntegrationService/pullTask/ProxyServices/pullTaskPS` req := LogoutReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "pullTask", Property: "", DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, TokenId: tokenId, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } var baseResp *SyncTaskResp err = json.Unmarshal(result, &baseResp) if err != nil { return } if baseResp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", baseResp.Out.ReturnCode, baseResp.Out.ReturnMsg, baseResp.Out.Message)) return } // 如果不是账号信息的话,那么就过滤,不做处理 if baseResp.Out.ObjectType != "TARGET_ACCOUNT" { return } // 解析数据 获取账号信息 err = json.Unmarshal(result, &userResp) if err != nil { return } return } type PullFinishReq struct { SystemCode string `json:"systemCode" description:"来源系统"` In0 In0 `json:"in0"` TokenId string `json:"tokenId" description:"令牌id,login接口生成"` TaskId string `json:"taskId" description:"任务ID,pullTask获取的下拉任务ID"` Guid string `json:"guid" description:"唯一标识,第三方系统中对象唯一标识success=true时必须"` Success bool `json:"success" description:"true 成功,false 失败"` Message string `json:"message" description:"错误消息,success=false时必须"` } // PullFinish // @Description: pullFinish接口作用是:应用系统调用pullTask 接口下拉数据,并对数据做处理后,将结果通过pullFinish接口回写到统一身份管理平台,统一身份管理平台根据结果标记下拉任务状态。 // @Description: pullFinish接口需要配合pullTask 接口同时使用。 // @author: Roc // @datetime 2024-01-16 14:17:27 // @param tokenId string // @param taskId string // @param guid string // @param success bool // @param message string // @return resp *BaseResponse // @return err error func PullFinish(tokenId, taskId, guid, message string, success bool) (err error) { urlPath := `/IAM/XYGIntegrationService/pullFinish/ProxyServices/pullFinishPS` req := PullFinishReq{ SystemCode: global.CONFIG.Xiangyu.SystemCode, In0: In0{ PageTotal: "", PageNo: "", DocType: "pullFinish", Property: "", DocCode: getDocCode(), Source: global.CONFIG.Xiangyu.SystemCode, Target: global.CONFIG.Xiangyu.UserSyncTarget, }, TokenId: tokenId, TaskId: taskId, Guid: guid, Success: success, Message: message, } postData, err := json.Marshal(req) if err != nil { return } result, err := HttpPost(urlPath, string(postData), "application/json") if err != nil { return } // 解析响应结果 var resp *LoginResp err = json.Unmarshal(result, &resp) if err != nil { return } if resp.Out.Success == false { err = errors.New(fmt.Sprintf("响应代码:%s,响应消息:%s,错误信息:%s", resp.Out.ReturnCode, resp.Out.ReturnMsg, resp.Out.Message)) return } return } // getDocCode // @Description: 获取单据号码 // @author: Roc // @datetime 2024-01-16 10:29:02 // @return string func getDocCode() string { return fmt.Sprintf("%s-%s", time.Now().Format(utils.FormatDateTimeUnSpace), utils.GetRandStringNoSpecialChar(32)) } func HttpPost(urlPath, postData string, params ...string) ([]byte, error) { if global.CONFIG.Xiangyu.UserSyncHost == `` { return nil, errors.New("统一用户同步接口地址为空") } // 请求地址 postUrl := global.CONFIG.Xiangyu.UserSyncHost + urlPath //body := strings.NewReader(postData) body := io.NopCloser(strings.NewReader(postData)) client := &http.Client{} req, err := http.NewRequest("POST", postUrl, body) if err != nil { return nil, err } contentType := "application/x-www-form-urlencoded;charset=utf-8" if len(params) > 0 && params[0] != "" { contentType = params[0] } req.Header.Set("content-Type", contentType) req.SetBasicAuth(global.CONFIG.Xiangyu.UserSyncAuthUserName, global.CONFIG.Xiangyu.UserSyncAuthPwd) resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() result, err := io.ReadAll(resp.Body) if err != nil { return nil, err } // 日志记录 global.FILE_LOG.Debug("统一身份:地址:" + postUrl + ";\n请求参数:" + postData + ";\n返回参数:" + string(result)) // 解析返回参数,判断是否是json if !json.Valid(result) { err = errors.New("返回参数不是json格式") } return result, err }