Selaa lähdekoodia

feat:象屿统一身份

Roc 1 vuosi sitten
vanhempi
commit
b24a63774c

+ 25 - 10
config/config.go

@@ -8,6 +8,7 @@ type Config struct {
 	OracleJY OracleJY `mapstructure:"oracle_jy" json:"oracle_jy" yaml:"oracle_jy"`
 	Business Business `mapstructure:"business" json:"business" yaml:"business"`
 	Smm      Smm      `mapstructure:"smm" json:"smm" yaml:"smm"`
+	Xiangyu  Xiangyu  `mapstructure:"xiangyu" json:"xiangyu" yaml:"xiangyu"`
 }
 
 // Serve gin服务配置
@@ -25,16 +26,18 @@ type Serve struct {
 
 // Log 日志配置
 type Log struct {
-	Prefix         string `mapstructure:"prefix" json:"prefix" yaml:"prefix" description:"日志输出前缀"`
-	LogFile        bool   `mapstructure:"log-file" json:"logFile" yaml:"log-file" description:""`
-	Stdout         string `mapstructure:"stdout" json:"stdout" yaml:"stdout" description:""`
-	FileStdout     string `mapstructure:"file-stdout" json:"file-stdout" yaml:"file-stdout" description:""`
-	SaveMaxDay     int    `mapstructure:"save-max-day" json:"save-max-day" yaml:"save-max-day" description:"最多保留多少天的日志"`
-	CuttingDay     int    `mapstructure:"cutting-day" json:"cutting-day" yaml:"cutting-day" description:"相隔几天切割文件"`
-	LogDirPath     string `mapstructure:"log-dir-path" json:"log-dir-path" yaml:"log-dir-path" description:"日志目录"`
-	LogSoftLink    string `mapstructure:"log-soft-link" json:"log-soft-link" yaml:"log-soft-link" description:"日志软链接"`
-	BinlogDirPath  string `mapstructure:"binlog-dir-path" json:"binlog-dir-path" yaml:"binlog-dir-path" description:"binlog日志目录"`
-	BinlogSoftLink string `mapstructure:"binlog-soft-link" json:"binlog-soft-link" yaml:"binlog-soft-link" description:"binlog日志软链接"`
+	Prefix          string `mapstructure:"prefix" json:"prefix" yaml:"prefix" description:"日志输出前缀"`
+	LogFile         bool   `mapstructure:"log-file" json:"logFile" yaml:"log-file" description:""`
+	Stdout          string `mapstructure:"stdout" json:"stdout" yaml:"stdout" description:""`
+	FileStdout      string `mapstructure:"file-stdout" json:"file-stdout" yaml:"file-stdout" description:""`
+	SaveMaxDay      int    `mapstructure:"save-max-day" json:"save-max-day" yaml:"save-max-day" description:"最多保留多少天的日志"`
+	CuttingDay      int    `mapstructure:"cutting-day" json:"cutting-day" yaml:"cutting-day" description:"相隔几天切割文件"`
+	LogDirPath      string `mapstructure:"log-dir-path" json:"log-dir-path" yaml:"log-dir-path" description:"日志目录"`
+	LogSoftLink     string `mapstructure:"log-soft-link" json:"log-soft-link" yaml:"log-soft-link" description:"日志软链接"`
+	BinlogDirPath   string `mapstructure:"binlog-dir-path" json:"binlog-dir-path" yaml:"binlog-dir-path" description:"binlog日志目录"`
+	BinlogSoftLink  string `mapstructure:"binlog-soft-link" json:"binlog-soft-link" yaml:"binlog-soft-link" description:"binlog日志软链接"`
+	FilelogDirPath  string `mapstructure:"filelog-dir-path" json:"filelog-dir-path" yaml:"filelog-dir-path" description:"用户自主记录的日志目录"`
+	FilelogSoftLink string `mapstructure:"filelog-soft-link" json:"filelog-soft-link" yaml:"filelog-soft-link" description:"用户自主记录的日志软链接"`
 }
 
 // Mysql 数据库配置
@@ -78,3 +81,15 @@ type Smm struct {
 	Username string `mapstructure:"username" json:"username" yaml:"username" description:"smm账号"`
 	Password string `mapstructure:"password" json:"password" yaml:"password" description:"smm密码"`
 }
+
+// Xiangyu 象屿的配置
+type Xiangyu struct {
+	SystemCode           string `mapstructure:"system-code" json:"system-code" yaml:"system-code" description:"系统编码"`
+	UserSyncTarget       string `mapstructure:"user-sync-target" json:"user-sync-target" yaml:"user-sync-target" description:"用户同步平台编码"`
+	UserSyncHost         string `mapstructure:"user-sync-host" json:"user-sync-host" yaml:"user-sync-host" description:"用户同步平台地址"`
+	UserSyncAuthUserName string `mapstructure:"user-sync-auth-user-name" json:"user-sync-auth-user-name" yaml:"user-sync-auth-user-name" description:"用户统一身份的鉴权username"`
+	UserSyncAuthPwd      string `mapstructure:"user-sync-auth-pwd" json:"user-sync-auth-pwd" yaml:"user-sync-auth-pwd" description:"用户统一身份的鉴权password"`
+	UserAuthHost         string `mapstructure:"user-auth-host" json:"user-auth-host" yaml:"user-auth-host" description:"用户认证平台地址"`
+	UserKey              string `mapstructure:"user-key" json:"user-key" yaml:"user-key" description:"统一平台秘钥"`
+	DefaultRoleId        int    `mapstructure:"default-role-id" json:"default-role-id" yaml:"default-role-id" description:"默认的角色id"`
+}

+ 107 - 0
controller/xiangyu/auth.go

@@ -0,0 +1,107 @@
+package xiangyu
+
+import (
+	"eta/eta_bridge/controller/resp"
+	"eta/eta_bridge/global"
+	"eta/eta_bridge/logic/xiangyu"
+	xyReq "eta/eta_bridge/models/request/xiangyu"
+	"github.com/gin-gonic/gin"
+	"github.com/go-playground/validator/v10"
+)
+
+// GetToken
+// @Description: 获取token
+// @author: Roc
+// @receiver xc
+// @datetime 2024-01-23 17:06:34
+// @param c *gin.Context
+func (xc *XiangyuController) GetToken(c *gin.Context) {
+	var req xyReq.GetTokenReq
+	if e := c.Bind(&req); e != nil {
+		err, ok := e.(validator.ValidationErrors)
+		if !ok {
+			resp.FailData("参数解析失败", "Err:"+e.Error(), c)
+			return
+		}
+		resp.FailData("参数解析失败", err.Translate(global.Trans), c)
+		return
+	}
+	if req.Code == "" {
+		resp.FailMsg("请传入code码", "请输入指标code码", c)
+		return
+	}
+
+	result, err, errMsg := xiangyu.LoginEta(req.Code)
+
+	if err != nil {
+		resp.FailData(errMsg, err.Error(), c)
+		return
+	}
+	resp.OkData("获取成功", result, c)
+
+	return
+}
+
+// RefreshToken
+// @Description: 刷新token
+// @author: Roc
+// @receiver xc
+// @datetime 2024-01-23 17:06:25
+// @param c *gin.Context
+func (xc *XiangyuController) RefreshToken(c *gin.Context) {
+	var req xyReq.RefreshTokenReq
+	if e := c.Bind(&req); e != nil {
+		err, ok := e.(validator.ValidationErrors)
+		if !ok {
+			resp.FailData("参数解析失败", "Err:"+e.Error(), c)
+			return
+		}
+		resp.FailData("参数解析失败", err.Translate(global.Trans), c)
+		return
+	}
+	if req.RefreshToken == "" {
+		resp.FailMsg("请传入refresh_token", "请传入refresh_token", c)
+		return
+	}
+
+	err := xiangyu.RefreshToken(req.RefreshToken)
+
+	if err != nil {
+		resp.FailData("参数解析失败", err.Error(), c)
+		return
+	}
+	resp.Ok("刷新成功", c)
+	return
+}
+
+// RevokeToken
+// @Description: 销毁token
+// @author: Roc
+// @receiver xc
+// @datetime 2024-01-23 17:06:12
+// @param c *gin.Context
+func (xc *XiangyuController) RevokeToken(c *gin.Context) {
+	var req xyReq.RevokeTokenReq
+	if e := c.Bind(&req); e != nil {
+		err, ok := e.(validator.ValidationErrors)
+		if !ok {
+			resp.FailData("参数解析失败", "Err:"+e.Error(), c)
+			return
+		}
+		resp.FailData("参数解析失败", err.Translate(global.Trans), c)
+		return
+	}
+	if req.Token == "" {
+		resp.FailMsg("请传入token", "请传入token", c)
+		return
+	}
+
+	err := xiangyu.RevokeToken(req.Token)
+
+	if err != nil {
+		resp.FailData("参数解析失败", err.Error(), c)
+		return
+	}
+	resp.Ok("销毁成功", c)
+	return
+}

+ 42 - 0
controller/xiangyu/user.go

@@ -0,0 +1,42 @@
+package xiangyu
+
+import (
+	"eta/eta_bridge/controller/resp"
+	"eta/eta_bridge/logic/xiangyu"
+	"github.com/gin-gonic/gin"
+)
+
+// XiangyuController 象屿指标
+type XiangyuController struct{}
+
+// SyncUser
+// @Description: 全量同步用户
+// @author: Roc
+// @receiver xc
+// @datetime 2024-01-22 15:51:25
+// @param c *gin.Context
+func (xc *XiangyuController) SyncUser(c *gin.Context) {
+	err := xiangyu.SyncUser()
+	if err != nil {
+		resp.FailData("同步失败", err.Error(), c)
+		return
+	}
+	resp.Ok("同步成功", c)
+	return
+}
+
+// PullUser
+// @Description: 增量同步用户
+// @author: Roc
+// @receiver xc
+// @datetime 2024-01-23 17:13:07
+// @param c *gin.Context
+func (xc *XiangyuController) PullUser(c *gin.Context) {
+	err := xiangyu.PullUser()
+	if err != nil {
+		resp.FailData("同步失败", err.Error(), c)
+		return
+	}
+	resp.Ok("同步成功", c)
+	return
+}

+ 25 - 0
core/log.go

@@ -44,6 +44,9 @@ func init() {
 
 	//初始化mysql数据库日志
 	initMysqlLog()
+
+	// 初始化自定义输出的日志
+	initFileLog()
 }
 
 // initMysqlLog 初始化mysql数据库日志
@@ -58,6 +61,28 @@ func initMysqlLog() {
 	global.MYSQL_LOG = fileWriter
 }
 
+// initFileLog 初始化自定义日志
+func initFileLog() {
+	logConfig := global.CONFIG.Log
+
+	// 没有单独配置的话,那就走默认的日志输出
+	if logConfig.FilelogDirPath == "" || logConfig.FilelogSoftLink == `` {
+		global.FILE_LOG = global.LOG
+		return
+	}
+
+	logger := oplogging.MustGetLogger(Module)
+	var backends []oplogging.Backend
+	//注册控制台输出
+	//registerStdout(logConfig, &backends)
+
+	// 注册文件输出(日志文件)
+	_ = registerFile(logConfig, &backends, logConfig.FilelogDirPath, logConfig.FilelogSoftLink)
+	defaultBackend := oplogging.AddModuleLevel(backends[0])
+	logger.SetBackend(defaultBackend)
+	global.FILE_LOG = logger
+}
+
 func registerStdout(c config.Log, backends *[]oplogging.Backend) {
 	if c.Stdout != "" {
 		level, err := oplogging.LogLevel(c.Stdout)

+ 1 - 0
global/global.go

@@ -18,6 +18,7 @@ import (
 var (
 	CONFIG        config.Config //配置文件
 	LOG           *oplogging.Logger
+	FILE_LOG      *oplogging.Logger   // 自定义的输出日志
 	MYSQL         map[string]*gorm.DB //数据库连接配置
 	MYSQL_LOG     io.Writer
 	DEFAULT_MYSQL *gorm.DB      //默认数据库连接配置

+ 2 - 1
go.mod

@@ -1,6 +1,6 @@
 module eta/eta_bridge
 
-go 1.18
+go 1.19
 
 require (
 	github.com/astaxie/beego v1.12.3
@@ -60,6 +60,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
+	github.com/robfig/cron/v3 v3.0.1 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 	github.com/spf13/afero v1.9.5 // indirect
 	github.com/spf13/cast v1.5.1 // indirect

+ 1 - 0
init_serve/router.go

@@ -20,5 +20,6 @@ func InitRouter() (r *gin.Engine) {
 	routers.InitAuth(rBase)
 	routers.InitEtaTrial(rBase)
 	routers.InitIndexData(rBase)
+	routers.InitXiangyu(rBase)
 	return
 }

+ 19 - 1
init_serve/task.go

@@ -1,6 +1,11 @@
 package init_serve
 
-import "eta/eta_bridge/services"
+import (
+	"eta/eta_bridge/global"
+	"eta/eta_bridge/logic/xiangyu"
+	"eta/eta_bridge/services"
+	"github.com/robfig/cron/v3"
+)
 
 func InitTask() {
 	// 角色-用户-部门-分组同步
@@ -10,4 +15,17 @@ func InitTask() {
 	go services.ListenSyncGroup()
 
 	go services.ListenSyncUserEnRole()
+
+	// 开始定时任务
+	c := cron.New(cron.WithSeconds())
+
+	//每1小时检测一次,增量用户生成
+	if global.CONFIG.Xiangyu.UserSyncHost != `` {
+		_, err := c.AddFunc("0 0 * * * *", xiangyu.TaskPullUser)
+		if err != nil {
+			global.LOG.Error("CheckIndexCreateMerge err" + err.Error())
+		}
+	}
+
+	c.Start()
 }

+ 1 - 0
latest_filelog

@@ -0,0 +1 @@
+log/filelog/2024-01-23-00-00.log

+ 72 - 0
logic/xiangyu/auth.go

@@ -0,0 +1,72 @@
+package xiangyu
+
+import (
+	"eta/eta_bridge/models/eta"
+	"eta/eta_bridge/models/response"
+	"eta/eta_bridge/services"
+	"eta/eta_bridge/services/xiangyu"
+	"fmt"
+)
+
+// LoginEta
+// @Description: 获取eta的session
+// @author: Roc
+// @datetime 2024-01-23 17:44:15
+// @param code string
+// @return resp response.LoginResp
+// @return err error
+// @return errMsg string
+func LoginEta(code string) (resp response.LoginResp, err error, errMsg string) {
+	//  获取xiangyu token
+	tokenResp, err := xiangyu.GetToken(code)
+	if err != nil {
+		return
+	}
+
+	// 获取eta用户信息
+	adminInfo, err := eta.GetSysUserByOutId(tokenResp.Uid)
+	if err != nil {
+		return
+	}
+
+	// 生成登录session
+	resp, err, errMsg = services.CreateEtaSession(adminInfo)
+
+	return
+}
+
+func GetToken(code string) (err error) {
+	fmt.Println(xiangyu.GetToken(code))
+
+	return
+}
+
+func GetUserInfo(token string) (err error) {
+	fmt.Println(xiangyu.GetUserInfo(token))
+
+	return
+}
+
+func CheckToken(token string) (err error) {
+	fmt.Println(xiangyu.CheckToken(token))
+
+	return
+}
+
+func GetTokenInfo(token string) (err error) {
+	fmt.Println(xiangyu.GetTokenInfo(token))
+
+	return
+}
+
+func RefreshToken(token string) (err error) {
+	fmt.Println(xiangyu.RefreshToken(token))
+
+	return
+}
+
+func RevokeToken(token string) (err error) {
+	fmt.Println(xiangyu.RevokeToken(token))
+
+	return
+}

+ 388 - 0
logic/xiangyu/user.go

@@ -0,0 +1,388 @@
+package xiangyu
+
+import (
+	"errors"
+	"eta/eta_bridge/global"
+	"eta/eta_bridge/models/eta"
+	"eta/eta_bridge/services/xiangyu"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// SyncUser
+// @Description: 全量同步用户
+// @author: Roc
+// @datetime 2024-01-22 10:28:39
+// @return err error
+func SyncUser() (err error) {
+	if global.CONFIG.Xiangyu.DefaultRoleId <= 0 {
+		err = errors.New("默认角色ID不能为空")
+		return
+	}
+	// 指定的角色
+	roleInfo, err := eta.GetSysRoleById(global.CONFIG.Xiangyu.DefaultRoleId)
+	if err != nil {
+		return
+	}
+
+	// 获取所有部门和分组
+	departmentMap, groupMap, userMap, err := getAllDepartmentAndGroup()
+	if err != nil {
+		return
+	}
+
+	// 获取token
+	tokenId, err := xiangyu.Login()
+	if err != nil {
+		return
+	}
+	//tokenId := `228f7024-1966-47bf-86b5-9ec61ac149ed`
+
+	defer func() {
+		// 获取用户列表
+		tmpErr := xiangyu.Logout(tokenId)
+		if tmpErr != nil {
+			fmt.Println("退出失败:", tmpErr.Error())
+		}
+	}()
+
+	for {
+		// 获取用户列表
+		userResp, tmpErr := xiangyu.SyncTask(tokenId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		// 没有数据了,那么结束同步了
+		if userResp == nil || userResp.Out.ObjectCode == `` {
+			return
+		}
+
+		// 是否同步成功
+		var isSync bool
+
+		var etaUserId, message string
+
+		//  处理用户信息,将象屿用户与eta用户做关联
+		tmpErr = handleUser(userResp.Out.Data, userResp.Out.Id, departmentMap, groupMap, userMap, roleInfo)
+		if tmpErr == nil {
+			isSync = true
+			if etUserInfo, ok := userMap[userResp.Out.Data.Username]; ok {
+				etaUserId = fmt.Sprint(etUserInfo.AdminId)
+			}
+		} else {
+			message = tmpErr.Error()
+		}
+
+		// 结束单次同步
+		err = xiangyu.SyncFinish(tokenId, userResp.Out.ObjectCode, userResp.Out.ObjectType, userResp.Out.Id, etaUserId, message, isSync)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// PullUser
+// @Description: 增量同步用户
+// @author: Roc
+// @datetime 2024-01-23 18:01:27
+// @return err error
+func PullUser() (err error) {
+	if global.CONFIG.Xiangyu.DefaultRoleId <= 0 {
+		err = errors.New("默认角色ID不能为空")
+		return
+	}
+	defer func() {
+		if err != nil {
+			global.FILE_LOG.Info("同步增量用户失败:" + err.Error())
+		}
+	}()
+
+	// 指定的角色
+	roleInfo, err := eta.GetSysRoleById(global.CONFIG.Xiangyu.DefaultRoleId)
+	if err != nil {
+		return
+	}
+
+	// 获取所有部门和分组
+	departmentMap, groupMap, userMap, err := getAllDepartmentAndGroup()
+	if err != nil {
+		return
+	}
+
+	// 获取token
+	tokenId, err := xiangyu.Login()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		// 获取用户列表
+		tmpErr := xiangyu.Logout(tokenId)
+		if tmpErr != nil {
+			fmt.Println("退出失败:", tmpErr.Error())
+		}
+	}()
+
+	for {
+		// 获取增量用户列表
+		userResp, tmpErr := xiangyu.PullTask(tokenId)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+
+		// 没有数据了,那么结束同步了
+		if userResp == nil || userResp.Out.ObjectCode == `` {
+			return
+		}
+
+		// 是否同步成功
+		var isSync bool
+
+		var etaUserId, message string
+
+		//  处理用户信息,将象屿用户与eta用户做关联
+		tmpErr = handleUser(userResp.Out.Data, userResp.Out.Id, departmentMap, groupMap, userMap, roleInfo)
+		if tmpErr == nil {
+			isSync = true
+			if etUserInfo, ok := userMap[userResp.Out.Data.Username]; ok {
+				etaUserId = fmt.Sprint(etUserInfo.AdminId)
+			}
+		} else {
+			message = tmpErr.Error()
+		}
+
+		// 结束单次增量同步
+		err = xiangyu.PullFinish(tokenId, userResp.Out.TaskId, etaUserId, message, isSync)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// TaskPullUser
+// @Description: 定时增量同步用户
+// @author: Roc
+// @datetime 2024-01-23 18:03:04
+func TaskPullUser() {
+	PullUser()
+	return
+}
+
+// getAllDepartmentAndGroup
+// @Description: 获取所有的部门和分组
+// @author: Roc
+// @datetime 2024-01-18 17:39:14
+// @return departmentMap map[string]*eta.SysDepartment
+// @return groupMap map[string]*eta.SysGroup
+// @return err error
+func getAllDepartmentAndGroup() (departmentMap map[string]*eta.SysDepartment, groupMap map[string]*eta.SysGroup, userMap map[string]*eta.Admin, err error) {
+	// 获取所有的部门
+	departmentList, err := eta.GetAllDepartment()
+	if err != nil {
+		return
+	}
+
+	// 部门信息
+	departmentMap = make(map[string]*eta.SysDepartment)
+	for _, department := range departmentList {
+		departmentMap[department.OutID] = department
+	}
+
+	// 获取所有的分组
+	groupList, err := eta.GetAllSysGroup()
+	if err != nil {
+		return
+	}
+
+	// 部门信息
+	groupMap = make(map[string]*eta.SysGroup)
+	for _, group := range groupList {
+		groupMap[group.OutID] = group
+	}
+
+	// 获取所有的用户
+	adminList, err := eta.GetAllSysUser()
+	if err != nil {
+		return
+	}
+
+	// 部门信息
+	userMap = make(map[string]*eta.Admin)
+	for _, admin := range adminList {
+		userMap[admin.AdminName] = admin
+	}
+
+	return
+}
+
+// handleUser
+// @Description: 用户信息处理
+// @author: Roc
+// @datetime 2024-01-18 18:00:04
+// @param xyUserInfo xiangyu.UserData
+// @param departmentMap map[string]*eta.SysDepartment
+// @param groupMap map[string]*eta.SysGroup
+// @param userMap map[string]*eta.Admin
+// @return err error
+func handleUser(xyUserInfo xiangyu.UserData, xyUserId string, departmentMap map[string]*eta.SysDepartment, groupMap map[string]*eta.SysGroup, userMap map[string]*eta.Admin, roleInfo *eta.SysRole) (err error) {
+	// 判断公司id是否存在,如果不存在,那么就去创建
+	departmentInfo, ok := departmentMap[xyUserInfo.CompanyId]
+	if !ok {
+		departmentInfo = &eta.SysDepartment{
+			DepartmentId:   0,
+			DepartmentName: xyUserInfo.CompanyName,
+			OutID:          xyUserInfo.CompanyId,
+			CreateTime:     time.Now(),
+			Sort:           0,
+		}
+
+		// 如果没有的话,那么就创建该部门
+		err = departmentInfo.Create()
+		if err != nil {
+			return
+		}
+
+		departmentMap[xyUserInfo.CompanyId] = departmentInfo
+	}
+
+	// 判断部门id是否存在,如果不存在,那么就去创建
+	groupInfo, ok := groupMap[xyUserInfo.DepartId]
+	if !ok {
+		groupInfo = &eta.SysGroup{
+			GroupId:      0,
+			DepartmentId: departmentInfo.DepartmentId,
+			GroupName:    xyUserInfo.DepartmentName,
+			CreateTime:   time.Now(),
+			ParentId:     0,
+			Sort:         0,
+			OutID:        xyUserInfo.DepartId,
+		}
+
+		// 如果没有的话,那么就创建该分组
+		err = groupInfo.Create()
+		if err != nil {
+			return
+		}
+
+		groupMap[xyUserInfo.DepartId] = groupInfo
+	}
+
+	enabled := 1
+	if xyUserInfo.IsDisabled {
+		enabled = 0
+	}
+	// 判断用户是否存在,如果不存在,那么就去创建
+	userInfo, ok := userMap[xyUserInfo.Username]
+	if !ok {
+
+		userInfo = &eta.Admin{
+			AdminId:     0,
+			AdminName:   xyUserInfo.Username,
+			AdminAvatar: "",
+			RealName:    xyUserInfo.FullName,
+			Password:    xyUserInfo.Password,
+			//LastUpdatedPasswordTime:   time.Time{},
+			Enabled: enabled,
+			Email:   "",
+			//LastLoginTime:             time.Time{},
+			CreatedTime:     time.Now(),
+			LastUpdatedTime: time.Now(),
+			//Role:            roleInfo.,
+			Mobile:         "",
+			RoleType:       0,
+			RoleId:         roleInfo.RoleId,
+			RoleName:       roleInfo.RoleName,
+			RoleTypeCode:   roleInfo.RoleTypeCode,
+			DepartmentId:   departmentInfo.DepartmentId,
+			DepartmentName: departmentInfo.DepartmentName,
+			GroupId:        groupInfo.GroupId,
+			GroupName:      groupInfo.GroupName,
+			Authority:      0,
+			Position:       xyUserInfo.PositionName,
+			//DisableTime:               time.Time{},
+			ChartPermission:           0,
+			EdbPermission:             0,
+			MysteelChemicalPermission: 0,
+			OpenId:                    "",
+			UnionId:                   "",
+			PredictEdbPermission:      0,
+			Province:                  "",
+			ProvinceCode:              "",
+			City:                      "",
+			CityCode:                  "",
+			EmployeeId:                xyUserInfo.EmployeeNo,
+			TelAreaCode:               "",
+			OutID:                     xyUserId,
+		}
+
+		if strings.Contains(roleInfo.RoleTypeCode, "researcher") {
+			userInfo.Role = "researcher"
+		} else if strings.Contains(userInfo.RoleTypeCode, "seller") {
+			userInfo.Role = "sales"
+		} else {
+			userInfo.Role = "admin"
+		}
+
+		// 如果没有的话,那么就创建该用户
+		err = userInfo.Create()
+		if err != nil {
+			return
+		}
+
+	} else {
+		updateColList := make([]string, 0)
+
+		//状态
+		if userInfo.Enabled != enabled {
+			updateColList = append(updateColList, "Enabled")
+			userInfo.Enabled = enabled
+		}
+
+		// 部门
+		if userInfo.DepartmentId != departmentInfo.DepartmentId {
+			updateColList = append(updateColList, "DepartmentId", "DepartmentName")
+			userInfo.DepartmentId = departmentInfo.DepartmentId
+			userInfo.DepartmentName = departmentInfo.DepartmentName
+		}
+
+		// 分组
+		if userInfo.GroupId != groupInfo.GroupId {
+			updateColList = append(updateColList, "GroupId", "GroupName")
+			userInfo.GroupId = groupInfo.GroupId
+			userInfo.GroupName = groupInfo.GroupName
+		}
+
+		// 职位
+		if userInfo.Position != xyUserInfo.PositionName {
+			updateColList = append(updateColList, "Position")
+			userInfo.Position = xyUserInfo.PositionName
+		}
+
+		// 姓名
+		if userInfo.RealName != xyUserInfo.FullName {
+			updateColList = append(updateColList, "RealName")
+			userInfo.RealName = xyUserInfo.FullName
+		}
+
+		if len(updateColList) > 0 {
+			updateColList = append(updateColList, "LastUpdatedTime")
+			userInfo.LastUpdatedTime = time.Now()
+			err = userInfo.Update(updateColList)
+			if err != nil {
+				return
+			}
+		}
+	}
+
+	userMap[xyUserInfo.Username] = userInfo
+
+	return
+}

+ 19 - 1
models/eta/admin.go

@@ -1,6 +1,7 @@
 package eta
 
 import (
+	"errors"
 	"eta/eta_bridge/global"
 	"time"
 )
@@ -13,7 +14,7 @@ type Admin struct {
 	RealName                  string    `gorm:"column:real_name;type:varchar(60)" json:"real_name"`
 	Password                  string    `gorm:"index:password;index:admin_pass;column:password;type:varchar(60);not null" json:"password"`
 	LastUpdatedPasswordTime   time.Time `gorm:"column:last_updated_password_time;type:datetime" json:"last_updated_password_time"`
-	Enabled                   int       `gorm:"uniqueIndex:un;column:enabled;type:tinyint(1);not null;default:1" json:"enabled"` // 1:有效,0:禁用
+	Enabled                   int       `gorm:"uniqueIndex:un;column:enabled;type:tinyint(1);not null" json:"enabled"` // 1:有效,0:禁用
 	Email                     string    `gorm:"column:email;type:varchar(60)" json:"email"`
 	LastLoginTime             time.Time `gorm:"column:last_login_time;type:datetime" json:"last_login_time"`                                        // 最近登陆时间
 	CreatedTime               time.Time `gorm:"index:created_time;column:created_time;type:datetime;default:CURRENT_TIMESTAMP" json:"created_time"` // 创建时间
@@ -43,6 +44,7 @@ type Admin struct {
 	CityCode                  string    `gorm:"column:city_code;type:varchar(100);default:''" json:"city_code"`                                           // 市编码
 	EmployeeId                string    `gorm:"column:employee_id;type:varchar(64);not null;default:''" json:"employee_id"`                               // 员工工号(钉钉/每刻报销)
 	TelAreaCode               string    `gorm:"column:tel_area_code;type:varchar(32);not null;default:'86'" json:"tel_area_code"`                         // 手机号区号
+	OutID                     string    `gorm:"column:out_id" json:"outId"`                                                                               // 外部id
 }
 
 func (m *Admin) TableName() string {
@@ -79,3 +81,19 @@ func DeleteSysUserByName(adminName string) (err error) {
 	err = global.MYSQL["hz_eta"].Exec(sql, adminName).Error
 	return
 }
+
+// GetAllSysUser 获取所有用户
+func GetAllSysUser() (items []*Admin, err error) {
+	err = global.MYSQL["hz_eta"].Find(&items).Error
+	return
+}
+
+// GetSysUserByOutId 通过外部id获取系统用户
+func GetSysUserByOutId(outId string) (item *Admin, err error) {
+	if outId == `` {
+		err = errors.New("outId不能为空")
+		return
+	}
+	err = global.MYSQL["hz_eta"].Where("out_id = ?", outId).First(&item).Error
+	return
+}

+ 7 - 0
models/eta/sys_department.go

@@ -8,6 +8,7 @@ import (
 type SysDepartment struct {
 	DepartmentId   int       `gorm:"primaryKey;column:department_id;type:int(11);not null" json:"department_id"`
 	DepartmentName string    `gorm:"unique;column:department_name;type:varchar(255);default:''" json:"department_name"` // 部门名称
+	OutID          string    `gorm:"column:out_id" json:"outId"`                                                        // 外部id
 	CreateTime     time.Time `gorm:"column:create_time;type:datetime" json:"create_time"`                               // 创建时间
 	Sort           int       `gorm:"column:sort;type:int(10);not null;default:0" json:"sort"`                           // 排序
 }
@@ -34,6 +35,12 @@ func GetDepartmentById(departmentId int) (item *SysDepartment, err error) {
 	return
 }
 
+// GetAllDepartment 获取所有部门
+func GetAllDepartment() (items []*SysDepartment, err error) {
+	err = global.MYSQL["hz_eta"].Find(&items).Error
+	return
+}
+
 // DeleteDepartmentById 主键删除部门
 func DeleteDepartmentById(departmentId int) (err error) {
 	sql := `DELETE FROM sys_department WHERE department_id = ? LIMIT 1`

+ 7 - 0
models/eta/sys_group.go

@@ -12,6 +12,7 @@ type SysGroup struct {
 	CreateTime   time.Time `gorm:"column:create_time;type:datetime" json:"create_time"`
 	ParentId     int       `gorm:"column:parent_id;type:int(11);default:0" json:"parent_id"`
 	Sort         int       `gorm:"column:sort;type:int(10);not null;default:0" json:"sort"` // 排序
+	OutID        string    `gorm:"column:out_id" json:"outId"`                              // 外部id
 }
 
 func (m *SysGroup) TableName() string {
@@ -36,6 +37,12 @@ func GetSysGroupByGroupId(groupId int) (item *SysGroup, err error) {
 	return
 }
 
+// GetAllSysGroup 获取所有的分组
+func GetAllSysGroup() (items []*SysGroup, err error) {
+	err = global.MYSQL["hz_eta"].Find(&items).Error
+	return
+}
+
 // DeleteGroupByGroupId 删除分组-根据主键
 func DeleteGroupByGroupId(groupId int) (err error) {
 	sql := `DELETE FROM sys_group WHERE group_id = ? LIMIT 1`

+ 16 - 0
models/request/xiangyu/auth.go

@@ -0,0 +1,16 @@
+package xiangyu
+
+// GetTokenReq 获取token请求体
+type GetTokenReq struct {
+	Code string `json:"code" form:"code" description:"code编码"`
+}
+
+// RefreshTokenReq 刷新token请求体
+type RefreshTokenReq struct {
+	RefreshToken string `json:"refresh_token" form:"refresh_token" description:"刷新token"`
+}
+
+// RevokeTokenReq 销毁token请求体
+type RevokeTokenReq struct {
+	Token string `json:"token" form:"token" description:"token"`
+}

+ 18 - 0
routers/xiangyu.go

@@ -0,0 +1,18 @@
+package routers
+
+import (
+	"eta/eta_bridge/controller/xiangyu"
+	"eta/eta_bridge/middleware"
+	"github.com/gin-gonic/gin"
+)
+
+// InitXiangyu 获取象屿
+func InitXiangyu(r *gin.RouterGroup) {
+	control := new(xiangyu.XiangyuController)
+	group := r.Group("xy/").Use(middleware.InternalToken())
+	group.GET("user/sync", control.SyncUser)
+	group.GET("user/pull", control.PullUser)
+	group.GET("auth/getToken", control.GetToken)
+	group.GET("auth/refreshToken", control.RefreshToken)
+	group.GET("auth/revokeToken", control.RevokeToken)
+}

+ 87 - 0
services/session.go

@@ -0,0 +1,87 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_bridge/models/eta"
+	"eta/eta_bridge/models/response"
+	"eta/eta_bridge/utils"
+	"time"
+)
+
+// CreateEtaSession
+// @Description: 创建ETA会话session
+// @author: Roc
+// @datetime 2024-01-23 17:42:54
+// @param sysUser *eta.Admin
+// @return login *response.LoginResp
+// @return err error
+// @return errMsg string
+func CreateEtaSession(sysUser *eta.Admin) (login response.LoginResp, err error, errMsg string) {
+	errMsg = `登录失败`
+	login = response.LoginResp{}
+
+	var token string
+	account := utils.MD5(sysUser.AdminName)
+	// 获取用户未过期的session, 避免过于频繁生成token
+	expired := time.Now().AddDate(0, 0, 1).Format(utils.FormatDateTime)
+	session, _ := eta.GetUserUnexpiredSysSession(sysUser.AdminName, expired)
+	if session != nil && session.AccessToken != "" {
+		token = session.AccessToken
+	} else {
+		token = utils.GenToken(account)
+		sysSession := new(eta.SysSession)
+		sysSession.UserName = sysUser.AdminName
+		sysSession.SysUserId = sysUser.AdminId
+		sysSession.ExpiredTime = time.Now().AddDate(0, 0, 90)
+		sysSession.IsRemember = 1
+		sysSession.CreatedTime = time.Now()
+		sysSession.LastUpdatedTime = time.Now()
+		sysSession.AccessToken = token
+		if e := eta.AddSysSession(sysSession); e != nil {
+			errMsg = "新增session失败, err: "
+			err = errors.New("新增session失败, err: " + e.Error())
+			return
+		}
+	}
+
+	login.Authorization = token
+	login.Authorization = "authorization=" + token + "$account=" + account
+	login.RealName = sysUser.RealName
+	login.AdminId = sysUser.AdminId
+	login.AdminName = sysUser.AdminName
+	login.RoleName = sysUser.RoleName
+	login.SysRoleTypeCode = sysUser.RoleTypeCode //系统角色编码
+	login.RoleTypeCode = sysUser.RoleTypeCode
+	login.Authority = sysUser.Authority
+
+	// 判断实际的角色类型
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleName == utils.ROLE_NAME_FICC_DIRECTOR {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+
+	// 角色产品ID
+	productId := GetProductId(sysUser.RoleTypeCode)
+	productIdName := map[int]string{
+		0:                             "admin",
+		utils.COMPANY_PRODUCT_FICC_ID: utils.COMPANY_PRODUCT_FICC_NAME,
+		utils.COMPANY_PRODUCT_RAI_ID:  utils.COMPANY_PRODUCT_RAI_NAME,
+	}
+	login.ProductName = productIdName[productId]
+
+	return
+}

+ 372 - 0
services/xiangyu/auth.go

@@ -0,0 +1,372 @@
+package xiangyu
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_bridge/global"
+	"fmt"
+	"io"
+	"net/http"
+	"strings"
+)
+
+// ErrResp
+// @Description: 错误信息返回
+type ErrResp struct {
+	ErrCode string `json:"errcode" description:"失败编码 1001:缺少参数client_id, 2001:缺少参数access_token, 2006:缺少参数uid, 2002:参数access _token,不正确或过期, 1005:参数client_id非法"`
+	Msg     string `json:"msg" description:"失败信息"`
+}
+
+// GetTokenResp
+// @Description: 获取token返回
+type GetTokenResp struct {
+	ErrResp
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	Uid          string `json:"uid"`
+	ExpiresIn    int    `json:"expires_in"`
+}
+
+// GetToken
+// @Description: 获取token信息
+// @author: Roc
+// @datetime 2024-01-23 15:40:56
+// @param code string
+// @return resp *GetTokenResp
+// @return err error
+func GetToken(code string) (resp *GetTokenResp, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/getToken?client_id=%s&grant_type=authorization_code&code=%s&client_secret=%s`, global.CONFIG.Xiangyu.SystemCode, code, global.CONFIG.Xiangyu.UserKey)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpPostAuth(urlPath, ``, "application/json")
+	if err != nil {
+		return
+	}
+
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.AccessToken == "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	return
+}
+
+// UserInfoResp
+// @Description: 用户信息
+type UserInfoResp struct {
+	ErrResp
+
+	Country     string `json:"country"`
+	SorgId      string `json:"sorgId"`
+	UpdateDate  string `json:"updateDate"`
+	Mail        string `json:"mail"`
+	Gender      string `json:"gender"`
+	LoginType   string `json:"loginType"`
+	Nation      string `json:"nation"`
+	DisplayName string `json:"displayName" description:"姓名"`
+	Uid         string `json:"uid" description:"唯一标识"`
+	LoginName   string `json:"loginName" description:"登录账号"`
+	//PositionNumber          string   `json:"positionNumber"`
+	//Title                   string   `json:"title"`
+	//LoginInterceptFlagTwo   int           `json:"loginInterceptFlagTwo"`
+	//LoginInterceptFlagFour  int           `json:"loginInterceptFlagFour"`
+	//EmployeeNumber          string   `json:"employeeNumber"`
+	//ChangePwdAt             string        `json:"changePwdAt"`
+	////SpRoleList              []interface{} `json:"spRoleList"`
+	//PwdPolicy               string        `json:"pwdPolicy"`
+	//LoginInterceptFlagFive  int           `json:"loginInterceptFlagFive"`
+	//LoginInterceptFlagThree int           `json:"loginInterceptFlagThree"`
+	//IdentityNumber          string  `json:"identityNumber"`
+	//IdentityType            string   `json:"identityType"`
+	//SecAccValid             int           `json:"secAccValid"`
+	//PinyinShortName         string   `json:"pinyinShortName"`
+	//OrgNumber               string   `json:"orgNumber"`
+	//WechatNo                string   `json:"wechatNo"`
+	//OrgNamePath             string        `json:"orgNamePath"`
+	//PasswordModifyRequired  int           `json:"passwordModifyRequired"`
+	//BirthDay                string        `json:"birthDay"`
+	//GivenName               string   `json:"givenName"`
+	//Mobile                  string        `json:"mobile"`
+	//LoginInterceptFlagOne   int           `json:"loginInterceptFlagOne"`
+	//CertSn                  interface{}   `json:"certSn"`
+	//EmployeeType            interface{}   `json:"employeeType"`
+	//OrgCodePath             interface{}   `json:"orgCodePath"`
+	//OtpKey                  interface{}   `json:"otpKey"`
+	//PositionStatus          interface{}   `json:"positionStatus"`
+	//DepartmentNumber        interface{}   `json:"departmentNumber"`
+	//CertDn                  interface{}   `json:"certDn"`
+	//SpNameList              []string      `json:"spNameList"`
+	//IsPassRemind            int           `json:"isPassRemind"`
+}
+
+// GetUserInfo
+// @Description: 获取用户信息
+// @author: Roc
+// @datetime 2024-01-23 15:49:38
+// @param token string
+// @return resp *UserInfoResp
+// @return err error
+func GetUserInfo(token string) (resp *UserInfoResp, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/getUserInfo?client_id=%s&access_token=%s`, global.CONFIG.Xiangyu.SystemCode, token)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpGetAuth(urlPath)
+	if err != nil {
+		return
+	}
+
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.ErrCode != "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	return
+}
+
+// CheckTokenResp
+// @Description: 校验token是否有效的返回
+type CheckTokenResp struct {
+	ErrResp
+	Result bool `json:"result" description:"校验结果,true 有效"`
+}
+
+// CheckToken
+// @Description: 校验token是否有效
+// @author: Roc
+// @datetime 2024-01-23 15:54:16
+// @param token string
+// @return isOk bool
+// @return err error
+func CheckToken(token string) (isOk bool, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/checkTokenValid?&access_token=%s`, token)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpGetAuth(urlPath)
+	if err != nil {
+		return
+	}
+
+	var resp *CheckTokenResp
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.ErrCode != "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	if resp.Result {
+		isOk = true
+	}
+
+	return
+}
+
+// GetTokenInfo
+// @Description: 查询授权信息
+// @author: Roc
+// @datetime 2024-01-23 15:57:04
+// @param token string
+// @return resp *GetTokenResp
+// @return err error
+func GetTokenInfo(token string) (resp *GetTokenResp, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/getTokenInfo?&access_token=%s`, token)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpGetAuth(urlPath)
+	if err != nil {
+		return
+	}
+
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.ErrCode != "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	return
+}
+
+// RefreshToken
+// @Description: 刷新token
+// @author: Roc
+// @datetime 2024-01-23 16:02:03
+// @param refreshToken string
+// @return resp *GetTokenResp
+// @return err error
+func RefreshToken(refreshToken string) (resp *GetTokenResp, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/refreshToken?client_id=%s&grant_type=refresh_token&client_secret=%s&refresh_token=%s`, global.CONFIG.Xiangyu.SystemCode, global.CONFIG.Xiangyu.UserKey, refreshToken)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpPostAuth(urlPath, ``, "application/json")
+	if err != nil {
+		return
+	}
+
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.ErrCode != "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	return
+}
+
+// RevokeToken
+// @Description: token销毁
+// @author: Roc
+// @datetime 2024-01-23 16:06:52
+// @param token string
+// @return isOk bool
+// @return err error
+func RevokeToken(token string) (isOk bool, err error) {
+	urlPath := fmt.Sprintf(`/idp/oauth2/revokeToken?&access_token=%s`, token)
+
+	if err != nil {
+		return
+	}
+	result, err := HttpGetAuth(urlPath)
+	if err != nil {
+		return
+	}
+
+	var resp *CheckTokenResp
+	//  解析响应结果
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.ErrCode != "" {
+		err = errors.New(fmt.Sprintf("响应代码:%s,错误信息:%s", resp.ErrCode, resp.Msg))
+		return
+	}
+
+	if resp.Result {
+		isOk = true
+	}
+
+	return
+}
+
+// HttpPostAuth
+// @Description: post请求
+// @author: Roc
+// @datetime 2024-01-23 16:20:10
+// @param urlPath string
+// @param postData string
+// @param params ...string
+// @return []byte
+// @return error
+func HttpPostAuth(urlPath, postData string, params ...string) ([]byte, error) {
+	if global.CONFIG.Xiangyu.UserAuthHost == `` {
+		return nil, errors.New("统一用户同步接口地址为空")
+	}
+	// 请求地址
+	postUrl := global.CONFIG.Xiangyu.UserAuthHost + urlPath
+
+	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)
+	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
+}
+
+// HttpGetAuth
+// @Description: get请求
+// @author: Roc
+// @datetime 2024-01-23 16:20:16
+// @param urlPath string
+// @return []byte
+// @return error
+func HttpGetAuth(urlPath string) ([]byte, error) {
+	if global.CONFIG.Xiangyu.UserAuthHost == `` {
+		return nil, errors.New("统一用户同步接口地址为空")
+	}
+	// 请求地址
+	getUrl := global.CONFIG.Xiangyu.UserAuthHost + urlPath
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", getUrl, nil)
+	if err != nil {
+		return nil, err
+	}
+	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("统一认证:地址:" + getUrl + ";\n返回参数:" + string(result))
+
+	//  解析返回参数,判断是否是json
+	if !json.Valid(result) {
+		err = errors.New("返回参数不是json格式")
+	}
+
+	return result, err
+}

+ 578 - 0
services/xiangyu/user.go

@@ -0,0 +1,578 @@
+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
+}