package common import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "net" "net/http" "os" "reflect" "strconv" "time" "unicode" "91porn-server/common/constant" "91porn-server/common/crypt" "91porn-server/common/log" "91porn-server/common/stderr" "91porn-server/common/version" "91porn-server/middleware/ua" "github.com/gin-gonic/gin" ) var ( env string pid = os.Getgid() cryptSecret360 string ErrUserNotExist = errors.New("userId not exist") FirstRealUserID uint64 ) type FileType string const ( FileCSV FileType = "csv" FileExcel FileType = "xlsx" firstRealUserIDTest = 300001 // 首个真实的用户id(测试环境) firstRealUserIDProd = 300001 // 首个真实的用户id(正式环境) ) // ServeFile serve request with an file by attachment. func ServeFile(c *gin.Context, fileName string, fileType FileType, fileBuffer *bytes.Buffer) { c.DataFromReader(http.StatusOK, int64(fileBuffer.Len()), "application/octet-stream", fileBuffer, map[string]string{ "Content-Disposition": fmt.Sprintf("attachment;filename=%s-%s.%s", fileName, time.Now().Local().Format("2006-01-02"), fileType), }) } // ServeJSON 返回数据并处理多语言 func ServeJSON(c *gin.Context, code stderr.Code, data interface{}) { ServeJsonWithExtra(c, code, data, nil) } func ServeJsonWithExtra(c *gin.Context, code stderr.Code, data interface{}, extra map[string]interface{}) { var hash bool if code != stderr.Success && code != stderr.ErrVersionUpdate { //发生错误,记录日志 var version, sysType, devType string id, _ := GetUID(c) ua, _ := GetUA(c) if ua.Ver != "" { version = ua.Ver } if ua.SysType != "" { sysType = ua.SysType } if devType != "" { devType = ua.DevType } // 预防打印出现空指针异常 PANIC=runtime error: invalid memory address or nil pointer dereference if data == nil { data = "nil" } log.WarnX(c, "Error:", log.Any("UID", strconv.FormatUint(id, 10)), log.Any("IP", c.ClientIP()), log.Any("Version", version), log.Any("SysType", sysType), log.Any("DevType", devType), log.Any("Router", c.Request.RequestURI), log.Any("PID", pid), log.Any("Data", data), log.Any("Code", code)) } if !IsNilOrEmpty(data) && code == stderr.Success { t := reflect.TypeOf(data) if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) { log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice", log.Any("path", c.Request.URL.Path)) } } // 在 data 加密前抽取一次 msg:敏感词命中等场景需要把 data 的明文详情同步到 msg msg := resolveMsg(code, data) if env == constant.ProdEnv { //返给前端是否加密 hash = true dataByte, _ := json.Marshal(data) cipher, _ := crypt.CoreAesEncryptEx(dataByte, 12, cryptSecret360) data = base64.StdEncoding.EncodeToString(cipher) } if IsNilOrEmpty(data) { data = "" } h := gin.H{ "code": code, "hash": hash, "msg": msg, "tip": code.Tip(), "data": data, "time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"), } if len(extra) > 0 { for k, v := range extra { h[k] = v } } c.JSON(http.StatusOK, h) } // resolveMsg 按 code 选择 msg:默认走 code.Msg();对于内容敏感词命中这类 // 详情完全包含在 data 里的错误码,把 data 的字符串明文同步覆盖到 msg, // 便于前端直接用 msg 弹窗,无需再额外读取 data 字段。 func resolveMsg(code stderr.Code, data interface{}) string { if code == stderr.ContentSensitiveHit { if s, ok := data.(string); ok && s != "" { return s } } return code.Msg() } // ServeToJSON 返回数据并处理多语言 func ServeToJSON(c *gin.Context, code stderr.Code, data interface{}) { var hash bool if code != stderr.Success && code != stderr.ErrVersionUpdate { //发生错误,记录日志 var version, sysType, devType string id, _ := GetUID(c) ua, _ := GetUA(c) if ua.Ver != "" { version = ua.Ver } if ua.SysType != "" { sysType = ua.SysType } if devType != "" { devType = ua.DevType } log.WarnX(c, "Error:", log.Any("UID", strconv.FormatUint(id, 10)), log.Any("IP", c.ClientIP()), log.Any("Version", version), log.Any("SysType", sysType), log.Any("DevType", devType), log.Any("Router", c.Request.RequestURI), log.Any("PID", pid), log.Any("Error", data), log.Any("Code", code)) } if !IsNilOrEmpty(data) && code == stderr.Success { t := reflect.TypeOf(data) if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr) { log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice", log.Any("kind", t.Kind()), log.Any("path", c.Request.URL.Path)) } } if IsNilOrEmpty(data) { data = gin.H{} } c.JSON(http.StatusOK, gin.H{ "code": code, "hash": hash, "msg": code.Msg(), "tip": code.Tip(), "data": data, "time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"), }) } func ServeError(c *gin.Context, err error) { cErr, ok := err.(*stderr.CustomErr) if ok { serveJsonLogic(c, cErr.Code, cErr.Msg, nil, cErr.Msg, cErr.Msg) } else { log.ErrorX(c, "ServeError", log.E(err)) var errMsg string if err != nil { errMsg = err.Error() } serveJsonLogic(c, stderr.Failure, errMsg, nil, stderr.Failure.Error(), stderr.Failure.Tip()) } } func serveJsonLogic(c *gin.Context, code stderr.Code, data interface{}, extra map[string]interface{}, msg, tip string) { var hash bool if code != stderr.Success && code != stderr.ErrVersionUpdate { //发生错误,记录日志 var version, sysType, devType string id, _ := GetUID(c) ua, _ := GetUA(c) if ua.Ver != "" { version = ua.Ver } if ua.SysType != "" { sysType = ua.SysType } if devType != "" { devType = ua.DevType } // 预防打印出现空指针异常 PANIC=runtime error: invalid memory address or nil pointer dereference if data == nil { data = "nil" } log.WarnX(c, "Error:", log.Any("UID", strconv.FormatUint(id, 10)), log.Any("IP", c.ClientIP()), log.Any("Version", version), log.Any("SysType", sysType), log.Any("DevType", devType), log.Any("Router", c.Request.RequestURI), log.Any("PID", pid), log.Any("Data", data), log.Any("Code", code)) } if !IsNilOrEmpty(data) && code == stderr.Success { t := reflect.TypeOf(data) if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) { log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice", log.Any("path", c.Request.URL.Path)) } } if env == constant.ProdEnv { //返给前端是否加密 hash = true dataByte, _ := json.Marshal(data) cipher, _ := crypt.CoreAesEncryptEx(dataByte, 12, cryptSecret360) data = base64.StdEncoding.EncodeToString(cipher) } if IsNilOrEmpty(data) { data = "" } h := gin.H{ "code": code, "hash": hash, "msg": msg, "tip": tip, "data": data, "time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"), } if len(extra) > 0 { for k, v := range extra { h[k] = v } } c.JSON(http.StatusOK, h) } // GetUID 获取用户uid // oauth.Auth执行后uid有效 func GetUID(ctx *gin.Context) (uid uint64, err error) { val, exists := ctx.Get(constant.CtxUserID) if !exists { err = ErrUserNotExist return } uid, ok := val.(uint64) if !ok { return 0, errors.New("userId type error") } return } // TryGetUID 尝试获取用户ID,用户未登录的情况下uid为0 func TryGetUID(ctx *gin.Context) (uid uint64) { val, exists := ctx.Get(constant.CtxUserID) if !exists { return } uid, _ = val.(uint64) return } // GetUA func GetUA(ctx *gin.Context) (u ua.UA, err error) { val, exists := ctx.Get(constant.CtxUA) if !exists { err = errors.New("user-agent not exists") return } u, ok := val.(ua.UA) if !ok { err = errors.New("user-agent type error") return } return u, nil } /** * 判断当前App 是否是制定的版本号 * 历史判断: 1. 是否是flutter版本 ver=2.0.0 主要更新列表,使用hasNext 替换total 2. 是否2.0.1版本 ver=2.0.1 用于获取账单更改 3. 是否2.0.9版本 ver=2.0.9 用户用户手机登陆流程 4. 是否2.1.0版本 ver=2.1.0 用于接口防重放 */ func IsGTESpecifyVer(ctx *gin.Context, specVer string) bool { val, exists := ctx.Get(constant.CtxUA) if !exists { log.Warn("user-agent not exists") return false } u, ok := val.(ua.UA) if !ok { log.Warn("user-agent type error") return false } if u.Ver == "" { return false } v1, err := version.New(u.Ver) if v1 == nil || err != nil { return false } v2, err := version.New(specVer) if v2 == nil || err != nil { return false } if v1.GTE(v2) { return true } return false } func IsSpecifyVerBaseOnUa(u ua.UA, specVer string) bool { if u.Ver == "" { return false } v1, err := version.New(u.Ver) if v1 == nil || err != nil { return false } v2, err := version.New(specVer) if v2 == nil || err != nil { return false } if v1.GTE(v2) { return true } return false } // GetAdminAct 获取管理员账号 // oauth.Auth执行后账号有效 func GetAdminAct(ctx *gin.Context) (string, error) { t1 := ctx.Request.Header.Get("mod") if t1 == "debug" { return "debug", nil } val, exists := ctx.Get(constant.CtxAdminAct) if !exists { return "", errors.New("Admin not exist") } v, ok := val.(string) if !ok { return "", errors.New("Admin type error") } return v, nil } // GetAdminRole 获取管理员账号 // oauth.Auth执行后账号有效 func GetAdminRole(ctx *gin.Context) (string, error) { val, exists := ctx.Get(constant.CtxAdminRole) if !exists { return "", errors.New("AdminRole not exist") } v, ok := val.(string) if !ok { return "", errors.New("AdminRole type error") } return v, nil } // GetJuShangId 获取管理员账号 // oauth.Auth执行后账号有效 func GetJuShangID(ctx *gin.Context) (string, error) { val, exists := ctx.Get(constant.CtxJuShangCID) if !exists { return "", errors.New("JuShangID not exist") } v, ok := val.(string) if !ok { return "", errors.New("JuShangID type error") } return v, nil } // GetUIDAct 获取管理员账号 // oauth.Auth执行后uid有效 func GetDistrictAct(ctx *gin.Context) (string, error) { val, exists := ctx.Get(constant.CtxDistrictName) if !exists { return "", errors.New("DistrictUserID not exist") } act, ok := val.(string) if !ok { return "", errors.New("DistrictAct type error") } return act, nil } // InitGinSecret InitResponseParam func InitGinSecret(secret, secret360, env_ string) { cryptSecret360 = secret360 env = env_ FirstRealUserID = firstRealUserIDTest if env == constant.ProdEnv { FirstRealUserID = firstRealUserIDProd } } func IsNilOrEmpty(in interface{}) bool { return in == nil || in == "" } // GetIP 获取真实IP func GetIP(ctx *gin.Context) string { relIP, exists := ctx.Get(constant.CtxIP) v, _ := relIP.(string) // CtxIP 未设置(或存的是空串)时回落到 ClientIP; // 此前写成 if !exists { if relIP == "" ... } —— relIP 为 nil 接口,永不等于 "",回落是死代码,会返回空串 if !exists || v == "" { v = ctx.ClientIP() } //log.Info(fmt.Sprintf("[IP-ROUTER] %s,PID %d", ctx.GetHeader("X-Forwarded-For"), os.Getpid())) return v } // 判断是否是ip4 func IsIP4(ip string) (bool, string) { ipAddr := net.ParseIP(ip).To4() if ipAddr == nil { return false, "" } return true, ipAddr.String() } // 判断是否是正常的设备id func IsNormalDevId(devId string) bool { for _, r := range devId { //判断是否包含中文汉子 if unicode.Is(unicode.Scripts["Han"], r) { return false } //判断是否包含空格 if unicode.IsSpace(r) { return false } } return true } func ServeJSONNoEncrypt(c *gin.Context, code stderr.Code, data interface{}) { ServeJsonWithExtraNoEncrypt(c, code, data, nil) } func ServeJsonWithExtraNoEncrypt(c *gin.Context, code stderr.Code, data interface{}, extra map[string]interface{}) { var hash bool if code != stderr.Success && code != stderr.ErrVersionUpdate { //发生错误,记录日志 var version, sysType, devType string id, _ := GetUID(c) ua, _ := GetUA(c) if ua.Ver != "" { version = ua.Ver } if ua.SysType != "" { sysType = ua.SysType } if devType != "" { devType = ua.DevType } // 预防打印出现空指针异常 PANIC=runtime error: invalid memory address or nil pointer dereference if data == nil { data = "nil" } log.WarnX(c, "Error:", log.Any("UID", strconv.FormatUint(id, 10)), log.Any("IP", c.ClientIP()), log.Any("Version", version), log.Any("SysType", sysType), log.Any("DevType", devType), log.Any("Router", c.Request.RequestURI), log.Any("PID", pid), log.Any("Data", data), log.Any("Code", code)) } if !IsNilOrEmpty(data) && code == stderr.Success { t := reflect.TypeOf(data) if !(t.Kind() == reflect.Map || t.Kind() == reflect.Struct || t.Kind() == reflect.Slice) { log.WarnX(c, "[===TypeError===] Return Data Type error is not struct or slice", log.Any("path", c.Request.URL.Path)) } } msg := resolveMsg(code, data) if IsNilOrEmpty(data) { data = "" } h := gin.H{ "code": code, "hash": hash, "msg": msg, "tip": code.Tip(), "data": data, "time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"), } if len(extra) > 0 { for k, v := range extra { h[k] = v } } c.JSON(http.StatusOK, h) }