diff --git a/src/bean/vo/response/login.go b/src/bean/vo/response/login.go index fdab7a84debba873e7899b477c07b225f594642c..f7f39c10890c7af3e04b36657b03421787c2d827 100644 --- a/src/bean/vo/response/login.go +++ b/src/bean/vo/response/login.go @@ -4,3 +4,22 @@ type Captcha struct { Id string `json:"id"` Captcha string `json:"captcha"` } + +type LocationResult struct { + Status int `json:"status"` + Message string `json:"message"` + Result Location `json:"result"` +} + +type Location struct { + Ip string `json:"ip"` + AdInfo AdInfo `json:"ad_info"` +} + +type AdInfo struct { + Nation string `json:"nation"` + Province string `json:"province"` + City string `json:"city"` + District string `json:"district"` + Adcode string `json:"adode"` +} diff --git a/src/bean/vo/response/system_organization.go b/src/bean/vo/response/system_organization.go index 199fc3850ff95f287c06dc36f5b021f0c6bfb24e..2d5b8ab482c4f2d094b3c31a8cb4729b73177ca0 100644 --- a/src/bean/vo/response/system_organization.go +++ b/src/bean/vo/response/system_organization.go @@ -24,12 +24,14 @@ type SystemOrganizationTree struct { // 组织管理员列表 type OrgAdminUser struct { - Id int `json:"id" xorm:"pk autoincr" ` // id + Id int `json:"id" xorm:"pk autoincr" ` // id + Name string `json:"name"` SystemAccount string `json:"system_account" xorm:"system_account"` // 系统账号 SystemRole []string `json:"system_role" xorm:"-"` // 系统角色 CreatedTime jsontime.Time `json:"created_time" xorm:"created" ` // 创建时间 State int `json:"state" xorm:"state"` // 状态0禁用1启用 Phone string `json:"phone" xorm:"phone"` // 手机号 + OrgName string `json:"org_name" xorm:"-"` // 手机号 } // 账号详情 diff --git a/src/common/conf/options.go b/src/common/conf/options.go index a788e8aabec102b5c3327c2dccee11ba636ad2df..7e170c597c80e613ddcbddfcaa825f2d1ecd0ce8 100644 --- a/src/common/conf/options.go +++ b/src/common/conf/options.go @@ -33,6 +33,8 @@ type Config struct { //TempDirPrefix string PrometheusHost string AccessRuleModeKey string + LocationUrl string + LocationKey string } const ( diff --git a/src/controller/system_user.go b/src/controller/system_user.go index 872ce0329388e2ec301a96479cdbd8a01dbd141e..426fa037bb0046d2938ee1c245c948ac06d5c9f9 100644 --- a/src/controller/system_user.go +++ b/src/controller/system_user.go @@ -121,6 +121,17 @@ func CheckRepetition(c *gin.Context) { SendJsonResponse(c, resp.OK, nil) } +// 运维人员获取 +func DevOps(c *gin.Context) { + svc := service.User{User: header.GetUser(c)} + list, err := svc.DevOps() + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonResponse(c, resp.OK, list) +} + // SystemUserEditPassword 修改密码 func SystemUserEditPassword(c *gin.Context) { params := request.SystemUserEditPasswordReq{} diff --git a/src/main.go b/src/main.go index b2851d1591e024f853f418a52b4915085d45cb67..164dbaa314211809db3f2dd1e6e5982a8b7d7a68 100644 --- a/src/main.go +++ b/src/main.go @@ -73,6 +73,8 @@ func initConfig() { //TempDirPrefix: util.SetEnvStr("TEMP_DIR_PREFIX", "/app/xlsx/"), //模板目录前缀 PrometheusHost: util.SetEnvStr("PROMETHEUS_HOST", "https://prometheus.wodcloud.com"), // Prometheus Host AccessRuleModeKey: "accessRuleMode", + LocationUrl: util.SetEnvStr("LOCATION_URL", "https://apis.map.qq.com/ws/location/v1/ip"), + LocationKey: util.SetEnvStr("LOCATION_KEY", "QKFBZ-PGGWJ-VZQFF-FHPA7-QWT5H-YHF4T"), } } diff --git a/src/router/middleware/header/responsebody.go b/src/router/middleware/header/responsebody.go index dbd18eba33ab929f08a561603ce34dd5c351825e..38edb6485f0ef9cb800e6a89e51b892a01c076a1 100644 --- a/src/router/middleware/header/responsebody.go +++ b/src/router/middleware/header/responsebody.go @@ -2,7 +2,22 @@ package header import ( "bytes" + "encoding/json" + "errors" + "fmt" "github.com/gin-gonic/gin" + "github.com/spf13/cast" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" + "go.uber.org/zap" + "io/ioutil" + "strings" + "time" ) type responseBodyWriter struct { @@ -20,3 +35,167 @@ func CopyResponseBody(c *gin.Context) { c.Writer = w c.Next() } + +// 添加日志 +func AddLogMiddleware(sysModule string, operateMethod string, operatorType int) func(ctx *gin.Context) { + return func(c *gin.Context) { + reqBody, err := c.GetRawData() + if err != nil { + conf.Logger.Error("SaveUserOpLog err", zap.Error(err)) + } + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) + + c.Next() + go func() { + var ( + ip = util.RemoteIp(c.Request) + url = c.Request.RequestURI + opStats = int8(1) + resData = resp.Resp{} + resBody = make([]byte, 0) + httpMethod = c.Request.Method + userInfo = entity.SystemUserInfo{} + appId = c.Query("app_id") + ) + if c.Writer.Status() == 404 { + return + } + if c.Writer.Status() >= 400 { + opStats = 2 + } + if val, ok := c.Writer.(*responseBodyWriter); ok { + resBody = val.body.Bytes() + // 解析body + err := json.Unmarshal(resBody, &resData) + if err != nil { + resBody = nil + conf.Logger.Error("Unmarshal.err", zap.Error(err)) + } else if resData.Code != 200 { + opStats = 2 + } + } + token, _ := c.Cookie(conf.CookieName) + conf.Logger.Info("当前token信息为", zap.String("bgToken", token)) + if token == "" { + conf.Logger.Error("登录超时", zap.Error(errors.New("登录超时"))) + } + loginInf := service.UserSvc{} + userInfo, err = loginInf.GetCurUser(token) + if err != nil || userInfo.Id == 0 { + conf.Logger.Error("获取用户信息失败") + } + log := service.LogSvc{} + logData := entity.SystemUserBehavior{ + UserId: userInfo.Id, + OrganizationId: userInfo.OrganizationId, + SystemModule: sysModule, + OperateType: constant.OpTypeMap[operatorType], + ReqMethod: cast.ToInt(constant.HttpMethodMap[httpMethod]), + OperateStatus: cast.ToInt(opStats), + OperateIp: ip, + OperateAddr: getAddressByIp(ip), + CreatedTime: time.Now(), + OperateMethod: operateMethod, + ResFields: util.Bytes2Str(resBody), + ReqParam: util.Bytes2Str(reqBody), + ReqUrl: url, + AppId: appId, + } + //是否切换ES查询 + + log.AddBehaviorLog(logData) + + }() + } +} + +// 添加登录日志 +func AddUserLoginLog(sysModule string, operateMethod string, operatorType int, c *gin.Context, userInfo entity.SystemUserInfo) { + reqBody, err := c.GetRawData() + if err != nil { + conf.Logger.Error("SaveUserOpLog err", zap.Error(err)) + } + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) + go func() { + var ( + ip = util.RemoteIp(c.Request) + url = c.Request.RequestURI + opStats = int8(1) + resData = resp.Resp{} + resBody = make([]byte, 0) + httpMethod = c.Request.Method + appId = c.Query("app_id") + ) + if c.Writer.Status() == 404 { + return + } + if c.Writer.Status() >= 400 { + opStats = 2 + } + if val, ok := c.Writer.(*responseBodyWriter); ok { + resBody = val.body.Bytes() + // 解析body + err := json.Unmarshal(resBody, &resData) + if err != nil { + resBody = nil + conf.Logger.Error("Unmarshal.err", zap.Error(err)) + } else if resData.Code != 200 { + opStats = 2 + } + } + log := service.LogSvc{} + logData := entity.SystemUserBehavior{ + UserId: userInfo.Id, + OrganizationId: userInfo.OrganizationId, + SystemModule: sysModule, + OperateType: constant.OpTypeMap[operatorType], + ReqMethod: cast.ToInt(constant.HttpMethodMap[httpMethod]), + OperateStatus: cast.ToInt(opStats), + OperateIp: ip, + OperateAddr: getAddressByIp(ip), + CreatedTime: time.Now(), + OperateMethod: operateMethod, + ResFields: util.Bytes2Str(resBody), + ReqParam: util.Bytes2Str(reqBody), + ReqUrl: url, + AppId: appId, + Phone: userInfo.Phone, + } + + log.AddBehaviorLog(logData) + + }() +} + +// 根据ip地址获取登录地点 +func getAddressByIp(ip string) string { + if ip == "" { + return "未知" + } + if strings.HasPrefix(ip, "127") || strings.HasPrefix(ip, "10") || strings.HasPrefix(ip, "172") || strings.HasPrefix(ip, "192") { + return "局域网" + } + requestUrl := fmt.Sprintf("%s?ip=%s&key=%s", conf.Options.LocationUrl, ip, conf.Options.LocationKey) + resData, err := util.HttpSend("GET", requestUrl, "", nil) + if err != nil { + fmt.Println("request location url err:", err) + return "未知" + } + body, _ := ioutil.ReadAll(resData.Body) + var result response.LocationResult + if err = json.Unmarshal(body, &result); err != nil { + fmt.Println("parse location resp err:", err) + return "未知" + } + var address string + if result.Result.AdInfo.Province == result.Result.AdInfo.City { + address = result.Result.AdInfo.Province + } else { + address = result.Result.AdInfo.Province + result.Result.AdInfo.City + } + if address == "" { + return "未知" + } else { + return address + } +} diff --git a/src/router/systemuserrouter.go b/src/router/systemuserrouter.go index e4970c9bbdad21cd08610e137b9af7d9ea4d5f15..9bcdf793163d982c278f5feafb9e755844d71d12 100644 --- a/src/router/systemuserrouter.go +++ b/src/router/systemuserrouter.go @@ -21,6 +21,7 @@ func InitSystemUserRouter(e *gin.Engine) { base.PUT("/:id", controller.OrgUpdateUser) //组织编辑用户 base.DELETE("/del", controller.DelOrgUser) //删除组织用户 base.POST("/check", controller.CheckRepetition) //去重校验 + base.GET("/devOps", controller.DevOps) //运维人员列表 base.POST("/updatePwd", controller.SystemUserEditPassword) // 修改账户密码 base.POST("/resetPwd", controller.ResetSystemUserPassword) // 重置账户密码 diff --git a/src/service/organization.go b/src/service/organization.go index b8dfbc5b03cfe3ba04d050f095e98105c925d64e..ba353249504f2b0fcdb3acc7db49dcf4d4a9028e 100644 --- a/src/service/organization.go +++ b/src/service/organization.go @@ -251,9 +251,18 @@ func (o *Organization) OrgDetail(input request.QueryOrgDetailInput) (interface{} return nil, resp.DbConnectError.WithError(err) } + var orgInfo response.OrgDetail + if _, err = db.Table("system_organization").Alias("so"). + Select("so.organization_code, so.name, so.created_time, t3.count as platform_users_number, so.description, (case when so.name <> '平台用户组织' then '政务机构' else '平台用户组织' end) as organization_type"). + Join("LEFT", "(SELECT count(id),su.organization_id from system_user as su where su.is_deleted = 0 and su.state = 1 GROUP BY su.organization_id) t3", "t3.organization_id = so.organization_id"). + Where("so.organization_id = ? and is_deleted = 0", input.OrganizationId).Get(&orgInfo); err != nil { + conf.Logger.Error("查询组织基本信息失败", zap.Error(err)) + return nil, resp.DbSelectError.WithError(err) + } + //查询用户 区分组织管理员和平台用户 var orgAdminUsers []response.OrgAdminUser - querySystemUser := db.Table("system_user").Alias("su").Select("su.id, su.system_account, su.created_time, su.state, su.phone"). + querySystemUser := db.Table("system_user").Alias("su").Select("su.id,su.name, su.system_account, su.created_time, su.state, su.phone"). Where("organization_id = ? and su.is_deleted = 0", input.OrganizationId) //DataType 2平台用户组织 @@ -288,6 +297,7 @@ func (o *Organization) OrgDetail(input request.QueryOrgDetailInput) (interface{} return nil, resp.DbSelectError.ErrorDetail(err) } for i, v := range orgAdminUsers { + orgAdminUsers[i].OrgName = orgInfo.Name for _, s := range systemUserRoles { if v.Id == s.UserId { orgAdminUsers[i].SystemRole = append(orgAdminUsers[i].SystemRole, s.RoleName) @@ -295,21 +305,12 @@ func (o *Organization) OrgDetail(input request.QueryOrgDetailInput) (interface{} } } - var orgInfo response.OrgDetail - if _, err = db.Table("system_organization").Alias("so"). - Select("so.organization_code, so.name, so.created_time, t3.count as platform_users_number, so.description, (case when so.name <> '平台用户组织' then '政务机构' else '平台用户组织' end) as organization_type"). - Join("LEFT", "(SELECT count(id),su.organization_id from system_user as su where su.is_deleted = 0 and su.state = 1 GROUP BY su.organization_id) t3", "t3.organization_id = so.organization_id"). - Where("so.organization_id = ? and is_deleted = 0", input.OrganizationId).Get(&orgInfo); err != nil { - conf.Logger.Error("查询组织基本信息失败", zap.Error(err)) - return nil, resp.DbSelectError.WithError(err) - } - result := map[string]interface{}{ "org_info": orgInfo, "org_users": map[string]interface{}{"data": orgAdminUsers, "total": count}, } - if orgInfo.OrganizationCode == "" { - result = nil - } + //if orgInfo.OrganizationCode == "" { + // result = nil + //} return result, nil } diff --git a/src/service/system_user.go b/src/service/system_user.go index 82346f3a0510c414b3e577f2062d3e8334290eac..2c98f22d70fa09dffce1b764a4ca9955e421ce63 100644 --- a/src/service/system_user.go +++ b/src/service/system_user.go @@ -302,6 +302,30 @@ func (u *User) SystemUserUpdateState(params request.SystemUserStateReq) error { return nil } +func (o *User) DevOps() ([]entity.SystemUserInfo, error) { + var ( + err error + result []entity.SystemUserInfo + ) + + db, err := client.GetDbClient() + if err != nil { + return result, resp.DbConnectError.WithError(err) + } + + err = db.Table("system_user").Alias("su"). + Join("LEFT", "system_user_role sur", "su.id = sur.user_id"). + Join("LEFT", "system_role sr", "sur.role_id = sr.role_id"). + Where("su.is_deleted = 0 and su.state =1 and sr.is_deleted = 0 and sr.state = 1 and role_type = 1"). + Select("DISTINCT su.*").Find(&result) + if err != nil { + return result, resp.DbSelectError.WithError(err) + } + + return result, err + +} + // SystemUserEditPassword 修改密码 func SystemUserEditPassword(params request.SystemUserEditPasswordReq) (err error) { db, err := client.GetDbClient() diff --git a/src/util/common.go b/src/util/common.go index d3c399cc1871124d86228ff9089b38e9990ec06e..8310d5b9b7f82bfe784ca8f1251975b5d0edaca7 100644 --- a/src/util/common.go +++ b/src/util/common.go @@ -12,6 +12,7 @@ import ( "net/http" "regexp" "strings" + "unsafe" "github.com/Luzifer/go-openssl/v4" "github.com/google/uuid" @@ -67,3 +68,7 @@ func SpecialEscape(keyword string) string { keyword = strings.Replace(keyword, "_", "\\_", -1) return keyword } + +func Bytes2Str(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/src/util/http.go b/src/util/http.go index addc7a8c98476305ca6280acaee17eada2b8d973..23753e77ea89d7f7d33d29b79ea024ed9017c96b 100644 --- a/src/util/http.go +++ b/src/util/http.go @@ -3,7 +3,11 @@ package util import ( "crypto/tls" "github.com/valyala/fasthttp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "go.uber.org/zap" "net" + "net/http" + "strings" "time" ) @@ -92,3 +96,28 @@ func Request(url string, method string, body []byte, headers map[string]string) // 返回响应体和错误信息 return resp.Body(), nil } + +// HttpSend , http请求 GET/DELETE/POST/PUT +func HttpSend(sendType string, url string, body string, header map[string]string) (result *http.Response, err error) { + conf.Logger.Debug("消息请求url", zap.String("url", url)) + conf.Logger.Debug("请求body", zap.Any("body", string(body))) + client := &http.Client{} + var reqest *http.Request + if sendType == "GET" || sendType == "DELETE" { + reqest, _ = http.NewRequest(sendType, url, nil) + } else if sendType == "POST" || sendType == "PUT" { + reqest, _ = http.NewRequest(sendType, url, strings.NewReader(body)) + } + if header == nil { + header = make(map[string]string) + } + header["Content-Type"] = "application/json" + for k, v := range header { + reqest.Header.Set(k, v) + } + response, err := client.Do(reqest) + if err != nil { + conf.Logger.Error("消息发送失败", zap.Error(err)) + } + return response, err +}