From 94682e39a5b80282b151cd6c6ee22577eb958699 Mon Sep 17 00:00:00 2001 From: NY Date: Tue, 11 Feb 2025 18:09:22 +0800 Subject: [PATCH] first --- .idea/.gitignore | 8 + .idea/epur-pay.iml | 9 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + api/login/message.go | 71 ++++ api/login/register.go | 140 ++++++ api/login/sso.go | 154 +++++++ api/order.go | 1 + api/payChannel.go | 65 +++ api/power.go | 178 ++++++++ api/test.go | 84 ++++ cache/api/blacklist.go | 33 ++ cache/api/config.go | 101 +++++ cache/api/language.go | 70 +++ cache/api/role.go | 57 +++ cache/api/user.go | 61 +++ cache/api/version.go | 46 ++ cache/caches.go | 63 +++ cache/ipLocation.go | 112 +++++ cmd/1.go | 54 +++ config/pay.ini | 73 ++++ config/pay_test.ini | 55 +++ go.mod | 85 ++++ go.sum | 401 ++++++++++++++++++ logs/access.log | 1 + logs/access.log.20250210.log | 18 + logs/access.log.20250211.log | 307 ++++++++++++++ logs/error.log | 1 + logs/error.log.20240118.log | Bin 0 -> 854276 bytes logs/error.log.20250211.log | 490 +++++++++++++++++++++ logs/rds.log | 1 + logs/rds.log.20250210.log | 107 +++++ logs/rds.log.20250211.log | 417 ++++++++++++++++++ logs/router.log | 1 + logs/router.log.20240117.log | 725 ++++++++++++++++++++++++++++++++ logs/router.log.20240118.log | 355 ++++++++++++++++ logs/router.log.20240119.log | 311 ++++++++++++++ logs/router.log.20250211.log | 66 +++ main.go | 35 ++ model/common.go | 195 +++++++++ model/http.go | 294 +++++++++++++ model/merchant.go | 16 + model/order.go | 84 ++++ model/pay.go | 51 +++ model/power.go | 73 ++++ model/publicStr.go | 172 ++++++++ model/user.go | 61 +++ pkg/aliyun/aliyun.go | 30 ++ pkg/aliyun/sms.go | 81 ++++ pkg/async/afterCallback.go | 20 + pkg/async/async.go | 17 + pkg/async/model.go | 37 ++ pkg/config/config.go | 70 +++ pkg/cron/cron.go | 60 +++ pkg/dapi/api.go | 141 +++++++ pkg/dapi/base.go | 155 +++++++ pkg/dapi/response.go | 188 +++++++++ pkg/dapi/utils.go | 458 ++++++++++++++++++++ pkg/dapi/valid-errors.go | 48 +++ pkg/fileserver/asw/asw.go | 150 +++++++ pkg/fileserver/config/config.go | 83 ++++ pkg/fileserver/cos/cos.go | 95 +++++ pkg/fileserver/file.go | 112 +++++ pkg/fileserver/local/local.go | 131 ++++++ pkg/fileserver/oss/oss.go | 162 +++++++ pkg/idGenerate/generate.go | 45 ++ pkg/limit/config.go | 11 + pkg/limit/limit.go | 17 + pkg/limit/method_limiter.go | 80 ++++ pkg/logger/log-rds.og.go | 77 ++++ pkg/logger/logger.go | 142 +++++++ pkg/mq/c.go | 86 ++++ pkg/mq/model.go | 70 +++ pkg/mq/mq.go | 176 ++++++++ pkg/rds/rds.go | 74 ++++ pkg/redis/redis.go | 60 +++ pkg/server/api.go | 83 ++++ pkg/server/server.go | 54 +++ pkg/tools/api.go | 25 ++ pkg/utils/error.go | 24 ++ pkg/utils/file.go | 50 +++ pkg/utils/string.go | 27 ++ pkg/utils/time.go | 9 + router/middleware.go | 49 +++ router/router.go | 56 +++ router/routerFunc.go | 171 ++++++++ tri/ehttp/.gitignore | 19 + tri/ehttp/README.md | 26 ++ tri/ehttp/go.mod | 3 + tri/ehttp/http/http.go | 191 +++++++++ tri/ehttp/http/model.go | 31 ++ tri/ehttp/http/option.go | 52 +++ tri/ehttp/http/utils.go | 55 +++ tri/ehttp/main.go | 19 + 94 files changed, 9536 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/epur-pay.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 api/login/message.go create mode 100644 api/login/register.go create mode 100644 api/login/sso.go create mode 100644 api/order.go create mode 100644 api/payChannel.go create mode 100644 api/power.go create mode 100644 api/test.go create mode 100644 cache/api/blacklist.go create mode 100644 cache/api/config.go create mode 100644 cache/api/language.go create mode 100644 cache/api/role.go create mode 100644 cache/api/user.go create mode 100644 cache/api/version.go create mode 100644 cache/caches.go create mode 100644 cache/ipLocation.go create mode 100644 cmd/1.go create mode 100644 config/pay.ini create mode 100644 config/pay_test.ini create mode 100644 go.mod create mode 100644 go.sum create mode 120000 logs/access.log create mode 100644 logs/access.log.20250210.log create mode 100644 logs/access.log.20250211.log create mode 100644 logs/error.log create mode 100644 logs/error.log.20240118.log create mode 100644 logs/error.log.20250211.log create mode 120000 logs/rds.log create mode 100644 logs/rds.log.20250210.log create mode 100644 logs/rds.log.20250211.log create mode 100644 logs/router.log create mode 100644 logs/router.log.20240117.log create mode 100644 logs/router.log.20240118.log create mode 100644 logs/router.log.20240119.log create mode 100644 logs/router.log.20250211.log create mode 100644 main.go create mode 100644 model/common.go create mode 100644 model/http.go create mode 100644 model/merchant.go create mode 100644 model/order.go create mode 100644 model/pay.go create mode 100644 model/power.go create mode 100644 model/publicStr.go create mode 100644 model/user.go create mode 100644 pkg/aliyun/aliyun.go create mode 100644 pkg/aliyun/sms.go create mode 100644 pkg/async/afterCallback.go create mode 100644 pkg/async/async.go create mode 100644 pkg/async/model.go create mode 100644 pkg/config/config.go create mode 100644 pkg/cron/cron.go create mode 100644 pkg/dapi/api.go create mode 100644 pkg/dapi/base.go create mode 100644 pkg/dapi/response.go create mode 100644 pkg/dapi/utils.go create mode 100644 pkg/dapi/valid-errors.go create mode 100644 pkg/fileserver/asw/asw.go create mode 100644 pkg/fileserver/config/config.go create mode 100644 pkg/fileserver/cos/cos.go create mode 100644 pkg/fileserver/file.go create mode 100644 pkg/fileserver/local/local.go create mode 100644 pkg/fileserver/oss/oss.go create mode 100644 pkg/idGenerate/generate.go create mode 100644 pkg/limit/config.go create mode 100644 pkg/limit/limit.go create mode 100644 pkg/limit/method_limiter.go create mode 100644 pkg/logger/log-rds.og.go create mode 100644 pkg/logger/logger.go create mode 100644 pkg/mq/c.go create mode 100644 pkg/mq/model.go create mode 100644 pkg/mq/mq.go create mode 100644 pkg/rds/rds.go create mode 100644 pkg/redis/redis.go create mode 100644 pkg/server/api.go create mode 100644 pkg/server/server.go create mode 100644 pkg/tools/api.go create mode 100644 pkg/utils/error.go create mode 100644 pkg/utils/file.go create mode 100644 pkg/utils/string.go create mode 100644 pkg/utils/time.go create mode 100644 router/middleware.go create mode 100644 router/router.go create mode 100644 router/routerFunc.go create mode 100644 tri/ehttp/.gitignore create mode 100644 tri/ehttp/README.md create mode 100644 tri/ehttp/go.mod create mode 100644 tri/ehttp/http/http.go create mode 100644 tri/ehttp/http/model.go create mode 100644 tri/ehttp/http/option.go create mode 100644 tri/ehttp/http/utils.go create mode 100644 tri/ehttp/main.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/epur-pay.iml b/.idea/epur-pay.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/epur-pay.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b31cc41 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/api/login/message.go b/api/login/message.go new file mode 100644 index 0000000..37afe0d --- /dev/null +++ b/api/login/message.go @@ -0,0 +1,71 @@ +package login + +import ( + "epur-pay/pkg/aliyun" + "epur-pay/pkg/dapi" + "epur-pay/pkg/logger" + "epur-pay/pkg/redis" + "errors" + "fmt" + redigo "github.com/gomodule/redigo/redis" +) + +type SsoSendSmsCodeParams struct { + Mobile string `json:"mobile"` +} + +func SsoSendSmsCode(a *dapi.ApiBase, params *SsoSendSmsCodeParams) error { + redisCodeKey := fmt.Sprintf("smscode:%s", params.Mobile) + redisDailyKey := fmt.Sprintf("smscode_daily:%s", params.Mobile) + + conn := redis.RPool.Get() + defer func() { + if err := conn.Close(); err != nil { + logger.ErrorLogger.Infoln(err.Error()) + } + }() + + code, err := conn.Do("GET", redisCodeKey) + if err == nil && code != nil { + return a.ReturnPublicErrorResponse(a.Translate("sms_code_sent_frequently")) + } + + dailyCount, err := redigo.Int(conn.Do("GET", redisDailyKey)) + if err != nil && !errors.Is(err, redigo.ErrNil) { + logger.ErrorLogger.Errorln("获取验证码次数失败:", err.Error()) + return a.ReturnPublicErrorResponse(a.Translate("sms_code_fetch_limit_failed")) + } + + if errors.Is(err, redigo.ErrNil) { + dailyCount = 0 + } + + if dailyCount >= 10 { + return a.ReturnPublicErrorResponse(a.Translate("sms_code_daily_limit")) + } + + captcha, err := aliyun.SendSmsLogin(params.Mobile) + if err != nil { + logger.ErrorLogger.Errorln("发送短信失败:", err.Error()) + return a.ReturnPublicErrorResponse(a.Translate("sms_code_send_failed")) + } + + if _, err := conn.Do("SETEX", redisCodeKey, 300, captcha); err != nil { + logger.ErrorLogger.Errorln("设置验证码60s时效失败:", err.Error()) + return a.ReturnPublicErrorResponse(a.Translate("sms_code_cache_failed")) + } + + if dailyCount == 0 { + if _, err := conn.Do("SETEX", redisDailyKey, 86400, 1); err != nil { + logger.ErrorLogger.Errorln("设置一天最大发送验证码次数失败:", err.Error()) + return a.ReturnPublicErrorResponse(a.Translate("sms_code_daily_record_failed")) + } + } else { + if _, err := conn.Do("INCR", redisDailyKey); err != nil { + logger.ErrorLogger.Errorln("验证码发送次数记录失败:", err.Error()) + return a.ReturnPublicErrorResponse(a.Translate("sms_code_daily_record_failed")) + } + } + + return a.ReturnSuccessCustomResponse(a.NewSuccessResponseCommon()) +} diff --git a/api/login/register.go b/api/login/register.go new file mode 100644 index 0000000..3d72f48 --- /dev/null +++ b/api/login/register.go @@ -0,0 +1,140 @@ +package login + +import ( + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/dapi" + "epur-pay/pkg/logger" + "epur-pay/pkg/redis" + "epur-pay/pkg/utils" + "fmt" + redigo "github.com/gomodule/redigo/redis" + "golang.org/x/crypto/bcrypt" + "regexp" +) + +type SsoRegisterParams struct { + Account string `json:"account"` // 注册账号 + Password string `json:"password"` // 密码 + Mobile string `json:"mobile"` // 手机号 + Email string `json:"email"` // 邮箱(可选) + Invite string `json:"invite"` // 邀请码 + Captcha string `json:"captcha"` // 验证码 +} + +type SsoRegisterResponse struct { + *dapi.ResponseCommon + Data struct { + Token string `json:"token"` + } `json:"data"` +} + +// SsoRegister 注册接口 +func SsoRegister(a *dapi.ApiBase, data *SsoRegisterParams) error { + if len(data.Mobile) == 0 { + return a.ReturnPublicErrorResponse(a.Translate("mobile_required")) + } + if len(data.Captcha) == 0 { + return a.ReturnPublicErrorResponse(a.Translate("captcha_required")) + } + + mobileRegex := `^1[3-9]\d{9}$` + matched, err := regexp.MatchString(mobileRegex, data.Mobile) + if err != nil || !matched { + return a.ReturnPublicErrorResponse(a.Translate("invalid_mobile")) + } + + if len(data.Password) < 6 || len(data.Password) > 72 { + return a.ReturnPublicErrorResponse(a.Translate("password_length_invalid")) + } + hasLetter := false + hasNumber := false + for _, char := range data.Password { + if char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' { + hasLetter = true + } + if char >= '0' && char <= '9' { + hasNumber = true + } + } + if !hasLetter || !hasNumber { + return a.ReturnPublicErrorResponse(a.Translate("password_complexity_invalid")) + } + + if len(data.Email) > 0 { + emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` + matched, err := regexp.MatchString(emailRegex, data.Email) + if err != nil || !matched { + return a.ReturnPublicErrorResponse(a.Translate("invalid_email")) + } + } + + redisCodeKey := fmt.Sprintf("smscode:%s", data.Mobile) + conn := redis.RPool.Get() + defer func() { + if err := conn.Close(); err != nil { + logger.ErrorLogger.Infoln(err.Error()) + } + }() + + storedCaptcha, err := redigo.String(conn.Do("GET", redisCodeKey)) + if err != nil || storedCaptcha != data.Captcha { + return a.ReturnPublicErrorResponse(a.Translate("invalid_captcha")) + } + + //_, _ = conn.Do("DEL", redisCodeKey) + + var count int64 + user := model.User{} + err = a.Ts.Table(user.TableName()).Where("mobile", data.Mobile).Count(&count).Error + if err != nil { + utils.Error(err) + } + if count > 0 { + return a.ReturnPublicErrorResponse(a.Translate("account_exists")) + } + + hashedPass, err := bcrypt.GenerateFromPassword([]byte(data.Password), bcrypt.DefaultCost) + if err != nil { + utils.Error(err) + } + + currentTime := utils.Time2StampSecond() + clientIp := a.ClientIp() + + newUser := model.User{ + Mobile: data.Mobile, + Email: data.Email, + Invite: data.Invite, + Status: "0", // 状态 0-正常 1-拉黑 + Role: model.ArryString{}, + } + + newUser.Detail = model.UserDetail{ + PassWord: string(hashedPass), + CreateTime: currentTime, + RegisterIp: clientIp, + RegisterIpAddress: cache.GetIpAddress(clientIp), + Nickname: data.Mobile, // 默认昵称与手机号一致 + } + + err = a.Ts.Table(newUser.TableName()).Create(&newUser).Error + if err != nil { + utils.Error(err) + } + + token := dapi.EncryptToken(newUser.Uid) + newUser.Detail.Token = token + + err = a.Ts.Table(newUser.TableName()).Where("uid", newUser.Uid). + Updates(map[string]interface{}{"detail": newUser.Detail}).Error + if err != nil { + utils.Error(err) + } + + response := SsoRegisterResponse{} + response.ResponseCommon = a.NewSuccessResponseCommon() + response.Data.Token = token + + return a.ReturnSuccessCustomResponse(response) +} diff --git a/api/login/sso.go b/api/login/sso.go new file mode 100644 index 0000000..3b59619 --- /dev/null +++ b/api/login/sso.go @@ -0,0 +1,154 @@ +package login + +import ( + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/dapi" + "epur-pay/pkg/utils" + "fmt" + "golang.org/x/crypto/bcrypt" +) + +type SsoLoginParams struct { + Mobile string `json:"mobile"` // 登陆手机号 + Password string `json:"passWord"` // 密码 + //Captcha string `json:"captcha"` // 验证码 +} + +type SsoLoginResponse struct { + *dapi.ResponseCommon + Data struct { + Token string `json:"token"` //返回token + } `json:"data"` //数据列表 +} + +/* + 登录 +*/ + +func SsoLogin(a *dapi.ApiBase, data *SsoLoginParams) error { + Response := SsoLoginResponse{} + + user := model.User{} + utils.Error(a.Ts.Table(user.TableName()).Where("mobile", data.Mobile).Scan(&user).Error) + + //if len(user.Role) <= 0 { + // return a.ReturnPublicErrorResponse(a.Translate("user_role_invalid")) + //} + + if a.Log != nil { + a.Log.Uid = user.Uid + a.Log.Name = user.Mobile + a.Log.Event = "登陆" + } + + lockNums := cache.Global.Caches.Config.GetInt64("lockNums") + lockMins := cache.Global.Caches.Config.GetInt64("lockMins") + + if lockNums > 0 && lockMins > 0 { + currTime := utils.Time2StampSecond() + if user.Detail.LoginErrorCount >= lockNums && + currTime-user.Detail.LoginErrorTime < lockMins*60 { //输入5次密码错误 锁5分钟 + + return a.ReturnPublicErrorResponse(fmt.Sprintf(a.Translate("pwd_error_lock"), + lockNums, user.Detail.LoginErrorTime+lockMins*60-currTime)) //密码输入错误超过%d次,%d秒后可以继续操作 + } + } + + if err := bcrypt.CompareHashAndPassword([]byte(user.Detail.PassWord), []byte(data.Password)); err != nil { + user.Detail.LoginErrorTime = utils.Time2StampSecond() + user.Detail.LoginErrorCount += 1 + utils.DbErrSkipRecordNotFound(a.Ts.Table(user.TableName()). + Where("uid", user.Uid). + Updates(map[string]interface{}{"detail": user.Detail}).Error) + return a.ReturnPublicErrorResponse(a.Translate("pwd_error")) + } + + if user.Status != "0" { + return a.ReturnPublicErrorResponse(a.Translate("user_blacklist")) + } + + user.Detail.Token = dapi.EncryptToken(user.Uid) + user.Detail.LoginIp = a.ClientIp() + user.Detail.LoginIpAddress = cache.GetIpAddress(user.Detail.LoginIp) + user.Detail.LoginTime = utils.Time2StampSecond() + user.Detail.LoginErrorCount = 0 + + utils.DbErrSkipRecordNotFound(a.Ts.Table(user.TableName()). + Where("uid", user.Uid). + Updates(map[string]interface{}{"detail": user.Detail}).Error) + + a.AfterCallback = func() { + // 这里需要刷新权限标识 + cache.Global.Caches.User.RefreshSelectRow(user.Uid) + } + + Response.Data.Token = user.Detail.Token + Response.ResponseCommon = a.NewSuccessResponseCommon() + return a.ReturnSuccessCustomResponse(Response) +} + +type UserInfoResponse struct { + *dapi.ResponseCommon + Data struct { + Uid int64 `json:"uid"` // 会员ID + Type string `json:"type"` // 用户类型 0-普通用户 1-体验用户 + Account string `json:"account"` // 登录账号 + Email string `json:"email"` // 绑定邮箱 + Mobile string `json:"mobile"` // 手机号 - 格式:+86 1234 + Invite string `json:"invite"` // 邀请码 + Auth string `json:"auth"` // 实名认证 0-未认证 1-审核中 2-已认证 + AdvancedStatus string `json:"advancedStatus"` // 高级认证状态 0-未认证 1-审核中 2-已认证 3-已拒绝 + Nickname string `json:"nickName"` // 用户名称 + Avatar string `json:"avatar"` // 用户头像 + ResourceId int64 `json:"resourceId"` // 头像资源文件 + Gender string `json:"gender"` // 0-男 1-女 2-未知 + Token string `json:"token"` // 登陆token + Integral string `json:"integral"` // 信用积分 + IsGoogle string `json:"isGoogle"` // 是否绑定google验证 0-是 1-否 + GoogleCodeUrl string `json:"googleCodeUrl"` // google地址 + IsPayPwd string `json:"isPayPwd"` // 是否设置支付密码 0-是 1-否 + Role model.ArryString `json:"role"` // 角色 + LoginIp string `json:"loginIp"` // 登陆ip + LoginIpAddress string `json:"loginIpAddress,omitempty"` // 登陆ip + Date string `json:"date"` + } `json:"data"` //数据列表 +} + +/* + @Summary 用户详情 + @Router /user/info [post] +*/ + +func UserInfo(a *dapi.ApiBase) error { + + Response := UserInfoResponse{} + + Response.Data.Uid = a.User.Uid + Response.Data.Account = a.User.Account + Response.Data.Email = a.User.Email + Response.Data.Mobile = a.User.Mobile + Response.Data.Invite = a.User.Invite + Response.Data.Nickname = a.User.Detail.Nickname + + Response.Data.Gender = a.User.Detail.Gender + Response.Data.Token = a.User.Detail.Token + Response.Data.LoginIp = a.User.Detail.LoginIp + Response.Data.LoginIpAddress = a.User.Detail.LoginIpAddress + + if len(Response.Data.LoginIpAddress) <= 0 { + Response.Data.LoginIpAddress = cache.GetIpAddress(Response.Data.LoginIp) + } + + if len(a.User.Detail.PayPassWord) > 0 { + Response.Data.IsPayPwd = "0" + } else { + Response.Data.IsPayPwd = "1" + } + + // 角色 + Response.Data.Role = a.User.Role + + Response.ResponseCommon = a.NewSuccessResponseCommon() + return a.ReturnSuccessCustomResponse(Response) +} diff --git a/api/order.go b/api/order.go new file mode 100644 index 0000000..778f64e --- /dev/null +++ b/api/order.go @@ -0,0 +1 @@ +package api diff --git a/api/payChannel.go b/api/payChannel.go new file mode 100644 index 0000000..1362311 --- /dev/null +++ b/api/payChannel.go @@ -0,0 +1,65 @@ +package api + +import ( + "epur-pay/model" + "epur-pay/pkg/dapi" + "epur-pay/pkg/utils" +) + +type GetPayChannelResponse struct { + *dapi.ResponseCommon + Data []model.PayChannel `json:"data"` //数据列表 +} + +func GetPayChannel(a *dapi.ApiBase) error { + + Response := GetPayChannelResponse{ + ResponseCommon: a.NewSuccessResponseCommon(), + } + + query := a.Ts.Table(model.PayChannel{}.TableName()) + + query.Count(&Response.Count) + + utils.Error(query.Limit(a.Limit).Offset(a.Offset).Scan(&Response.Data).Error) + return a.ReturnSuccessCustomResponse(Response) +} + +type AddPayChannelParams struct { + Name string `json:"name"` // 渠道名称 + Logo string `json:"logo"` // 渠道Logo + Status bool `json:"status"` // 状态 +} + +func AddPayChannel(a *dapi.ApiBase, data *AddPayChannelParams) error { + + instance := model.PayChannel{ + Name: data.Name, + Logo: data.Logo, + Status: data.Status, + CreateTime: utils.Time2StampSecond(), + } + utils.Error(a.Ts.Create(&instance).Error) + + return a.ReturnSuccessResponse() +} + +type EditPayChannelParams struct { + Id int64 `json:"id"` // ID + Request model.PayRequests `json:"payRequests"` // 接入 +} + +func EditPayChannel(a *dapi.ApiBase, data *EditPayChannelParams) error { + + instance := model.PayChannel{} + utils.Error(a.Ts.Table(instance.TableName()).Where("id = ?", data.Id).Scan(&instance).Error) + if instance.Id <= 0 { + return a.ReturnPublicErrorResponse("不存在") + } + + utils.DbErrSkipRecordNotFound(a.Ts.Table(instance.TableName()). + Where("id", instance.Id). + Updates(map[string]interface{}{"pay_requests": data.Request}).Error) + + return a.ReturnSuccessResponse() +} diff --git a/api/power.go b/api/power.go new file mode 100644 index 0000000..20eccce --- /dev/null +++ b/api/power.go @@ -0,0 +1,178 @@ +package api + +import ( + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/dapi" + "epur-pay/pkg/utils" +) + +type MenuResponse struct { + *dapi.ResponseCommon + Data []model.Menu `json:"data"` //数据列表 +} + +// @Summary 角色菜单列表 +func GetMenuList(a *dapi.ApiBase) error { + // 查询菜单 + menu := []model.Menu{} + utils.Error(a.Ts.Raw(` + select DISTINCT m.id,m.pid,m.title,m.icon,m.path,m.component,m.target,m.permission,m.type,m.status,m.hide,m.note,m.sort + from menu as m left join role_menu as rm on rm.menu_id = m.id + left join user_role as ur on ur.role_id = rm.role_id + where ur.uid = ? and m.type = 0 and m.status = 1 and m.is_delete = 0 + order by m.pid asc, m.sort asc`, a.User.Uid).Scan(&menu).Error) + + Response := MenuResponse{} + Response.Data = GetMenu(a.User.Detail.Token, menu, 0, nil) + Response.ResponseCommon = a.NewSuccessResponseCommon() + return a.ReturnSuccessCustomResponse(Response) +} + +// 重装结构方式 +func GetMenu(token string, menuList []model.Menu, pid int64, message map[string]int64) []model.Menu { + treeList := []model.Menu{} + for _, v := range menuList { + if v.Pid == pid { + child := GetMenu(token, menuList, v.Id, message) + v.Children = child + // 消息数量 + if message != nil { + v.Meta.Badge = message[v.Permission] + } + treeList = append(treeList, v) + } + } + return treeList +} + +type MenuPermissionList struct { + Permission string `json:"permission"` +} + +type UserPermissionListResponse struct { + *dapi.ResponseCommon + Data struct { + Id int64 `json:"id"` // 用户ID + Nickname string `json:"nickname"` // 用户昵称 + RealName string `json:"realName"` // 别名 + Avatar string `json:"avatar"` // 头像 + PermissionList []string `json:"permissionList"` + } `json:"data"` //数据列表 +} + +// @Summary 权限标识列表 +func GetPermissionList(a *dapi.ApiBase) error { + Response := UserPermissionListResponse{} + + permission := []MenuPermissionList{} + utils.Error(a.Ts.Raw(`select m.permission from menu as m left join role_menu as rm on rm.menu_id = m.id + left join user_role as ur on ur.role_id = rm.role_id + where ur.uid = ? and m.type = 1 and m.status = 1 and m.is_delete = 0`, + a.User.Uid).Scan(&permission).Error) + + var permissionList []string + for _, v := range permission { + permissionList = append(permissionList, v.Permission) + } + + Response.Data.Id = a.User.Uid + Response.Data.Nickname = a.User.Detail.Nickname + Response.Data.RealName = a.User.Detail.Nickname + //Response.Avatar = a.User.Detail.Avatar + Response.Data.PermissionList = permissionList + + Response.ResponseCommon = a.NewSuccessResponseCommon() + return a.ReturnSuccessCustomResponse(Response) +} + +type GetPermissionMenuParams struct { + MenuId int64 `json:"menuId"` //菜单集合 +} + +type MenuAllResponse struct { + *dapi.ResponseCommon + Data []model.Menu `json:"data"` //数据列表 +} + +// @Summary 获取所有菜单 +func GetAllMenuList(a *dapi.ApiBase) error { + Response := MenuResponse{} + + utils.Error(a.Ts.Raw( + `select m.* from menu as m where m.is_delete = 0 order by m.pid asc, m.sort asc`).Scan(&Response.Data).Error) + + Response.ResponseCommon = a.NewSuccessResponseCommon() + return a.ReturnSuccessCustomResponse(Response) +} + +type EditMenuResponse struct { + Id int64 `json:"id,omitempty"` //菜单ID + Pid int64 `json:"pid"` //菜单父级ID + Title string `json:"title"` //菜单名称 + Icon string `json:"icon"` //菜单图标 + Path string `json:"path"` //菜单路径 + Component string `json:"component"` //菜单组件 + Target int64 `json:"target"` //打开方式 0-组件 1-内链 2-外链 + Permission string `json:"permission"` //权限标识 + Type int `json:"type"` //类型 0-菜单 1-节点 + Status int `json:"status"` //状态 1-正常 2-禁用 + Hide int `json:"hide"` //类型 0-显示 1-隐藏 + Note string `json:"note"` //备注 + Sort int `json:"sort"` //排序 +} + +// @Summary 编辑菜单 +func EditMenuInfo(a *dapi.ApiBase, data *EditMenuResponse) error { + if data.Id > 0 { + utils.Error(a.Ts.Model(&model.Menu{}).Where("id", data.Id).Updates(map[string]interface{}{ + "pid": data.Pid, + "status": 1, + "title": data.Title, + "type": data.Type, + "permission": data.Permission, + "hide": data.Hide, + "sort": data.Sort, + "target": data.Target, + "path": data.Path, + "component": data.Component, + "icon": data.Icon, + }).Error) + // 这里要刷新下权限 + cache.Global.Caches.RolePermission = cache.Global.Caches.RolePermission.Refresh() + } else { + menu := &model.Menu{} + menu.Pid = data.Pid + menu.Title = data.Title + menu.Type = data.Type + menu.Permission = data.Permission + menu.Hide = data.Hide + menu.Sort = data.Sort + menu.Target = data.Target + menu.Path = data.Path + menu.Component = data.Component + menu.Icon = data.Icon + menu.Status = 1 + // 如果是菜单的时候, 需要判断菜单权限表示是否重复 + if data.Type == 0 { + if len(data.Permission) <= 0 { + return a.ReturnPublicErrorResponse(a.Translate("AT021")) + } + + var total int64 + utils.Error(a.Ts.Table(model.Menu{}.TableName()).Where(`permission = ?`, data.Permission).Count(&total).Error) + if total > 0 { + return a.ReturnPublicErrorResponse(a.Translate("AT000")) + } + } + + utils.Error(a.Ts.Create(&menu).Error) + } + + return a.ReturnSuccessResponse() +} + +type CommonIdsResponse struct { + Id []interface{} `json:"id"` + Status string `json:"status"` +} diff --git a/api/test.go b/api/test.go new file mode 100644 index 0000000..e7c69f6 --- /dev/null +++ b/api/test.go @@ -0,0 +1,84 @@ +package api + +import ( + "epur-pay/model" + "epur-pay/pkg/dapi" + "epur-pay/pkg/logger" + "github.com/shopspring/decimal" +) + +type TestParams struct { + model.Http +} + +type TestResponse struct { + *dapi.ResponseCommon + Data struct { + RequestQuery interface{} `json:"requestQuery"` + RequestBody interface{} `json:"requestBody"` + Response interface{} `json:"response"` + } `json:"data"` //数据列表 +} + +func Test(a *dapi.ApiBase, data *TestParams) error { + + Response := TestResponse{ + ResponseCommon: a.NewSuccessResponseCommon(), + } + + //fmt.Println(utils.ToJson(data)) + if req, err := data.Do(); err != nil { + return a.ReturnPublicErrorResponse(err.Error()) + } else { + Response.Data.RequestQuery = req.QueryParams + Response.Data.RequestBody = req.Body + Response.Data.Response = string(req.Result) + } + + return a.ReturnSuccessCustomResponse(Response) +} + +type Test1Params struct { + Amount decimal.Decimal `json:"amount"` + OrderId string `json:"orderId"` +} + +type Test1Response struct { + *dapi.ResponseCommon + Data struct { + Url string `json:"url"` // 支付连接 + Status string `json:"status"` // 支付状态 0-成功 1-失败 + } `json:"data"` //数据列表 +} + +func Test1(a *dapi.ApiBase, data *Test1Params) error { + + logger.AccessLogger.Infoln(a.C.Query("11")) + + logger.AccessLogger.Infof("支付金额[%s] 订单号[%s]", data.Amount, data.OrderId) + Response := Test1Response{} + + Response.ResponseCommon = a.NewSuccessResponseCommon() + Response.Data.Url = "https://www.baidu.com" + Response.Data.Status = "0" + return a.ReturnSuccessCustomResponse(Response) +} + +// +//type Test2Params struct { +// model.Http +//} +// +//func Test2(a *dapi.ApiBase, data *Test2Params) error { +// +// Response := TestResponse{} +// +// if req, err := data.Do(); err != nil { +// return a.ReturnPublicErrorResponse(err.Error()) +// } else { +// Response.ResponseCommon = a.NewSuccessResponseCommon() +// Response.Data = string(req.Result) +// } +// +// return a.ReturnSuccessCustomResponse(Response) +//} diff --git a/cache/api/blacklist.go b/cache/api/blacklist.go new file mode 100644 index 0000000..502920a --- /dev/null +++ b/cache/api/blacklist.go @@ -0,0 +1,33 @@ +package cacheApi + +import ( + "epur-pay/model" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "strings" +) + +type BlackList struct { + Ip model.ArryString + duplicate *BlackList +} + +func (p *BlackList) Refresh() *BlackList { + p.duplicate = &BlackList{} + + blackInfo := model.BlackList{} + utils.Error(rds.DB.Raw(`select * from black_list where id = ?`, 1).Scan(&blackInfo).Error) + + p.duplicate.Ip = blackInfo.Ip + + return p.duplicate +} + +// 验证码ip +func (p *BlackList) Check(ip string) bool { + if len(strings.ReplaceAll(ip, " ", "")) <= 0 { + return false + } + + return p.Ip.Of(ip) +} diff --git a/cache/api/config.go b/cache/api/config.go new file mode 100644 index 0000000..99ec0fd --- /dev/null +++ b/cache/api/config.go @@ -0,0 +1,101 @@ +package cacheApi + +import ( + "encoding/json" + "epur-pay/model" + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "strconv" + "strings" + "sync" +) + +type Config struct { + Data sync.Map +} + +type configData struct { + Code string `json:"code"` //key + Comment string `json:"comment"` //comment +} + +func (p *Config) Refresh() *Config { + data := []configData{} + utils.Error(rds.DB.Raw(`select * from config where pid != 0`).Scan(&data).Error) + + for _, v := range data { + p.Data.Store(v.Code, v.Comment) + } + + return p +} + +func (p *Config) RefreshRow(data *model.Config) { + p.Data.Store(data.Code, data.Comment) +} + +func (p *Config) RefreshDel(data *model.Config) { + p.Data.Delete(data.Code) +} + +func (p *Config) Get(key string) string { + + if row, ok := p.Data.Load(key); ok { + return row.(string) + } else { + logger.AccessLogger.Warnln("缓存不存在了,注意!", key) + return "" + } +} + +func (p *Config) GetByte(key string) []byte { + + if row, ok := p.Data.Load(key); ok { + return []byte(row.(string)) + } else { + logger.AccessLogger.Warnln("缓存不存在了,注意!", key) + return nil + } +} + +func (p *Config) GetJsonRaw(key string) (row json.RawMessage) { + data := p.GetByte(key) + if data != nil { + _ = json.Unmarshal(data, &row) + } + return row +} + +func (p *Config) GetMap(key string) map[string]interface{} { + data := p.GetByte(key) + row := make(map[string]interface{}) + if data != nil { + configJson := []model.ConfigJson{} + _ = json.Unmarshal(data, &configJson) + for _, v := range configJson { + row[v.Key] = v.Value + } + } + + return row +} + +func (p *Config) GetArrayString(key string) []string { + row := []string{} + data := p.Get(key) + + if len(data) > 0 { + row = strings.Split(p.Get(key), ",") + } + return row +} + +func (p *Config) GetInt64(key string) int64 { + if row, ok := p.Data.Load(key); ok { + r, _ := strconv.Atoi(row.(string)) + return int64(r) + } else { + return 0 + } +} diff --git a/cache/api/language.go b/cache/api/language.go new file mode 100644 index 0000000..e568522 --- /dev/null +++ b/cache/api/language.go @@ -0,0 +1,70 @@ +package cacheApi + +import ( + "epur-pay/model" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "sync" +) + +type Language struct { + List []model.SysLanguage + Map *sync.Map + TranslateMap *sync.Map + DefaultLanguage string + duplicate *Language +} + +func (p *Language) Refresh() *Language { + + p.duplicate = &Language{} + p.duplicate.Map = new(sync.Map) + p.duplicate.TranslateMap = new(sync.Map) + + utils.Error(rds.DB.Raw(`select * from sys_language order by sort`).Scan(&p.duplicate.List).Error) + + for _, v := range p.duplicate.List { + if v.IsDefault == "0" { + p.duplicate.DefaultLanguage = v.Akey + } + } + if len(p.duplicate.DefaultLanguage) <= 0 { + p.duplicate.DefaultLanguage = "en_US" + } + + // 资源文件 + translate := []model.Translate{} + utils.Error(rds.DB.Table(model.Translate{}.TableName()).Where(`type = ?`, 0).Order(`id asc`).Scan(&translate).Error) + + for idx, v := range translate { + p.duplicate.TranslateMap.Store(v.LanguageKey, translate[idx].To) + } + + return p.duplicate +} + +func (p *Language) GetLang(lang string, key string) string { + + if trans, ok := p.TranslateMap.Load(key); ok { + for _, v := range trans.(model.TranslateTos) { + if v.GoogleKey == lang { + return v.To + } + } + } + + return "" +} + +func (p *Language) GetTranslate(key string) model.TranslateTos { + if row, ok := p.TranslateMap.Load(key); ok { + return row.(model.TranslateTos) + } + return nil +} + +func (p *Language) GetDefaultLang(key string) string { + row, _ := p.Map.Load(p.DefaultLanguage) + result, _ := row.(*sync.Map).Load(key) + return result.(string) +} diff --git a/cache/api/role.go b/cache/api/role.go new file mode 100644 index 0000000..d764c7d --- /dev/null +++ b/cache/api/role.go @@ -0,0 +1,57 @@ +package cacheApi + +import ( + "epur-pay/model" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "fmt" + "sync" +) + +type RolePermission struct { + Map *sync.Map + duplicate *RolePermission +} + +type MenuPermission struct { + RoleId int64 `gorm:"column:role_id" json:"roleId"` + Permission string `gorm:"column:permission" json:"permission"` +} + +func (p *RolePermission) Refresh() *RolePermission { + permission := []MenuPermission{} + utils.Error(rds.DB.Raw(` + select rm.role_id, permission from role_menu rm inner join menu as m + on rm .menu_id = m.id + `).Scan(&permission).Error) + + p.duplicate = &RolePermission{} + p.duplicate.Map = &sync.Map{} + + for _, v := range permission { + roleId := fmt.Sprintf("%d", v.RoleId) + if row, ok := p.duplicate.Map.Load(roleId); ok { + row = append(row.(model.ArryString), v.Permission) + + p.duplicate.Map.Store(roleId, row) + } else { + p.duplicate.Map.Store(roleId, model.ArryString{v.Permission}) + } + } + + return p.duplicate +} + +// 验证是否有权限 +func (p *RolePermission) Check(roleAttr model.Arry, permission string) bool { + // 用户支持多个角色 + for i := range roleAttr { + if row, ok := p.Map.Load(fmt.Sprintf("%d", roleAttr[i])); ok { + if row.(model.ArryString).Of(permission) { + return true + } + } + } + + return false +} diff --git a/cache/api/user.go b/cache/api/user.go new file mode 100644 index 0000000..bd044e5 --- /dev/null +++ b/cache/api/user.go @@ -0,0 +1,61 @@ +package cacheApi + +import ( + "epur-pay/model" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "github.com/muesli/cache2go" + "time" +) + +type User struct { + Data *cache2go.CacheTable + duplicate *User +} + +func (p *User) Refresh() *User { + p.duplicate = &User{} + p.duplicate.Data = cache2go.Cache("userList") + return p.duplicate +} + +func (p *User) RefreshRow(u *model.User) *model.User { + p.Data.Add(u.Uid, time.Duration(7*24*60*60)*time.Second, u) + return u +} + +func (p *User) RefreshBatchRow(u ...*model.User) { + for index, _ := range u { + p.RefreshRow(u[index]) + } +} + +func (p *User) RefreshSelectRow(uid int64) *model.User { + user := model.User{} + utils.DbErrSkipRecordNotFound(rds.DB.Raw(`select * from user where uid = ?`, uid).Scan(&user).Error) + + if user.Uid <= 0 { + return nil + } + // 获取用户拥有的权限 + utils.DbErrSkipRecordNotFound(rds.DB.Table(model.UserRole{}.TableName()).Select(`role_id`). + Where(`uid = ?`, user.Uid).Scan(&user.RoleIds).Error) + + // 刷新缓存 + p.RefreshRow(&user) + return &user +} + +func (p *User) RefreshSelectBatchRow(uid ...int64) { + for _, item := range uid { + p.RefreshSelectRow(item) + } +} + +func (p *User) Get(uid int64) *model.User { + if row, err := p.Data.Value(uid); err == nil { + return row.Data().(*model.User) + } else { + return p.RefreshSelectRow(uid) + } +} diff --git a/cache/api/version.go b/cache/api/version.go new file mode 100644 index 0000000..be1abcb --- /dev/null +++ b/cache/api/version.go @@ -0,0 +1,46 @@ +package cacheApi + +import ( + "epur-pay/model" + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "sync" +) + +type Version struct { + Data sync.Map + duplicate *Version +} + +func (p *Version) Refresh() *Version { + + p.duplicate = &Version{} + + versions := make([]model.Version, 0) + + utils.Error(rds.DB.Table(model.Version{}.TableName()).Where(`status = 0`).Scan(&versions).Error) + + for index, _ := range versions { + p.duplicate.Data.Store(versions[index].Type, versions[index]) + } + + return p.duplicate +} + +func (p *Version) RefreshRow(data interface{}) { + + instance := data.(model.Version) + p.Data.Store(instance.Type, instance) +} + +func (p *Version) Get(key string) *model.Version { + + if row, ok := p.Data.Load(key); ok { + t1 := row.(model.Version) + return &t1 + } else { + logger.AccessLogger.Errorln("缓存不存在了,注意!", key) + return nil + } +} diff --git a/cache/caches.go b/cache/caches.go new file mode 100644 index 0000000..d1d7e1b --- /dev/null +++ b/cache/caches.go @@ -0,0 +1,63 @@ +package cache + +import ( + cacheApi "epur-pay/cache/api" + "epur-pay/model" + "epur-pay/pkg/config" + "epur-pay/pkg/limit" + "github.com/oschwald/geoip2-golang" + "time" +) + +var Global *GlobalServerCache + +/* + 全局公用缓存层 +*/ + +type GlobalServerCache struct { + ProjectInitStatus bool // 项目是否初始化完成 + IpLocation *geoip2.Reader // IP库 + DefaultTimeZone *time.Location // 默认时区 + LimitRouter struct { + RuleKey string + Rule *limit.LimitConfRules + Api limit.LimiterIface + } // 限流器 + Routers []*model.RouterMethod + Caches struct { + Config *cacheApi.Config + Language *cacheApi.Language + Version *cacheApi.Version + RolePermission *cacheApi.RolePermission // 角色菜单权限 + User *cacheApi.User + BlackList *cacheApi.BlackList // 黑名单 + } +} + +func New() { + Global = &GlobalServerCache{} + Global.CacheDataInit() +} + +func (g *GlobalServerCache) CacheDataInit() { + + g.DefaultTimeZone, _ = time.LoadLocation("Asia/Shanghai") + g.LimitRouter.Rule = &limit.LimitConfRules{ + Rules: make(map[string]*limit.LimitOpt), + } + g.Caches.Config = (&cacheApi.Config{}).Refresh() + g.Caches.Language = (&cacheApi.Language{}).Refresh() + g.Caches.Version = (&cacheApi.Version{}).Refresh() + g.Caches.BlackList = (&cacheApi.BlackList{}).Refresh() + g.Caches.RolePermission = (&cacheApi.RolePermission{}).Refresh() + g.Caches.User = (&cacheApi.User{}).Refresh() + + if config.Cf.Common.RunMode == "release" { + NewIpLocation() + } + + go func() { + g.ProjectInitStatus = true + }() +} diff --git a/cache/ipLocation.go b/cache/ipLocation.go new file mode 100644 index 0000000..96a34cb --- /dev/null +++ b/cache/ipLocation.go @@ -0,0 +1,112 @@ +package cache + +import ( + "ehttp/http" + "epur-pay/pkg/logger" + "epur-pay/pkg/redis" + "errors" + "fmt" + "github.com/oschwald/geoip2-golang" + "net" + "reflect" + "runtime" +) + +const IpStoreKey = "ipAA" + +func NewIpLocation() { + go func() { + if err := LoadIpStore(); err != nil && err.Error() == "加载IP库失败,未找到IP库" { + _ = GetIpStore(Global.Caches.Config.Get("IpStoreRemoteAddr")) + } + }() +} + +func LoadIpStore() (err error) { + + conn := redis.RPool.Get() + + defer func() { + if err1 := conn.Close(); err1 != nil { + logger.ErrorLogger.Infoln(err1.Error()) + err = err1 + } + }() + + var reply interface{} + + if reply, err = conn.Do("GET", IpStoreKey); err != nil { + logger.AccessLogger.Errorln("加载IP库失败:", err.Error()) + return + } else if reply != nil { + if Global.IpLocation, err = geoip2.FromBytes(reply.([]byte)); err != nil { + logger.AccessLogger.Errorln("Error opening IP2Location database:", err) + return + } + logger.AccessLogger.Infoln("加载IP库成功...") + } else { + logger.AccessLogger.Warnln("加载IP库失败,未找到IP库") + err = errors.New("加载IP库失败,未找到IP库") + return + } + return +} + +func GetIpStore(url string) (err error) { + + logger.AccessLogger.Infoln("开始获取IP库数据...") + + conn := redis.RPool.Get() + + defer func() { + if err1 := conn.Close(); err1 != nil { + logger.ErrorLogger.Infoln(err1.Error()) + err = err1 + } else { + _ = LoadIpStore() + } + }() + + req := http.New( + http.WithUrl(url), + http.WithMethod(http.GET), + ) + if err = req.Do(); err != nil { + logger.AccessLogger.Errorln("获取远端IP失败:", err.Error()) + return + } + if err = conn.Send("SET", IpStoreKey, req.Result); err != nil { + logger.AccessLogger.Errorln("获取远端IP失败:", err.Error()) + return + } + if err = conn.Flush(); err != nil { + logger.AccessLogger.Errorln("获取远端IP失败:", err.Error()) + return + } + logger.AccessLogger.Infoln("IP库数据获取完毕...") + + return +} + +func GetIpAddress(ip string) string { + + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + e := reflect.ValueOf(err) + logger.AccessLogger.Errorln(e.String()) + } + }() + + if len(ip) > 0 && Global.IpLocation != nil { + // 查询 IP 地址信息 + record, err := Global.IpLocation.City(net.ParseIP(ip)) + if err != nil { + return "" + } + return fmt.Sprintf("%s %s", record.Country.Names["zh-CN"], record.City.Names["zh-CN"]) + } + return "" +} diff --git a/cmd/1.go b/cmd/1.go new file mode 100644 index 0000000..fd7c77b --- /dev/null +++ b/cmd/1.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "github.com/deatil/go-array/array" +) + +func main() { + + arrData := map[string]any{ + "a": 123, + "b": map[string]any{ + "c": "ccc", + "d": map[string]any{ + "e": "eee", + "f": map[string]any{ + "g": "ggg", + }, + }, + "dd": []any{ + "ccccc", + "ddddd", + "fffff", + }, + "ff": map[any]any{ + 111: "fccccc", + 222: "fddddd", + 333: "dfffff", + }, + "hh": map[int]any{ + 1115: "hccccc", + 2225: "hddddd", + 3335: map[any]string{ + "qq1": "qq1ccccc", + "qq2": "qq2ddddd", + "qq3": "qq3fffff", + }, + }, + "kJh21ay": map[string]any{ + "Hjk2": "fccDcc", + "23rt": "^hgcF5c", + }, + }, + } + + fmt.Println(array.Get(arrData, "b.d.e")) + // output: eee + + fmt.Println(array.Get(arrData, "b.dd.1")) + // output: ddddd + + fmt.Println(array.Get(arrData, "b.hh.3335.qq2")) + // output: qq2ddddd +} diff --git a/config/pay.ini b/config/pay.ini new file mode 100644 index 0000000..c63cd6d --- /dev/null +++ b/config/pay.ini @@ -0,0 +1,73 @@ + +[Common] +LogFilePath = logs +Local = zh +AesKey = `PTIU3VR6HdfhziklcFQBXee1lkdpnesr` +IsLocal = 0 + +[AppServer] +Port = 11000 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[ApiServer] +Port = 11001 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[AdminServer] +Port = 11002 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[PcServer] +Port = 11003 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[AppDownServer] +Port = 11004 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[Rds] +Type = mysql +Host = 127.0.0.1 +Name = e2 +Port = 3307 +User = root +PassWord = `fsdfds12TTckc3ofsd123.,123.,,,...addf1jjjOO&6&^!@#!@#` + +CharSet = utf8mb4 +#空闲连接池中连接的最大数量 +MaxIdleConns = 10 +#打开数据库连接的最大数量 +MaxOpenConns = 20 +#慢日志打印时间(ms) +SlowThreshold = 100 + +[Redis] +Type = redis +Host = 127.0.0.1 +Port = 6379 +PassWord = `afasdf12313%%%^^^^` +Name = 8 + +MaxIdle = 10 +### +#连接池在给定时间分配的最大连接数。 +#当为零时,池中的连接数没有限制。 +### +MaxActive = 20 +#读超时时间 +ReadTimeout = 10 +#写超时时间 +WriteTimeout = 60 +#连接超时时间 +ConnectTimeout = 2 +### +#在此期间保持空闲状态后关闭连接。如果值 +#为零,则空闲连接未关闭。应设置应用程序 +#超时值小于服务器的超时值。 +### +IdleTimeout = 60 \ No newline at end of file diff --git a/config/pay_test.ini b/config/pay_test.ini new file mode 100644 index 0000000..f940b5b --- /dev/null +++ b/config/pay_test.ini @@ -0,0 +1,55 @@ + +[Common] +LogFilePath = logs +Local = zh + +[ApiServer] +Port = 11000 +ReadTimeOut = 60 +WriteTimeOut = 60 + +[Rds] +Type = mysql +Host = localhost +#Host = 192.168.1.181 +Name = epur-pay +Port = 3306 +User = root +#PassWord = `fadsfsfsdfds12TTckc3ofsd123.,123.,,,...addf1jjjOO&6&^!@#!@#` +PassWord = `tG0f6PVYh18le41BCb` + +CharSet = utf8mb4 +#空闲连接池中连接的最大数量 +MaxIdleConns = 10 +#打开数据库连接的最大数量 +MaxOpenConns = 20 +#慢日志打印时间(ms) +SlowThreshold = 100 + +[Redis] +Type = redis +#Host = 8.217.170.205 +#Host = 192.168.1.181 +Host = localhost +Port = 6379 +; PassWord = `afasdf12313%%%^^^^` +Name = default + +MaxIdle = 10 +### +#连接池在给定时间分配的最大连接数。 +#当为零时,池中的连接数没有限制。 +### +MaxActive = 20 +#读超时时间 +ReadTimeout = 2 +#写超时时间 +WriteTimeout = 60 +#连接超时时间 +ConnectTimeout = 2 +### +#在此期间保持空闲状态后关闭连接。如果值 +#为零,则空闲连接未关闭。应设置应用程序 +#超时值小于服务器的超时值。 +### +IdleTimeout = 60 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ee51550 --- /dev/null +++ b/go.mod @@ -0,0 +1,85 @@ +module epur-pay + +go 1.21.2 + +require ( + ehttp v1.0.0 + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/aws/aws-sdk-go v1.49.19 + github.com/bwmarrin/snowflake v0.3.0 + github.com/deatil/go-cryptobin v1.0.2042 + github.com/deckarep/golang-set v1.8.0 + github.com/gin-contrib/gzip v0.0.6 + github.com/gin-gonic/gin v1.9.1 + github.com/go-playground/validator/v10 v10.16.0 + github.com/go-sql-driver/mysql v1.7.0 + github.com/gomodule/redigo v1.8.9 + github.com/itsjamie/gin-cors v0.0.0-20220228161158-ef28d3d2a0a8 + github.com/juju/ratelimit v1.0.2 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 + github.com/oschwald/geoip2-golang v1.9.0 + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/robfig/cron v1.2.0 + github.com/sirupsen/logrus v1.9.3 + github.com/tencentyun/cos-go-sdk-v5 v0.7.45 + gopkg.in/ini.v1 v1.67.0 + gorm.io/driver/mysql v1.5.2 + gorm.io/gorm v1.25.5 +) + +require ( + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 // indirect + github.com/alibabacloud-go/debug v1.0.1 // indirect + github.com/alibabacloud-go/dysmsapi-20170525/v4 v4.1.1 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect + github.com/alibabacloud-go/openapi-util v0.1.1 // indirect + github.com/alibabacloud-go/tea v1.2.2 // indirect + github.com/alibabacloud-go/tea-utils v1.3.1 // indirect + github.com/alibabacloud-go/tea-utils/v2 v2.0.6 // indirect + github.com/alibabacloud-go/tea-xml v1.1.3 // indirect + github.com/aliyun/credentials-go v1.3.10 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/clbanning/mxj v1.8.4 // indirect + github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/deatil/go-array v1.0.1010 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang-module/carbon/v2 v2.3.5 // indirect + github.com/google/go-querystring v1.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/lestrrat-go/strftime v1.0.6 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mozillazg/go-httpheader v0.2.1 // indirect + github.com/oschwald/maxminddb-golang v1.11.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace ehttp v1.0.0 => ./tri/ehttp diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5303525 --- /dev/null +++ b/go.sum @@ -0,0 +1,401 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= +github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= +github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= +github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/dysmsapi-20170525/v4 v4.1.1 h1:ONFaIS+OmKYQq6eyW6Cj+cDXZPbSQna16OwuCiQM8B0= +github.com/alibabacloud-go/dysmsapi-20170525/v4 v4.1.1/go.mod h1:+0hEVb1D5a8xOWp/IApSN/gukLm+cfKgLSn98MYE4ik= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28= +github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= +github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.6 h1:ZkmUlhlQbaDC+Eba/GARMPy6hKdCLiSke5RsN5LcyQ0= +github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= +github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= +github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= +github.com/aliyun/credentials-go v1.3.10 h1:45Xxrae/evfzQL9V10zL3xX31eqgLWEaIdCoPipOEQA= +github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aws/aws-sdk-go v1.49.19 h1:oZryiqeQpeJsIcAmZlp86duMu/s/DJ43qyfwa51qmLg= +github.com/aws/aws-sdk-go v1.49.19/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= +github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deatil/go-array v1.0.1010 h1:RSHqfTVwc1qM78dJ8uQn0z/gTuwL1YVpsnK2yCoLuMs= +github.com/deatil/go-array v1.0.1010/go.mod h1:Wnv75khfeDx8RZJYyiaWu/CIdHBFWKUa7+4ECtVojmU= +github.com/deatil/go-cryptobin v1.0.2042 h1:5a2X6YAVQuaUY3FG0t61NZBQ5Avt4AIAeC4gO+Kc0MI= +github.com/deatil/go-cryptobin v1.0.2042/go.mod h1:Dx4hF97i2U49NxbGviHI86LhhDaeCHC1wfd9CdbTYsY= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-module/carbon/v2 v2.3.5 h1:c7uWPX2nAG4NR27iFmen2blNoyrH/yTsiyRQZKkM8iY= +github.com/golang-module/carbon/v2 v2.3.5/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/itsjamie/gin-cors v0.0.0-20220228161158-ef28d3d2a0a8 h1:3n0c+dqwjqfvvoV+Q3hWvXT58q/YGnegkFx8w56Kj44= +github.com/itsjamie/gin-cors v0.0.0-20220228161158-ef28d3d2a0a8/go.mod h1:AYdLvrSBFloDBNt7Y8xkQ6gmhCODGl8CPikjyIOnNzA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= +github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= +github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= +github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= +github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g= +github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= +github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= +github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= +github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= +github.com/tencentyun/cos-go-sdk-v5 v0.7.45 h1:5/ZGOv846tP6+2X7w//8QjLgH2KcUK+HciFbfjWquFU= +github.com/tencentyun/cos-go-sdk-v5 v0.7.45/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= +gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/logs/access.log b/logs/access.log new file mode 120000 index 0000000..7669713 --- /dev/null +++ b/logs/access.log @@ -0,0 +1 @@ +access.log.20250210.log \ No newline at end of file diff --git a/logs/access.log.20250210.log b/logs/access.log.20250210.log new file mode 100644 index 0000000..1315d15 --- /dev/null +++ b/logs/access.log.20250210.log @@ -0,0 +1,18 @@ +[GOID:1] [WARNING] 2025-02-10 16:52:05 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:52:05 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [WARNING] 2025-02-10 16:56:40 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:56:40 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [WARNING] 2025-02-10 16:57:42 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:57:42 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [WARNING] 2025-02-10 16:59:12 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:59:12 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [WARNING] 2025-02-10 16:59:17 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:59:17 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [WARNING] 2025-02-10 16:59:28 [\cache\api/config.go:47] 缓存不存在了,注意! storeType +[GOID:1] [WARNING] 2025-02-10 16:59:28 [\cache\api/config.go:57] 缓存不存在了,注意! storeJson_ +[GOID:1] [INFO] 2025-02-10 17:00:05 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-10 17:00:05 [\pkg\server/api.go:55] server listening ... +[GOID:1] [WARNING] 2025-02-10 17:00:15 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:67] [WARNING] 2025-02-10 17:00:15 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-10 17:00:15 [\pkg\server/api.go:33] server exiting diff --git a/logs/access.log.20250211.log b/logs/access.log.20250211.log new file mode 100644 index 0000000..bdc9954 --- /dev/null +++ b/logs/access.log.20250211.log @@ -0,0 +1,307 @@ +[GOID:1] [INFO] 2025-02-11 09:21:54 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 09:21:54 [\pkg\server/api.go:55] server listening ... +[GOID:1] [WARNING] 2025-02-11 09:22:02 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:35] [WARNING] 2025-02-11 09:22:02 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 09:22:02 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 09:37:09 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 09:37:09 [\pkg\server/api.go:55] server listening ... +[GOID:53] [INFO] 2025-02-11 09:42:23 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:53] [ERROR] 2025-02-11 09:42:23 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:53] [INFO] 2025-02-11 09:42:23 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:53] [INFO] 2025-02-11 09:42:36 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:53] [ERROR] 2025-02-11 09:42:36 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:53] [INFO] 2025-02-11 09:42:36 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:45] [INFO] 2025-02-11 09:59:47 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:45] [ERROR] 2025-02-11 09:59:47 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:45] [INFO] 2025-02-11 09:59:47 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:45] [INFO] 2025-02-11 09:59:58 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:45] [ERROR] 2025-02-11 09:59:58 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:45] [INFO] 2025-02-11 09:59:58 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:45] [INFO] 2025-02-11 10:00:10 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:45] [ERROR] 2025-02-11 10:00:10 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:45] [INFO] 2025-02-11 10:00:10 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:1] [WARNING] 2025-02-11 10:00:14 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:39] [WARNING] 2025-02-11 10:00:14 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 10:00:14 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 10:00:22 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 10:00:22 [\pkg\server/api.go:55] server listening ... +[GOID:67] [INFO] 2025-02-11 10:00:37 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:67] [ERROR] 2025-02-11 10:00:37 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:67] [INFO] 2025-02-11 10:00:37 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:67] [INFO] 2025-02-11 10:00:52 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:67] [ERROR] 2025-02-11 10:00:52 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:67] [INFO] 2025-02-11 10:00:52 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:67] [INFO] 2025-02-11 10:01:48 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:67] [ERROR] 2025-02-11 10:01:48 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:67] [INFO] 2025-02-11 10:02:16 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:1] [WARNING] 2025-02-11 10:02:16 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:15] [WARNING] 2025-02-11 10:02:16 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 10:02:16 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 10:02:38 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 10:02:38 [\pkg\server/api.go:55] server listening ... +[GOID:37] [INFO] 2025-02-11 10:03:01 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:37] [ERROR] 2025-02-11 10:03:01 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:37] [INFO] 2025-02-11 10:03:09 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:37] [INFO] 2025-02-11 10:03:45 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:37] [ERROR] 2025-02-11 10:03:45 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:37] [INFO] 2025-02-11 10:03:53 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:37] [INFO] 2025-02-11 10:04:14 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:37] [ERROR] 2025-02-11 10:04:24 [\pkg\dapi/utils.go:149] ===> BindJSON: +[GOID:37] [INFO] 2025-02-11 10:04:55 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"","data":null} + +[GOID:1] [WARNING] 2025-02-11 10:04:56 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:26] [WARNING] 2025-02-11 10:04:56 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 10:04:56 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 10:40:33 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 10:40:33 [\pkg\server/api.go:55] server listening ... +[GOID:41] [INFO] 2025-02-11 10:40:41 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> +[GOID:41] [ERROR] 2025-02-11 10:40:41 [\pkg\dapi/utils.go:149] ===> BindJSON: Parameter failure +[GOID:41] [INFO] 2025-02-11 10:40:41 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":965,"message":"Parameter failure","data":null} + +[GOID:41] [INFO] 2025-02-11 10:41:25 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {} +[GOID:41] [INFO] 2025-02-11 10:41:25 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Invalid captcha code","data":null} + +[GOID:1] [WARNING] 2025-02-11 10:46:03 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:23] [WARNING] 2025-02-11 10:46:03 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 10:46:03 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 11:43:15 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 11:43:15 [\pkg\server/api.go:55] server listening ... +[GOID:66] [INFO] 2025-02-11 11:43:21 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {} +[GOID:66] [INFO] 2025-02-11 11:43:21 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Account is required","data":null} + +[GOID:66] [INFO] 2025-02-11 11:43:42 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"account":"0745dk"} +[GOID:66] [INFO] 2025-02-11 11:43:42 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Password is required","data":null} + +[GOID:68] [INFO] 2025-02-11 11:58:19 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"account":"0745dk","password":"123456789","mobile":"114514"} +[GOID:68] [INFO] 2025-02-11 11:58:19 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Captcha code is required","data":null} + +[GOID:68] [INFO] 2025-02-11 11:58:22 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"account":"0745dk","password":"123456789","mobile":"114514"} +[GOID:68] [INFO] 2025-02-11 11:58:22 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Captcha code is required","data":null} + +[GOID:68] [INFO] 2025-02-11 11:58:39 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"account":"0745dk","password":"123456789","mobile":"114514","captcha":"1919810"} +[GOID:68] [INFO] 2025-02-11 11:58:39 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Invalid captcha code","data":null} + +[GOID:47] [INFO] 2025-02-11 12:06:07 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> +[GOID:47] [ERROR] 2025-02-11 12:06:07 [\pkg\dapi/utils.go:149] ===> BindJSON: Parameter failure +[GOID:47] [INFO] 2025-02-11 12:06:07 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":965,"message":"Parameter failure","data":null} + +[GOID:47] [INFO] 2025-02-11 12:06:16 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"query":"\n query IntrospectionQuery {\n __schema {\n \n queryType { name }\n mutationType { name }\n subscriptionType { name }\n types {\n ...FullType\n }\n directives {\n name\n description\n \n locations\n args {\n ...InputValue\n }\n }\n }\n }\n\n fragment FullType on __Type {\n kind\n name\n description\n \n fields(includeDeprecated: true) {\n name\n description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n ...TypeRef\n }\n enumValues(includeDeprecated: true) {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n }\n\n fragment InputValue on __InputValue {\n name\n description\n type { ...TypeRef }\n defaultValue\n \n \n }\n\n fragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n }\n "} +[GOID:47] [INFO] 2025-02-11 12:06:16 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:47] [INFO] 2025-02-11 12:06:35 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:47] [INFO] 2025-02-11 12:06:35 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:47] [INFO] 2025-02-11 12:06:43 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:47] [INFO] 2025-02-11 12:06:43 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:06:57 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:22] [WARNING] 2025-02-11 12:06:57 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:06:57 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:07:06 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:07:06 [\pkg\server/api.go:55] server listening ... +[GOID:54] [INFO] 2025-02-11 12:07:12 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:54] [INFO] 2025-02-11 12:07:12 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:54] [INFO] 2025-02-11 12:07:42 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:54] [INFO] 2025-02-11 12:08:34 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:08:34 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:51] [WARNING] 2025-02-11 12:08:34 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:08:34 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:09:42 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:09:42 [\pkg\server/api.go:55] server listening ... +[GOID:38] [INFO] 2025-02-11 12:09:45 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:38] [INFO] 2025-02-11 12:09:45 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:38] [INFO] 2025-02-11 12:09:46 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:38] [INFO] 2025-02-11 12:09:46 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:38] [INFO] 2025-02-11 12:09:52 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:38] [INFO] 2025-02-11 12:10:07 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"SMS code sent too frequently","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:10:07 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:50] [WARNING] 2025-02-11 12:10:07 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:10:07 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:10:13 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:10:13 [\pkg\server/api.go:55] server listening ... +[GOID:20] [INFO] 2025-02-11 12:10:15 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:20] [INFO] 2025-02-11 12:10:15 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"Failed to fetch SMS code limit","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:11:03 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:54] [WARNING] 2025-02-11 12:11:03 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:11:03 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:13:54 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:13:54 [\pkg\server/api.go:55] server listening ... +[GOID:53] [INFO] 2025-02-11 12:14:05 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:53] [INFO] 2025-02-11 12:14:26 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":999,"message":"Failed to fetch SMS code limit","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:14:26 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:67] [WARNING] 2025-02-11 12:14:26 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:14:26 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:17:09 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:17:09 [\pkg\server/api.go:55] server listening ... +[GOID:51] [INFO] 2025-02-11 12:17:10 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:51] [INFO] 2025-02-11 12:17:11 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":200,"message":"Success"} + +[GOID:68] [INFO] 2025-02-11 12:18:19 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"189897"} +[GOID:68] [INFO] 2025-02-11 12:18:19 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Password length minimum 6 characters, maximum 72 characters.","data":null} + +[GOID:68] [INFO] 2025-02-11 12:18:32 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"189897","password":"123456"} +[GOID:68] [INFO] 2025-02-11 12:18:32 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Password must contain at least one letter, one number.","data":null} + +[GOID:68] [INFO] 2025-02-11 12:18:36 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"189897","password":"a123456"} +[GOID:68] [INFO] 2025-02-11 12:18:36 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":500,"message":"System error","data":null} + +[GOID:68] [INFO] 2025-02-11 12:19:05 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"189897","password":"a123456"} +[GOID:68] [INFO] 2025-02-11 12:19:05 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":999,"message":"Invalid captcha code","data":null} + +[GOID:68] [INFO] 2025-02-11 12:19:16 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:68] [INFO] 2025-02-11 12:19:17 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":200,"message":"Success"} + +[GOID:68] [INFO] 2025-02-11 12:19:42 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:68] [INFO] 2025-02-11 12:19:42 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":500,"message":"System error","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:19:49 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:16] [WARNING] 2025-02-11 12:19:49 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:19:49 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:19:55 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:19:55 [\pkg\server/api.go:55] server listening ... +[GOID:51] [INFO] 2025-02-11 12:20:04 [\pkg\dapi/utils.go:139] /api/v1/login/captcha 请求数据 -> {"mobile":"17308457960"} +[GOID:51] [INFO] 2025-02-11 12:20:04 [\pkg\dapi/response.go:127] /api/v1/login/captcha 返回数据 -> {"code":200,"message":"Success"} + +[GOID:51] [INFO] 2025-02-11 12:20:32 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"525989","password":"a123456"} +[GOID:51] [INFO] 2025-02-11 12:21:41 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":500,"message":"System error","data":null} + +[GOID:82] [INFO] 2025-02-11 12:21:56 [\pkg\dapi/utils.go:139] /api/v1/login/register 请求数据 -> {"mobile":"17308457960","captcha":"525989","password":"a123456"} +[GOID:82] [INFO] 2025-02-11 12:21:59 [\pkg\dapi/response.go:127] /api/v1/login/register 返回数据 -> {"code":200,"message":"Success","data":{"token":"Ia2yzOFPsPtj%2BZvM2PnRGg%3D%3D"}} + +[GOID:1] [WARNING] 2025-02-11 12:23:36 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:15] [WARNING] 2025-02-11 12:23:36 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:23:36 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:28:34 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:28:34 [\pkg\server/api.go:55] server listening ... +[GOID:22] [INFO] 2025-02-11 12:28:35 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:22] [INFO] 2025-02-11 12:28:35 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Invalid user role","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:31:52 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:68] [WARNING] 2025-02-11 12:31:52 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:31:52 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:31:58 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:31:58 [\pkg\server/api.go:55] server listening ... +[GOID:13] [INFO] 2025-02-11 12:32:05 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:13] [INFO] 2025-02-11 12:32:05 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"","data":null} + +[GOID:13] [INFO] 2025-02-11 12:32:15 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:13] [INFO] 2025-02-11 12:32:31 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:34:09 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:44] [WARNING] 2025-02-11 12:34:09 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:34:09 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:34:13 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:34:13 [\pkg\server/api.go:55] server listening ... +[GOID:82] [INFO] 2025-02-11 12:34:19 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:19 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:24 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:24 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:25 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:25 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:25 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:25 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:26 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:26 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:27 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:27 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:28 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:28 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:29 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:29 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:30 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:30 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:30 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:30 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:31 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:31 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:43 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s23456"} +[GOID:82] [INFO] 2025-02-11 12:34:55 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:34:59 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:82] [INFO] 2025-02-11 12:34:59 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:35:01 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:82] [INFO] 2025-02-11 12:35:01 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:82] [INFO] 2025-02-11 12:35:17 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:82] [INFO] 2025-02-11 12:35:28 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:35:28 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:67] [WARNING] 2025-02-11 12:35:28 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:35:28 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:41:18 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:41:18 [\pkg\server/api.go:55] server listening ... +[GOID:67] [INFO] 2025-02-11 12:41:20 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:67] [INFO] 2025-02-11 12:41:28 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":200,"message":"Success","data":{"token":"4NQXTFsyTQBdxosv5K0wCw%3D%3D"}} + +[GOID:67] [INFO] 2025-02-11 12:41:32 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s3456"} +[GOID:67] [INFO] 2025-02-11 12:41:48 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:1] [WARNING] 2025-02-11 12:41:59 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:21] [WARNING] 2025-02-11 12:41:59 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:41:59 [\pkg\server/api.go:33] server exiting +[GOID:1] [INFO] 2025-02-11 12:42:15 [\pkg\server/api.go:63] :11000 +[GOID:1] [INFO] 2025-02-11 12:42:15 [\pkg\server/api.go:55] server listening ... +[GOID:38] [INFO] 2025-02-11 12:42:18 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s3456"} +[GOID:38] [INFO] 2025-02-11 12:42:18 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:38] [INFO] 2025-02-11 12:42:20 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a1s3456"} +[GOID:38] [INFO] 2025-02-11 12:42:20 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:38] [INFO] 2025-02-11 12:42:23 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a13456"} +[GOID:38] [INFO] 2025-02-11 12:42:23 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:38] [INFO] 2025-02-11 12:42:24 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a13456"} +[GOID:38] [INFO] 2025-02-11 12:42:24 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":999,"message":"Incorrect password.","data":null} + +[GOID:38] [INFO] 2025-02-11 12:42:28 [\pkg\dapi/utils.go:139] /api/v1/login/login 请求数据 -> {"mobile":"17308457960","captcha":"137038","password":"a123456"} +[GOID:38] [INFO] 2025-02-11 12:42:28 [\pkg\dapi/response.go:127] /api/v1/login/login 返回数据 -> {"code":200,"message":"Success","data":{"token":"PcOq70btqslY6TJdvLxH2A%3D%3D"}} + +[GOID:1] [WARNING] 2025-02-11 12:42:46 [\pkg\server/api.go:23] Get Signal: interrupt Shutdown Server ... +[GOID:25] [WARNING] 2025-02-11 12:42:46 [\pkg\server/api.go:40] HTTP listen: http: Server closed + +[GOID:1] [INFO] 2025-02-11 12:42:46 [\pkg\server/api.go:33] server exiting diff --git a/logs/error.log b/logs/error.log new file mode 100644 index 0000000..944337b --- /dev/null +++ b/logs/error.log @@ -0,0 +1 @@ +error.log.20240118.log \ No newline at end of file diff --git a/logs/error.log.20240118.log b/logs/error.log.20240118.log new file mode 100644 index 0000000000000000000000000000000000000000..37e36c0bbc2d9e5bc3f7a88b84c736aff7d95627 GIT binary patch literal 854276 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 0 { + for idx := range p.Body { + if p.Body[idx].DataType != "event" { + if value, ok := p.Body[idx].Get1(); !ok { + return req, errors.New(fmt.Sprintf("[%s]:类型不匹配", p.Body[idx].Field)) + } else { + req.SetBody(p.Body[idx].Field, value) + allValues[p.Body[idx].Field] = value + } + } + } + } + + if len(p.Query) > 0 { + for idx := range p.Query { + if p.Query[idx].DataType != "event" { + if value, ok := p.Query[idx].Get1(); !ok { + return req, errors.New(fmt.Sprintf("[%s]:类型不匹配", p.Query[idx].Field)) + } else { + allValues[p.Query[idx].Field] = value + req.QueryParams.Add(p.Query[idx].Field, http.ConvertToString(value)) + } + } + + } + } + + logger.AccessLogger.Infoln(utils.ToJson(&allValues)) + if len(p.Body) > 0 { + for idx := range p.Body { + if p.Body[idx].DataType == "event" { + values := make([]interface{}, 0) + + BodyValues := p.Body[idx].Value.Value.([]any) + + logger.AccessLogger.Infoln(utils.ToJson(&BodyValues)) + + for index := range BodyValues { + logger.AccessLogger.Infoln(BodyValues[index].(string), array.Get(allValues, BodyValues[index].(string))) + values = append(values, array.Get(allValues, BodyValues[index].(string))) + } + logger.AccessLogger.Infoln(utils.ToJson(&values)) + v := (&tools.Api{ + Tools: p.Body[idx].Value.Tools, + Values: values, + }).Run() + allValues[p.Body[idx].Field] = v + req.SetBody(p.Body[idx].Field, v) + } + } + } + + if len(p.Query) > 0 { + for idx := range p.Query { + if p.Query[idx].DataType == "event" { + values := make([]interface{}, 0) + for _, item := range p.Query[idx].Value.Value.([]interface{}) { + values = append(values, array.Get(allValues, item.(string))) + } + v := (&tools.Api{ + Tools: p.Query[idx].Value.Tools, + Values: values, + }).Run() + allValues[p.Query[idx].Field] = v + req.SetBody(p.Query[idx].Field, v) + } + } + } + + if err := req.Do(); err != nil { + return nil, err + } + return req, nil +} + +type HttpParams struct { + + /* + DataType: + array // 数组 + object // 对象 + string // 字符串 + int64 // 整型 + float64 // 浮点 + decimal // 金额 + dataTime // 时间类型 + timeStamp // 时间戳 + file // 文件类型 + event // 事件 + pool // 参数池 + customPool // 自定义参数池 + */ + + Field string `json:"field"` + Value HttpParamsValue `json:"value"` + DataType string `json:"dataType"` // 数据类型 +} + +type HttpParamsValue struct { + Value interface{} `json:"value"` + Tools string `json:"tools,omitempty"` // 如果是事件 那么这里是工具选择 +} + +func (p *HttpParams) Get() interface{} { + row, _ := p.Get1() + return row +} + +func (p *HttpParams) Get1() (interface{}, bool) { + + switch p.DataType { + case "string": + row, ok := p.Value.Value.(string) + return row, ok + case "int64": + if row, ok := p.Value.Value.(string); !ok { + return nil, false + } else { + if row1, err := strconv.ParseInt(row, 10, 64); err != nil { + return nil, false + } else { + return row1, true + } + } + case "float64": + row, ok := p.Value.Value.(float64) + return row, ok + case "decimal": + if row, ok := p.Value.Value.(string); ok { + if row1, err1 := decimal.NewFromString(row); err1 == nil { + return row1, true + } else { + return nil, false + } + } else { + return row, ok + } + case "object": + if b, err := json.Marshal(p.Value.Value); err != nil { + return nil, false + } else { + row := make([]HttpParams, 0) + if err1 := json.Unmarshal(b, &row); err1 != nil { + return nil, false + } + result := map[string]interface{}{} + for _, item := range row { + if row1, ok := item.Get1(); !ok { + return nil, false + } else { + result[item.Field] = row1 + } + } + return result, true + } + case "array": + if b, err := json.Marshal(p.Value.Value); err != nil { + return nil, false + } else { + row := make([]HttpParams, 0) + if err1 := json.Unmarshal(b, &row); err1 != nil { + return nil, false + } + result := make([]interface{}, 0) + for _, item := range row { + if row1, ok := item.Get1(); !ok { + return nil, false + } else { + result = append(result, row1) + } + } + return result, true + } + case "dataTime": + if row, ok := p.Value.Value.(string); !ok { + return nil, false + } else { + return carbon.Now().Format(row), true + } + case "timeStamp": + if row, ok := p.Value.Value.(string); !ok { + return nil, false + } else { + switch row { + case "0": + return carbon.Now().Timestamp(), true + case "1": + return carbon.Now().TimestampMilli(), true + case "2": + return carbon.Now().TimestampMicro(), true + case "3": + return carbon.Now().TimestampNano(), true + default: + return nil, false + } + } + //case "event": + // var value interface{} + // var ok bool + // + // lastHttpParams := HttpParams{} + // routers := strings.Split(p.Value.Value.(string), ".") + // + // if lastHttpParams.DataType == "" { + // for idx := range EventValues { + // if routers[0] == EventValues[idx].Field { + // lastHttpParams = EventValues[idx] + // } + // } + // } + // + // if lastHttpParams.DataType == "" { + // return nil, false + // } + // + // if value, ok = lastHttpParams.Get1(EventValues); !ok { + // return nil, ok + // } + // + // value = array.Get(value, strings.Join(routers[1:], ".")) + // return value, ok + } + + return nil, false +} + +/* + 请求事件 +*/ + +//type HttpEvents []HttpEvent +// +//type HttpEvent struct { +// +// /* +// 工具选择 +// MD5 +// AES +// RSA +// SORT // 排序工具 +// */ +// EventName string `json:"eventName"` // 事件名称 +// Tools HttpTools `json:"tools"` // 工具选择 +//} + +//type HttpTools struct { +// Field string `json:"field"` +// Inputs []string `json:"inputs,omitempty"` // 传参 HttpParams中的Field +//} + +/* + 回调 +*/ + +type HttpCallback struct { + White ArryString `json:"white"` // 白名单 + ResponseType string `json:"responseType"` // 回调类型 + Query []HttpParams `json:"query"` // 回调参数 + Body []HttpParams `json:"body"` // 回调参数 +} diff --git a/model/merchant.go b/model/merchant.go new file mode 100644 index 0000000..d443569 --- /dev/null +++ b/model/merchant.go @@ -0,0 +1,16 @@ +package model + +/* + 商家 +*/ + +type Merchant struct { + Id int64 `gorm:"primary_key;column:id" json:"Id"` // 商户ID + Name string `gorm:"column:name" json:"name"` // 商家名称 + AdminUid int64 `gorm:"column:admin_uid" json:"adminUid"` // 管理员UID + ApiKey string `gorm:"column:api_key" json:"apiKey"` // 密钥 +} + +func (Merchant) TableName() string { + return "merchant" +} diff --git a/model/order.go b/model/order.go new file mode 100644 index 0000000..d981e00 --- /dev/null +++ b/model/order.go @@ -0,0 +1,84 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" + "github.com/shopspring/decimal" +) + +/* + 订单模块 +*/ + +const ( + REGISTER_STATUS_SUCCESS = "0" // 成功 + REGISTER_STATUS_FIAL = "1" // 失败 + REGISTER_STATUS_WAITPAY = "2" // 待支付 +) + +type Order struct { + + /* + 代收,代扣 + 充值: + status: + 0 - 成功 + 1 - 失败 + 2 - 待支付 + 提现: + status: + 0 - 成功 + 1 - 失败 + 2 - 待审核 + 3 - 审核成功 + 4 - 审核拒绝 + 转账 + type: + 0 - 充值 + 1 - 提现 + 2 - 转账 + 3 - 虚拟转账(电子户) + */ + + Id int64 `gorm:"primary_key;column:id" json:"id"` // + OrderId string `gorm:"column:order_id" json:"orderId"` // 订单号 + MerchantOrderId string `gorm:"column:merchant_order_id" json:"merchantOrderId"` // 商户订单号 + ChannelOrderId string `gorm:"column:channel_order_id" json:"channelOrderId"` // 渠道订单号 + MerchantId int64 `gorm:"column:merchant_id" json:"merchantId"` // 商户ID + ChannelId int64 `gorm:"column:channel_id" json:"channelId"` // 渠道ID + Amount decimal.Decimal `gorm:"column:amount" json:"amount"` // 金额 + ActAmount decimal.Decimal `gorm:"column:act_amount" json:"actAmount"` // 实际到账金额 + Fee decimal.Decimal `gorm:"column:fee" json:"fee"` // 手续费 + Type string `gorm:"column:type" json:"type"` // 0-代收 1-代扣 + SuccessTime int64 `gorm:"column:success_time" json:"successTime"` // 成功时间 + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间 + Status string `gorm:"column:status" json:"status"` // 订单状态 + ErrMsg string `gorm:"column:err_msg" json:"errMsg"` // 失败原因 + CallbackStatus bool `gorm:"column:callback_status" json:"callbackStatus"` // 回调状态 + CallbackErrMsg string `gorm:"column:callback_err_msg" json:"callbackErrMsg"` // 回调失败原因 + CallbackTime int64 `gorm:"column:callback_time" json:"callbackTime"` // 回调时间 + Uid int64 `gorm:"column:uid" json:"uid"` // 操作人UID + From OrderAccount `gorm:"column:from" json:"from"` // from + To OrderAccount `gorm:"column:to" json:"to"` // to + Customer JSON `gorm:"column:customer" json:"customer"` // 自定义字段 回调时返回 +} + +func (Order) TableName() string { + return "order" +} + +type OrderAccount struct { + /* + 账户信息 + */ + Account string `json:"account,omitempty"` // 账号 + Name string `json:"name,omitempty"` // 名称 +} + +func (j *OrderAccount) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j OrderAccount) Value() (driver.Value, error) { + return json.Marshal(j) +} diff --git a/model/pay.go b/model/pay.go new file mode 100644 index 0000000..b09336a --- /dev/null +++ b/model/pay.go @@ -0,0 +1,51 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" +) + +type PayRequests []PayRequest + +type PayRequest struct { + Field string `json:"field"` + Host string `json:"host"` // 域名 + Status bool `json:"status"` // 是否启用 + CustomPool []HttpParams `json:"customPool"` // 自定义参数池 //map[string]HttpParams + PayFuncs PayFuncs `json:"payFuncs"` // 请求集合 + White ArryString `json:"white"` // 白名单 +} + +type PayFuncs []PayFunc + +func (j *PayRequests) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j PayRequests) Value() (driver.Value, error) { + return json.Marshal(j) +} + +type PayFunc struct { + Name string `json:"name"` // 名称 + Field string `json:"field"` + Http Http `json:"http"` // http请求 + Sort int `json:"sort"` // 序号 +} + +/* + 支付渠道 +*/ + +type PayChannel struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 渠道ID + Name string `gorm:"column:name" json:"name"` // 渠道名称 + Logo string `gorm:"column:logo" json:"logo"` // 渠道Logo + Request PayRequests `gorm:"column:pay_requests" json:"payRequests"` // 接入 + Status bool `gorm:"column:status" json:"status"` // 是否启用 + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间 +} + +func (PayChannel) TableName() string { + return "pay_channel" +} diff --git a/model/power.go b/model/power.go new file mode 100644 index 0000000..0715bf6 --- /dev/null +++ b/model/power.go @@ -0,0 +1,73 @@ +package model + +/* + 权限管理 +*/ + +type Menu struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 菜单ID + Pid int64 `gorm:"column:pid" json:"pid"` // 菜单父级ID + Title string `gorm:"column:title" json:"title"` // 菜单名称 + Icon string `gorm:"column:icon" json:"icon"` // 菜单图标 + Path string `gorm:"column:path" json:"path"` // 菜单路径 + Component string `gorm:"column:component" json:"component"` // 菜单组件 + Target int64 `gorm:"column:target" json:"target"` // 打开方式 0-组件 1-内链 2-外链 + Permission string `gorm:"column:permission" json:"permission"` // 权限标识 + Type int `gorm:"column:type" json:"type"` // 类型 0-菜单 1-节点 + Status int `gorm:"column:status" json:"status"` // 状态 1-正常 2-禁用 + Hide int `gorm:"column:hide" json:"hide"` // 类型 0-显示 1-隐藏 + Note string `gorm:"column:note" json:"note"` // 备注 + Sort int `gorm:"column:sort" json:"sort"` // 排序 + Children []Menu `gorm:"-" json:"children,omitempty"` // 子菜单 + Checked int `gorm:"-" json:"checked,omitempty"` // 是否选中-sys_role_menu表作用于权限分配时 + Meta MenuMeta `gorm:"-" json:"meta,omitempty"` // 气泡 +} + +func (Menu) TableName() string { + return "menu" +} + +type MenuMeta struct { + Badge int64 `json:"badge"` // 消息数量 +} + +type MenuShow struct { + Menu + Meta MenuMeta `gorm:"-" json:"meta"` // 气泡 +} + +/* + 角色 +*/ + +type Role struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 角色ID + Name string `gorm:"column:name" json:"name" binding:"required"` // 角色名称 + Memo string `gorm:"column:memo" json:"memo"` // 角色说明 + Status int `gorm:"column:status" json:"status" binding:"required"` // 状态 1-正常 2-禁用 + Sort int `gorm:"column:sort" json:"sort" binding:"required"` // 排序 + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间 +} + +func (Role) TableName() string { + return "role" +} + +type RoleMenu struct { + Rid int64 `gorm:"primary_key;column:rid" json:"rid"` // ID + RoleId int64 `gorm:"column:role_id" json:"role_id" binding:"required"` // 角色ID + MenuId int64 `gorm:"column:menu_id" json:"menu_id" binding:"required"` // 菜单ID +} + +func (RoleMenu) TableName() string { + return "role_menu" +} + +type UserRole struct { + Uid int64 `gorm:"uid" json:"uid" binding:"required"` // 用户ID + RoleId int64 `gorm:"role_id" json:"roleId" binding:"required"` // 权限ID +} + +func (UserRole) TableName() string { + return "user_role" +} diff --git a/model/publicStr.go b/model/publicStr.go new file mode 100644 index 0000000..9cac077 --- /dev/null +++ b/model/publicStr.go @@ -0,0 +1,172 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" + mapset "github.com/deckarep/golang-set" + "time" +) + +type ListenServer struct { + Port int `yaml:"Port"` //Server监听端口 + ReadTimeOut time.Duration `yaml:"ReadTimeOut"` //Tcp 读超时时间 + WriteTimeOut time.Duration `yaml:"WriteTimeOut"` //Tcp 写超时时间 +} + +type Redis struct { + Type string `yaml:"Type"` + Host string `yaml:"Host"` + Port string `yaml:"Port"` + PassWord string `yaml:"PassWord"` + Name int `yaml:"Name"` + MaxIdle int `yaml:"MaxIdle"` + MaxActive int `yaml:"MaxActive"` + ReadTimeout time.Duration `yaml:"ReadTimeout"` + WriteTimeout time.Duration `yaml:"WriteTimeout"` + ConnectTimeout time.Duration `yaml:"ConnectTimeout"` + IdleTimeout time.Duration `yaml:"IdleTimeout"` +} + +type Rds struct { + Host string `yaml:"Host"` + Port int `yaml:"Port"` + User string `yaml:"User"` + Name string `yaml:"Name"` + PassWord string `yaml:"PassWord"` + CharSet string `yaml:"CharSet"` + MaxIdleConns int `yaml:"MaxIdleConns"` + MaxOpenConns int `yaml:"MaxOpenConns"` + SlowThreshold int `yaml:"SlowThreshold"` +} + +//type RMq struct { +// Type string `yaml:"Type"` +// Host string `yaml:"Host"` +// Port int `yaml:"Port"` +// User string `yaml:"User"` +// PassWord string `yaml:"PassWord"` +//} + +type Arry []int64 + +func (j *Arry) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j Arry) Value() (driver.Value, error) { + return json.Marshal(j) +} + +func (j Arry) Set() mapset.Set { + + s1 := mapset.NewSet() + + for _, item := range j { + s1.Add(item) + } + return s1 +} + +func (j Arry) SetToValue(s1 mapset.Set) Arry { + + s2 := Arry{} + + for _, item := range s1.ToSlice() { + s2 = append(s2, item.(int64)) + } + return s2 +} + +func (j Arry) ToInt64() []int64 { + s1 := []int64{} + for _, item := range j { + s1 = append(s1, item) + } + return s1 +} + +func (j Arry) Of(id int64) bool { + for _, item := range j { + if item == id { + return true + } + } + return false +} + +type ArryString []string + +func (j *ArryString) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j ArryString) Value() (driver.Value, error) { + return json.Marshal(j) +} + +func (j ArryString) Set() mapset.Set { + + s1 := mapset.NewSet() + + for _, item := range j { + s1.Add(item) + } + return s1 +} + +func (j ArryString) SetToValue(s1 mapset.Set) ArryString { + + s2 := ArryString{} + + for _, item := range s1.ToSlice() { + s2 = append(s2, item.(string)) + } + return s2 +} + +func (j ArryString) ToString() []string { + s1 := []string{} + for _, item := range j { + s1 = append(s1, item) + } + return s1 +} + +func (j ArryString) Of(id string) bool { + for _, item := range j { + if item == id { + return true + } + } + return false +} + +func (j *ArryString) Add(id string) *ArryString { + *j = append(*j, id) + return j +} + +func (j *ArryString) Remove(id string) *ArryString { + // 如果未找到要移除的元素,直接返回原数组 + return j +} + +type JSON map[string]interface{} + +func (j *JSON) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j JSON) Value() (driver.Value, error) { + return json.Marshal(j) +} + +type RouterMethod struct { + Name string + Path string + Params interface{} + Method string + Leaf bool // 是否叶子节点 + F interface{} + Child []*RouterMethod +} diff --git a/model/user.go b/model/user.go new file mode 100644 index 0000000..d92283a --- /dev/null +++ b/model/user.go @@ -0,0 +1,61 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" +) + +/* + 用户模块 +*/ + +const ( + USER_ROLE_ADMIN = "0" // 管理员 + USER_ROLE_AGENT = "1" // 代理 + USER_ROLE_MEMBER = "2" // 用户 +) + +type User struct { + Uid int64 `gorm:"primary_key;column:uid" json:"uid"` // 用户ID + Role ArryString `gorm:"column:role" json:"role"` // 角色类型 [0,1,2,3] - [管理员,代理,用户] + Account string `gorm:"column:account" json:"account"` // 登录账号 + Email string `gorm:"column:email" json:"email"` // 绑定邮箱 + Mobile string `gorm:"column:mobile" json:"mobile"` // 手机号 - 格式:+86 1234 + Status string `gorm:"column:status" json:"status"` // 状态 0-正常 1-拉黑 + Invite string `gorm:"column:invite" json:"invite"` // 邀请码 + MerchantId int64 `gorm:"column:merchant_id" json:"merchantId"` // 商户ID + Merchant Merchant `gorm:"-" json:"merchant,omitempty"` // 商户结构 + RoleIds Arry `gorm:"-" json:"-"` // 拥有的角色权限 + Detail UserDetail `gorm:"column:detail" json:"detail"` // 详细信息 +} + +func (User) TableName() string { + return "user" +} + +type UserDetail struct { + Nickname string `json:"nickName"` // 用户名称 + Avatar int64 `json:"avatar"` // 用户头像 + Gender string `json:"gender"` // 0-男 1-女 2-未知 + Token string `json:"token"` // 登陆token + PassWord string `json:"passWord"` // 登录密码 + PayPassWord string `json:"payPassWord"` // 资金密码(支付密码) + PowerOver ArryString `json:"powerOver"` // 不能操作的权限 + Memo string `json:"memo"` // 备注 + RegisterIp string `json:"registerIp"` // 注册ip地址 + RegisterIpAddress string `json:"registerIpAddress,omitempty"` // 注册ip地址 + LoginErrorTime int64 `json:"loginErrorTime,omitempty"` // 登录错误时间 + LoginErrorCount int64 `json:"loginErrorCount,omitempty"` // 登录错误次数 + LoginTime int64 `json:"loginTime"` // 登陆时间 + LoginIp string `json:"loginIp"` // 登陆ip + LoginIpAddress string `json:"loginIpAddress,omitempty"` // 登陆ip + CreateTime int64 `json:"createTime"` // 创建时间 +} + +func (j *UserDetail) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j UserDetail) Value() (driver.Value, error) { + return json.Marshal(j) +} diff --git a/pkg/aliyun/aliyun.go b/pkg/aliyun/aliyun.go new file mode 100644 index 0000000..4f20995 --- /dev/null +++ b/pkg/aliyun/aliyun.go @@ -0,0 +1,30 @@ +package aliyun + +import ( + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v4/client" + "github.com/alibabacloud-go/tea/tea" +) + +// Description: +// +// 使用AK&SK初始化账号Client +// +// @return Client +// +// @throws Exception +func CreateClient(accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) { + // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。 + // 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html。 + config := &openapi.Config{ + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。 + AccessKeyId: accessKeyId, + // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 + AccessKeySecret: accessKeySecret, + } + // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi + config.Endpoint = tea.String("dysmsapi.aliyuncs.com") + _result = &dysmsapi20170525.Client{} + _result, _err = dysmsapi20170525.NewClient(config) + return _result, _err +} diff --git a/pkg/aliyun/sms.go b/pkg/aliyun/sms.go new file mode 100644 index 0000000..4630078 --- /dev/null +++ b/pkg/aliyun/sms.go @@ -0,0 +1,81 @@ +package aliyun + +import ( + "encoding/json" + "fmt" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v4/client" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" + "math/rand" + "strings" + "time" +) + +func SendSmsLogin(mobile string) (string, error) { + captcha := generateCaptcha() + err := send(mobile, "逸采", "SMS_478575808", fmt.Sprintf(`{"code":"%s"}`, captcha)) + if err != nil { + return "", err + } + return captcha, nil +} + +func generateCaptcha() string { + rand.Seed(time.Now().UnixNano()) + captcha := rand.Intn(900000) + 100000 + return fmt.Sprintf("%d", captcha) +} + +func send(mobile, signName, templateCode, TemplateParam string) error { + client, _err := CreateClient(tea.String("LTAI5tBqQqufN9SctTUGLYMn"), tea.String("Kj1t2pMEjo8Cgj4D0CwqLS6CDPgczV")) + if _err != nil { + return _err + } + + sendSmsRequest := &dysmsapi20170525.SendSmsRequest{ + PhoneNumbers: tea.String(mobile), + SignName: tea.String(signName), + TemplateCode: tea.String(templateCode), + TemplateParam: tea.String(TemplateParam), + } + tryErr := func() (_e error) { + defer func() { + if r := tea.Recover(recover()); r != nil { + _e = r + } + }() + // 复制代码运行请自行打印 API 的返回值 + msg, _err := client.SendSmsWithOptions(sendSmsRequest, &util.RuntimeOptions{}) + fmt.Println(msg.String()) + if _err != nil { + return _err + } + + return nil + }() + + if tryErr != nil { + var error = &tea.SDKError{} + if _t, ok := tryErr.(*tea.SDKError); ok { + error = _t + } else { + error.Message = tea.String(tryErr.Error()) + } + // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。 + // 错误 message + fmt.Println(tea.StringValue(error.Message)) + // 诊断地址 + var data interface{} + d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data))) + d.Decode(&data) + if m, ok := data.(map[string]interface{}); ok { + recommend, _ := m["Recommend"] + fmt.Println(recommend) + } + _, _err = util.AssertAsString(error.Message) + if _err != nil { + return _err + } + } + return _err +} diff --git a/pkg/async/afterCallback.go b/pkg/async/afterCallback.go new file mode 100644 index 0000000..3a7c112 --- /dev/null +++ b/pkg/async/afterCallback.go @@ -0,0 +1,20 @@ +package async + +import ( + "epur-pay/pkg/mq" +) + +func AfterCallbackProducer(a *Async, req AsyncRequest) { + mq.AysncInstance.Producer(&mq.Task{ + Topics: a.Name, + Delay: req.Delay, + Transaction: false, + Store: false, + Func: req.Func, + }) +} + +func AfterCallback(data mq.ComsumerParams) error { + data.Func() + return nil +} diff --git a/pkg/async/async.go b/pkg/async/async.go new file mode 100644 index 0000000..ea0996a --- /dev/null +++ b/pkg/async/async.go @@ -0,0 +1,17 @@ +package async + +import "epur-pay/pkg/mq" + +var Asyncs *AsyncsModel + +func New() { + Asyncs = &AsyncsModel{} + + Asyncs.AfterCallback = &Async{ + Run: AfterCallbackProducer, + Name: "afterCallback", + } + + mq.AysncInstance. + Consumer(Asyncs.AfterCallback.Name, AfterCallback, 1) //接口After调用 +} diff --git a/pkg/async/model.go b/pkg/async/model.go new file mode 100644 index 0000000..e17eaf9 --- /dev/null +++ b/pkg/async/model.go @@ -0,0 +1,37 @@ +package async + +import "epur-pay/pkg/mq" + +type Async struct { + Run func(a *Async, req AsyncRequest) + Name string +} + +type AsyncRequest struct { + Data interface{} + Chains []*mq.Task + ChainErrorTriger bool + Func func() + Delay int64 +} + +func NewAsyncRequest(data interface{}) AsyncRequest { + return AsyncRequest{ + Data: data, + } +} + +func NewAsyncRequestFunc(data func(), Delay int64) AsyncRequest { + return AsyncRequest{ + Func: data, + Delay: Delay, + } +} + +type AsyncsModel struct { + AfterCallback *Async +} + +func (p *Async) Add(req AsyncRequest) { + p.Run(p, req) +} diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..b7249c9 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,70 @@ +package config + +import ( + "bytes" + "encoding/json" + "epur-pay/model" + "fmt" + "gopkg.in/ini.v1" +) + +type Config struct { + Common struct { + RunMode string `yaml:"RunMode"` + LogFilePath string `yaml:"LogFilePath"` //日志根路径 + Domain string `yaml:"Domain"` + ResourcePath string `yaml:"ResourcePath"` + } `yaml:"Common"` + ApiServer model.ListenServer `yaml:"ApiServer"` + Rds model.Rds `yaml:"Rds"` + Redis model.Redis `yaml:"Redis"` +} + +var Cf = Config{} + +func New(Name string) { + + var ( + cfg *ini.File + err error + configPath string + ) + + if Cf.Common.RunMode == "debug" { + configPath = fmt.Sprintf("config/%s_test.ini", Name) + } else { + configPath = fmt.Sprintf("config/%s.ini", Name) + } + + if cfg, err = ini.Load(configPath); err != nil { + panic(err) + } + + mapTo("Common", &Cf.Common, cfg) + mapTo("ApiServer", &Cf.ApiServer, cfg) + mapTo("Rds", &Cf.Rds, cfg) + mapTo("Redis", &Cf.Redis, cfg) + + fmt.Println("Cf -> config load success.") +} + +func (conf *Config) String() string { + b, err := json.Marshal(*conf) + if err != nil { + return fmt.Sprintf("%+v", *conf) + } + var out bytes.Buffer + err = json.Indent(&out, b, "", " ") + if err != nil { + return fmt.Sprintf("%+v", *conf) + } + return out.String() +} + +// mapTo map section +func mapTo(section string, v interface{}, cfg *ini.File) { + err := cfg.Section(section).MapTo(v) + if err != nil { + panic(err) + } +} diff --git a/pkg/cron/cron.go b/pkg/cron/cron.go new file mode 100644 index 0000000..1206def --- /dev/null +++ b/pkg/cron/cron.go @@ -0,0 +1,60 @@ +package cron + +import ( + "epur-pay/pkg/logger" + "epur-pay/pkg/mq" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "github.com/robfig/cron" + "gorm.io/gorm/clause" + "reflect" + "runtime" +) + +var ( + Cron *cron.Cron +) + +func New() { + Cron = cron.New() + Cron.Start() + + { + /* + 每个月将任务表转移到历史数据表 + */ + utils.Error(Cron.AddFunc("* * * * 7", MqStoreToHistory)) + } +} + +func MqStoreToHistory() { + + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + e := reflect.ValueOf(err) + logger.ErrorLogger.Errorln(e.String(), string(buf)) + //p.E = errors.New(string(buf)) + } + }() + + rows := mq.AsyncTasks{} + + utils.DbErrSkipRecordNotFound(rds.DB.Raw(`select * from async_task where create_time < ?`, utils.Time2StampSecond()-6*24*60*60).Scan(&rows).Error) + + ids := []int64{} + + for _, item := range rows { + ids = append(ids, item.Id) + } + + if len(rows) > 0 { + utils.Error(rds.DB.Table("async_task_history").Clauses(clause.OnConflict{DoNothing: true}).Create(&rows).Error) + } + + if len(ids) > 0 { + utils.Error(rds.DB.Exec(`delete from async_task where id in ?`, ids).Error) + } +} diff --git a/pkg/dapi/api.go b/pkg/dapi/api.go new file mode 100644 index 0000000..354bdde --- /dev/null +++ b/pkg/dapi/api.go @@ -0,0 +1,141 @@ +package dapi + +import ( + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/async" + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "gorm.io/gorm" +) + +func (a *ApiBase) apiAfter() error { + + if a.AfterCallback != nil { + async.Asyncs.AfterCallback.Add(async.NewAsyncRequestFunc(a.AfterCallback, a.AfterCallbackDelay)) + } + + if Cf.ApiAfter != nil { + if err := Cf.ApiAfter(a); err != nil { + return err + } + } + + return nil +} + +func (a *ApiBase) apiBefore() error { + + if err := a.limitRouterRuleHandler(); err != nil { + return err + } + + if err := a.headerHandler(); err != nil { + return err + } + + if Cf.ApiBefore != nil { + if err := Cf.ApiBefore(a); err != nil { + return err + } + } + + if !cache.Global.ProjectInitStatus { + return a.ReturnPublicErrorResponse(a.Translate("during_upgrade")) + } + return nil +} + +func (a *ApiBase) apiRunEx() error { + + if err := a.authHandler(); err != nil { + return err + } + if err := a.lockTrade(); err != nil { + return err + } + + a.saveLogBefore() + + if err := a.apiHandler(); err != nil { + return err + } + + return nil +} + +func (a *ApiBase) saveLogBefore() { + //记录日志 + if a.InParams.IsSaveLog || a.InParams.IsSkipErrorSaveLog { + a.Log = new(model.Log) + + a.Log.Event = a.InParams.SaveEvent + if a.User != nil { + a.Log.Uid = a.User.Uid + a.Log.Name = a.User.Account + } + a.Log.Ip = a.ClientIp() + a.Log.CreateTime = utils.Time2StampSecond() + if a.Site == "h5App" { + a.Log.UserType = "0" + } else if a.Site == "h5Admin" { + a.Log.UserType = "1" + } + a.Log.IsSuper = "0" + } +} + +func (a *ApiBase) apiRun() error { + + if a.InParams.IsTransaction { + if err := rds.DB.Transaction(func(ts *gorm.DB) error { + a.Ts = ts + if err := a.apiRunEx(); err != nil && a.Error == nil { + a.Error = err + } + if a.Error != nil { + return a.Error + } else { + return nil + } + }); err != nil { + logger.AccessLogger.Errorln(err.Error()) + } + } else { + a.Ts = rds.DB + if err := a.apiRunEx(); err != nil && a.Error == nil { + a.Error = err + } + } + return a.Error +} + +func (a *ApiBase) apiMain() error { + + if err := a.apiBefore(); err != nil { + return err + } + if err := a.apiRun(); err != nil { + return err + } + + a.freeTrade() + + if err := a.apiAfter(); err != nil { + return err + } + return nil +} + +func (a *ApiBase) TMapGet(data model.JSON) string { + if row, ok := data[a.Lang]; ok { + return row.(string) + } else { + if row1, ok1 := data[cache.Global.Caches.Language.DefaultLanguage]; ok1 { + return row1.(string) + } else { + return "" + } + } +} diff --git a/pkg/dapi/base.go b/pkg/dapi/base.go new file mode 100644 index 0000000..e7a61db --- /dev/null +++ b/pkg/dapi/base.go @@ -0,0 +1,155 @@ +package dapi + +import ( + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/limit" + "epur-pay/pkg/logger" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "reflect" + "runtime" + "time" +) + +var Cf *Config + +func New() { + Cf = &Config{} +} + +func (p *Config) SetApiBefore(f func(a *ApiBase) error) *Config { + p.ApiBefore = f + return p +} + +func (p *Config) SetApiAuth(f func(a *ApiBase) error) *Config { + p.ApiAuth = f + return p +} + +func (p *Config) SetApiAfter(f func(a *ApiBase) error) *Config { + p.ApiAfter = f + return p +} + +//func (p *Config) SetApiGetLang(f func(Lang, Key string) string) *Config { +// p.ApiGetLang = f +// return p +//} + +func (p *Config) SetRefreshCache(f func(Key string) error) *Config { + p.ApiRefreshCache = f + return p +} + +//func (p *Config) SetKKStop(f *bool) *Config { +// p.KKStop = f +// return p +//} +// +//func (p *Config) SetProjectInitStatus(f *bool) *Config { +// p.ProjectInitStatus = f +// return p +//} + +type Config struct { + ApiBefore func(a *ApiBase) error + //ApiRun func() error + ApiAfter func(a *ApiBase) error + ApiAuth func(a *ApiBase) error //鉴权 + //ApiGetLang func(Lang, Key string) string + ApiRefreshCache func(Key string) error // 刷新项目缓存 + //KKStop *bool + //ProjectInitStatus *bool //项目是否初始化完成 +} + +type InParams struct { + IsTransaction bool // 是否开启事务 + IsTicket bool // 是否校验Token获取用户校验用户合法性 + IsForUpdate bool // 是否加用户锁 + IsTicketSkip bool // 有就获取用户 没有也不报错 + IsErrors bool // 是否多条错误返回 + IsCertified bool // 是否判断认证 + IsNoCheckBlackList bool // 是否不校验黑名单 + IsKKTrade bool + IsSaveLog bool + IsSkipErrorSaveLog bool + SaveEvent string + IsAgentAction bool // 是否允许代理操作 true-是 false-否 + IsSuperOnly bool // 是否只允许Super操作 + Permission string // 权限标识 + LimitRouterRuleKey string + LimitRouterRule *limit.LimitOpt +} + +type ApiBase struct { + C *gin.Context + Ts *gorm.DB + InParams *InParams + IsCustomResponse bool // 是否自定义返回 + Response *Response // 返回结构 + CustomResponse *interface{} // 自定义返回结构 + User *model.User // 用户结构 + Token string // 鉴权Token + Local *time.Location // 时区 + Site string // 站点类型: h5App h5Pc h5Admin + Currency string // 币种 + Lang string // 语言 + Error error // 错误信息 + body interface{} // 请求的body数据 + paramNum int // 函数参数数量 + funcValue reflect.Value // 函数指针 + Limit int // 当前页数显示条数 + Offset int // 跳过条数 + AfterCallback func() // 后续处理 + AfterCallbackDelay int64 // 延迟时间 + Log *model.Log +} + +func ApiDecorator(handlerFunc interface{}, InParams *InParams) func(*gin.Context) { + + paramNum := reflect.TypeOf(handlerFunc).NumIn() + funcValue := reflect.ValueOf(handlerFunc) + funcType := reflect.TypeOf(handlerFunc) + + if funcType.Kind() != reflect.Func { + panic("the route handlerFunc must be a function") + } + + if len(InParams.LimitRouterRuleKey) > 0 && InParams.LimitRouterRule != nil { + cache.Global.LimitRouter.Rule.Rules[InParams.LimitRouterRuleKey] = InParams.LimitRouterRule + } + + return func(c *gin.Context) { + + a := &ApiBase{Response: &Response{}} + a.InParams = InParams + a.paramNum = paramNum + a.funcValue = funcValue + a.C = c + + if paramNum > 1 { + a.body = reflect.New(funcType.In(1).Elem()).Interface() + } + apiProxy(a) + } +} + +func apiProxy(a *ApiBase) { + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + e := reflect.ValueOf(err) + logger.ErrorLogger.Errorln(e.String(), string(buf)) + _ = a.ReturnErrorResponse(ServerError, a.Translate("sys_error")) + } + + a.freeTrade() + a.saveLog() + a.response() + }() + a.Error = a.apiMain() +} diff --git a/pkg/dapi/response.go b/pkg/dapi/response.go new file mode 100644 index 0000000..22c3a92 --- /dev/null +++ b/pkg/dapi/response.go @@ -0,0 +1,188 @@ +package dapi + +import ( + "bytes" + "encoding/json" + "epur-pay/pkg/config" + "epur-pay/pkg/logger" + "errors" + "github.com/go-sql-driver/mysql" + "gorm.io/gorm" + "net/http" +) + +var ( + //成功 + Success = 200 + //服务错误 + ServerError = 500 + //资源不存在 + NotFound = 404 + //参数错误 + InvalidParams = 965 + //请求错误 + PublicError = 999 + //Token失效 + TokenInvalid = 600 +) + +type RequestLimit struct { + Page int `json:"page"` //页数 + Size int `json:"size"` //条数 +} + +// 返回公共结构体 +type ResponseCommon struct { + Code int `json:"code"` //返回Code 200 成功 其他是失败 + Msg interface{} `json:"message"` //返回信息 + Count int64 `json:"count,omitempty"` // 条数 +} + +// 返回结构体 带Data +type Response struct { + *ResponseCommon + Data interface{} `json:"data"` //返回的数据 +} + +func (a *ApiBase) DbErrRecordNotFound(err error, msg string) { + if err == gorm.ErrRecordNotFound { + panic(msg) + } else if err != nil { + panic(err.Error()) + } +} + +func (a *ApiBase) DbErrSkipRecordNotFound(err error) { + if err != nil && err != gorm.ErrRecordNotFound { + panic(err.Error()) + } +} + +func (a *ApiBase) DbErrRecordNotFoundBool(err error) bool { + if err == gorm.ErrRecordNotFound { + return false + } else if err != nil { + return false + } + return true +} + +func (a *ApiBase) DbErrUniqueIndexConflict(err error, msg string) error { + if err != nil { + if err.(*mysql.MySQLError).Number == 1062 { + return a.ReturnErrorResponse(PublicError, msg) + } else { + return a.ReturnErrorResponse(ServerError, a.Translate("sys_error")) + } + } else { + return nil + } +} + +func (a *ApiBase) DbErrSkipRecordNotFoundBool(err error) bool { + if err != nil && err != gorm.ErrRecordNotFound { + return false + } + return true +} + +func (a *ApiBase) JSONMarshal(t interface{}) ([]byte, error) { + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + err := encoder.Encode(t) + return buffer.Bytes(), err +} + +func (a *ApiBase) response() { + + var response []byte + + if a.IsCustomResponse { + return + } + + if a.Error == nil { + if a.CustomResponse != nil { + response, _ = a.JSONMarshal(&a.CustomResponse) + } else if a.Response != nil { + response, _ = a.JSONMarshal(&a.Response) + } else { + _ = a.ReturnErrorResponse(500, a.Error.Error()) + response, _ = a.JSONMarshal(&a.Response) + } + } else { + if a.CustomResponse != nil { + response, _ = a.JSONMarshal(&a.CustomResponse) + } else if a.Response != nil { + response, _ = a.JSONMarshal(&a.Response) + } else { + _ = a.ReturnErrorResponse(500, a.Error.Error()) + response, _ = a.JSONMarshal(&a.Response) + } + } + + r := string(response) + if config.Cf.Common.RunMode == "debug" { + logger.AccessLogger.Infof("%s 返回数据 -> %s", a.C.FullPath(), r) + } + a.C.String(http.StatusOK, r) +} + +func (a *ApiBase) NewResponseCommon(code int, msg string) *ResponseCommon { + return &ResponseCommon{ + Code: code, + Msg: msg, + } +} + +func (a *ApiBase) NewSuccessResponseCommon() *ResponseCommon { + return a.NewResponseCommon(Success, a.Translate("success")) +} + +func (a *ApiBase) NewResponse(code int, msg string, data ...interface{}) *Response { + return &Response{ + ResponseCommon: a.NewResponseCommon(code, msg), + Data: dataHandler(data...), + } +} + +func (a *ApiBase) NewSuccessResponse(data ...interface{}) *Response { + return &Response{ + ResponseCommon: a.NewSuccessResponseCommon(), + Data: dataHandler(data...), + } +} + +func (a *ApiBase) ReturnSuccessResponse(data ...interface{}) error { + a.Response = a.NewSuccessResponse(data) + return nil +} + +func (a *ApiBase) ReturnErrorResponse(code int, msg string, data ...interface{}) error { + a.Response = a.NewResponse(code, msg, data) + a.Error = errors.New(msg) + return a.Error +} + +func (a *ApiBase) ReturnPublicErrorResponse(msg string, data ...interface{}) error { + + if len(msg) <= 0 { + //logger.AccessLogger.Errorln("系统错误!") + //msg = cache.Global.GetLang(a.Lang, "sys_error") + } + return a.ReturnErrorResponse(PublicError, msg, data...) +} + +func (a *ApiBase) ReturnSuccessCustomResponse(data interface{}) error { + a.CustomResponse = &data + return nil +} + +func dataHandler(data ...interface{}) interface{} { + if len(data) == 1 { + return data[0] + } else { + return data + } +} diff --git a/pkg/dapi/utils.go b/pkg/dapi/utils.go new file mode 100644 index 0000000..13eabbd --- /dev/null +++ b/pkg/dapi/utils.go @@ -0,0 +1,458 @@ +package dapi + +import ( + "encoding/json" + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/config" + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "fmt" + "github.com/deatil/go-cryptobin/cryptobin/crypto" + "net/url" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +var LockPool sync.Map + +func (a *ApiBase) apiHandler() error { + if a.paramNum == 1 { + a.funcValue.Call(valOf(a)) + } else { + if err := a.getBody(); err != nil { + return err + } + a.funcValue.Call(valOf(a, a.body)) + } + return nil +} + +func (a *ApiBase) limitRouterRuleHandler() error { + + if cache.Global.LimitRouter.Api != nil { + key := cache.Global.LimitRouter.Api.Key(a.C) + if bucket, ok := cache.Global.LimitRouter.Api.GetBucket(key); ok { + count := bucket.TakeAvailable(1) + if count == 0 { + return a.ReturnPublicErrorResponse(a.Translate("rateLimit")) + } + } + } + + return nil +} + +func (a *ApiBase) headerHandler() error { + + //站点处理 + a.siteHandler() + //币种处理 + a.currencyHander() + //时区处理 + a.localHandler() + //分页参数处理 + a.pageHandler() + //语言处理 + a.getLangByAppRequest() + //版本管理 + if err := a.versionHander(); err != nil { + return err + } + + return nil +} + +func (a *ApiBase) siteHandler() { + a.Site = a.C.GetHeader("site") +} + +func (a *ApiBase) currencyHander() { + a.Currency = a.C.GetHeader("currency") + if len(a.Currency) <= 0 { + a.Currency = "USD" + } +} + +func (a *ApiBase) GetPage() int { + page1, _ := a.C.GetQuery("page") + page, _ := strconv.Atoi(page1) + if page <= 0 { + page = 1 + } + return page +} +func (a *ApiBase) pageHandler() { + page1, _ := a.C.GetQuery("page") + size1, _ := a.C.GetQuery("size") + + page, _ := strconv.Atoi(page1) + limit, _ := strconv.Atoi(size1) + + if page <= 0 { + page = 1 + } + if limit >= 100 { + limit = 100 + } else if limit <= 0 { + limit = 20 + } + + a.Limit = limit + a.Offset = (page - 1) * limit +} + +func (a *ApiBase) localHandler() { + var err error + a.Local, err = time.LoadLocation(a.C.GetHeader("Timezone")) //等同于"UTC" + if err != nil { + a.Local = nil + } + + if len(a.C.GetHeader("Timezone")) == 0 { + a.Local = nil + } +} + +func (a *ApiBase) getBody() error { + + if a.C.Request.Method == "POST" || + a.C.Request.Method == "PUT" || + a.C.Request.Method == "PATCH" { + + row, err := a.C.GetRawData() + if err != nil { + logger.ErrorLogger.Errorln(err.Error()) + return err + } + + var data string + + data = string(row) + + if config.Cf.Common.RunMode == "debug" { + logger.AccessLogger.Infof("%s 请求数据 -> %s", a.C.FullPath(), data) + } + + if a.Log != nil && a.Log.Event != "登陆" { + a.Log.Data = data + } + + err = json.Unmarshal([]byte(data), &a.body) + if err != nil { + + logger.AccessLogger.Errorf("===> BindJSON: %s", a.Translate("param_fail")) + + return a.ReturnErrorResponse(InvalidParams, a.Translate("param_fail")) + } + if a.InParams.IsErrors { + var errMsg []string + ValidateField(a.body, func(typeOfCat reflect.Type, fieldName string) { + field, ok := typeOfCat.FieldByName(fieldName) //通过反射获取filed + if ok { + errMsg = append(errMsg, a.Translate(field.Tag.Get("label"))) + } + }) + if len(errMsg) > 0 { + return a.ReturnErrorResponse(InvalidParams, strings.Join(errMsg, ",")) + } + } else { + var errMsg string + ValidateField(a.body, func(typeOfCat reflect.Type, fieldName string) { + field, ok := typeOfCat.FieldByName(fieldName) //通过反射获取filed + if ok { + errMsg = field.Tag.Get("label") + return + } + }) + if len(errMsg) > 0 { + return a.ReturnErrorResponse(InvalidParams, a.Translate(errMsg)) + } + } + } + return nil +} + +func valOf(i ...interface{}) []reflect.Value { + var rt []reflect.Value + for _, i2 := range i { + rt = append(rt, reflect.ValueOf(i2)) + } + return rt +} + +func (a *ApiBase) ClientIp() string { + + ip := a.C.Request.Header.Get("Cf-Connecting-Ip") + + if len(ip) <= 0 { + ip = a.C.Request.Header.Get("X-Real-Ip") + } + + if len(ip) <= 0 { + ip = a.C.ClientIP() + } + + return ip +} + +func (a *ApiBase) Translate(key string) string { + return cache.Global.Caches.Language.GetLang(a.Lang, key) +} + +func (a *ApiBase) RefreshCache(key string) error { + return Cf.ApiRefreshCache(key) +} + +func (a *ApiBase) authHandler() error { + if a.InParams.IsTicket || a.InParams.IsTicketSkip { + if err := a.getUser(a.C.Request.Header.Get("Token")); err != nil { + return err + } + } + + return nil +} + +func (a *ApiBase) getUser(token string) error { + + var uid int64 + a.Token = token + + if a.InParams.IsTicket && len(a.Token) <= 0 { + return a.ReturnErrorResponse(600, a.Translate("reset_login")) + } else if len(a.Token) > 0 { + //logger.AccessLogger.Infoln("---> getUser DecryptToken ", a.Token) + t, e := DecryptToken(a.Token) + if !e { + if a.InParams.IsTicket { + return a.ReturnErrorResponse(600, a.Translate("not_found")) + } + } else { + uid, _ = strconv.ParseInt(strings.Split(t, ",")[0], 10, 64) + } + } + + if uid > 0 { + a.User = cache.Global.Caches.User.Get(uid) + + if a.User == nil { //解决用户被删除后token无效的问题 + if a.InParams.IsTicket { + return a.ReturnErrorResponse(600, a.Translate("reset_login")) + } else { + return nil + } + } + + if a.User.Detail.Token != a.Token { + if a.InParams.IsTicket { + return a.ReturnErrorResponse(600, a.Translate("reset_login")) + } else { + a.User = nil + return nil + } + } + + if a.User.Uid <= 0 { + if a.InParams.IsTicket { + return a.ReturnErrorResponse(600, a.Translate("not_found")) + } else { + a.User = nil + return nil + } + } + + // 如果必须是超级权限操作,需要判断是否存在权限ID=1的数据 //!a.User.RoleIds.Of(1) + if a.InParams.IsSuperOnly && a.User.Uid != 1 { + logger.AccessLogger.Errorf("---> Uid: [%d] Super权限错误 %s", a.User.Uid, a.InParams.Permission) + return a.ReturnErrorResponse(602, a.Translate("AT004")) + } + + // 这里验证用户是否拥有接口操作权限 + if len(a.InParams.Permission) > 0 { + if !cache.Global.Caches.RolePermission.Check(a.User.RoleIds, a.InParams.Permission) { + logger.AccessLogger.Errorf("---> Uid: [%d] 尚无权限: [%s] %v", a.User.Uid, a.InParams.Permission, a.User.RoleIds) + return a.ReturnPublicErrorResponse(a.Translate("AT058")) + } + } + + if a.User.Status != "0" { + if a.InParams.IsTicket || (a.InParams.IsTicketSkip && a.User != nil) { + if !a.InParams.IsNoCheckBlackList { + return a.ReturnErrorResponse(602, a.Translate("black_list")) + } + } else { + a.User = nil + return nil + } + } + } + + return nil +} + +func (a *ApiBase) lockTrade() error { + //logger.AccessLogger.Infof("params:%+v user:%+v", a.InParams.IsLockTradeForMerchant, a.User) + if a.InParams.IsForUpdate && a.User != nil { + if _, ok := LockPool.Load(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid)); ok { + return a.ReturnPublicErrorResponse(a.Translate("LLLP")) + } else { + LockPool.Store(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid), true) + } + } + return nil + +} + +func (a *ApiBase) freeTrade() { + if a.InParams.IsForUpdate && a.User != nil { + LockPool.Delete(fmt.Sprintf("%s%d", a.C.FullPath(), a.User.Uid)) + } +} + +func (a *ApiBase) saveLog() { + + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + e := reflect.ValueOf(err) + logger.ErrorLogger.Errorln(e.String()) + } + }() + + //logger.AccessLogger.Warnln(a.Error, a.InParams.IsSkipErrorSaveLog) + + if a.Log != nil && a.Log.Uid > 0 && + (a.Error == nil || a.InParams.IsSkipErrorSaveLog) { + // 如果是super操作也记录 + if a.Log.Name == "super" && a.Log.UserType == "1" { + a.Log.IsSuper = "1" + a.Log.Ip = "" + } + a.Log.A1 = cache.GetIpAddress(a.Log.Ip) + + if a.Error != nil { + a.Log.Event = fmt.Sprintf("%s(失败)", a.Log.Event) + } + + if err := rds.DB.Create(a.Log).Error; err != nil { + logger.ErrorLogger.Errorln(err.Error()) + } + } +} + +func (a *ApiBase) getLangByAppRequest() { + + lan := a.C.Request.Header.Get("Language") + + languageCode := "" + countryCode := "" + if len(strings.Split(lan, "_")) > 1 { + languageCode = strings.Split(lan, "_")[0] + countryCode = strings.Split(lan, "_")[1] + } + + row := "" + + tmpLanguageCodes := make([]model.SysLanguage, 0) + for _, v := range cache.Global.Caches.Language.List { + if v.LanguageCode == languageCode { + tmpLanguageCodes = append(tmpLanguageCodes, v) + } + } + isFind := false + for _, v := range tmpLanguageCodes { + if v.CountryCode == countryCode { + isFind = true + row = fmt.Sprintf("%s_%s", v.LanguageCode, v.CountryCode) + break + } + } + if !isFind && len(tmpLanguageCodes) > 0 { + row = fmt.Sprintf("%s_%s", tmpLanguageCodes[0].LanguageCode, tmpLanguageCodes[0].CountryCode) + } + + if len(row) <= 0 { + row = cache.Global.Caches.Language.DefaultLanguage + } + + if len(row) <= 0 { + row = "en_US" + } + a.Lang = row +} + +func (a *ApiBase) versionHander() error { + + appVersion := a.C.Request.Header.Get("appversion") + { + + var version *model.Version + + if a.Site == "h5Pc" { + version = cache.Global.Caches.Version.Get("2") + } else if a.Site == "h5App" { + version = cache.Global.Caches.Version.Get("1") + } else if a.Site == "h5Admin" { + version = cache.Global.Caches.Version.Get("0") + } + + if version != nil && version.Id > 0 { + if len(appVersion) > 0 { + + requestVersion, err := strconv.ParseInt(appVersion, 10, 64) + if err != nil { + logger.AccessLogger.Errorln("requestVersion version error", appVersion) + return nil + } + currVersion, err := strconv.ParseInt(version.Version, 10, 64) + if err != nil { + logger.AccessLogger.Errorln("currVersion version error", version.Version) + return nil + } + if currVersion > requestVersion && version.Force == "0" { + return a.ReturnErrorResponse(920, a.Translate("versionUpdateing")) + } + } + } + } + return nil +} + +// 加密 +func EncryptToken(uid int64) string { + + return url.QueryEscape(crypto.FromString(fmt.Sprintf("%d,%d", uid, utils.Time2StampSecond()+24*60*60)). + SetKey("dfertf12dfertf12"). + SetIv("dfertf12dfertf12"). + Aes(). + CBC(). + PKCS7Padding(). + Encrypt(). + ToBase64String()) +} + +func DecryptToken(token string) (string, bool) { + token, _ = url.QueryUnescape(token) + + cyptde := crypto. + FromBase64String(token). + SetKey("dfertf12dfertf12"). + SetIv("dfertf12dfertf12"). + Aes(). + CBC(). + PKCS7Padding(). + Decrypt(). + ToString() + if len(cyptde) <= 0 { + return "", false + } + return cyptde, true +} diff --git a/pkg/dapi/valid-errors.go b/pkg/dapi/valid-errors.go new file mode 100644 index 0000000..3638359 --- /dev/null +++ b/pkg/dapi/valid-errors.go @@ -0,0 +1,48 @@ +package dapi + +import ( + "github.com/go-playground/validator/v10" + "reflect" + "strings" +) + +type ValidError struct { + Key string + Message string +} + +type ValidErrors []*ValidError + +func (v *ValidError) Error() string { + return v.Message +} + +func (v ValidErrors) Error() string { + return strings.Join(v.Errors(), ",") +} + +func (v ValidErrors) Errors() []string { + var errs []string + for _, err := range v { + errs = append(errs, err.Error()) + } + + return errs +} + +func ValidateField(usr interface{}, f func(typeOfCat reflect.Type, fieldName string)) { + + validate := validator.New() + typeOfCat := reflect.TypeOf(usr) + if typeOfCat.Kind() == reflect.Ptr { + //err = err.Elem + typeOfCat = typeOfCat.Elem() + } + err := validate.Struct(usr) + if err != nil { + for _, err := range err.(validator.ValidationErrors) { + fieldName := err.Field() //获取是哪个字段不合乎格局 + f(typeOfCat, fieldName) + } + } +} diff --git a/pkg/fileserver/asw/asw.go b/pkg/fileserver/asw/asw.go new file mode 100644 index 0000000..8cbea13 --- /dev/null +++ b/pkg/fileserver/asw/asw.go @@ -0,0 +1,150 @@ +package fileAsw + +import ( + "bytes" + "epur-pay/pkg/logger" + "epur-pay/pkg/utils" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "io" + "reflect" + "time" +) + +/* +亚马逊oss存储 +*/ +type S3 struct { + Path string + AccessKeyId string + AccessKeySecret string + BucketName string + EndPoint string + RegionId string + Access string + ReadPrivate bool + sess *session.Session + svc *s3.S3 +} + +func New(accessKeyId, accessKeySecret, bucket, endPoint, access, regionId string) (*S3, error) { + var instance *S3 + instance = &S3{ + Path: "exchange", + BucketName: bucket, + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + EndPoint: endPoint, + RegionId: regionId, + Access: access, + } + + sess, err := session.NewSession(&aws.Config{ + Credentials: credentials.NewStaticCredentials(instance.AccessKeyId, instance.AccessKeySecret, ""), + Endpoint: aws.String(instance.EndPoint), + Region: aws.String(instance.RegionId), + DisableSSL: aws.Bool(false), // true - 本地http false -- 远端 + S3ForcePathStyle: aws.Bool(false), + }) + + if err != nil { + return nil, err + } + + instance.sess = sess + instance.svc = s3.New(sess) + + return instance, nil +} + +func (this *S3) ReadUrl(filePath string) string { + return this.url(filePath) +} + +func (this *S3) url(fileName string) string { + return fmt.Sprintf("https://%s/%s/%s/%s", this.Access, this.BucketName, this.Path, fileName) +} + +func (this *S3) Put(fileName string, contentType string, data interface{}) string { + var ossData io.Reader + var err error + typeOf := reflect.TypeOf(data).Kind() + logger.AccessLogger.Infoln("ContentType ", contentType, "typeOf ", typeOf) + + switch typeOf { + case reflect.Slice: + ossData = bytes.NewBuffer(data.([]byte)) + case reflect.Struct: + ossData = data.(io.Reader) + case reflect.Ptr: + a1 := reflect.ValueOf(data).Interface() + + switch v := a1.(type) { + case []uint8: + // 处理 []uint8 类型的情况 + logger.AccessLogger.Infoln("It's a []uint8 slice:", v) + case string: + // 处理 string 类型的情况 + logger.AccessLogger.Infoln("It's a string:", &v) + default: + // 处理其他类型的情况 + logger.AccessLogger.Infoln("It's some other type:", &v) + } + + d, ok := a1.([]uint8) + if !ok { + logger.AccessLogger.Errorf("---> Upload Ptr Error. %+v", a1) + } + ossData = bytes.NewBuffer(d) + default: + panic("数据不合法--!") + } + + //if len(contentType) <= 0 { + contentType, err = utils.GetContentTypeForIoRead(fileName, ossData) + if err != nil { + logger.ErrorLogger.Errorln(err.Error()) + } + //} + + uploader := s3manager.NewUploader(this.sess) + _, err = uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String(fmt.Sprintf("%s/", this.BucketName)), + Key: aws.String(fmt.Sprintf("%s/%s", this.Path, fileName)), + Body: ossData, + ContentType: aws.String(contentType), + //ACL: aws.String("public-read"), + }) + + if err != nil { + return err.Error() + } + + return this.url(fileName) +} + +// 获取预签名地址 +func (this *S3) Token(key string, contentType string) (map[string]interface{}, error) { + params := &s3.PutObjectInput{ + Bucket: aws.String(fmt.Sprintf("%s/", this.BucketName)), + Key: aws.String(fmt.Sprintf("%s/%s", this.Path, key)), + ContentType: &contentType, + } + + req, _ := this.svc.PutObjectRequest(params) + url, err := req.Presign(15 * time.Minute) + if err != nil { + logger.AccessLogger.Errorln("---> %s ", err.Error()) + return nil, err + } + + token := make(map[string]interface{}) + + token["token"] = url + token["url"] = this.ReadUrl(key) + return token, err +} diff --git a/pkg/fileserver/config/config.go b/pkg/fileserver/config/config.go new file mode 100644 index 0000000..d60e300 --- /dev/null +++ b/pkg/fileserver/config/config.go @@ -0,0 +1,83 @@ +package fileConfig + +import ( + fileAsw "epur-pay/pkg/fileserver/asw" + fileCos "epur-pay/pkg/fileserver/cos" + fileLocal "epur-pay/pkg/fileserver/local" + fileOss "epur-pay/pkg/fileserver/oss" + "epur-pay/pkg/logger" + "epur-pay/pkg/utils" +) + +var Cf *config + +type config struct { + Store *Store //当前使用的桶 + Stores []*Store +} + +/* +存储类型 + + oss 阿里云oss + cos 腾讯云 + asw 亚马逊 + local 本地local +*/ +type Store struct { + StoreType string + Api api +} + +type api interface { + Put(fileName string, contentType string, data interface{}) string + ReadUrl(filePath string) string + Token(key string, contentType string) (map[string]interface{}, error) +} + +type FileStoreRequest struct { + AccessKeyId, AccessKeySecret, BucketName, EndPoint, Access, RegionId string + Private bool + StoreType string +} + +func New(f *FileStoreRequest) { + + Cf = &config{ + Stores: []*Store{}, + } + + if f.StoreType == "oss" { + store, err := fileOss.New(f.AccessKeyId, f.AccessKeySecret, f.BucketName, f.EndPoint, f.Access, f.RegionId, f.Private) + utils.Error(err) + currStore := &Store{StoreType: f.StoreType, Api: store} + Cf.Stores = append(Cf.Stores, currStore) + Cf.Store = currStore + + logger.AccessLogger.Infof("---> StoreType: %s Access: %s", f.StoreType, store.Access) + } else if f.StoreType == "cos" { + store, err := fileCos.New(f.AccessKeyId, f.AccessKeySecret, f.BucketName, f.EndPoint, f.Access, f.RegionId) + utils.Error(err) + currStore := &Store{StoreType: f.StoreType, Api: store} + Cf.Stores = append(Cf.Stores, currStore) + Cf.Store = currStore + + logger.AccessLogger.Infof("---> StoreType: %s Access: %s", f.StoreType, store.Access) + } else if f.StoreType == "asw" { + store, err := fileAsw.New(f.AccessKeyId, f.AccessKeySecret, f.BucketName, f.EndPoint, f.Access, f.RegionId) + utils.Error(err) + currStore := &Store{StoreType: f.StoreType, Api: store} + Cf.Stores = append(Cf.Stores, currStore) + Cf.Store = currStore + + logger.AccessLogger.Infof("---> StoreType: %s Access: %s", f.StoreType, store.Access) + } else if f.StoreType == "local" { + store, err := fileLocal.New(f.AccessKeyId, f.AccessKeySecret, f.BucketName, f.EndPoint, f.Access, f.RegionId) + utils.Error(err) + currStore := &Store{StoreType: f.StoreType, Api: store} + Cf.Stores = append(Cf.Stores, currStore) + Cf.Store = currStore + + logger.AccessLogger.Infof("---> StoreType: %s Access: %s", f.StoreType, store.Access) + } +} diff --git a/pkg/fileserver/cos/cos.go b/pkg/fileserver/cos/cos.go new file mode 100644 index 0000000..cdd2381 --- /dev/null +++ b/pkg/fileserver/cos/cos.go @@ -0,0 +1,95 @@ +package fileCos + +import ( + "bytes" + "context" + "epur-pay/pkg/logger" + "fmt" + "github.com/tencentyun/cos-go-sdk-v5" + "io" + "net/http" + "net/url" + "reflect" +) + +/* +腾讯Cos +*/ +type Cos struct { + AccessKeyId string + AccessKeySecret string + BucketName string + EndPoint string + Access string + ReadPrivate bool + Appid string + client *cos.Client +} + +func New(accessKeyId, accessKeySecret, bucketName, endPoint string, access string, Appid string) (*Cos, error) { + + var instance *Cos + instance = &Cos{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + BucketName: bucketName, + EndPoint: endPoint, + Access: access, + Appid: Appid, + } + //cos.NewClient() + + uu := fmt.Sprintf( + "https://%s-%s.%s", instance.BucketName, instance.Appid, instance.EndPoint) + + logger.AccessLogger.Infoln("cos -> ", uu) + endPoint1, _ := url.Parse(uu) + + client := cos.NewClient( + &cos.BaseURL{BucketURL: endPoint1}, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: instance.AccessKeyId, + SecretKey: instance.AccessKeySecret, + }, + }) + + instance.client = client + return instance, nil +} + +func (this *Cos) url(fileName string) string { + return fmt.Sprintf("https://%s/%s", this.Access, fileName) +} + +func (this *Cos) ReadUrl(filePath string) string { + return this.url(filePath) +} + +func (this *Cos) Put(fileName string, contentType string, data interface{}) string { + + var ossData io.Reader + + typeOf := reflect.TypeOf(data).Kind() + //logger.AccessLogger.Infoln("类型:", typeOf) + + switch typeOf { + case reflect.Slice: + ossData = bytes.NewBuffer(data.([]byte)) + case reflect.Struct: + ossData = data.(io.Reader) + default: + panic("数据不合法!") + } + //oss.SetHeader("content-type", contentType) + + _, err := this.client.Object.Put(context.Background(), fileName, ossData, nil) + if err != nil { + logger.AccessLogger.Error("上传失败!") + panic(err.Error()) + } + return this.url(fileName) +} + +func (this *Cos) Token(key string, contentType string) (map[string]interface{}, error) { + return nil, nil +} diff --git a/pkg/fileserver/file.go b/pkg/fileserver/file.go new file mode 100644 index 0000000..e86827e --- /dev/null +++ b/pkg/fileserver/file.go @@ -0,0 +1,112 @@ +package fileserver + +import ( + "ehttp/http" + "epur-pay/cache" + fileConfig "epur-pay/pkg/fileserver/config" + "epur-pay/pkg/utils" + "fmt" + "io" + "reflect" +) + +type File struct { + FileName *string + Data interface{} + DataLen int64 + ContentType *string + Encryption bool + Key string + Iv string +} + +func New() { + // 根据类型加载存储方式 + storeType := utils.GetStoreType(cache.Global.Caches.Config.Get("storeType")) + storeJson := cache.Global.Caches.Config.GetMap(fmt.Sprintf("storeJson_%s", storeType)) + + if storeJson != nil { + fileConfig.New(&fileConfig.FileStoreRequest{ + AccessKeyId: storeJson["AccessKeyId"].(string), + AccessKeySecret: storeJson["AccessKeySecret"].(string), + BucketName: storeJson["BucketName"].(string), + EndPoint: storeJson["EndPoint"].(string), + Access: storeJson["Access"].(string), + RegionId: storeJson["regionId"].(string), + StoreType: storeType, + }) + } +} + +// 文件上传 +func (f *File) UpLoad() string { + + filePathResponse := "" + f.encryption() + filePathResponse = fileConfig.Cf.Store.Api.Put(*f.FileName, "", f.Data) + return filePathResponse +} + +// 加密 +func (f *File) encryption() { + + typeOf := reflect.TypeOf(f.Data).Kind() + handlerData := make([]byte, f.DataLen+1) + //logger.AccessLogger.Infoln("类型:", typeOf) + switch typeOf { + case reflect.String: + handlerData = f.DownLoad(f.Data.(string)) + case reflect.Slice: + handlerData = f.Data.([]byte) + case reflect.Struct: + _, err := f.Data.(io.Reader).Read(handlerData) + if err != nil { + panic(err) + } + //case reflect.Ptr: + // _, err := f.Data.(*os.File).Read(handlerData) + // if err != nil { + // panic(err) + // } + default: + panic("数据不合法!") + } + + if f.Encryption { + f.Data = handlerData + //s := aes.Encrypt(handlerData, []byte("PTIU3VR6HdfhziklcFQBXee1lkdpnesr")) + //encoded := base64.StdEncoding.EncodeToString(handlerData) + //f.ContentType = utils.PString("application/octet-stream") + //f.Data = []byte(aesnew.P1(f.Key, f.Iv, base64.StdEncoding.EncodeToString(handlerData))) + } else { + f.Data = handlerData + } +} + +// 文件下载 +func (f *File) DownLoad(url string) []byte { + r := http.New( + http.WithUrl(url), + http.WithMethod(http.GET), + ) + if err := r.Do(); err != nil { + panic(err.Error()) + } + return r.Result +} + +// 文件解密 +func (f *File) DecryptFromUrl(url string) string { + result := f.DownLoad(url) + return string(result) + //return aesnew.P2(f.Key, f.Iv, string(result)) +} + +func (f *File) SetKeyIv(key, iv string) { + f.Key = key + f.Iv = iv +} + +//func GetImageKeyIv(uid int64) (string, string) { +// return kk.KK2(fmt.Sprintf("%d", uid), 6), kk.KK2(fmt.Sprintf("%d", uid), 10) +//} diff --git a/pkg/fileserver/local/local.go b/pkg/fileserver/local/local.go new file mode 100644 index 0000000..c490b6c --- /dev/null +++ b/pkg/fileserver/local/local.go @@ -0,0 +1,131 @@ +package fileLocal + +import ( + "bytes" + "encoding/json" + "epur-pay/pkg/logger" + "fmt" + "io" + "mime/multipart" + "net/http" + "reflect" + "time" +) + +type Local struct { + AccessKeyId string + AccessKeySecret string + BucketName string + EndPoint string + Access string + RegionId string +} + +type ReturnUpload struct { + Code int64 `json:"code"` + Message string `json:"message"` + Data struct { + FileUrl string `json:"fileUrl"` + } `json:"data"` +} + +func New(accessKeyId, accessKeySecret, bucketName, endPoint string, access string, regionId string) (*Local, error) { + instance := &Local{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + BucketName: bucketName, + EndPoint: endPoint, + Access: access, + RegionId: regionId, + } + return instance, nil +} + +func (this *Local) ReadUrl(filePath string) string { + return this.url(filePath) +} + +func (this *Local) url(fileName string) string { + return fmt.Sprintf("http://%s/%s", this.Access, fileName) +} + +func (this *Local) Put(fileName string, contentType string, data interface{}) string { + var ossData io.Reader + typeOf := reflect.TypeOf(data).Kind() + switch typeOf { + case reflect.Slice: + ossData = bytes.NewBuffer(data.([]byte)) + case reflect.Struct: + ossData = data.(io.Reader) + case reflect.Ptr: + a1 := reflect.ValueOf(data).Interface() + d := a1.([]uint8) + ossData = bytes.NewBuffer(d) + default: + panic("数据不合法--!") + } + + var buffer bytes.Buffer + writer := multipart.NewWriter(&buffer) + writer.WriteField("path", "test/file") + // 创建一个新的表单文件字段,并将文件内容的io.Reader传递给它 + fileField, err := writer.CreateFormFile("file", fileName) + if err != nil { + fmt.Println("创建表单文件字段时出错:", err) + return "" + } + // 将文件内容的io.Reader复制到表单文件字段中 + _, err = io.Copy(fileField, ossData) + if err != nil { + fmt.Println("复制文件内容时出错:", err) + return "" + } + // 关闭multipart写入器以完成请求 + writer.Close() + + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s", this.EndPoint), &buffer) + if err != nil { + logger.AccessLogger.Errorf("---> local upload request fail. %s", err.Error()) + return "" + } + request.Header.Set("Content-Type", writer.FormDataContentType()) + + client := http.Client{Timeout: 5 * time.Second} + response, errs := client.Do(request) + if errs != nil { + logger.AccessLogger.Errorf("---> local upload fail. %s", errs.Error()) + return "" + } + + if response.StatusCode == 200 { + result := ReturnUpload{} + bodyBuffer := new(bytes.Buffer) + _, err = bodyBuffer.ReadFrom(response.Body) + + logger.AccessLogger.Errorf("----> %s", bodyBuffer.String()) + if err != nil { + logger.AccessLogger.Errorf("---> local upload fail. %s", errs.Error()) + return "" + } + + if err1 := json.Unmarshal([]byte(bodyBuffer.String()), &result); err1 != nil { + panic(result.Message) + } + + if result.Code == 200 { + return result.Data.FileUrl + } + } + defer response.Body.Close() + + return "" +} + +// 获取直传token +func (this *Local) Token(key string, contentType string) (map[string]interface{}, error) { + token := make(map[string]interface{}) + token["host"] = fmt.Sprintf("http://%s", this.EndPoint) + token["directory"] = "pic" + + return token, nil +} diff --git a/pkg/fileserver/oss/oss.go b/pkg/fileserver/oss/oss.go new file mode 100644 index 0000000..2b37956 --- /dev/null +++ b/pkg/fileserver/oss/oss.go @@ -0,0 +1,162 @@ +package fileOss + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "encoding/json" + "epur-pay/pkg/idGenerate" + "epur-pay/pkg/utils" + "fmt" + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "hash" + "io" + "reflect" + "time" +) + +type ConfigStruct struct { + Expiration string `json:"expiration"` + Conditions [][]string `json:"conditions"` +} + +type CallbackParam struct { + CallbackUrl string `json:"callbackUrl"` + CallbackBody string `json:"callbackBody"` + CallbackBodyType string `json:"callbackBodyType"` +} + +/* +阿里云oss存储 +*/ +type Oss struct { + Key string + AccessKeyId string + AccessKeySecret string + BucketName string + EndPoint string + Access string + RegionId string + ReadPrivate bool + client *oss.Client + bucket *oss.Bucket +} + +func New(accessKeyId, accessKeySecret, bucketName, endPoint string, access string, RegionId string, private bool) (*Oss, error) { + + var instance *Oss + instance = &Oss{ + ReadPrivate: private, + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + BucketName: bucketName, + EndPoint: endPoint, + RegionId: RegionId, + Access: access, + } + if err := instance.init(instance.AccessKeyId, instance.AccessKeySecret, instance.BucketName, instance.EndPoint); err != nil { + return nil, err + } + return instance, nil +} + +func (this *Oss) ReadUrl(filePath string) string { + return this.url(filePath) +} + +func (this *Oss) init(accessKeyId, accessKeySecret, bucketName, endPoint string) error { + var err error + this.client, err = oss.New(endPoint, accessKeyId, accessKeySecret) + if err != nil { + return err + } + if bucketName != "" { + this.bucket, err = this.client.Bucket(bucketName) + if err != nil { + return err + } + } + return nil +} + +func (this *Oss) url(fileName string) string { + return fmt.Sprintf("https://%s/%s", this.Access, fileName) +} + +func (this *Oss) Put(fileName string, contentType string, data interface{}) string { + + var ossData io.Reader + + typeOf := reflect.TypeOf(data).Kind() + + //logger.AccessLogger.Infoln("fileName: ", fileName, " 类型:", typeOf) + //logger.AccessLogger.Infoln("contentType: ", contentType) + + switch typeOf { + case reflect.Slice: + ossData = bytes.NewBuffer(data.([]byte)) + case reflect.Struct: + ossData = data.(io.Reader) + case reflect.Ptr: + a1 := reflect.ValueOf(data).Interface() + d := a1.([]uint8) + ossData = bytes.NewBuffer(d) + default: + panic("数据不合法--!") + } + + oss.SetHeader("content-type", contentType) + utils.Error(this.bucket.PutObject(fileName, ossData)) + return this.url(fileName) +} + +func (this *Oss) Get_gmt_iso8601(expire_end int64) string { + var tokenExpire = time.Unix(expire_end, 0).UTC().Format("2006-01-02T15:04:05Z") + return tokenExpire +} + +// 获取直传token +func (this *Oss) Token(key string, contentType string) (map[string]interface{}, error) { + now := time.Now().Unix() + expire_end := now + 300 + tokenExpire := this.Get_gmt_iso8601(expire_end) + //create post policy json + var config ConfigStruct + config.Expiration = tokenExpire + var condition []string + condition = append(condition, "starts-with") + condition = append(condition, "$key") + condition = append(condition, "pic") + config.Conditions = append(config.Conditions, condition) + + //calucate signature + result, err := json.Marshal(config) + debyte := base64.StdEncoding.EncodeToString(result) + h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(this.AccessKeySecret)) + io.WriteString(h, debyte) + signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + var callbackParam CallbackParam + callbackParam.CallbackUrl = "" + callbackParam.CallbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}" + callbackParam.CallbackBodyType = "application/x-www-form-urlencoded" + callback_str, err := json.Marshal(callbackParam) + if err != nil { + fmt.Println("callback json err:", err) + } + callbackBase64 := base64.StdEncoding.EncodeToString(callback_str) + + token := make(map[string]interface{}) + + token["accessId"] = this.AccessKeyId + token["host"] = fmt.Sprintf("https://%s", this.Access) + token["expire"] = expire_end + token["signature"] = signedStr + token["directory"] = "pic" + token["policy"] = debyte + token["callback"] = callbackBase64 + token["x:callbackkey"] = utils.EncodeMD5(idGenerate.ID.Generate("")) + + return token, nil +} diff --git a/pkg/idGenerate/generate.go b/pkg/idGenerate/generate.go new file mode 100644 index 0000000..ca1abd1 --- /dev/null +++ b/pkg/idGenerate/generate.go @@ -0,0 +1,45 @@ +package idGenerate + +import ( + "epur-pay/pkg/logger" + "github.com/bwmarrin/snowflake" +) + +var ID *id + +type id struct { + node *snowflake.Node +} + +func init() { + + var err error + ID = &id{} + + snowflake.Epoch = 1668423088000 + ID.node, err = snowflake.NewNode(1) + if err != nil { + logger.AccessLogger.Error(err.Error()) + panic(err) + } +} + +const ( + CoinsOrder = "C" // 币币订单 + Recharge = "R" // 充值 + Withdraw = "W" // 提现 + FOrder = "F" // 理财产品 + Ico = "I" // ICO申购 + C2C = "T" // C2C + NULL = "" // 其它 + CHAT = "M" // 聊天 + Pledge = "P" // 质押 +) + +func (i *id) generate() snowflake.ID { + return i.node.Generate() +} + +func (i *id) Generate(Type string) string { + return Type + i.generate().String() +} diff --git a/pkg/limit/config.go b/pkg/limit/config.go new file mode 100644 index 0000000..8946248 --- /dev/null +++ b/pkg/limit/config.go @@ -0,0 +1,11 @@ +package limit + +type LimitConfRules struct { + Rules map[string]*LimitOpt `mapstructure:"rules"` +} + +type LimitOpt struct { + Interval int64 `mapstructure:"interval"` + Capacity int64 `mapstructure:"capacity"` + Quantum int64 `mapstructure:"quantum"` +} diff --git a/pkg/limit/limit.go b/pkg/limit/limit.go new file mode 100644 index 0000000..f60b2dc --- /dev/null +++ b/pkg/limit/limit.go @@ -0,0 +1,17 @@ +package limit + +import ( + "github.com/gin-gonic/gin" + "github.com/juju/ratelimit" +) + +type LimiterIface interface { + Key(c *gin.Context) string + GetBucket(key string) (*ratelimit.Bucket, bool) + AddBucketsByUri(uri string, fillInterval, capacity, quantum int64) LimiterIface + AddBucketByConf(rules map[string]*LimitOpt) LimiterIface +} + +type Limiter struct { + limiterBuckets map[string]*ratelimit.Bucket +} diff --git a/pkg/limit/method_limiter.go b/pkg/limit/method_limiter.go new file mode 100644 index 0000000..8a139b6 --- /dev/null +++ b/pkg/limit/method_limiter.go @@ -0,0 +1,80 @@ +package limit + +import ( + "github.com/gin-gonic/gin" + "github.com/juju/ratelimit" + "strings" + "time" +) + +type UriLimiter struct { + *Limiter + Rule *LimitConfRules +} + +func NewUriLimiter() LimiterIface { + return &UriLimiter{ + Limiter: &Limiter{ + limiterBuckets: make(map[string]*ratelimit.Bucket), + }, + } +} + +func (l *UriLimiter) Key(c *gin.Context) string { + uri := c.Request.RequestURI + index := strings.Index(uri, "?") + if index == -1 { + return uri + } + return uri[:index] +} + +func (l *UriLimiter) GetBucket(key string) (*ratelimit.Bucket, bool) { + //logger.AccessLogger.Infoln(key, l.limiterBuckets) + bucket, ok := l.limiterBuckets[key] + return bucket, ok +} + +func (l *UriLimiter) AddBucketsByUri(uri string, fillInterval, capacity, quantum int64) LimiterIface { + bucket := ratelimit.NewBucketWithQuantum(time.Second*time.Duration(fillInterval), capacity, quantum) + l.limiterBuckets[uri] = bucket + return l +} + +// +//func (l *UriLimiter) getConf() *LimitConfRules { +// rule := &LimitConfRules{} +// +// rule.Rules = make(map[string]*LimitOpt) +// +// // 抢红包接口 +// rule.Rules["/app/v1/red/rob"] = &LimitOpt{ +// Interval: 1, +// Capacity: 100, // 总共可以存多少个令牌 +// Quantum: 60, // 每次 Interval 时间往桶里扔 多少个令牌 +// } +// +// // 红包详情接口 +// rule.Rules["/app/v1/red/info"] = &LimitOpt{ +// Interval: 1, +// Capacity: 100, +// Quantum: 50, +// } +// +// rule.Rules["/app/v1/sso/test"] = &LimitOpt{ +// Interval: 1, +// Capacity: 1, +// Quantum: 1, +// } +// +// return rule +//} + +func (l *UriLimiter) AddBucketByConf(rules map[string]*LimitOpt) LimiterIface { + //rule := l.getConf() + + for k, v := range rules { + l.AddBucketsByUri(k, v.Interval, v.Capacity, v.Quantum) + } + return l +} diff --git a/pkg/logger/log-rds.og.go b/pkg/logger/log-rds.og.go new file mode 100644 index 0000000..fd32d65 --- /dev/null +++ b/pkg/logger/log-rds.og.go @@ -0,0 +1,77 @@ +package logger + +import ( + "fmt" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" + "path" + "strings" + "time" +) + +// 定义自己的Writer +type RdsLoggerWriter struct { + logger *logrus.Logger +} + +// 实现gorm/logger.Writer接口 +func (r *RdsLoggerWriter) Printf(format string, v ...interface{}) { + r.logger.Info(fmt.Sprintf(format, v...)) +} + +type RdsLogFormatter struct{} + +func (s *RdsLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { + + local, _ := time.LoadLocation("Asia/Shanghai") + timestamp := time.Now().In(local).Format("2006-01-02 15:04:05") + + //fmt.Println(entry.Data) + msg := fmt.Sprintf( + "[GOID:%d] [%s] %s %s\n", + getGID(), + strings.ToUpper(entry.Level.String()), + timestamp, + entry.Message) + return []byte(msg), nil +} + +func newRdsLoggerWriter(LogFilePath string, LogFileName string, LoggerInstance *logrus.Logger, RunMode string) *RdsLoggerWriter { + + //配置logrus + + fileName := path.Join(LogFilePath, LogFileName) + + outSelect(LoggerInstance, RunMode) + + logWriter, _ := rotatelogs.New( + // 分割后的文件名称 + fileName+".%Y%m%d.log", + + // 生成软链,指向最新日志文件 + rotatelogs.WithLinkName(fileName), + + // 设置最大保存时间(7天) + rotatelogs.WithMaxAge(7*24*time.Hour), + + // 设置日志切割时间间隔(1天) + rotatelogs.WithRotationTime(24*time.Hour), + ) + + writeMap := lfshook.WriterMap{ + logrus.InfoLevel: logWriter, + logrus.FatalLevel: logWriter, + logrus.DebugLevel: logWriter, + logrus.WarnLevel: logWriter, + logrus.ErrorLevel: logWriter, + logrus.PanicLevel: logWriter, + } + + lfHook := lfshook.NewHook(writeMap, &RdsLogFormatter{}) + + // 新增 Hook + LoggerInstance.AddHook(lfHook) + + return &RdsLoggerWriter{logger: LoggerInstance} +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..2b61b3c --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,142 @@ +package logger + +import ( + "bytes" + "fmt" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" + "os" + "path" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" +) + +var ( + AccessLogger = logrus.New() + RouterLogger = logrus.New() + ErrorLogger = logrus.New() + RdsLogger *RdsLoggerWriter +) + +type CustomLogger struct { + File string + Logger *logrus.Logger +} + +func New(LogFilePath string, RunMode string) { + + var err error + _, err = os.Stat(LogFilePath) + switch { + case os.IsNotExist(err): + err = os.MkdirAll(LogFilePath, os.ModePerm) + if err != nil { + panic(err) + } + case os.IsPermission(err): + panic(err) + } + + for _, item := range []CustomLogger{ + {File: "access.log", Logger: AccessLogger}, + {File: "router.log", Logger: RouterLogger}, + {File: "error.log", Logger: ErrorLogger}, + } { + loggerToFile(LogFilePath, item.File, item.Logger, RunMode) + } + + RdsLogger = newRdsLoggerWriter(LogFilePath, "rds.log", logrus.New(), RunMode) +} + +type LogFormatter struct{} + +func (s *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) { + + var ( + file string + dir string + l int + ) + + local, _ := time.LoadLocation("Asia/Shanghai") + timestamp := time.Now().In(local).Format("2006-01-02 15:04:05") + + if entry.Caller != nil { + file = filepath.Base(entry.Caller.File) + dir = filepath.Dir(entry.Caller.File) + l = entry.Caller.Line + } + + ssss, _ := os.Getwd() + //fmt.Println(entry.Data) + msg := fmt.Sprintf( + "[GOID:%d] [%s] %s [%s/%s:%d] %s\n", + getGID(), + strings.ToUpper(entry.Level.String()), + timestamp, strings.ReplaceAll(dir, ssss, ""), file, l, + entry.Message) + return []byte(msg), nil +} + +func getGID() uint64 { + b := make([]byte, 64) + b = b[:runtime.Stack(b, false)] + b = bytes.TrimPrefix(b, []byte("goroutine ")) + b = b[:bytes.IndexByte(b, ' ')] + n, _ := strconv.ParseUint(string(b), 10, 64) + return n +} + +func loggerToFile(LogFilePath string, LogFileName string, LoggerInstance *logrus.Logger, RunMode string) { + fileName := path.Join(LogFilePath, LogFileName) + + //writer := bufio.NewWriter(src) + //LoggerInstance.SetOutput(writer) + outSelect(LoggerInstance, RunMode) + LoggerInstance.SetReportCaller(true) + //LoggerInstance.SetFormatter(new(LogFormatter)) + + logWriter, _ := rotatelogs.New( + // 分割后的文件名称 + fileName+".%Y%m%d.log", + + // 生成软链,指向最新日志文件 + rotatelogs.WithLinkName(fileName), + + // 设置最大保存时间(2天) + rotatelogs.WithMaxAge(2*24*time.Hour), + + // 设置日志切割时间间隔(1天) + rotatelogs.WithRotationTime(24*time.Hour), + ) + + writeMap := lfshook.WriterMap{ + logrus.InfoLevel: logWriter, + logrus.FatalLevel: logWriter, + logrus.DebugLevel: logWriter, + logrus.WarnLevel: logWriter, + logrus.ErrorLevel: logWriter, + logrus.PanicLevel: logWriter, + } + + lfHook := lfshook.NewHook(writeMap, &LogFormatter{}) + + // 新增 Hook + LoggerInstance.AddHook(lfHook) +} + +func outSelect(LoggerInstance *logrus.Logger, RunMode string) { + if RunMode != "debug" { + src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend) + if err != nil { + panic(err) + } + LoggerInstance.SetOutput(src) + } else { + LoggerInstance.SetOutput(os.Stdout) + } +} diff --git a/pkg/mq/c.go b/pkg/mq/c.go new file mode 100644 index 0000000..b97a31f --- /dev/null +++ b/pkg/mq/c.go @@ -0,0 +1,86 @@ +package mq + +import ( + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "errors" + "gorm.io/gorm" + "reflect" + "runtime" +) + +func (p *ConsumerApi) consumerEx() { + + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + e := reflect.ValueOf(err) + logger.ErrorLogger.Errorln(e.String(), string(buf)) + p.E = errors.New(string(buf)) + } + }() + + var c int + + for { + p.E = p.F(ComsumerParams{Data: []byte(p.A.Data), Func: p.A.Func, DB: p.DB}) + if p.E != nil && p.A.Ack && p.A.RetryCount > c { + c++ + continue + } else { + break + } + } +} + +func (p *ConsumerApi) consumer() { + + if p.A.Transaction { + if err := rds.DB.Transaction(func(ts *gorm.DB) error { + p.DB = ts + p.consumerEx() + p.end() + return p.E + }); err != nil { + logger.ErrorLogger.Errorln(err.Error()) + } + } else { + p.DB = rds.DB + p.consumerEx() + p.end() + } +} + +func (p *ConsumerApi) end() { + + if p.E != nil { + p.A.Status = "1" + + if p.A.Store { + utils.Error(rds.DB.Table(p.A.TableName()).Where( + "id=?", p.A.Id).Updates(map[string]interface{}{ + "status": "1", + "err_msg": p.E.Error(), + }).Error) + } + } else { + p.A.Status = "0" + + if p.A.Store { + utils.Error(rds.DB.Table(p.A.TableName()).Where( + "id=?", p.A.Id).Updates(map[string]interface{}{ + "status": "0", + "complete_time": utils.Time2StampSecond(), + }).Error) + } + } + + if !(p.E != nil && !p.A.ChainErrorTriger) { + for idx := range p.A.Chains { + p.P.add(p.A.Chains[idx]) + } + } +} diff --git a/pkg/mq/model.go b/pkg/mq/model.go new file mode 100644 index 0000000..d4d5d2e --- /dev/null +++ b/pkg/mq/model.go @@ -0,0 +1,70 @@ +package mq + +import ( + "database/sql/driver" + "encoding/json" + "gorm.io/gorm" +) + +type AsyncTask struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 唯一值 + ParentId int64 `gorm:"column:parent_id" json:"parentId"` // 父级ID + Topics string `gorm:"column:topics" json:"topics"` // 主题 + IsDelay bool `gorm:"column:is_delay" json:"isDelay"` // 是否延迟执行 + Delay int64 `gorm:"column:delay" json:"delay"` // 延迟秒数 + ExpireTime int64 `gorm:"column:exprie_time" json:"expireTime"` // 延迟执行时间 + Data string `gorm:"column:data" json:"data"` // 数据 + ErrMsg string `gorm:"column:err_msg" json:"errMsg"` // 错误信息 + Status string `gorm:"column:status" json:"status"` // 0-成功 1-失败 2-等待执行 + Ack bool `gorm:"column:ack" json:"ack"` // 是否Ack + RetryCount int `gorm:"column:retry_count" json:"retryCount"` // 重试次数 + Transaction bool `gorm:"column:transaction" json:"transaction"` // 是否开启事物 + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间 + CompleteTime int64 `gorm:"column:complete_time" json:"completeTime"` // 完成时间 + ChainErrorTriger bool `gorm:"column:chain_error_triger" json:"chainErrorTriger"` // 是否错误继续调用 + Store bool `gorm:"-" json:"-"` // 是否持久话 + Chains AsyncTasks `gorm:"-" json:"chains"` // 链式调用 + Func func() `gorm:"-" json:"-"` // 函数调用 +} + +func (AsyncTask) TableName() string { + return "async_task" +} + +type AsyncTasks []*AsyncTask + +func (j *AsyncTasks) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} + +func (j AsyncTasks) Value() (driver.Value, error) { + return json.Marshal(j) +} + +func (s AsyncTasks) Len() int { + return len(s) +} + +func (s AsyncTasks) Less(i, j int) bool { + return s[i].ExpireTime < s[j].ExpireTime +} + +func (s AsyncTasks) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +type ComsumerParams struct { + Data []byte + Func func() + DB *gorm.DB +} + +type ConsumerFunc func(body ComsumerParams) error + +type ConsumerApi struct { + E error + F ConsumerFunc + A *AsyncTask + P *Async + DB *gorm.DB +} diff --git a/pkg/mq/mq.go b/pkg/mq/mq.go new file mode 100644 index 0000000..c813783 --- /dev/null +++ b/pkg/mq/mq.go @@ -0,0 +1,176 @@ +package mq + +import ( + "epur-pay/pkg/idGenerate" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "strconv" + "sync" + "time" +) + +var AysncInstance *Async + +type Async struct { + //Tasks AsyncTasks // 数据 model.AsyncTasks + Queue *sync.Map // chan model.AsyncTask + handlerCh chan []interface{} +} + +type Task struct { + Topics string `json:"topics"` // 主题 + Delay int64 `json:"delay"` // 延迟秒数 + Data []byte `json:"data"` // 数据 + Ack bool `json:"bool"` // 是否Ack + RetryCount int `json:"retryCount"` // 重试次数 + Transaction bool `json:"transaction"` // 是否开启事物 + Store bool `json:"store"` // 是否持久化 + ChainErrorTriger bool `json:"chainErrorTriger"` // 是否错误继续调用 + Chains []*Task `json:"-"` // 链式调用 + Func func() `json:"-"` // 函数调用 +} + +func New() *Async { + AysncInstance = &Async{handlerCh: make(chan []interface{}, 1024*10), Queue: new(sync.Map)} + return AysncInstance +} + +func (p *Async) SetTopics(data ...string) *Async { + for _, item := range data { + ch := make(chan AsyncTask, 1024*10) + AysncInstance.Queue.Store(item, ch) + } + return p +} + +func (p *Async) Listen() { + + tasks := AsyncTasks{} + utils.Error(rds.DB.Table(AsyncTask{}.TableName()).Where( + "status in ?", []string{"2"}).Scan(&tasks).Error) + + for index := range tasks { + tasks[index].Store = true + AysncInstance.add(tasks[index]) + } +} + +func (p *Async) createTaskTo(task *Task, parentTask *AsyncTask) *AsyncTask { + + task1 := &AsyncTask{ + Topics: task.Topics, + Data: string(task.Data), + Status: "2", + Ack: task.Ack, + Delay: task.Delay, + CreateTime: utils.Time2StampSecond(), + RetryCount: task.RetryCount, + Transaction: task.Transaction, + Store: task.Store, + ChainErrorTriger: task.ChainErrorTriger, + Func: task.Func, + } + + if task1.Func != nil { + task1.Store = false + } + + if parentTask != nil { + parentTask.Chains = append(parentTask.Chains, task1) + task1.ParentId = parentTask.Id + task1.Store = parentTask.Store + task1.ChainErrorTriger = parentTask.ChainErrorTriger + } + + if task1.Delay > 0 { + task1.IsDelay = true + task1.ExpireTime = task1.CreateTime + task1.Delay + } + if task1.RetryCount <= 0 { + task1.RetryCount = 2 + } + + if task1.Store { + utils.Error(rds.DB.Create(task1).Error) + } else { + task1.Id, _ = strconv.ParseInt(idGenerate.ID.Generate(""), 10, 64) + } + + for idx := range task.Chains { + if task.Chains[idx] != nil { + p.createTaskTo(task.Chains[idx], task1) + } + } + + return task1 +} + +func (p *Async) Producer(task *Task) { + task1 := p.createTaskTo(task, nil) + if len(task1.Chains) > 0 { + utils.DbErrSkipRecordNotFound(rds.DB.Save(task1).Error) + } + p.add(task1) +} + +func (p *Async) Consumer(topics string, f ConsumerFunc, poolCount int) *Async { + + if _, ok := AysncInstance.Queue.Load(topics); !ok { + ch := make(chan *AsyncTask, 1024*10) + AysncInstance.Queue.Store(topics, ch) + } else { + return p + } + + if poolCount <= 0 { + poolCount = 1 + } + + for i := 0; i < poolCount; i++ { + go func(topics string, f ConsumerFunc) { + if ch, ok := AysncInstance.Queue.Load(topics); ok { + + //logger.AccessLogger.Infoln("MQ订阅:", topics) + for row := range ch.(chan *AsyncTask) { + //logger.AccessLogger.Infoln("TaskId:", row.Id) + go func(row *AsyncTask) { + api := ConsumerApi{ + F: f, + A: row, + P: p, + } + api.consumer() + }(row) + } + } + }(topics, f) + } + return p +} + +func (p *Async) push(c chan *AsyncTask, task *AsyncTask) { + c <- task +} + +func (p *Async) add(task *AsyncTask) { + + queue, ok := p.Queue.Load(task.Topics) + if !ok { + return + } + + if !task.IsDelay { + p.push(queue.(chan *AsyncTask), task) + } else if task.IsDelay && task.ExpireTime <= utils.Time2StampSecond() { + p.push(queue.(chan *AsyncTask), task) + } else { + go func(task *AsyncTask, c chan *AsyncTask) { + timer := time.NewTimer(time.Duration(task.Delay) * time.Second) + select { + case <-timer.C: + p.push(queue.(chan *AsyncTask), task) + } + timer.Stop() + }(task, queue.(chan *AsyncTask)) + } +} diff --git a/pkg/rds/rds.go b/pkg/rds/rds.go new file mode 100644 index 0000000..a6997c6 --- /dev/null +++ b/pkg/rds/rds.go @@ -0,0 +1,74 @@ +package rds + +import ( + "epur-pay/model" + myLogger "epur-pay/pkg/logger" + "fmt" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "time" +) + +var ( + DB *gorm.DB +) + +func New(Rds *model.Rds, RunMode string) { + + var ( + err error + loggerConfig logger.Config + ) + + dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", + Rds.User, + Rds.PassWord, + Rds.Host, + Rds.Port, + Rds.Name, + Rds.CharSet) + + if RunMode == "release" { + loggerConfig = logger.Config{ + //慢SQL阈值 + SlowThreshold: time.Duration(Rds.SlowThreshold) * time.Millisecond, + //设置日志级别,只有Warn以上才会打印sql + LogLevel: logger.Warn, + Colorful: false, + IgnoreRecordNotFoundError: true, + } + } else { + loggerConfig = logger.Config{ + //慢SQL阈值 + SlowThreshold: time.Duration(Rds.SlowThreshold) * time.Millisecond, + //设置日志级别,只有Warn以上才会打印sql + LogLevel: logger.Info, + Colorful: false, + IgnoreRecordNotFoundError: true, + } + } + + slowLogger := logger.New( + myLogger.RdsLogger, + loggerConfig, + ) + + if DB, err = gorm.Open( + mysql.Open(dns), + &gorm.Config{ + //禁用全局事务 + SkipDefaultTransaction: true, + Logger: slowLogger, + }); err != nil { + myLogger.AccessLogger.Error("connect rds error!") + panic(err.Error()) + } + + fmt.Println("Rds ok") + + sdb, _ := DB.DB() + sdb.SetMaxIdleConns(Rds.MaxIdleConns) + sdb.SetMaxOpenConns(Rds.MaxOpenConns) + sdb.SetConnMaxLifetime(60 * time.Second) +} diff --git a/pkg/redis/redis.go b/pkg/redis/redis.go new file mode 100644 index 0000000..6a046fc --- /dev/null +++ b/pkg/redis/redis.go @@ -0,0 +1,60 @@ +package redis + +import ( + "epur-pay/model" + "epur-pay/pkg/logger" + "fmt" + "github.com/gomodule/redigo/redis" + "time" +) + +var RPool *redis.Pool + +func New(r *model.Redis) { + RPool = newPool(r) +} + +func newPool(r *model.Redis) *redis.Pool { + + return &redis.Pool{ + MaxIdle: r.MaxIdle, + MaxActive: r.MaxActive, + Wait: true, + IdleTimeout: r.IdleTimeout * time.Second, + Dial: func() (redis.Conn, error) { + return Dial(r) + }, + } +} + +func Dial(r *model.Redis) (redis.Conn, error) { + + for { + c, err := dial(r) + if err != nil { + logger.ErrorLogger.Errorln(err.Error()) + } + return c, nil + } +} + +func dial(r *model.Redis) (redis.Conn, error) { + c, err := redis.Dial( + "tcp", + fmt.Sprintf("%s:%s", r.Host, r.Port), + redis.DialDatabase(r.Name), + redis.DialPassword(r.PassWord), + redis.DialReadTimeout(time.Second*r.ReadTimeout), + redis.DialWriteTimeout(time.Second*r.WriteTimeout), + redis.DialConnectTimeout(time.Second*r.ConnectTimeout)) + if err != nil { + logger.ErrorLogger.Errorln("connect redis error!", err.Error()) + return nil, err + } + _, err = redis.String(c.Do("PING")) + if err != nil { + logger.ErrorLogger.Errorln("connect redis error!", err.Error()) + return nil, err + } + return c, err +} diff --git a/pkg/server/api.go b/pkg/server/api.go new file mode 100644 index 0000000..be1a24e --- /dev/null +++ b/pkg/server/api.go @@ -0,0 +1,83 @@ +package server + +import ( + "context" + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/config" + "epur-pay/pkg/limit" + "epur-pay/pkg/logger" + "fmt" + "net/http" + "os" + "os/signal" + "time" +) + +func (p *App) WaitServerAppListen() { + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt) + sig := <-signalChan + + logger.AccessLogger.Warnln("Get Signal:", sig, " Shutdown Server ...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + for _, v := range p.ServerApps { + if err := v.Shutdown(ctx); err != nil { + logger.AccessLogger.Errorln("Shutdown: ", err) + } + } + logger.AccessLogger.Infoln("server exiting") +} + +func (p *App) ListenServerApp() *App { + for _, v := range p.ServerApps { + go func(v *http.Server) { + if err := v.ListenAndServe(); err != nil { + logger.AccessLogger.Warnf("HTTP listen: %s\n", err) + } + }(v) + } + + go func() { + for !cache.Global.ProjectInitStatus { + time.Sleep(100 * time.Millisecond) + } + if p.RunMode == "release" { + cache.Global.LimitRouter.Api = limit.NewUriLimiter(). + AddBucketByConf(cache.Global.LimitRouter.Rule.Rules) + } + }() + + logger.AccessLogger.Infof("server listening ...") + return p +} + +func (p *App) AddServerApp(s1 model.ListenServer, r http.Handler) *App { + + endPoint := fmt.Sprintf(":%d", s1.Port) + + logger.AccessLogger.Infoln(endPoint) + ReadTimeout := time.Second + WriteTimeout := time.Second + + if config.Cf.Common.RunMode == "release" { + ReadTimeout = s1.ReadTimeOut * time.Second + WriteTimeout = s1.WriteTimeOut * time.Second + } else { + ReadTimeout = 60 * 60 * time.Second + WriteTimeout = 60 * 60 * time.Second + } + + p.ServerApps = append(p.ServerApps, &http.Server{ + Addr: endPoint, + Handler: r, + ReadTimeout: ReadTimeout, + WriteTimeout: WriteTimeout, + MaxHeaderBytes: 1 << 20, + }) + return p +} diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000..119059f --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,54 @@ +package server + +import ( + "epur-pay/pkg/config" + "epur-pay/pkg/logger" + "epur-pay/pkg/mq" + "epur-pay/pkg/rds" + "epur-pay/pkg/redis" + "github.com/robfig/cron" + "net/http" +) + +type App struct { + RunMode string + ServerName string + ServerApps []*http.Server +} + +func NewApp(RunMode, ServerName string) *App { + p := &App{RunMode: RunMode, ServerName: ServerName} + config.Cf.Common.RunMode = p.RunMode + config.New(p.ServerName) + logger.New(config.Cf.Common.LogFilePath, config.Cf.Common.RunMode) + return p +} + +func (p *App) LoadDB() *App { + rds.New(&config.Cf.Rds, config.Cf.Common.RunMode) + return p +} + +func (p *App) LoadRedis() *App { + redis.New(&config.Cf.Redis) + return p +} + +func (p *App) LoadCron() *App { + cron.New() + return p +} + +func (p *App) LoadMq() *App { + mq.New() + return p +} + +func (p *App) LoadCustomApp(F func()) *App { + + if F != nil { + F() + } + + return p +} diff --git a/pkg/tools/api.go b/pkg/tools/api.go new file mode 100644 index 0000000..0a0501b --- /dev/null +++ b/pkg/tools/api.go @@ -0,0 +1,25 @@ +package tools + +import ( + "crypto/md5" + "encoding/hex" +) + +type Api struct { + Tools string `json:"tools"` + Values []interface{} `json:"values"` +} + +func (p *Api) Run() interface{} { + switch p.Tools { + case "MD5": + return p.Md5(p.Values[0].(string)) + } + return nil +} + +func (p *Api) Md5(data string) string { + m := md5.New() + m.Write([]byte(data)) + return hex.EncodeToString(m.Sum(nil)) +} diff --git a/pkg/utils/error.go b/pkg/utils/error.go new file mode 100644 index 0000000..cdddb5b --- /dev/null +++ b/pkg/utils/error.go @@ -0,0 +1,24 @@ +package utils + +import "gorm.io/gorm" + +func Error(err error) { + if err != nil { + panic(err.Error()) + } +} + +func DbErrSkipRecordNotFound(err error) { + if err != nil && err != gorm.ErrRecordNotFound { + panic(err.Error()) + } +} + +func DbErrRecordNotFoundBool(err error) bool { + if err == gorm.ErrRecordNotFound { + return true + } else if err != nil { + panic(err.Error()) + } + return false +} diff --git a/pkg/utils/file.go b/pkg/utils/file.go new file mode 100644 index 0000000..c08d88a --- /dev/null +++ b/pkg/utils/file.go @@ -0,0 +1,50 @@ +package utils + +import ( + "io" + "mime" + "path/filepath" +) + +func GetContentType(filePath string, body []byte) (string, error) { + + contentType := "" + if len(filePath) > 0 { + ext := filepath.Ext(filePath) + // 根据扩展名获取 MIME 类型 + contentType = mime.TypeByExtension(ext) + } + + //if contentType == "" { + // + // contentType = http.DetectContentType(body) + //} + + return contentType, nil +} + +func GetContentTypeForIoRead(filePath string, body io.Reader) (string, error) { + + //buffer := make([]byte, 512) // 读取文件的前 512 字节用于检测内容类型 + //_, err := body.Read(buffer) + //if err != nil { + // return "", err + //} + return GetContentType(filePath, nil) +} + +// 获取存储类型 +func GetStoreType(key string) (storeType string) { + switch key { + case "0": + storeType = "oss" + case "1": + storeType = "cos" + case "2": + storeType = "asw" + case "3": + storeType = "local" + } + + return storeType +} diff --git a/pkg/utils/string.go b/pkg/utils/string.go new file mode 100644 index 0000000..e50bb80 --- /dev/null +++ b/pkg/utils/string.go @@ -0,0 +1,27 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "epur-pay/pkg/logger" +) + +func EncodeMD5(value string) string { + m := md5.New() + m.Write([]byte(value)) + return hex.EncodeToString(m.Sum(nil)) +} + +func ToJson(data interface{}) string { + if data == nil { + return "" + } + + if e, err := json.Marshal(data); err != nil { + logger.AccessLogger.Warnln(err.Error()) + } else { + return string(e) + } + return "" +} diff --git a/pkg/utils/time.go b/pkg/utils/time.go new file mode 100644 index 0000000..5942ce0 --- /dev/null +++ b/pkg/utils/time.go @@ -0,0 +1,9 @@ +package utils + +import "time" + +func Time2StampSecond() int64 { + t := time.Now() + millisecond := t.UnixNano() / 1e6 + return millisecond / 1000 +} diff --git a/router/middleware.go b/router/middleware.go new file mode 100644 index 0000000..b7ac51d --- /dev/null +++ b/router/middleware.go @@ -0,0 +1,49 @@ +package router + +import ( + "epur-pay/cache" + "epur-pay/pkg/logger" + "github.com/gin-gonic/gin" + "net/http" + "time" +) + +func ClientIp(c *gin.Context) string { + + ip := c.Request.Header.Get("Cf-Connecting-Ip") + + if len(ip) <= 0 { + ip = c.Request.Header.Get("X-Real-Ip") + } + if len(ip) <= 0 { + ip = c.ClientIP() + } + return ip +} + +func IPBlackList() gin.HandlerFunc { + return func(c *gin.Context) { + if cache.Global.Caches.BlackList.Check(ClientIp(c)) == true { + logger.AccessLogger.Warnf("黑名单请求IP [%s]", ClientIp(c)) + c.AbortWithStatus(http.StatusServiceUnavailable) + return + } + } +} + +func Logs() gin.HandlerFunc { + return func(c *gin.Context) { + + start := time.Now() + + c.Next() + + logger.RouterLogger.Infoln( + time.Now().Sub(start), + c.Request.Header.Get("Cf-Connecting-Ip"), + c.Request.Header.Get("Cf-Ipcountry"), + c.Request.Method, + c.Writer.Status(), + c.Request.RequestURI) + } +} diff --git a/router/router.go b/router/router.go new file mode 100644 index 0000000..0edb484 --- /dev/null +++ b/router/router.go @@ -0,0 +1,56 @@ +package router + +import ( + "epur-pay/cache" + "epur-pay/pkg/config" + "epur-pay/pkg/dapi" + "github.com/gin-contrib/gzip" + "github.com/gin-gonic/gin" + cors "github.com/itsjamie/gin-cors" +) + +func Router() *gin.Engine { + + gin.SetMode(config.Cf.Common.RunMode) + + engine := gin.New() + engine.Use(Logs(), cors.Middleware(cors.Config{ + ValidateHeaders: false, + Origins: "*", + RequestHeaders: "*", + ExposedHeaders: "*", + Methods: "*", + MaxAge: 0, + Credentials: false, + }), gzip.Gzip(gzip.DefaultCompression), IPBlackList(), gin.Recovery()) + + goApi := engine.Group("/api/v1") + + NewRouterFunc() + for index, item := range cache.Global.Routers { + var apiRouter *gin.RouterGroup + if !item.Leaf { + apiRouter = goApi.Group(item.Path) + for indexChild, itemChild := range item.Child { + item.Child[indexChild].Params.(*dapi.InParams).SaveEvent = itemChild.Name + switch itemChild.Method { + case "POST": + apiRouter.POST(itemChild.Path, dapi.ApiDecorator(itemChild.F, itemChild.Params.(*dapi.InParams))) + default: + apiRouter.GET(itemChild.Path, dapi.ApiDecorator(itemChild.F, itemChild.Params.(*dapi.InParams))) + } + } + } else { + apiRouter = goApi + cache.Global.Routers[index].Params.(*dapi.InParams).SaveEvent = item.Name + switch item.Method { + case "POST": + apiRouter.POST(item.Path, dapi.ApiDecorator(item.F, item.Params.(*dapi.InParams))) + default: + apiRouter.GET(item.Path, dapi.ApiDecorator(item.F, item.Params.(*dapi.InParams))) + } + } + } + + return engine +} diff --git a/router/routerFunc.go b/router/routerFunc.go new file mode 100644 index 0000000..b63721d --- /dev/null +++ b/router/routerFunc.go @@ -0,0 +1,171 @@ +package router + +import ( + "epur-pay/api" + "epur-pay/api/login" + "epur-pay/cache" + "epur-pay/model" + "epur-pay/pkg/dapi" +) + +func RouterAppend(R *model.RouterMethod) []*model.RouterMethod { + cache.Global.Routers = append(cache.Global.Routers, R) + return cache.Global.Routers +} + +func NewRouterFunc() { + + RouterAppend( + &model.RouterMethod{ + Name: "测试", + Path: "/test", + Child: []*model.RouterMethod{ + { + Name: "测试", + Method: "POST", + Path: "/test", + Leaf: true, + F: api.Test, + Params: &dapi.InParams{}, + }, + { + Name: "测试", + Method: "POST", + Path: "/test1", + Leaf: true, + F: api.Test1, + Params: &dapi.InParams{}, + }, + }, + }, + ) + + RouterAppend( + &model.RouterMethod{ + Name: "首页", + Path: "/index", + Leaf: false, + Child: []*model.RouterMethod{ + { + Name: "菜单列表获取", + Path: "/getMenuList", + Method: "POST", + Leaf: true, + F: api.GetMenuList, + Params: &dapi.InParams{ + IsTicket: true, + IsAgentAction: true, + }, + }, + { + Name: "用户信息获取", + Path: "/permission", + Method: "POST", + Leaf: true, + F: api.GetPermissionList, + Params: &dapi.InParams{ + IsTicket: true, IsAgentAction: true}, + }, + }, + }, + ) + + RouterAppend( + &model.RouterMethod{ + Name: "登陆", + Path: "/login", + Child: []*model.RouterMethod{ + { + Name: "登陆", + Method: "POST", + Path: "/login", + Leaf: true, + F: login.SsoLogin, + Params: &dapi.InParams{ + IsSaveLog: true, + }, + }, + { + Name: "注册", + Method: "POST", + Path: "/register", + Leaf: true, + F: login.SsoRegister, + Params: &dapi.InParams{ + IsSaveLog: true, + }, + }, + { + Name: "验证码", + Method: "POST", + Path: "/captcha", + Leaf: true, + F: login.SsoSendSmsCode, + Params: &dapi.InParams{ + IsSaveLog: true, + }, + }, + }, + }, + ) + + RouterAppend( + &model.RouterMethod{ + Name: "登陆", + Path: "/user", + Child: []*model.RouterMethod{ + { + Name: "用户信息", + Method: "POST", + Path: "/info", + Leaf: true, + F: login.UserInfo, + Params: &dapi.InParams{ + IsTicket: true, + }, + }, + }, + }, + ) + + RouterAppend( + &model.RouterMethod{ + Name: "支付渠道管理", + Path: "/pay_channel", + Child: []*model.RouterMethod{ + { + Name: "列表", + Method: "POST", + Path: "/get", + Leaf: true, + F: api.GetPayChannel, + Params: &dapi.InParams{ + IsTicket: true, + }, + }, + { + Name: "添加", + Method: "POST", + Path: "/add", + Leaf: true, + F: api.AddPayChannel, + Params: &dapi.InParams{ + IsTicket: true, + IsForUpdate: true, + }, + }, + { + Name: "编辑", + Method: "POST", + Path: "/edit", + Leaf: true, + F: api.EditPayChannel, + Params: &dapi.InParams{ + IsTicket: true, + IsForUpdate: true, + }, + }, + }, + }, + ) +} diff --git a/tri/ehttp/.gitignore b/tri/ehttp/.gitignore new file mode 100644 index 0000000..61df1a4 --- /dev/null +++ b/tri/ehttp/.gitignore @@ -0,0 +1,19 @@ +# ---> Go +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +logs +.idea +.DS_Store +# Dependency directories (remove the comment below to include it) +# vendor/ + diff --git a/tri/ehttp/README.md b/tri/ehttp/README.md new file mode 100644 index 0000000..b923c44 --- /dev/null +++ b/tri/ehttp/README.md @@ -0,0 +1,26 @@ + + + +### Example + +```go +package main + +import ( + "fmt" + "github.com/tangchen2018/go-utils/http" +) + +func main() { + + req := http.New( + http.WithUrl("http://www.baidu.com"), + ) + + if err := req.Do(); err != nil { + panic(err) + } + + fmt.Println(string(req.Result)) +} +``` diff --git a/tri/ehttp/go.mod b/tri/ehttp/go.mod new file mode 100644 index 0000000..9e57645 --- /dev/null +++ b/tri/ehttp/go.mod @@ -0,0 +1,3 @@ +module ehttp + +go 1.21.2 diff --git a/tri/ehttp/http/http.go b/tri/ehttp/http/http.go new file mode 100644 index 0000000..d6d658a --- /dev/null +++ b/tri/ehttp/http/http.go @@ -0,0 +1,191 @@ +package http + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "strings" + "time" +) + +type Request struct { + Method string + Url string + Host string + Timeout time.Duration + Header http.Header + Client *http.Client + Transport *http.Transport + requestType RequestType + contentType string + QueryParams url.Values + Body map[string]interface{} + bodySize int + Result []byte + Response *http.Response +} + +func New(opts ...RequestOption) *Request { + req := &Request{ + Method: GET, + Client: &http.Client{ + Timeout: 60 * time.Second, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DisableKeepAlives: true, + Proxy: http.ProxyFromEnvironment, + }, + }, + Transport: nil, + bodySize: 10, // default is 10MB + Header: make(http.Header), + QueryParams: make(url.Values), + requestType: TypeJSON, + } + for _, option := range opts { + option(req) + } + return req +} + +func (p *Request) SetBody(key string, value interface{}) *Request { + if p.Body == nil { + p.Body = make(map[string]interface{}) + } + p.Body[key] = value + return p +} + +func (p *Request) structureRequest() (io.Reader, error) { + + if len(p.Method) <= 0 { + p.Method = GET + } + + var ( + body io.Reader + bw *multipart.Writer + ) + // multipart-form-data + if p.requestType == TypeMultipartFormData { + body = &bytes.Buffer{} + bw = multipart.NewWriter(body.(io.Writer)) + } + + if p.QueryParams != nil && len(p.QueryParams) > 0 { + p.Url = fmt.Sprintf("%s?%s", p.Url, p.QueryParams.Encode()) + } + + switch p.Method { + case GET: + switch p.requestType { + case TypeJSON: + p.contentType = types[TypeJSON] + case TypeForm, TypeFormData, TypeUrlencoded: + p.contentType = types[TypeForm] + case TypeMultipartFormData: + p.contentType = bw.FormDataContentType() + case TypeXML: + p.contentType = types[TypeXML] + default: + return body, errors.New("Request type Error ") + } + case POST, PUT, DELETE, PATCH: + switch p.requestType { + case TypeJSON: + if p.Body != nil { + if bodyTmp, err := json.Marshal(p.Body); err != nil { + return body, errors.New("marshal error") + } else { + body = strings.NewReader(string(bodyTmp)) + } + } + p.contentType = types[TypeJSON] + case TypeForm, TypeFormData, TypeUrlencoded: + body = strings.NewReader(FormatURLParam(p.Body)) + p.contentType = types[TypeForm] + + case TypeMultipartFormData: + for k, v := range p.Body { + // file 参数 + if file, ok := v.(*File); ok { + fw, err := bw.CreateFormFile(k, file.Name) + if err != nil { + return body, err + } + _, _ = fw.Write(file.Content) + continue + } + // text 参数 + vs, ok2 := v.(string) + if ok2 { + _ = bw.WriteField(k, vs) + } else if ss := convertToString(v); ss != "" { + _ = bw.WriteField(k, ss) + } + } + _ = bw.Close() + p.contentType = bw.FormDataContentType() + case TypeXML: + body = strings.NewReader(FormatURLParam(p.Body)) + p.contentType = types[TypeXML] + default: + return body, errors.New("Request type Error ") + } + default: + return body, errors.New("Only support GET and POST and PUT and DELETE ") + } + return body, nil +} + +func (p *Request) Do() error { + + var ( + err error + body io.Reader + req *http.Request + ) + + if body, err = p.structureRequest(); err != nil { + return err + } + + if req, err = http.NewRequestWithContext(context.Background(), p.Method, p.Url, body); err != nil { + return err + } + + req.Header = p.Header + + req.Header.Set("Content-Type", p.contentType) + if p.Transport != nil { + p.Client.Transport = p.Transport + } + if p.Host != "" { + req.Host = p.Host + } + if p.Timeout > 0 { + p.Client.Timeout = p.Timeout + } + + p.Response, err = p.Client.Do(req) + if err != nil { + return err + } + defer func() { + _ = p.Response.Body.Close() + }() + + p.Result, err = ioutil.ReadAll(io.LimitReader(p.Response.Body, int64(p.bodySize<<20))) // default 10MB change the size you want + if err != nil { + return err + } + return nil +} diff --git a/tri/ehttp/http/model.go b/tri/ehttp/http/model.go new file mode 100644 index 0000000..af3d882 --- /dev/null +++ b/tri/ehttp/http/model.go @@ -0,0 +1,31 @@ +package http + +type RequestType string + +const ( + GET = "GET" + POST = "POST" + PUT = "PUT" + DELETE = "DELETE" + PATCH = "PATCH" + TypeJSON RequestType = "json" + TypeXML RequestType = "xml" + TypeUrlencoded RequestType = "urlencoded" + TypeForm RequestType = "form" + TypeFormData RequestType = "form-data" + TypeMultipartFormData RequestType = "multipart-form-data" +) + +var types = map[RequestType]string{ + TypeJSON: "application/json", + TypeXML: "application/xml", + TypeUrlencoded: "application/x-www-form-urlencoded", + TypeForm: "application/x-www-form-urlencoded", + TypeFormData: "application/x-www-form-urlencoded", + TypeMultipartFormData: "multipart/form-data", +} + +type File struct { + Name string `json:"name"` + Content []byte `json:"content"` +} diff --git a/tri/ehttp/http/option.go b/tri/ehttp/http/option.go new file mode 100644 index 0000000..356bd93 --- /dev/null +++ b/tri/ehttp/http/option.go @@ -0,0 +1,52 @@ +package http + +import ( + "net/http" + "time" +) + +type RequestOption func(*Request) + +func WithHost(host string) RequestOption { + return func(p *Request) { + p.Host = host + } +} + +func WithUrl(url string) RequestOption { + return func(p *Request) { + p.Url = url + } +} + +func WithBodySize(bodySize int) RequestOption { + return func(p *Request) { + p.bodySize = bodySize + } +} + +func WithMethod(method string) RequestOption { + return func(p *Request) { + p.Method = method + } +} + +func WithTransport(transport *http.Transport) RequestOption { + return func(p *Request) { + p.Transport = transport + } +} + +func WithTimeout(timeout time.Duration) RequestOption { + return func(p *Request) { + p.Timeout = timeout + } +} + +func WithRequestType(requestType RequestType) RequestOption { + return func(p *Request) { + if _, ok := types[requestType]; ok { + p.requestType = requestType + } + } +} diff --git a/tri/ehttp/http/utils.go b/tri/ehttp/http/utils.go new file mode 100644 index 0000000..bc8539c --- /dev/null +++ b/tri/ehttp/http/utils.go @@ -0,0 +1,55 @@ +package http + +import ( + "encoding/json" + "net/url" + "sort" + "strings" +) + +func FormatURLParam(body map[string]interface{}) (urlParam string) { + var ( + buf strings.Builder + keys []string + ) + for k := range body { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + v, ok := body[k].(string) + if !ok { + v = convertToString(body[k]) + } + if v != "" { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + buf.WriteByte('&') + } + } + if buf.Len() <= 0 { + return "" + } + return buf.String()[:buf.Len()-1] +} + +func convertToString(v interface{}) (str string) { + if v == nil { + return "" + } + var ( + bs []byte + err error + ) + if bs, err = json.Marshal(v); err != nil { + return "" + } + str = string(bs) + return +} + +func ConvertToString(v interface{}) (str string) { + str = convertToString(v) + return +} diff --git a/tri/ehttp/main.go b/tri/ehttp/main.go new file mode 100644 index 0000000..60c62e6 --- /dev/null +++ b/tri/ehttp/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "ehttp/http" + "fmt" +) + +func main() { + + req := http.New( + http.WithUrl("http://www.baidu.com"), + ) + + if err := req.Do(); err != nil { + panic(err) + } + + fmt.Println(string(req.Result)) +}