@@ -0,0 +1,529 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user