530 lines
13 KiB
Go
530 lines
13 KiB
Go
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)
|
|
}
|