From f258956df73092e719a8dadd7fd22e62fd4870c0 Mon Sep 17 00:00:00 2001 From: AstarF <18842336486Q@163.com> Date: Fri, 12 Jul 2024 18:11:21 +0800 Subject: [PATCH] first commit --- .env.example | 62 + .gitignore | 4 + README.md | 22 + app/.DS_Store | Bin 0 -> 6148 bytes app/caches/bannerlistcache/banner_list.go | 34 + .../bannerlistcache/banner_list_test.go | 59 + app/caches/cache_key.go | 8 + app/console/command.go | 9 + app/console/kernel.go | 15 + app/console/test.go | 7 + app/constants/common/common.go | 1 + app/constants/errorcode/error_code.go | 37 + app/constants/logtype/log_type.go | 7 + app/http/controllers/base.go | 108 ++ app/http/controllers/merchant/merchant.go | 164 ++ app/http/controllers/orders/orders.go | 215 +++ app/http/controllers/product/product.go | 167 +++ app/http/controllers/test.go | 113 ++ .../controllers/transfersys/transfersys.go | 82 + app/http/controllers/whitelist/whitelist.go | 169 +++ app/http/controllers/zhiliansys/zhiliansys.go | 68 + app/http/entities/merchant/merchant.go | 52 + app/http/entities/orders/orders.go | 95 ++ app/http/entities/product/product.go | 54 + app/http/entities/test.go | 43 + app/http/entities/transfersys/transfersys.go | 32 + app/http/entities/whitelist/whitelist.go | 55 + app/http/entities/zhiliansys/zhiliansys.go | 31 + app/http/formatters/bannerformatter/banner.go | 34 + .../formatters/bannerformatter/banner_test.go | 45 + app/http/metric/metric.go | 77 + app/http/middlewares/ip_verify.go | 26 + app/http/middlewares/metric.go | 22 + app/http/middlewares/request_log.go | 33 + app/http/middlewares/server_recovery.go | 50 + app/http/middlewares/sign_verify.go | 92 ++ app/http/middlewares/tracer.go | 55 + app/http/routes/route.go | 112 ++ app/http/trace/trace.go | 42 + app/jobs/basejob/base_job.go | 66 + app/jobs/kernel.go | 56 + app/jobs/test.go | 24 + app/models/bannermodel/banner.go | 57 + app/models/bannermodel/banner_test.go | 54 + app/models/merchant/merchant.go | 117 ++ app/models/orders/orders.go | 277 ++++ app/models/product/product.go | 117 ++ app/models/whitelist/whitelist.go | 134 ++ app/services/bannerservice/banner.go | 25 + app/services/merchant/merchant.go | 48 + app/services/orders/orders.go | 76 + app/services/product/product.go | 48 + app/services/transfersys/transfersys.go | 39 + app/services/transfersys/transfersys_rdb.go | 72 + app/services/whitelist/whitelist.go | 48 + app/services/zhiliansys/zhiliansys.go | 42 + app/utils/.gitkeep | 0 app/utils/httpclient/httpclient.go | 190 +++ app/utils/metric/reporter.go | 177 +++ app/utils/rdbmq/rdbmq.go | 84 ++ bootstrap/bootstrap.go | 63 + build/bin/.gitignore | 2 + build/shell/build.sh | 9 + build/sql/db.sql | 38 + config/config.go | 73 + config/option.go | 45 + docs/docs.go | 48 + go.mod | 24 + go.sum | 1322 +++++++++++++++++ logs/.gitignore | 2 + main.go | 122 ++ 71 files changed, 5800 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/.DS_Store create mode 100644 app/caches/bannerlistcache/banner_list.go create mode 100644 app/caches/bannerlistcache/banner_list_test.go create mode 100644 app/caches/cache_key.go create mode 100644 app/console/command.go create mode 100644 app/console/kernel.go create mode 100644 app/console/test.go create mode 100644 app/constants/common/common.go create mode 100644 app/constants/errorcode/error_code.go create mode 100644 app/constants/logtype/log_type.go create mode 100644 app/http/controllers/base.go create mode 100644 app/http/controllers/merchant/merchant.go create mode 100644 app/http/controllers/orders/orders.go create mode 100644 app/http/controllers/product/product.go create mode 100644 app/http/controllers/test.go create mode 100644 app/http/controllers/transfersys/transfersys.go create mode 100644 app/http/controllers/whitelist/whitelist.go create mode 100644 app/http/controllers/zhiliansys/zhiliansys.go create mode 100644 app/http/entities/merchant/merchant.go create mode 100644 app/http/entities/orders/orders.go create mode 100644 app/http/entities/product/product.go create mode 100644 app/http/entities/test.go create mode 100644 app/http/entities/transfersys/transfersys.go create mode 100644 app/http/entities/whitelist/whitelist.go create mode 100644 app/http/entities/zhiliansys/zhiliansys.go create mode 100644 app/http/formatters/bannerformatter/banner.go create mode 100644 app/http/formatters/bannerformatter/banner_test.go create mode 100644 app/http/metric/metric.go create mode 100644 app/http/middlewares/ip_verify.go create mode 100644 app/http/middlewares/metric.go create mode 100644 app/http/middlewares/request_log.go create mode 100644 app/http/middlewares/server_recovery.go create mode 100644 app/http/middlewares/sign_verify.go create mode 100644 app/http/middlewares/tracer.go create mode 100644 app/http/routes/route.go create mode 100644 app/http/trace/trace.go create mode 100644 app/jobs/basejob/base_job.go create mode 100644 app/jobs/kernel.go create mode 100644 app/jobs/test.go create mode 100644 app/models/bannermodel/banner.go create mode 100644 app/models/bannermodel/banner_test.go create mode 100644 app/models/merchant/merchant.go create mode 100644 app/models/orders/orders.go create mode 100644 app/models/product/product.go create mode 100644 app/models/whitelist/whitelist.go create mode 100644 app/services/bannerservice/banner.go create mode 100644 app/services/merchant/merchant.go create mode 100644 app/services/orders/orders.go create mode 100644 app/services/product/product.go create mode 100644 app/services/transfersys/transfersys.go create mode 100644 app/services/transfersys/transfersys_rdb.go create mode 100644 app/services/whitelist/whitelist.go create mode 100644 app/services/zhiliansys/zhiliansys.go create mode 100644 app/utils/.gitkeep create mode 100644 app/utils/httpclient/httpclient.go create mode 100644 app/utils/metric/reporter.go create mode 100644 app/utils/rdbmq/rdbmq.go create mode 100644 bootstrap/bootstrap.go create mode 100644 build/bin/.gitignore create mode 100644 build/shell/build.sh create mode 100644 build/sql/db.sql create mode 100644 config/config.go create mode 100644 config/option.go create mode 100644 docs/docs.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 logs/.gitignore create mode 100644 main.go diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8b93b8c --- /dev/null +++ b/.env.example @@ -0,0 +1,62 @@ +# toml配置文件 +# Wiki:https://github.com/toml-lang/toml +ServiceName = "snow" +Debug = true +Env = "local" # local-本地 develop-开发 beta-预发布 production-线上 +PrometheusCollectEnable = true +SkyWalkingOapServer = "127.0.0.1:11800" + +[Log] +Handler = "file" +Dir = "./logs" +Level = "info" + +[Db] +Driver = "mysql" + +[Db.Option] +MaxConns = 128 +MaxIdle = 32 +IdleTimeout = 180 # second +Charset = "utf8mb4" +ConnectTimeout = 3 # second + +[Db.Master] +Host = "127.0.0.1" +Port = 3306 +User = "root" +Password = "123456" +DBName = "test" + +[[Db.Slaves]] # 支持多个从库 +Host = "127.0.0.1" +Port = 3306 +User = "root" +Password = "123456" +DBName = "test" + +[Api] +Host = "0.0.0.0" +Port = 8080 + +[Cache] +Driver = "redis" + +[Redis.Master] +Host = "127.0.0.1" +Port = 6379 +#Password = "" +#DB = 0 + +#[Redis.Option] +#MaxIdle = 64 +#MaxConns = 256 +#IdleTimeout = 180 # second +#ConnectTimeout = 1 +#ReadTimeout = 1 +#WriteTimeout = 1 + +[AliMns] +Url = "" +AccessKeyId = "" +AccessKeySecret = "" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7dbf37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.idea +/vendor +/.env +!/.env.example diff --git a/README.md b/README.md new file mode 100644 index 0000000..82b5fa4 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +## Snow +Snow是一套简单易用的Go语言业务框架,整体逻辑设计简洁,支持HTTP服务、队列调度和任务调度等常用业务场景模式。 + +## Quick start + +### Build +sh build/shell/build.sh + +### Run +```shell +1. build/bin/snow -a api #启动Api服务 +2. build/bin/snow -a cron #启动Cron定时任务服务 +3. build/bin/snow -a job #启动队列调度服务 +4. build/bin/snow -a command -m test #执行名称为test的脚本任务 +``` + +## Documents + +- [项目地址](https://github.com/qit-team/snow) +- [中文文档](https://github.com/qit-team/snow/wiki) +- [changelog](https://github.com/qit-team/snow/blob/master/CHANGLOG.md) +- [xorm](http://gobook.io/read/github.com/go-xorm/manual-zh-CN/) diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ac5007ab7074f9cbc9915be326d9c3b018216b83 GIT binary patch literal 6148 zcmeH~F$w}f3`G;&V!>uh%V|7-HyA`uuotipw2)Oqt>@_eWP;#oEh0aX{7Ghl>??LQ zBBHC?ZYk1`}A&B+l9z-pOb`wkae)0wh2JBtQZ` zM8NKCSUaoANCG540#5?=en@cB99pXSs{_GD0BD1<8`eHcK$8`qIkZ$o1*S1QXtb)2 zAy)TxXo_<=v{bF_qA`4E{Il8=1Jl|rnvlRWyD*Rd35*C#Yu?%Yzkz?6|3@uMNq_|Y zjDR-le!ap&#ohY$cvjy=)z$?L_2URH9|1`0DxSmLu%B!J&7q|#DlmQsI0gn1_$q-1 D5`_}e literal 0 HcmV?d00001 diff --git a/app/caches/bannerlistcache/banner_list.go b/app/caches/bannerlistcache/banner_list.go new file mode 100644 index 0000000..d239148 --- /dev/null +++ b/app/caches/bannerlistcache/banner_list.go @@ -0,0 +1,34 @@ +package bannerlistcache + +import ( + "sync" + + "com.snow.auto_monitor/app/caches" + + "github.com/qit-team/snow-core/cache" +) + +const ( + prefix = caches.BannerList //缓存key的前缀 +) + +var ( + instance *bannerListCache + once sync.Once +) + +type bannerListCache struct { + cache.BaseCache +} + +//单例模式 +func GetInstance() *bannerListCache { + once.Do(func() { + instance = new(bannerListCache) + instance.Prefix = prefix + //instance.DiName = redis.SingletonMain //设置缓存依赖的实例别名 + //instance.DriverType = cache.DriverTypeRedis //设置缓存驱动的类型,默认redis + //instance.SeTTL(86400) 设置默认缓存时间 默认86400 + }) + return instance +} diff --git a/app/caches/bannerlistcache/banner_list_test.go b/app/caches/bannerlistcache/banner_list_test.go new file mode 100644 index 0000000..2fd0215 --- /dev/null +++ b/app/caches/bannerlistcache/banner_list_test.go @@ -0,0 +1,59 @@ +package bannerlistcache + +import ( + "context" + "fmt" + "testing" + + "com.snow.auto_monitor/config" + + "github.com/qit-team/snow-core/cache" + _ "github.com/qit-team/snow-core/cache/rediscache" + "github.com/qit-team/snow-core/redis" +) + +func init() { + //加载配置文件 + conf, err := config.Load("../../../.env") + if err != nil { + fmt.Println(err) + } + + //注册redis类 + err = redis.Pr.Register(cache.DefaultDiName, conf.Redis) + if err != nil { + fmt.Println(err) + } +} + +func Test_GetMulti(t *testing.T) { + ctx := context.TODO() + cache := GetInstance() + res, _ := cache.Set(ctx, "1000", "a") + if res != true { + t.Errorf("set key:%s is error", "1000") + } + + keys := []string{"1000", "-2000", "9999"} + cacheList, err := cache.GetMulti(ctx, keys...) + if err != nil { + t.Errorf("getMulti error:%s", err.Error()) + } + fmt.Println(cacheList) + i := 0 + for k, v := range cacheList { + i++ + if k == "1000" { + if v != "a" { + t.Errorf("value of key:%s is error %v", k, v) + } + } else { + if v != "" { + t.Errorf("value of key:%s is error %v", k, v) + } + } + } + if i != len(keys) { + t.Errorf("count of cache key is error: %d", i) + } +} diff --git a/app/caches/cache_key.go b/app/caches/cache_key.go new file mode 100644 index 0000000..560c59e --- /dev/null +++ b/app/caches/cache_key.go @@ -0,0 +1,8 @@ +package caches + +//缓存前缀key,不同的业务使用不同的前缀,避免了业务之间的重用冲突 +const ( + Cookie = "ck:" + Copy = "cp:" + BannerList = "bl:" +) diff --git a/app/console/command.go b/app/console/command.go new file mode 100644 index 0000000..e312c14 --- /dev/null +++ b/app/console/command.go @@ -0,0 +1,9 @@ +package console + +import ( + "github.com/qit-team/snow-core/command" +) + +func RegisterCommand(c *command.Command) { + c.AddFunc("test", test) +} diff --git a/app/console/kernel.go b/app/console/kernel.go new file mode 100644 index 0000000..23f081d --- /dev/null +++ b/app/console/kernel.go @@ -0,0 +1,15 @@ +package console + +import ( + "github.com/robfig/cron" +) + +/** + * 配置执行计划 + * @wiki https://godoc.org/github.com/robfig/cron + */ +func RegisterSchedule(c *cron.Cron) { + //c.AddFunc("0 30 * * * *", test) + //c.AddFunc("@hourly", test) + c.AddFunc("@every 10s", test) +} diff --git a/app/console/test.go b/app/console/test.go new file mode 100644 index 0000000..1319e62 --- /dev/null +++ b/app/console/test.go @@ -0,0 +1,7 @@ +package console + +import "fmt" + +func test() { + fmt.Println("run test") +} diff --git a/app/constants/common/common.go b/app/constants/common/common.go new file mode 100644 index 0000000..805d0c7 --- /dev/null +++ b/app/constants/common/common.go @@ -0,0 +1 @@ +package common diff --git a/app/constants/errorcode/error_code.go b/app/constants/errorcode/error_code.go new file mode 100644 index 0000000..b68da4a --- /dev/null +++ b/app/constants/errorcode/error_code.go @@ -0,0 +1,37 @@ +package errorcode + +const ( + //成功 + Success = 200 + + //参数错误 + ParamError = 400 + + //未经授权 + NotAuth = 401 + + //请求被禁止 + Forbidden = 403 + + //找不到页面 + NotFound = 404 + + //系统错误 + SystemError = 500 +) + +var MsgEN = map[int]string{ + Success: "success", + ParamError: "param error", + NotAuth: "not authorized", + Forbidden: "forbidden", + NotFound: "not found", + SystemError: "system error", +} + +func GetMsg(code int) string { + if msg, ok := MsgEN[code]; ok { + return msg + } + return "" +} diff --git a/app/constants/logtype/log_type.go b/app/constants/logtype/log_type.go new file mode 100644 index 0000000..02852f2 --- /dev/null +++ b/app/constants/logtype/log_type.go @@ -0,0 +1,7 @@ +package logtype + +const ( + Message = "message" + GoPanic = "go.panic" + HTTP = "http" +) diff --git a/app/http/controllers/base.go b/app/http/controllers/base.go new file mode 100644 index 0000000..e98c8b4 --- /dev/null +++ b/app/http/controllers/base.go @@ -0,0 +1,108 @@ +package controllers + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + + "com.snow.auto_monitor/app/constants/errorcode" + + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" + "gopkg.in/go-playground/validator.v9" +) + +/** + * 成功时返回 + */ +func Success(c *gin.Context, data interface{}) { + c.JSON(http.StatusOK, gin.H{ + "code": errorcode.Success, + "message": "ok", + "request_uri": c.Request.URL.Path, + "data": data, + }) + c.Abort() +} + +/** + * 成功时返回 + */ +func SuccessWithList(c *gin.Context, data interface{}, total int64) { + c.JSON(http.StatusOK, gin.H{ + "code": errorcode.Success, + "message": "ok", + "request_uri": c.Request.URL.Path, + "data": gin.H{ + "list": data, + "total": total, + }, + }) + c.Abort() +} + +/** + * 失败时返回 + */ +func Error(c *gin.Context, code int, msg ...string) { + message := "" + if len(msg) > 0 { + message = msg[0] + } else { + message = errorcode.GetMsg(code) + } + + c.JSON(http.StatusOK, gin.H{ + "code": code, + "message": message, + "request_uri": c.Request.URL.Path, + "data": make(map[string]string), + }) + c.Abort() +} + +func Error404(c *gin.Context) { + Error(c, errorcode.NotFound, "路由不存在") +} + +func Error500(c *gin.Context) { + Error(c, errorcode.SystemError) +} + +type HTTPError struct { + Code int `json:"code" example:"400"` + Message string `json:"message" example:"status bad request"` +} + +/** + * 将请求的body转换为request数据结构 + * @param c + * @param request 传入request数据结构的指针 如 new(TestRequest) + */ +func GenRequest(c *gin.Context, request interface{}) (err error) { + body, err := ReadBody(c) + if err != nil { + return + } + err = json.Unmarshal(body, request) + if err == nil { + validate := validator.New() + errValidate := validate.Struct(request) + if errValidate != nil { + logger.Error(c, "param_validator_exception:"+c.Request.URL.Path, errValidate) + return errValidate + } + } + return err +} + +// 重复读取body +func ReadBody(c *gin.Context) (body []byte, err error) { + body, err = ioutil.ReadAll(c.Request.Body) + if err != nil { + return + } + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + return +} diff --git a/app/http/controllers/merchant/merchant.go b/app/http/controllers/merchant/merchant.go new file mode 100644 index 0000000..e5fd52c --- /dev/null +++ b/app/http/controllers/merchant/merchant.go @@ -0,0 +1,164 @@ +package merchant + +import ( + "time" + + "com.snow.auto_monitor/app/constants/errorcode" + common "com.snow.auto_monitor/app/http/controllers" + merEnt "com.snow.auto_monitor/app/http/entities/merchant" + merMod "com.snow.auto_monitor/app/models/merchant" + merServ "com.snow.auto_monitor/app/services/merchant" + "github.com/gin-gonic/gin" +) + +func GetById(c *gin.Context) { + request := new(merEnt.GetListByIdReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + res, err := merServ.GetById(request.Id) + if err != nil { + common.Error500(c) + return + } + + var response *merEnt.GetListByIdResp = nil + + if res != nil { + + response = &merEnt.GetListByIdResp{ + Id: res.Id, + Name: res.Name, + CreatedAt: res.CreatedAt.Format(time.RFC3339), + } + } + + common.Success(c, response) +} + +func Search(c *gin.Context) { + request := new(merEnt.SearcgReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + start, end := "", "" + if len(request.CreatedAt) > 1 { + start = request.CreatedAt[0] + end = request.CreatedAt[1] + } + res, err := merServ.Search( + request.Id, + request.Name, + start, + end, + request.PageSize, + request.PageNum, + ) + if err != nil { + common.Error500(c) + return + } + + var response []*merEnt.SearchResp = nil + + if len(res) > 0 { + for _, item := range res { + response = append(response, &merEnt.SearchResp{ + Id: item.Id, + Name: item.Name, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + }) + } + } + + total, err := merServ.CountAll( + request.Id, + request.Name, + start, + end) + if err != nil { + common.Error500(c) + return + } + + common.SuccessWithList(c, response, total) +} + +func Create(c *gin.Context) { + request := new(merEnt.CreateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + merchant := &merMod.Merchant{ + Name: request.Name, + Key: request.Key, + } + + affected, err := merServ.Create(merchant) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &merEnt.CreateResp{ + Id: merchant.Id, + } + + common.Success(c, response) +} + +func Update(c *gin.Context) { + request := new(merEnt.UpdateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + merchant := &merMod.Merchant{ + Id: request.Id, + Name: request.Name, + Key: request.Key, + } + + affected, err := merServ.Update(merchant) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &merEnt.UpdateResp{ + Id: merchant.Id, + } + + common.Success(c, response) +} + +func Delete(c *gin.Context) { + request := new(merEnt.DeleteReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + affected, err := merServ.Delete(request.Id) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &merEnt.DeleteResp{ + Id: request.Id, + } + + common.Success(c, response) +} diff --git a/app/http/controllers/orders/orders.go b/app/http/controllers/orders/orders.go new file mode 100644 index 0000000..00d6c9e --- /dev/null +++ b/app/http/controllers/orders/orders.go @@ -0,0 +1,215 @@ +package orders + +import ( + "time" + + "com.snow.auto_monitor/app/constants/errorcode" + common "com.snow.auto_monitor/app/http/controllers" + orderEnt "com.snow.auto_monitor/app/http/entities/orders" + orderMod "com.snow.auto_monitor/app/models/orders" + orderServ "com.snow.auto_monitor/app/services/orders" + "github.com/gin-gonic/gin" +) + +func GetById(c *gin.Context) { + request := new(orderEnt.GetListByIdReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + item, err := orderServ.GetById(request.Id) + if err != nil { + common.Error500(c) + return + } + + var response *orderEnt.GetListByIdResp = nil + + if item != nil { + response = &orderEnt.GetListByIdResp{ + Id: item.Id, + OrderNo: item.OrderNo, + MerchantId: item.MerchantId, + ProductId: item.ProductId, + OutTradeNo: item.OutTradeNo, + RechargeAccount: item.RechargeAccount, + AccountType: item.AccountType, + Number: item.Number, + NotifyUrl: item.NotifyUrl, + ExtendParameter: item.ExtendParameter, + Status: item.Status, + TransferStatus: item.TransferStatus, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + } + } + + common.Success(c, response) +} + +func Search(c *gin.Context) { + request := new(orderEnt.SearchReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + start, end := "", "" + if len(request.CreatedAt) > 1 { + start = request.CreatedAt[0] + end = request.CreatedAt[1] + } + res, err := orderServ.Search( + request.Id, + request.OrderNo, + request.MerchantId, + request.ProductId, + request.OutTradeNo, + request.RechargeAccount, + request.AccountType, + request.Status, + request.TransferStatus, + start, + end, + request.PageSize, + request.PageNum, + ) + if err != nil { + common.Error500(c) + return + } + + var response []*orderEnt.SearchResp = nil + + if len(res) > 0 { + for _, item := range res { + response = append(response, &orderEnt.SearchResp{ + Id: item.Id, + OrderNo: item.OrderNo, + MerchantId: item.MerchantId, + ProductId: item.ProductId, + OutTradeNo: item.OutTradeNo, + RechargeAccount: item.RechargeAccount, + AccountType: item.AccountType, + Number: item.Number, + NotifyUrl: item.NotifyUrl, + ExtendParameter: item.ExtendParameter, + Status: item.Status, + TransferStatus: item.TransferStatus, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + }) + } + } + + total, err := orderServ.CountAll( + request.Id, + request.OrderNo, + request.MerchantId, + request.ProductId, + request.OutTradeNo, + request.RechargeAccount, + request.AccountType, + request.Status, + request.TransferStatus, + start, + end, + ) + if err != nil { + common.Error500(c) + return + } + + common.SuccessWithList(c, response, total) +} + +func Create(c *gin.Context) { + request := new(orderEnt.CreateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + orders := &orderMod.Orders{ + OrderNo: "12321", + MerchantId: request.MerchantId, + ProductId: request.ProductId, + OutTradeNo: request.OutTradeNo, + RechargeAccount: request.RechargeAccount, + AccountType: request.AccountType, + Number: request.Number, + NotifyUrl: request.NotifyUrl, + ExtendParameter: request.ExtendParameter, + Status: request.Status, + TransferStatus: request.TransferStatus, + } + + affected, err := orderServ.Create(orders) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &orderEnt.CreateResp{ + Id: orders.Id, + } + + common.Success(c, response) +} + +func Update(c *gin.Context) { + request := new(orderEnt.UpdateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + orders := &orderMod.Orders{ + Id: request.Id, + MerchantId: request.MerchantId, + ProductId: request.ProductId, + OutTradeNo: request.OutTradeNo, + RechargeAccount: request.RechargeAccount, + AccountType: request.AccountType, + Number: request.Number, + NotifyUrl: request.NotifyUrl, + ExtendParameter: request.ExtendParameter, + Status: request.Status, + TransferStatus: request.TransferStatus, + } + + affected, err := orderServ.Update(orders) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &orderEnt.UpdateResp{ + Id: orders.Id, + } + + common.Success(c, response) +} + +func Delete(c *gin.Context) { + request := new(orderEnt.DeleteReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + affected, err := orderServ.Delete(request.Id) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &orderEnt.DeleteResp{ + Id: request.Id, + } + + common.Success(c, response) +} diff --git a/app/http/controllers/product/product.go b/app/http/controllers/product/product.go new file mode 100644 index 0000000..b4cdac8 --- /dev/null +++ b/app/http/controllers/product/product.go @@ -0,0 +1,167 @@ +package product + +import ( + "time" + + "com.snow.auto_monitor/app/constants/errorcode" + common "com.snow.auto_monitor/app/http/controllers" + proEnt "com.snow.auto_monitor/app/http/entities/product" + proMod "com.snow.auto_monitor/app/models/product" + proServ "com.snow.auto_monitor/app/services/product" + "github.com/gin-gonic/gin" +) + +func GetById(c *gin.Context) { + request := new(proEnt.GetListByIdReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + res, err := proServ.GetById(request.Id) + if err != nil { + common.Error500(c) + return + } + + var response *proEnt.GetListByIdResp = nil + + if res != nil { + + response = &proEnt.GetListByIdResp{ + Id: res.Id, + Name: res.Name, + Price: res.Price, + CreatedAt: res.CreatedAt.Format(time.RFC3339), + } + } + + common.Success(c, response) +} + +func Search(c *gin.Context) { + request := new(proEnt.SearcgReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + start, end := "", "" + if len(request.CreatedAt) > 1 { + start = request.CreatedAt[0] + end = request.CreatedAt[1] + } + res, err := proServ.Search( + request.Id, + request.Name, + start, + end, + request.PageSize, + request.PageNum, + ) + if err != nil { + common.Error500(c) + return + } + + var response []*proEnt.SearchResp = nil + + if len(res) > 0 { + for _, item := range res { + response = append(response, &proEnt.SearchResp{ + Id: item.Id, + Name: item.Name, + Price: item.Price, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + }) + } + } + + total, err := proServ.CountAll( + request.Id, + request.Name, + start, + end, + ) + if err != nil { + common.Error500(c) + return + } + + common.SuccessWithList(c, response, total) +} + +func Create(c *gin.Context) { + request := new(proEnt.CreateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + product := &proMod.Product{ + Name: request.Name, + Price: request.Price, + } + + affected, err := proServ.Create(product) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &proEnt.CreateResp{ + Id: product.Id, + } + + common.Success(c, response) +} + +func Update(c *gin.Context) { + request := new(proEnt.UpdateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + product := &proMod.Product{ + Id: request.Id, + Name: request.Name, + Price: request.Price, + } + + affected, err := proServ.Update(product) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &proEnt.UpdateResp{ + Id: product.Id, + } + + common.Success(c, response) +} + +func Delete(c *gin.Context) { + request := new(proEnt.DeleteReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + affected, err := proServ.Delete(request.Id) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &proEnt.DeleteResp{ + Id: request.Id, + } + + common.Success(c, response) +} diff --git a/app/http/controllers/test.go b/app/http/controllers/test.go new file mode 100644 index 0000000..1a676a9 --- /dev/null +++ b/app/http/controllers/test.go @@ -0,0 +1,113 @@ +package controllers + +import ( + "strconv" + "time" + + "com.snow.auto_monitor/app/constants/errorcode" + "com.snow.auto_monitor/app/http/entities" + "com.snow.auto_monitor/app/http/formatters/bannerformatter" + "com.snow.auto_monitor/app/services/bannerservice" + "com.snow.auto_monitor/app/utils/httpclient" + + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" +) + +// hello示例 +func HandleHello(c *gin.Context) { + logger.Debug(c, "hello", "test message") + client := httpclient.NewClient(c.Request.Context()) + resposne, err := client.R().Get("https://www.baidu.com") + if err != nil { + Error(c, errorcode.SystemError, err.Error()) + return + } + logger.Info(c, "HandleHello", resposne.String()) + Success(c, "hello world!") + return +} + +// request和response的示例 +// HandleTest godoc +// @Summary request和response的示例 +// @Description request和response的示例 +// @Tags snow +// @Accept json +// @Produce json +// @Param test body entities.TestRequest true "test request" +// @Success 200 {array} entities.TestResponse +// @Failure 400 {object} controllers.HTTPError +// @Failure 404 {object} controllers.HTTPError +// @Failure 500 {object} controllers.HTTPError +// @Router /test [post] +func HandleTest(c *gin.Context) { + request := new(entities.TestRequest) + err := GenRequest(c, request) + if err != nil { + Error(c, errorcode.ParamError) + return + } + + response := new(entities.TestResponse) + response.Name = request.Name + response.Url = request.Url + response.Id = time.Now().Unix() + Success(c, response) + return +} + +// 测试数据库服务示例 +func GetBannerList(c *gin.Context) { + pageStr := c.Query("page") + limitStr := c.DefaultQuery("limit", "20") + + page, _ := strconv.Atoi(pageStr) + if page <= 0 { + page = 1 + } + + limit, _ := strconv.Atoi(limitStr) + if limit <= 0 { + limit = 20 + } + + list, err := bannerservice.GetListByPid(1, limit, page) + if err != nil { + Error500(c) + return + } + + data := map[string]interface{}{ + "page": page, + "limit": limit, + "data": bannerformatter.FormatList(list), + } + + Success(c, data) +} + +// validator的示例 +// HandleTestValidator godoc +// @Summary HandleTestValidator的示例 +// @Description HandleTestValidator的示例 +// @Tags snow +// @Accept json +// @Produce json +// @Param testValidator body entities.TestValidatorRequest true "example of validator" +// @Success 200 {array} entities.TestValidatorRequest +// @Failure 400 {object} controllers.HTTPError +// @Failure 404 {object} controllers.HTTPError +// @Failure 500 {object} controllers.HTTPError +// @Router /test_validator [post] +func HandleTestValidator(c *gin.Context) { + request := new(entities.TestValidatorRequest) + err := GenRequest(c, request) + if err != nil { + Error(c, errorcode.ParamError) + return + } + + Success(c, request) + return +} diff --git a/app/http/controllers/transfersys/transfersys.go b/app/http/controllers/transfersys/transfersys.go new file mode 100644 index 0000000..8e124f6 --- /dev/null +++ b/app/http/controllers/transfersys/transfersys.go @@ -0,0 +1,82 @@ +package orders + +import ( + "time" + + common "com.snow.auto_monitor/app/http/controllers" + transEnt "com.snow.auto_monitor/app/http/entities/transfersys" + orderMod "com.snow.auto_monitor/app/models/orders" + ransServ "com.snow.auto_monitor/app/services/transfersys" + "github.com/gin-gonic/gin" +) + +func GetOrder(c *gin.Context) { + item, err := ransServ.ReadGroup() + if err != nil { + common.Error(c, 400, err.Error()) + return + } + + var response *transEnt.GetOrderResp = nil + + if item != nil { + response = &transEnt.GetOrderResp{ + Id: item.Id, + OrderNo: item.OrderNo, + MerchantId: item.MerchantId, + ProductId: item.ProductId, + OutTradeNo: item.OutTradeNo, + RechargeAccount: item.RechargeAccount, + AccountType: item.AccountType, + Number: item.Number, + NotifyUrl: item.NotifyUrl, + ExtendParameter: item.ExtendParameter, + Status: item.Status, + TransferStatus: item.TransferStatus, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + } + } else { + c.JSON(500, gin.H{ + "code": 500, + "message": "no data", + }) + return + } + + common.Success(c, response) +} + +func FinishOrder(c *gin.Context) { + request := new(transEnt.FinishOrderReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, 400, err.Error()) + return + } + + status := 5 + if request.Result == "success" { + status = 1 + } else if request.Result == "fail" { + status = 4 + } + + orders := &orderMod.Orders{ + Id: request.Id, + ProductId: request.ProductId, + MerchantId: request.MerchantId, + TransferStatus: int64(status), + } + + affected, err := ransServ.FinishOrder(orders) + if err != nil || affected == 0 { + common.Error(c, 400, "无法修改已完成订单") + return + } + + response := &transEnt.FinishOrderResp{ + Id: orders.Id, + } + + common.Success(c, response) +} diff --git a/app/http/controllers/whitelist/whitelist.go b/app/http/controllers/whitelist/whitelist.go new file mode 100644 index 0000000..3388cda --- /dev/null +++ b/app/http/controllers/whitelist/whitelist.go @@ -0,0 +1,169 @@ +package whitelist + +import ( + "time" + + "com.snow.auto_monitor/app/constants/errorcode" + common "com.snow.auto_monitor/app/http/controllers" + wlCon "com.snow.auto_monitor/app/http/entities/whitelist" + wlMod "com.snow.auto_monitor/app/models/whitelist" + wlServ "com.snow.auto_monitor/app/services/whitelist" + "github.com/gin-gonic/gin" +) + +func GetById(c *gin.Context) { + request := new(wlCon.GetListByIdReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + res, err := wlServ.GetById(request.Id) + if err != nil { + common.Error500(c) + return + } + + var response *wlCon.GetListByIdResp = nil + + if res != nil { + + response = &wlCon.GetListByIdResp{ + Id: res.Id, + MerchantId: res.MerchantId, + Ip: res.Ip, + CreatedAt: res.CreatedAt.Format(time.RFC3339), + } + } + + common.Success(c, response) +} + +func Search(c *gin.Context) { + request := new(wlCon.SearcgReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + start, end := "", "" + if len(request.CreatedAt) > 1 { + start = request.CreatedAt[0] + end = request.CreatedAt[1] + } + res, err := wlServ.Search( + request.Id, + request.MerchantId, + request.Ip, + start, + end, + request.PageSize, + request.PageNum, + ) + if err != nil { + common.Error500(c) + return + } + + var response []*wlCon.SearchResp = nil + + if len(res) > 0 { + for _, item := range res { + response = append(response, &wlCon.SearchResp{ + Id: item.Id, + MerchantId: item.MerchantId, + Ip: item.Ip, + CreatedAt: item.CreatedAt.Format(time.RFC3339), + }) + } + } + + total, err := wlServ.CountAll( + request.Id, + request.MerchantId, + request.Ip, + start, + end, + ) + if err != nil { + common.Error500(c) + return + } + + common.SuccessWithList(c, response, total) +} + +func Create(c *gin.Context) { + request := new(wlCon.CreateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + whitelist := &wlMod.Whitelist{ + MerchantId: request.MerchantId, + Ip: request.Ip, + } + + affected, err := wlServ.Create(whitelist) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &wlCon.CreateResp{ + Id: whitelist.Id, + } + + common.Success(c, response) +} + +func Update(c *gin.Context) { + request := new(wlCon.UpdateReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + whitelist := &wlMod.Whitelist{ + Id: request.Id, + MerchantId: request.MerchantId, + Ip: request.Ip, + } + + affected, err := wlServ.Update(whitelist) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &wlCon.UpdateResp{ + Id: whitelist.Id, + } + + common.Success(c, response) +} + +func Delete(c *gin.Context) { + request := new(wlCon.DeleteReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, errorcode.ParamError) + return + } + + affected, err := wlServ.Delete(request.Id) + if err != nil && affected > 0 { + common.Error500(c) + return + } + + response := &wlCon.DeleteResp{ + Id: request.Id, + } + + common.Success(c, response) +} diff --git a/app/http/controllers/zhiliansys/zhiliansys.go b/app/http/controllers/zhiliansys/zhiliansys.go new file mode 100644 index 0000000..beeac1a --- /dev/null +++ b/app/http/controllers/zhiliansys/zhiliansys.go @@ -0,0 +1,68 @@ +package orders + +import ( + common "com.snow.auto_monitor/app/http/controllers" + zhilianEnt "com.snow.auto_monitor/app/http/entities/zhiliansys" + orderMod "com.snow.auto_monitor/app/models/orders" + zhilianServ "com.snow.auto_monitor/app/services/zhiliansys" + "github.com/gin-gonic/gin" +) + +func GetByOutTradeNo(c *gin.Context) { + + request := new(zhilianEnt.GetByOutTradeNoReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, 400, err.Error()) + return + } + + item, err := zhilianServ.GetByOutTradeNo(request.OutTradeNo, request.MerchantId) + if err != nil { + common.Error(c, 400, err.Error()) + return + } + + var response *zhilianEnt.GetByOutTradeNoResp = nil + + if item != nil { + response = &zhilianEnt.GetByOutTradeNoResp{ + OutTradeNo: item.OutTradeNo, + Status: item.Status, + } + } + + common.Success(c, response) +} + +func CreateOrder(c *gin.Context) { + request := new(zhilianEnt.CreateOrderReq) + err := common.GenRequest(c, request) + if err != nil { + common.Error(c, 400, err.Error()) + return + } + + orders := &orderMod.Orders{ + OutTradeNo: request.OutTradeNo, + ProductId: request.ProductId, + RechargeAccount: request.RechargeAccount, + AccountType: request.AccountType, + Number: request.Number, + NotifyUrl: request.NotifyUrl, + ExtendParameter: request.ExtendParameter, + MerchantId: request.MerchantId, + } + + affected, err := zhilianServ.CreateOrder(orders) + if err != nil || affected == 0 { + common.Error(c, 400, "创建订单失败") + return + } + + response := &zhilianEnt.CreateOrderResp{ + OutTradeNo: orders.OutTradeNo, + } + + common.Success(c, response) +} diff --git a/app/http/entities/merchant/merchant.go b/app/http/entities/merchant/merchant.go new file mode 100644 index 0000000..85d4727 --- /dev/null +++ b/app/http/entities/merchant/merchant.go @@ -0,0 +1,52 @@ +package merchant + +type GetListByIdReq struct { + Id int64 `json:"id" validate:"required" example:"1"` +} + +type GetListByIdResp struct { + Id int64 `json:"id" example:"1"` + Name string `json:"name" example:"snow"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type SearcgReq struct { + Id int64 `json:"id"` + Name string `json:"name"` + CreatedAt []string `json:"created_at" form:"created_at"` + PageNum int `json:"page_num" form:"page" validate:"required"` + PageSize int `json:"page_size" form:"page_size" validate:"required"` +} + +type SearchResp struct { + Id int64 `json:"id" example:"1"` + Name string `json:"name" example:"snow"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type CreateReq struct { + Name string `json:"name" validate:"required"` + Key string `json:"key" validate:"required"` +} + +type CreateResp struct { + Id int64 `json:"id" example:"1"` +} + +type UpdateReq struct { + Id int64 `json:"id" validate:"required"` + Name string `json:"name"` + Key string `json:"key"` +} + +type UpdateResp struct { + Id int64 `json:"id" example:"1"` +} + +type DeleteReq struct { + Id int64 `json:"id" validate:"required"` +} + +type DeleteResp struct { + Id int64 `json:"id" example:"1"` +} diff --git a/app/http/entities/orders/orders.go b/app/http/entities/orders/orders.go new file mode 100644 index 0000000..895baf5 --- /dev/null +++ b/app/http/entities/orders/orders.go @@ -0,0 +1,95 @@ +package merchant + +type GetListByIdReq struct { + Id int64 `json:"id" validate:"required"` +} + +type GetListByIdResp struct { + Id int64 `json:"id" ` + OrderNo string `json:"order_no"` + MerchantId int64 `json:"merchant_id"` + ProductId int64 `json:"product_id"` + OutTradeNo string `json:"out_trade_no"` + RechargeAccount string `json:"recharge_account"` + AccountType int64 `json:"account_type"` + Number int64 `json:"number"` + NotifyUrl string `json:"notify_url"` + ExtendParameter string `json:"extend_parameter"` + Status int64 `json:"status"` + TransferStatus int64 `json:"transfer_status"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type SearchReq struct { + Id int64 `json:"id"` + OrderNo string `json:"order_no"` + MerchantId int64 `json:"merchant_id"` + ProductId int64 `json:"product_id"` + OutTradeNo string `json:"out_trade_no"` + RechargeAccount string `json:"recharge_account"` + AccountType int64 `json:"account_type"` + Status int64 `json:"status"` + TransferStatus int64 `json:"transfer_status"` + CreatedAt []string `json:"created_at" form:"created_at"` + PageNum int `json:"page_num" form:"page" validate:"required"` + PageSize int `json:"page_size" form:"page_size" validate:"required"` +} + +type SearchResp struct { + Id int64 `json:"id" ` + OrderNo string `json:"order_no"` + MerchantId int64 `json:"merchant_id"` + ProductId int64 `json:"product_id"` + OutTradeNo string `json:"out_trade_no"` + RechargeAccount string `json:"recharge_account"` + AccountType int64 `json:"account_type"` + Number int64 `json:"number"` + NotifyUrl string `json:"notify_url"` + ExtendParameter string `json:"extend_parameter"` + Status int64 `json:"status"` + TransferStatus int64 `json:"transfer_status"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type CreateReq struct { + MerchantId int64 `json:"merchant_id" validate:"required"` + ProductId int64 `json:"product_id" validate:"required"` + OutTradeNo string `json:"out_trade_no" validate:"required"` + RechargeAccount string `json:"recharge_account" validate:"required"` + AccountType int64 `json:"account_type" validate:"required"` + Number int64 `json:"number" validate:"required"` + NotifyUrl string `json:"notify_url" validate:"required"` + ExtendParameter string `json:"extend_parameter" validate:"required"` + Status int64 `json:"status" validate:"required"` + TransferStatus int64 `json:"transfer_status" validate:"required"` +} + +type CreateResp struct { + Id int64 `json:"id" ` +} + +type UpdateReq struct { + Id int64 `json:"id" validate:"required"` + MerchantId int64 `json:"merchant_id" validate:"required"` + ProductId int64 `json:"product_id" validate:"required"` + OutTradeNo string `json:"out_trade_no" validate:"required"` + RechargeAccount string `json:"recharge_account" validate:"required"` + AccountType int64 `json:"account_type" validate:"required"` + Number int64 `json:"number" validate:"required"` + NotifyUrl string `json:"notify_url" validate:"required"` + ExtendParameter string `json:"extend_parameter" validate:"required"` + Status int64 `json:"status" validate:"required"` + TransferStatus int64 `json:"transfer_status" validate:"required"` +} + +type UpdateResp struct { + Id int64 `json:"id" ` +} + +type DeleteReq struct { + Id int64 `json:"id" validate:"required"` +} + +type DeleteResp struct { + Id int64 `json:"id" ` +} diff --git a/app/http/entities/product/product.go b/app/http/entities/product/product.go new file mode 100644 index 0000000..b22caeb --- /dev/null +++ b/app/http/entities/product/product.go @@ -0,0 +1,54 @@ +package merchant + +type GetListByIdReq struct { + Id int64 `json:"id" validate:"required" example:"1"` +} + +type GetListByIdResp struct { + Id int64 `json:"id" example:"1"` + Name string `json:"name" example:"snow"` + Price int64 `json:"price" example:"100"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type SearcgReq struct { + Id int64 `json:"id"` + Name string `json:"name"` + CreatedAt []string `json:"created_at" form:"created_at"` + PageNum int `json:"page_num" form:"page" validate:"required"` + PageSize int `json:"page_size" form:"page_size" validate:"required"` +} + +type SearchResp struct { + Id int64 `json:"id" example:"1"` + Name string `json:"name" example:"snow"` + Price int64 `json:"price" example:"100"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type CreateReq struct { + Name string `json:"name" validate:"required"` + Price int64 `json:"price" validate:"required"` +} + +type CreateResp struct { + Id int64 `json:"id" example:"1"` +} + +type UpdateReq struct { + Id int64 `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + Price int64 `json:"price" validate:"required"` +} + +type UpdateResp struct { + Id int64 `json:"id" example:"1"` +} + +type DeleteReq struct { + Id int64 `json:"id" validate:"required"` +} + +type DeleteResp struct { + Id int64 `json:"id" example:"1"` +} diff --git a/app/http/entities/test.go b/app/http/entities/test.go new file mode 100644 index 0000000..dd94d86 --- /dev/null +++ b/app/http/entities/test.go @@ -0,0 +1,43 @@ +package entities + +//请求数据结构 +type TestRequest struct { + Name string `json:"name" example:"snow"` + Url string `json:"url" example:"github.com/qit-team/snow"` +} + +//返回数据结构 +type TestResponse struct { + Id int64 `json:"id" example:"1"` + Name string `json:"name" example:"snow"` + Url string `json:"url" example:"github.com/qit-team/snow"` +} + +/* + * validator.v9文档 + * 地址https://godoc.org/gopkg.in/go-playground/validator.v9 + * 列了几个大家可能会用到的,如有遗漏,请看上面文档 + */ + +//请求数据结构 +type TestValidatorRequest struct { + //tips,因为组件required不管是没传值或者传 0 or "" 都通过不了,但是如果用指针类型,那么0就是0,而nil无法通过校验 + Id *int64 `json:"id" validate:"required" example:"1"` + Age int `json:"age" validate:"required,gte=0,lte=130" example:"20"` + Name *string `json:"name" validate:"required" example:"snow"` + Email string `json:"email" validate:"required,email" example:"snow@github.com"` + Url string `json:"url" validate:"required" example:"github.com/qit-team/snow"` + Mobile string `json:"mobile" validate:"required" example:"snow"` + RangeNum int `json:"range_num" validate:"max=10,min=1" example:"3"` + TestNum *int `json:"test_num" validate:"required,oneof=5 7 9" example:"7"` + Content *string `json:"content" example:"snow"` + Addresses []*Address `json:"addresses" validate:"required,dive,required" ` +} + +// Address houses a users address information +type Address struct { + Street string `json:"street" validate:"required" example:"huandaodonglu"` + City string `json:"city" validate:"required" example:"xiamen"` + Planet string `json:"planet" validate:"required" example:"snow"` + Phone string `json:"phone" validate:"required" example:"snow"` +} \ No newline at end of file diff --git a/app/http/entities/transfersys/transfersys.go b/app/http/entities/transfersys/transfersys.go new file mode 100644 index 0000000..7ed1cff --- /dev/null +++ b/app/http/entities/transfersys/transfersys.go @@ -0,0 +1,32 @@ +package merchant + +type GetOrderReq struct { +} + +type GetOrderResp struct { + Id int64 `json:"id" ` + OrderNo string `json:"order_no"` + MerchantId int64 `json:"merchant_id"` + ProductId int64 `json:"product_id"` + OutTradeNo string `json:"out_trade_no"` + RechargeAccount string `json:"recharge_account"` + AccountType int64 `json:"account_type"` + Number int64 `json:"number"` + NotifyUrl string `json:"notify_url"` + ExtendParameter string `json:"extend_parameter"` + Status int64 `json:"status"` + TransferStatus int64 `json:"transfer_status"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type FinishOrderReq struct { + Id int64 `json:"id" validate:"required"` + OrderNo string `json:"order_no" validate:"required"` + ProductId int64 `json:"product_id" validate:"required"` + MerchantId int64 `json:"merchant_id" validate:"required"` + Result string `json:"result" validate:"required"` +} + +type FinishOrderResp struct { + Id int64 `json:"id"` +} diff --git a/app/http/entities/whitelist/whitelist.go b/app/http/entities/whitelist/whitelist.go new file mode 100644 index 0000000..575791a --- /dev/null +++ b/app/http/entities/whitelist/whitelist.go @@ -0,0 +1,55 @@ +package merchant + +type GetListByIdReq struct { + Id int64 `json:"id" validate:"required" example:"1"` +} + +type GetListByIdResp struct { + Id int64 `json:"id"` + MerchantId int64 `json:"merchant_id"` + Ip string `json:"ip"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type SearcgReq struct { + Id int64 `json:"id"` + MerchantId int64 `json:"merchant_id"` + Ip string `json:"ip"` + CreatedAt []string `json:"created_at" form:"created_at"` + PageNum int `json:"page_num" form:"page" validate:"required"` + PageSize int `json:"page_size" form:"page_size" validate:"required"` +} + +type SearchResp struct { + Id int64 `json:"id"` + MerchantId int64 `json:"merchant_id"` + Ip string `json:"ip"` + CreatedAt string `json:"created_at" example:"2020-01-01 00:00:00"` +} + +type CreateReq struct { + MerchantId int64 `json:"merchant_id" validate:"required"` + Ip string `json:"ip" validate:"required"` +} + +type CreateResp struct { + Id int64 `json:"id" example:"1"` +} + +type UpdateReq struct { + Id int64 `json:"id" validate:"required"` + MerchantId int64 `json:"merchant_id" validate:"required"` + Ip string `json:"ip" validate:"required"` +} + +type UpdateResp struct { + Id int64 `json:"id" example:"1"` +} + +type DeleteReq struct { + Id int64 `json:"id" validate:"required"` +} + +type DeleteResp struct { + Id int64 `json:"id" example:"1"` +} diff --git a/app/http/entities/zhiliansys/zhiliansys.go b/app/http/entities/zhiliansys/zhiliansys.go new file mode 100644 index 0000000..ac3ae63 --- /dev/null +++ b/app/http/entities/zhiliansys/zhiliansys.go @@ -0,0 +1,31 @@ +package merchant + +type GetByOutTradeNoReq struct { + OutTradeNo string `json:"out_trade_no" validate:"required"` + MerchantId int64 `json:"merchant_id" validate:"required"` + TimeStamp int64 `json:"time_stamp" validate:"required"` + Sign string `json:"sign" validate:"required"` +} + +type GetByOutTradeNoResp struct { + Status int64 `json:"status"` + OutTradeNo string `json:"out_trade_no"` +} + +type CreateOrderReq struct { + OutTradeNo string `json:"out_trade_no" validate:"required"` + ProductId int64 `json:"product_id" validate:"required"` + RechargeAccount string `json:"recharge_account" validate:"required"` + AccountType int64 `json:"account_type" validate:"required"` + Number int64 `json:"number" validate:"required"` + NotifyUrl string `json:"notify_url" validate:"required"` + ExtendParameter string `json:"extend_parameter" validate:"required"` + + MerchantId int64 `json:"merchant_id" validate:"required"` + TimeStamp int64 `json:"time_stamp" validate:"required"` + Sign string `json:"sign" validate:"required"` +} + +type CreateOrderResp struct { + OutTradeNo string `json:"out_trade_no"` +} diff --git a/app/http/formatters/bannerformatter/banner.go b/app/http/formatters/bannerformatter/banner.go new file mode 100644 index 0000000..0f883a9 --- /dev/null +++ b/app/http/formatters/bannerformatter/banner.go @@ -0,0 +1,34 @@ +package bannerformatter + +import ( + "com.snow.auto_monitor/app/models/bannermodel" +) + +type BannerFormatter struct { + Id int `json:"id"` + Title string `json:"title"` + Img string `json:"image"` + Url string `json:"url"` +} + +func FormatList(bannerList []*bannermodel.Banner) (res []*BannerFormatter) { + res = make([]*BannerFormatter, len(bannerList)) + + for k, banner := range bannerList { + one := FormatOne(banner) + res[k] = one + } + + return res +} + +//单条消息的格式化, +func FormatOne(banner *bannermodel.Banner) (res *BannerFormatter) { + res = &BannerFormatter{ + Id: int(banner.Id), + Title: banner.Title, + Img: banner.ImageUrl, + Url: banner.Url, + } + return +} \ No newline at end of file diff --git a/app/http/formatters/bannerformatter/banner_test.go b/app/http/formatters/bannerformatter/banner_test.go new file mode 100644 index 0000000..e77157d --- /dev/null +++ b/app/http/formatters/bannerformatter/banner_test.go @@ -0,0 +1,45 @@ +package bannerformatter + +import ( + "testing" + + "com.snow.auto_monitor/app/models/bannermodel" +) + +func TesFormatOne(t *testing.T) { + a := &bannermodel.Banner{ + Id: 1, + Title: "test", + ImageUrl: "http://x/1.jpg", + Url: "http://x", + Status: "1", + } + b := FormatOne(a) + if b.Title != a.Title || b.Img != a.ImageUrl || b.Url != a.Url { + t.Error("FormatOne not same") + } +} + +func TesFormatList(t *testing.T) { + a := make([]*bannermodel.Banner, 2) + a[0] = &bannermodel.Banner{ + Id: 1, + Title: "test", + ImageUrl: "http://x1/1.jpg", + Url: "http://x1", + Status: "1", + } + a[1] = &bannermodel.Banner{ + Id: 2, + Title: "test2", + ImageUrl: "http://x/2.jpg", + Url: "http://x2", + Status: "2", + } + b := FormatList(a) + for k, v := range b { + if v.Title != a[k].Title || v.Img != a[k].ImageUrl || v.Url != a[k].Url { + t.Error("FormatList not same") + } + } +} diff --git a/app/http/metric/metric.go b/app/http/metric/metric.go new file mode 100644 index 0000000..8af899c --- /dev/null +++ b/app/http/metric/metric.go @@ -0,0 +1,77 @@ +package metric + +import ( + "net/http" + + "com.snow.auto_monitor/app/utils/metric" + + "github.com/prometheus/client_golang/prometheus" +) + +const ( + HOST = "host" + PATH = "path" // 路径 + METHOD = "method" // 方法 + CODE = "code" // 错误码 + + // metric + ALL_REQ_TOTAL_COUNT = "all_req_total_count" // 所有URL总请求数 + ALL_REQ_COST_TIME = "all_req_cost_time" // 所有URL请求耗时 + + REQ_TOTAL_COUNT = "req_total_count" // 每个URL总请求数 + REQ_COST_TIME = "req_cost_time" // 每个URL请求耗时 +) + +func init() { + metric.RegisterCollector(reqTotalCounter, reqCostTimeObserver, allReqTotalCounter, allReqCostTimeObserver) +} + +var ( + reqTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: REQ_TOTAL_COUNT, + }, []string{PATH, METHOD}) + + reqCostTimeObserver = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: REQ_COST_TIME, + Buckets: []float64{ + 100, + 200, + 500, + 1000, + 3000, + 5000, + }, + }, []string{PATH, METHOD}) + + allReqTotalCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: ALL_REQ_TOTAL_COUNT, + }, []string{HOST}) + + allReqCostTimeObserver = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: ALL_REQ_COST_TIME, + Buckets: []float64{ + 100, + 200, + 500, + 1000, + 3000, + 5000, + }, + }, []string{HOST}) +) + +func AddReqCount(req *http.Request) { + reqTotalCounter.WithLabelValues(req.URL.Path, req.Method).Inc() +} + +func CollectReqCostTime(req *http.Request, ms int64) { + reqCostTimeObserver.WithLabelValues(req.URL.Path, req.Method).Observe(float64(ms)) +} + +func AddAllReqCount(req *http.Request) { + allReqTotalCounter.WithLabelValues(req.Host).Inc() +} + +func CollectAllReqCostTime(req *http.Request, ms int64) { + allReqCostTimeObserver.WithLabelValues(req.Host).Observe(float64(ms)) +} diff --git a/app/http/middlewares/ip_verify.go b/app/http/middlewares/ip_verify.go new file mode 100644 index 0000000..cbd6a26 --- /dev/null +++ b/app/http/middlewares/ip_verify.go @@ -0,0 +1,26 @@ +package middlewares + +import ( + common "com.snow.auto_monitor/app/http/controllers" + wlmod "com.snow.auto_monitor/app/models/whitelist" + "github.com/gin-gonic/gin" +) + +func VerifyIp() gin.HandlerFunc { + return func(c *gin.Context) { + // 读取请求体 + reqIp := c.ClientIP() + //验证商户是否存在 + _, has, err := wlmod.GetInstance().GetByIp(reqIp) + if err != nil { + common.Error500(c) + c.Abort() + return + } + if !has { + common.Error(c, 400, "ip无权限访问") + c.Abort() + return + } + } +} diff --git a/app/http/middlewares/metric.go b/app/http/middlewares/metric.go new file mode 100644 index 0000000..2c4e331 --- /dev/null +++ b/app/http/middlewares/metric.go @@ -0,0 +1,22 @@ +package middlewares + +import ( + "time" + + "com.snow.auto_monitor/app/http/metric" + + "github.com/gin-gonic/gin" +) + +func CollectMetric() gin.HandlerFunc { + return func(ctx *gin.Context) { + start := time.Now() + ctx.Next() + dur := time.Now().Sub(start).Milliseconds() + + metric.AddAllReqCount(ctx.Request) + metric.CollectAllReqCostTime(ctx.Request, dur) + metric.AddReqCount(ctx.Request) + metric.CollectReqCostTime(ctx.Request, dur) + } +} diff --git a/app/http/middlewares/request_log.go b/app/http/middlewares/request_log.go new file mode 100644 index 0000000..59dd7e6 --- /dev/null +++ b/app/http/middlewares/request_log.go @@ -0,0 +1,33 @@ +package middlewares + +import ( + "bytes" + "io/ioutil" + "time" + + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" +) + +func RequestLog() gin.HandlerFunc { + return func(c *gin.Context) { + bodyBytes, err := ioutil.ReadAll(c.Request.Body) + if err != nil { + logger.Info(c, "LogRequset", "Failed to read request body:"+err.Error()) + c.Next() + return + } + // 将请求体重置,以便后续处理可以再次读取 + c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) + + infstr := "Date: " + time.Now().Format("2006-01-02 15:04:05") + " " + // 构建日志信息 + infstr += "Request: " + c.Request.Method + " " + c.Request.URL.Path + " " + infstr += "From: " + c.ClientIP() + " " + infstr += "Body: " + string(bodyBytes) + logger.Info(c, "LogRequset", infstr) + + // 调用下一个中间件 + c.Next() + } +} diff --git a/app/http/middlewares/server_recovery.go b/app/http/middlewares/server_recovery.go new file mode 100644 index 0000000..9c133b5 --- /dev/null +++ b/app/http/middlewares/server_recovery.go @@ -0,0 +1,50 @@ +package middlewares + +import ( + "encoding/json" + syslog "log" + "net/http/httputil" + "runtime/debug" + + "com.snow.auto_monitor/app/constants/logtype" + "com.snow.auto_monitor/config" + + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" +) + +func ServerRecovery() gin.HandlerFunc { + return func(c *gin.Context) { + + defer func() { + if err := recover(); err != nil { + httpRequest, _ := httputil.DumpRequest(c.Request, false) + msg := map[string]interface{}{ + "error": err, + "request": string(httpRequest), + "stack": string(debug.Stack()), + } + msgJson, _ := json.Marshal(msg) + logger.GetLogger().Error(string(msgJson), logtype.GoPanic, c) + + if config.IsDebug() { + //本地开发 debug 模式开启时输出错误信息到shell + syslog.Println(err) + } + + c.JSON(500, gin.H{ + "code": 500, + "msg": "system error", + "request_uri": c.Request.URL.Path, + "data": make(map[string]string), + }) + } + }() + + //before request + + c.Next() + + //after request + } +} diff --git a/app/http/middlewares/sign_verify.go b/app/http/middlewares/sign_verify.go new file mode 100644 index 0000000..3005cb2 --- /dev/null +++ b/app/http/middlewares/sign_verify.go @@ -0,0 +1,92 @@ +package middlewares + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "sort" + "strings" + + common "com.snow.auto_monitor/app/http/controllers" + mermod "com.snow.auto_monitor/app/models/merchant" + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" +) + +type Verify struct { + MerchantId int64 `json:"merchant_id" validate:"required"` + TimeStamp int64 `json:"time_stamp" validate:"required"` + Sign string `json:"sign" validate:"required"` +} + +func getMD5Hash(input string) string { + hash := md5.Sum([]byte(input)) + return hex.EncodeToString(hash[:]) +} + +func GenMD5Sign(data map[string]interface{}, secretKey string) string { + keys := make([]string, 0, len(data)) + for key := range data { + if key != "sign" && key != "Sign" { + keys = append(keys, key) + } + } + sort.Strings(keys) + + rawStr := "" + for _, key := range keys { + value := fmt.Sprintf("%v", data[key]) + if rawStr != "" { + rawStr += "&" + } + rawStr += fmt.Sprintf("%s=%s", key, value) + } + rawStr += "&key=" + secretKey + sign := strings.ToUpper(getMD5Hash(rawStr)) + return sign +} + +func VerifySign() gin.HandlerFunc { + return func(c *gin.Context) { + var data map[string]interface{} + body, err := common.ReadBody(c) + if err != nil { + common.Error(c, 400, err.Error()) + c.Abort() + return + } + err = json.Unmarshal(body, &data) + if err != nil { + common.Error(c, 400, err.Error()) + c.Abort() + return + } + if data["merchant_id"] == nil || data["time_stamp"] == nil || data["sign"] == nil { + common.Error(c, 400, "参数错误") + c.Abort() + return + } + //验证商户是否存在 + merchanId := int64(data["merchant_id"].(float64)) + merchant, has, err := mermod.GetInstance().GetById(merchanId) + if err != nil { + common.Error500(c) + c.Abort() + return + } + if !has { + common.Error(c, 400, "商户不存在") + c.Abort() + return + } + //验证签名是否正确 + hash := GenMD5Sign(data, merchant.Key) + logger.Info(c, "Sign", hash) + if hash != data["sign"] { + common.Error(c, 400, "签名错误") + c.Abort() + return + } + } +} diff --git a/app/http/middlewares/tracer.go b/app/http/middlewares/tracer.go new file mode 100644 index 0000000..dac7227 --- /dev/null +++ b/app/http/middlewares/tracer.go @@ -0,0 +1,55 @@ +package middlewares + +import ( + "fmt" + "strconv" + "time" + + "com.snow.auto_monitor/app/http/trace" + + "github.com/SkyAPM/go2sky" + "github.com/SkyAPM/go2sky/propagation" + v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent" + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/log/logger" +) + +const ( + componentIDGOHttpServer = 5004 +) + +func Trace() gin.HandlerFunc { + return func(c *gin.Context) { + tracer, err := trace.Tracer() + if err != nil { + logger.Error(c, "Trace", err.Error()) + c.Next() + return + } + r := c.Request + operationName := fmt.Sprintf("/%s%s", r.Method, r.URL.Path) + span, ctx, err := tracer.CreateEntrySpan(c, operationName, func() (string, error) { + // 从http头部捞取上一层的调用链信息, 当前使用v3版本的协议 + // https://github.com/apache/skywalking/blob/master/docs/en/protocols/Skywalking-Cross-Process-Propagation-Headers-Protocol-v3.md + return r.Header.Get(propagation.Header), nil + }) + if err != nil { + logger.Error(c, "Trace", err.Error()) + c.Next() + return + } + span.SetComponent(componentIDGOHttpServer) + // 可以自定义tag + span.Tag(go2sky.TagHTTPMethod, r.Method) + span.Tag(go2sky.TagURL, fmt.Sprintf("%s%s", r.Host, r.URL.Path)) + span.SetSpanLayer(v3.SpanLayer_Http) + c.Request = c.Request.WithContext(ctx) + c.Next() + code := c.Writer.Status() + if code >= 400 { + span.Error(time.Now(), fmt.Sprintf("Error on handling request, statusCode: %d", code)) + } + span.Tag(go2sky.TagStatusCode, strconv.Itoa(code)) + span.End() + } +} diff --git a/app/http/routes/route.go b/app/http/routes/route.go new file mode 100644 index 0000000..d02d1ea --- /dev/null +++ b/app/http/routes/route.go @@ -0,0 +1,112 @@ +package routes + +/** + * 配置路由 + */ +import ( + "com.snow.auto_monitor/app/http/controllers" + "com.snow.auto_monitor/app/http/middlewares" + "com.snow.auto_monitor/app/http/trace" + "com.snow.auto_monitor/app/utils/metric" + "com.snow.auto_monitor/config" + + merCon "com.snow.auto_monitor/app/http/controllers/merchant" + ordersCon "com.snow.auto_monitor/app/http/controllers/orders" + proCon "com.snow.auto_monitor/app/http/controllers/product" + transCon "com.snow.auto_monitor/app/http/controllers/transfersys" + wlCon "com.snow.auto_monitor/app/http/controllers/whitelist" + zlCon "com.snow.auto_monitor/app/http/controllers/zhiliansys" + "github.com/gin-gonic/gin" + "github.com/qit-team/snow-core/http/middleware" + "github.com/qit-team/snow-core/log/logger" + ginSwagger "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" +) + +// api路由配置 +func RegisterRoute(router *gin.Engine) { + //middleware: 服务错误处理 => 生成请求id => access log + router.Use(middlewares.ServerRecovery(), middleware.GenRequestId, middleware.GenContextKit, middleware.AccessLog(), middlewares.VerifyIp()) + + if config.GetConf().PrometheusCollectEnable && config.IsEnvEqual(config.ProdEnv) { + router.Use(middlewares.CollectMetric()) + metric.Init(metric.EnableRuntime(), metric.EnableProcess()) + metricHandler := metric.Handler() + router.GET("/metrics", func(ctx *gin.Context) { + metricHandler.ServeHTTP(ctx.Writer, ctx.Request) + }) + } + + if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) { + err := trace.InitTracer(config.GetConf().ServiceName, config.GetConf().SkyWalkingOapServer) + if err != nil { + logger.Error(nil, "InitTracer", err.Error()) + } else { + router.Use(middlewares.Trace()) + } + } + + router.NoRoute(controllers.Error404) + router.GET("/hello", controllers.HandleHello) + router.POST("/test", controllers.HandleTest) + router.POST("/test_validator", controllers.HandleTestValidator) + + //api版本 + v1 := router.Group("/v1") + { + v1.GET("/banner_list", controllers.GetBannerList) + } + + merchant := router.Group("/merchant") + { + merchant.POST("/get_by_id", merCon.GetById) + merchant.POST("/search", merCon.Search) + merchant.POST("/create", merCon.Create) + // merchant.POST("/update", merCon.Update) + // merchant.POST("/delete", merCon.Delete) + } + + orders := router.Group("/orders") + { + orders.POST("/get_by_id", ordersCon.GetById) + orders.POST("/search", ordersCon.Search) + // orders.POST("/create", ordersCon.Create) + // orders.POST("/update", ordersCon.Update) + // orders.POST("/delete", ordersCon.Delete) + } + + product := router.Group("/product") + { + product.POST("/get_by_id", proCon.GetById) + product.POST("/search", proCon.Search) + product.POST("/create", proCon.Create) + // product.POST("/update", proCon.Update) + // product.POST("/delete", proCon.Delete) + } + + whitelist := router.Group("/whitelist") + { + whitelist.POST("/get_by_id", wlCon.GetById) + whitelist.POST("/search", wlCon.Search) + whitelist.POST("/create", wlCon.Create) + // whitelist.POST("/update", wlCon.Update) + // whitelist.POST("/delete", wlCon.Delete) + } + + zhilian := router.Group("/zhilian") + // zhilian.Use(middlewares.VerifySign()) + zhilian.Use(middlewares.RequestLog()) + { + zhilian.POST("/recharge/order", zlCon.CreateOrder) + zhilian.POST("/recharge/query", zlCon.GetByOutTradeNo) + } + + transfersys := router.Group("/transfersys") + transfersys.Use(middlewares.RequestLog()) + { + transfersys.GET("/recharge/get_order", transCon.GetOrder) + transfersys.POST("/recharge/finish", transCon.FinishOrder) + } + + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) +} diff --git a/app/http/trace/trace.go b/app/http/trace/trace.go new file mode 100644 index 0000000..5061c6f --- /dev/null +++ b/app/http/trace/trace.go @@ -0,0 +1,42 @@ +package trace + +import ( + "sync" + + "com.snow.auto_monitor/config" + + "github.com/SkyAPM/go2sky" + "github.com/SkyAPM/go2sky/reporter" +) + +var ( + tracer *go2sky.Tracer + lock sync.Mutex +) + +func Tracer() (*go2sky.Tracer, error) { + if tracer == nil { + // 有err, 不适合用sync.Once做单例 + lock.Lock() + defer lock.Unlock() + if tracer == nil { + err := InitTracer(config.GetConf().ServiceName, config.GetConf().SkyWalkingOapServer) + if err != nil { + return nil, err + } + } + } + return tracer, nil +} + +func InitTracer(serviceName, skyWalkingOapServer string) error { + report, err := reporter.NewGRPCReporter(skyWalkingOapServer) + if err != nil { + return err + } + tracer, err = go2sky.NewTracer(serviceName, go2sky.WithReporter(report)) + if err != nil { + return err + } + return nil +} diff --git a/app/jobs/basejob/base_job.go b/app/jobs/basejob/base_job.go new file mode 100644 index 0000000..7024e97 --- /dev/null +++ b/app/jobs/basejob/base_job.go @@ -0,0 +1,66 @@ +package basejob + +import ( + "context" + "sync" + + "github.com/qit-team/work" +) + +var ( + jb *work.Job + register func(job *work.Job) + mu sync.RWMutex +) + +func SetJob(job *work.Job) { + if jb == nil { + jb = job + } +} + +func SetJobRegister(r func(*work.Job)) { + register = r +} + +func GetJob() *work.Job { + if jb == nil { + if register != nil { + mu.Lock() + defer mu.Unlock() + jb = work.New() + register(jb) + } else { + panic("job register is nil") + } + } + return jb +} + +/** + * 消息入队 -- 原始message + */ +func Enqueue(ctx context.Context, topic string, message string, args ...interface{}) (isOk bool, err error) { + return GetJob().Enqueue(ctx, topic, message, args...) +} + +/** + * 消息入队 -- Task数据结构 + */ +func EnqueueWithTask(ctx context.Context, topic string, task work.Task, args ...interface{}) (isOk bool, err error) { + return GetJob().EnqueueWithTask(ctx, topic, task, args...) +} + +/** + * 消息批量入队 -- 原始message + */ +func BatchEnqueue(ctx context.Context, topic string, messages []string, args ...interface{}) (isOk bool, err error) { + return GetJob().BatchEnqueue(ctx, topic, messages, args...) +} + +/** + * 消息批量入队 -- Task数据结构 + */ +func BatchEnqueueWithTask(ctx context.Context, topic string, tasks []work.Task, args ...interface{}) (isOk bool, err error) { + return GetJob().BatchEnqueueWithTask(ctx, topic, tasks, args...) +} diff --git a/app/jobs/kernel.go b/app/jobs/kernel.go new file mode 100644 index 0000000..bb67e98 --- /dev/null +++ b/app/jobs/kernel.go @@ -0,0 +1,56 @@ +package jobs + +import ( + "strings" + + "com.snow.auto_monitor/app/jobs/basejob" + "com.snow.auto_monitor/config" + + "github.com/qit-team/snow-core/log/logger" + "github.com/qit-team/snow-core/queue" + "github.com/qit-team/snow-core/redis" + "github.com/qit-team/work" +) + +/** + * 配置队列任务 + */ +func RegisterWorker(job *work.Job) { + basejob.SetJob(job) + + //设置worker的任务投递回调函数 + job.AddFunc("topic-test", test) + //设置worker的任务投递回调函数,和并发数 + job.AddFunc("topic-test1", test, 2) + //使用worker结构进行注册 + job.AddWorker("topic-test2", &work.Worker{Call: work.MyWorkerFunc(test), MaxConcurrency: 1}) + + RegisterQueueDriver(job) + SetOptions(job) +} + +/** + * 给topic注册对应的队列服务 + */ +func RegisterQueueDriver(job *work.Job) { + //设置队列服务,需要实现work.Queue接口的方法 + q := queue.GetQueue(redis.SingletonMain, queue.DriverTypeRedis) + //针对topic设置相关的queue + job.AddQueue(q, "topic-test1", "topic-test2") + //设置默认的queue, 没有设置过的topic会使用默认的queue + job.AddQueue(q) +} + +/** + * 设置配置参数 + */ +func SetOptions(job *work.Job) { + //设置logger,需要实现work.Logger接口的方法 + job.SetLogger(logger.GetLogger()) + + //设置启用的topic,未设置表示启用全部注册过topic + if config.GetOptions().Queue != "" { + topics := strings.Split(config.GetOptions().Queue, ",") + job.SetEnableTopics(topics...) + } +} diff --git a/app/jobs/test.go b/app/jobs/test.go new file mode 100644 index 0000000..04eece4 --- /dev/null +++ b/app/jobs/test.go @@ -0,0 +1,24 @@ +package jobs + +import ( + "fmt" + "time" + + "github.com/qit-team/work" +) + +func test(task work.Task) (work.TaskResult) { + time.Sleep(time.Millisecond * 5) + s, err := work.JsonEncode(task) + if err != nil { + //work.StateFailed 不会进行ack确认 + //work.StateFailedWithAck 会进行actk确认 + //return work.TaskResult{Id: task.Id, State: work.StateFailed} + return work.TaskResult{Id: task.Id, State: work.StateFailedWithAck} + } else { + //work.StateSucceed 会进行ack确认 + fmt.Println("do task", s) + return work.TaskResult{Id: task.Id, State: work.StateSucceed} + } + +} diff --git a/app/models/bannermodel/banner.go b/app/models/bannermodel/banner.go new file mode 100644 index 0000000..bb0d7d2 --- /dev/null +++ b/app/models/bannermodel/banner.go @@ -0,0 +1,57 @@ +package bannermodel + +import ( + "sync" + "time" + + "github.com/qit-team/snow-core/db" +) + +var ( + once sync.Once + m *bannerModel +) +/** + * Banner实体 + */ +type Banner struct { + Id int64 `xorm:"pk autoincr"` //注:使用getOne 或者ID() 需要设置主键 + Pid int + Title string + ImageUrl string `xorm:"'img_url'"` + Url string + Status string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time `xorm:"deleted"` //此特性会激发软删除 +} + +/** + * 表名规则 + * @wiki http://gobook.io/read/github.com/go-xorm/manual-zh-CN/chapter-02/3.tags.html + */ +func (m *Banner) TableName() string { + return "banner" +} + +/** + * 私有化,防止被外部new + */ +type bannerModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +//单例模式 +func GetInstance() *bannerModel { + once.Do(func() { + m = new(bannerModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} + +func (m *bannerModel) GetListByPid(pid int, limits ...int) (banners []*Banner, err error) { + banners = make([]*Banner, 0) + err = m.GetList(&banners, "pid = ?", []interface{}{pid}, limits) + return +} diff --git a/app/models/bannermodel/banner_test.go b/app/models/bannermodel/banner_test.go new file mode 100644 index 0000000..01c6af7 --- /dev/null +++ b/app/models/bannermodel/banner_test.go @@ -0,0 +1,54 @@ +package bannermodel + +import ( + "fmt" + "testing" + + _ "github.com/go-sql-driver/mysql" + "github.com/qit-team/snow-core/config" + "github.com/qit-team/snow-core/db" + "github.com/qit-team/snow-core/utils" +) + +func init() { + m := config.DbBaseConfig{ + Host: "127.0.0.1", + Port: 3306, + User: "root", + Password: "123456", + DBName: "test", + } + dbConf := config.DbConfig{ + Driver: "mysql", + Master: m, + } + + err := db.Pr.Register("db", dbConf, true) + if err != nil { + fmt.Println(err) + } +} + +func TestGetOne(t *testing.T) { + bannerModel := GetInstance() + banner := new(Banner) + res, err := bannerModel.GetOne(1, banner) + if err != nil { + t.Error(err) + } else if res != true { + t.Error("missing banner data") + } else if banner.Id == 0 { + t.Error("missing banner data") + } + fmt.Println(utils.JsonEncode(banner)) +} + +func TestGetList(t *testing.T) { + bannerModel := GetInstance() + banners := make([]*Banner, 0) + err := bannerModel.GetList(&banners, "pid >= ?", []interface{}{1}, []int{10}, "status desc, id desc") + if err != nil { + t.Error(err) + } + fmt.Println(utils.JsonEncode(banners)) +} diff --git a/app/models/merchant/merchant.go b/app/models/merchant/merchant.go new file mode 100644 index 0000000..8a21db3 --- /dev/null +++ b/app/models/merchant/merchant.go @@ -0,0 +1,117 @@ +package merchant + +import ( + "sync" + "time" + + "github.com/qit-team/snow-core/db" +) + +var ( + once sync.Once + m *merchantModel +) + +/** + * Merchant + */ +type Merchant struct { + Id int64 `xorm:"pk autoincr"` //注:使用getOne 或者ID() 需要设置主键 + Name string + Key string + CreatedAt time.Time `xorm:"created"` +} + +/** + * 表名规则 + * @wiki http://gobook.io/read/github.com/go-xorm/manual-zh-CN/chapter-02/3.tags.html + */ +func (m *Merchant) TableName() string { + return "merchant" +} + +/** + * 私有化,防止被外部new + */ +type merchantModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +// 单例模式 +func GetInstance() *merchantModel { + once.Do(func() { + m = new(merchantModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} + +/** + * 查询主键ID的记录 + * @param id 主键ID + * @return has 是否有记录 + * @return err 错误信息 + * @return merchant 查询结果 + */ +func (m *merchantModel) GetById(id int64) (merchant *Merchant, has bool, err error) { + merchant = &Merchant{} + has, err = m.GetDb().ID(id).Get(merchant) + if err == nil || !has { + merchant = nil + } + return +} + +func (m *merchantModel) Search(id int64, name string, startTime string, endTime string, limit int, page int) (merchant []*Merchant, err error) { + merchant = make([]*Merchant, 0) + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if name != "" { + sql += " and name = ?" + args = append(args, name) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + err = m.GetDb().Where(sql, args...).Limit(limit, page).Find(&merchant) + return +} + +func (m *merchantModel) CountAll(id int64, name string, startTime string, endTime string) (res int64, err error) { + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if name != "" { + sql += " and name = ?" + args = append(args, name) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + res, err = m.GetDb().Table("merchant").Where(sql, args...).Count() + return +} + +func (m *merchantModel) Create(merchant *Merchant) (affected int64, err error) { + affected, err = m.GetDb().Insert(merchant) + return +} + +func (m *merchantModel) Update(merchant *Merchant) (affected int64, err error) { + affected, err = m.GetDb().ID(merchant.Id).Update(merchant) + return +} + +func (m *merchantModel) Delete(id int64) (affected int64, err error) { + affected, err = m.GetDb().ID(id).Delete(&Merchant{}) + return +} diff --git a/app/models/orders/orders.go b/app/models/orders/orders.go new file mode 100644 index 0000000..02106a3 --- /dev/null +++ b/app/models/orders/orders.go @@ -0,0 +1,277 @@ +package orders + +import ( + "encoding/json" + "sync" + "time" + + "github.com/qit-team/snow-core/db" +) + +var ( + once sync.Once + m *ordersModel +) + +/** + * Orders + */ +type Orders struct { + Id int64 `xorm:"pk autoincr"` //注:使用getOne 或者ID() 需要设置主键 + OrderNo string + MerchantId int64 + ProductId int64 + + OutTradeNo string + RechargeAccount string + AccountType int64 + Number int64 + NotifyUrl string + ExtendParameter string + + Status int64 + TransferStatus int64 + CreatedAt time.Time `xorm:"created"` +} + +func (o *Orders) MarshalBinary() ([]byte, error) { + return json.Marshal(o) +} + +/** + * 表名规则 + * @wiki http://gobook.io/read/github.com/go-xorm/manual-zh-CN/chapter-02/3.tags.html + */ +func (m *Orders) TableName() string { + return "orders" +} + +/** + * 私有化,防止被外部new + */ +type ordersModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +// 单例模式 +func GetInstance() *ordersModel { + once.Do(func() { + m = new(ordersModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} + +/** + * 查询主键ID的记录 + * @param id 主键ID + * @return has 是否有记录 + * @return err 错误信息 + * @return orders 查询结果 + */ +func (m *ordersModel) GetById(id int64) (orders *Orders, has bool, err error) { + orders = &Orders{} + has, err = m.GetDb().ID(id).Get(orders) + if err == nil || !has { + orders = nil + } + return +} + +func (m *ordersModel) GetByOutTradeNo(out_trade_no string, merchant_id int64) (orders *Orders, has bool, err error) { + orders = &Orders{} + has, err = m.GetDb().Where("out_trade_no = ? and merchant_id = ?", out_trade_no, merchant_id).Get(orders) + if err == nil || !has { + orders = nil + } + return +} + +func (m *ordersModel) Search( + id int64, + order_no string, + merchant_id int64, + product_id int64, + out_trade_no string, + recharge_account string, + account_type int64, + + status int64, + transfer_status int64, + startTime string, + endTime string, + limit int, + page int) (orders []*Orders, err error) { + orders = make([]*Orders, 0) + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if order_no != "" { + sql += " and order_no = ?" + args = append(args, order_no) + } + if merchant_id != 0 { + sql += " and merchant_id = ?" + args = append(args, merchant_id) + } + if product_id != 0 { + sql += " and merchant_id = ?" + args = append(args, product_id) + } + if out_trade_no != "" { + sql += " and out_trade_no = ?" + args = append(args, out_trade_no) + } + if recharge_account != "" { + sql += " and recharge_account = ?" + args = append(args, recharge_account) + } + if account_type != 0 { + sql += " and account_type = ?" + args = append(args, recharge_account) + } + if status != 0 { + sql += " and status = ?" + args = append(args, status) + } + if transfer_status != 0 { + sql += " and transfer_status = ?" + args = append(args, transfer_status) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + err = m.GetDb().Where(sql, args...).Limit(limit, page).Find(&orders) + return +} + +func (m *ordersModel) CountAll( + id int64, + order_no string, + merchant_id int64, + product_id int64, + out_trade_no string, + recharge_account string, + account_type int64, + + status int64, + transfer_status int64, + startTime string, + endTime string, +) (res int64, err error) { + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if order_no != "" { + sql += " and order_no = ?" + args = append(args, order_no) + } + if merchant_id != 0 { + sql += " and merchant_id = ?" + args = append(args, merchant_id) + } + if product_id != 0 { + sql += " and merchant_id = ?" + args = append(args, product_id) + } + if out_trade_no != "" { + sql += " and out_trade_no = ?" + args = append(args, out_trade_no) + } + if recharge_account != "" { + sql += " and recharge_account = ?" + args = append(args, recharge_account) + } + if account_type != 0 { + sql += " and account_type = ?" + args = append(args, recharge_account) + } + if status != 0 { + sql += " and status = ?" + args = append(args, status) + } + if transfer_status != 0 { + sql += " and transfer_status = ?" + args = append(args, transfer_status) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + res, err = m.GetDb().Where(sql, args...).Table("orders").Count() + return +} + +func (m *ordersModel) Create(orders *Orders) (affected int64, err error) { + affected, err = m.GetDb().Insert(orders) + return +} + +func (m *ordersModel) Update(orders *Orders) (affected int64, err error) { + affected, err = m.GetDb().ID(orders.Id).Update(orders) + return +} + +func (m *ordersModel) Delete(id int64) (affected int64, err error) { + affected, err = m.GetDb().ID(id).Delete(&Orders{}) + return +} + +func (m *ordersModel) GetIdleOrder() (orders *Orders, has bool, err error) { + orders = &Orders{} + + session := m.GetDb().NewSession() + defer session.Close() + // add Begin() before any action + if err = session.Begin(); err != nil { + orders = nil + return + } + + has, err = session.Where("transfer_status = 3").Limit(1).Get(orders) + if err != nil || !has { + orders = nil + return + } + orders.TransferStatus = 2 // 1.成功 2.充值中 3. 等待充值 4.充值失败 5.异常需要人工处理 + affected, err := session.ID(orders.Id).Where("transfer_status = 3").Update(orders) + if err != nil || affected == 0 { + orders = nil + return + } + err = session.Commit() + return +} + +func (m *ordersModel) SetDealingOrder(orders *Orders) (affected int64, err error) { + + session := m.GetDb().NewSession() + defer session.Close() + // add Begin() before any action + if err = session.Begin(); err != nil { + orders = nil + return + } + + has, err := session.Where("id = ? and merchant_id = ? and product_id = ? and transfer_status = 2", + orders.Id, orders.MerchantId, orders.ProductId).Get(&Orders{}) + if err != nil || !has { + orders = nil + return + } + // 1.成功 2.充值中 3. 等待充值 4.充值失败 5.异常需要人工处理 + affected, err = session.ID(orders.Id).Where("transfer_status = 2").Update(orders) + if err != nil || affected == 0 { + orders = nil + return + } + err = session.Commit() + return +} diff --git a/app/models/product/product.go b/app/models/product/product.go new file mode 100644 index 0000000..de99c39 --- /dev/null +++ b/app/models/product/product.go @@ -0,0 +1,117 @@ +package product + +import ( + "sync" + "time" + + "github.com/qit-team/snow-core/db" +) + +var ( + once sync.Once + m *productModel +) + +/** + * Product + */ +type Product struct { + Id int64 `xorm:"pk autoincr"` //注:使用getOne 或者ID() 需要设置主键 + Name string + Price int64 + CreatedAt time.Time `xorm:"created"` +} + +/** + * 表名规则 + * @wiki http://gobook.io/read/github.com/go-xorm/manual-zh-CN/chapter-02/3.tags.html + */ +func (m *Product) TableName() string { + return "product" +} + +/** + * 私有化,防止被外部new + */ +type productModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +// 单例模式 +func GetInstance() *productModel { + once.Do(func() { + m = new(productModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} + +/** + * 查询主键ID的记录 + * @param id 主键ID + * @return has 是否有记录 + * @return err 错误信息 + * @return product 查询结果 + */ +func (m *productModel) GetById(id int64) (product *Product, has bool, err error) { + product = &Product{} + has, err = m.GetDb().ID(id).Get(product) + if err == nil || !has { + product = nil + } + return +} + +func (m *productModel) Search(id int64, name string, startTime string, endTime string, limit int, page int) (product []*Product, err error) { + product = make([]*Product, 0) + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if name != "" { + sql += " and name = ?" + args = append(args, name) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + err = m.GetDb().Where(sql, args...).Limit(limit, page).Find(&product) + return +} + +func (m *productModel) CountAll(id int64, name string, startTime string, endTime string) (res int64, err error) { + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if name != "" { + sql += " and name = ?" + args = append(args, name) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + res, err = m.GetDb().Table("product").Where(sql, args...).Count() + return +} + +func (m *productModel) Create(product *Product) (affected int64, err error) { + affected, err = m.GetDb().Insert(product) + return +} + +func (m *productModel) Update(product *Product) (affected int64, err error) { + affected, err = m.GetDb().ID(product.Id).Update(product) + return +} + +func (m *productModel) Delete(id int64) (affected int64, err error) { + affected, err = m.GetDb().ID(id).Delete(&Product{}) + return +} diff --git a/app/models/whitelist/whitelist.go b/app/models/whitelist/whitelist.go new file mode 100644 index 0000000..316290e --- /dev/null +++ b/app/models/whitelist/whitelist.go @@ -0,0 +1,134 @@ +package whitelist + +import ( + "sync" + "time" + + "github.com/qit-team/snow-core/db" +) + +var ( + once sync.Once + m *whitelistModel +) + +/** + * Whitelist + */ +type Whitelist struct { + Id int64 `xorm:"pk autoincr"` //注:使用getOne 或者ID() 需要设置主键 + MerchantId int64 + Ip string + CreatedAt time.Time `xorm:"created"` +} + +/** + * 表名规则 + * @wiki http://gobook.io/read/github.com/go-xorm/manual-zh-CN/chapter-02/3.tags.html + */ +func (m *Whitelist) TableName() string { + return "whitelist" +} + +/** + * 私有化,防止被外部new + */ +type whitelistModel struct { + db.Model //组合基础Model,集成基础Model的属性和方法 +} + +// 单例模式 +func GetInstance() *whitelistModel { + once.Do(func() { + m = new(whitelistModel) + //m.DiName = "" //设置数据库实例连接,默认db.SingletonMain + }) + return m +} + +/** + * 查询主键ID的记录 + * @param id 主键ID + * @return has 是否有记录 + * @return err 错误信息 + * @return whitelist 查询结果 + */ +func (m *whitelistModel) GetById(id int64) (whitelist *Whitelist, has bool, err error) { + whitelist = &Whitelist{} + has, err = m.GetDb().ID(id).Get(whitelist) + if err == nil || !has { + whitelist = nil + } + return +} + +func (m *whitelistModel) GetByIp(ip string) (whitelist *Whitelist, has bool, err error) { + whitelist = &Whitelist{} + has, err = m.GetDb().Where("ip = ?", ip).Get(whitelist) + if err == nil || !has { + whitelist = nil + } + return +} + +func (m *whitelistModel) Search(id int64, merchant_id int64, ip string, startTime string, endTime string, limit int, page int) (whitelist []*Whitelist, err error) { + whitelist = make([]*Whitelist, 0) + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if merchant_id != 0 { + sql += " and merchant_id = ?" + args = append(args, merchant_id) + } + if ip != "" { + sql += " and ip = ?" + args = append(args, ip) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + err = m.GetDb().Where(sql, args...).Limit(limit, page).Find(&whitelist) + return +} + +func (m *whitelistModel) CountAll(id int64, merchant_id int64, ip string, startTime string, endTime string) (res int64, err error) { + sql := "1=1" + var args []interface{} + if id != 0 { + sql += " and id = ?" + args = append(args, id) + } + if merchant_id != 0 { + sql += " and merchant_id = ?" + args = append(args, merchant_id) + } + if ip != "" { + sql += " and ip = ?" + args = append(args, ip) + } + if startTime != "" && endTime != "" { + sql += " and created_at >= ? and created_at <= ?" + args = append(args, startTime, endTime) + } + res, err = m.GetDb().Table("whitelist").Where(sql, args...).Count() + return +} + +func (m *whitelistModel) Create(whitelist *Whitelist) (affected int64, err error) { + affected, err = m.GetDb().Insert(whitelist) + return +} + +func (m *whitelistModel) Update(whitelist *Whitelist) (affected int64, err error) { + affected, err = m.GetDb().ID(whitelist.Id).Update(whitelist) + return +} + +func (m *whitelistModel) Delete(id int64) (affected int64, err error) { + affected, err = m.GetDb().ID(id).Delete(&Whitelist{}) + return +} diff --git a/app/services/bannerservice/banner.go b/app/services/bannerservice/banner.go new file mode 100644 index 0000000..f2dc878 --- /dev/null +++ b/app/services/bannerservice/banner.go @@ -0,0 +1,25 @@ +package bannerservice + +import ( + "com.snow.auto_monitor/app/models/bannermodel" +) + +func GetListByPid(pid int, limit int, page int) (banners []*bannermodel.Banner, err error) { + limitStart := GetLimitStart(limit, page) + banners, err = bannermodel.GetInstance().GetListByPid(pid, limitStart...) + return +} + +func GetLimitStart(limit int, page int) (arr []int) { + arr = make([]int, 2) + if limit <= 0 { + limit = 20 + } + arr[0] = limit + if page > 0 { + arr[1] = (page - 1) * limit + } else { + arr[1] = 0 + } + return +} diff --git a/app/services/merchant/merchant.go b/app/services/merchant/merchant.go new file mode 100644 index 0000000..0047748 --- /dev/null +++ b/app/services/merchant/merchant.go @@ -0,0 +1,48 @@ +package merchant + +import ( + models "com.snow.auto_monitor/app/models/merchant" +) + +func GetLimitStart(limit int, page int) (int, int) { + if limit <= 0 { + limit = 20 + } + if page > 0 { + page = (page - 1) * limit + } else { + page = 0 + } + return limit, page +} + +func GetById(id int64) (res *models.Merchant, err error) { + res, _, err = models.GetInstance().GetById(id) + return +} + +func Search(id int64, name string, startTime string, endTime string, limit int, page int) (res []*models.Merchant, err error) { + limit, page = GetLimitStart(limit, page) + res, err = models.GetInstance().Search(id, name, startTime, endTime, limit, page) + return +} + +func CountAll(id int64, name string, startTime string, endTime string) (res int64, err error) { + res, err = models.GetInstance().CountAll(id, name, startTime, endTime) + return +} + +func Create(merchant *models.Merchant) (affected int64, err error) { + affected, err = models.GetInstance().Create(merchant) + return +} + +func Update(merchant *models.Merchant) (affected int64, err error) { + affected, err = models.GetInstance().Update(merchant) + return +} + +func Delete(id int64) (affected int64, err error) { + affected, err = models.GetInstance().Delete(id) + return +} diff --git a/app/services/orders/orders.go b/app/services/orders/orders.go new file mode 100644 index 0000000..2ed8df3 --- /dev/null +++ b/app/services/orders/orders.go @@ -0,0 +1,76 @@ +package orders + +import ( + models "com.snow.auto_monitor/app/models/orders" +) + +func GetLimitStart(limit int, page int) (int, int) { + if limit <= 0 { + limit = 20 + } + if page > 0 { + page = (page - 1) * limit + } else { + page = 0 + } + return limit, page +} + +func GetById(id int64) (res *models.Orders, err error) { + res, _, err = models.GetInstance().GetById(id) + return +} + +func Search(id int64, + order_no string, + merchant_id int64, + product_id int64, + out_trade_no string, + recharge_account string, + account_type int64, + + status int64, + transfer_status int64, + startTime string, + endTime string, + limit int, + page int) (res []*models.Orders, err error) { + limit, page = GetLimitStart(limit, page) + res, err = models.GetInstance().Search(id, order_no, merchant_id, product_id, out_trade_no, + recharge_account, account_type, status, transfer_status, + startTime, endTime, limit, page) + return +} + +func CountAll(id int64, + order_no string, + merchant_id int64, + product_id int64, + out_trade_no string, + recharge_account string, + account_type int64, + + status int64, + transfer_status int64, + startTime string, + endTime string) (res int64, err error) { + res, err = models.GetInstance().CountAll(id, order_no, merchant_id, product_id, out_trade_no, + recharge_account, account_type, status, transfer_status, + startTime, endTime) + return +} + +func Create(orders *models.Orders) (affected int64, err error) { + affected, err = models.GetInstance().Create(orders) + return +} + +func Update(orders *models.Orders) (affected int64, err error) { + affected, err = models.GetInstance().Update(orders) + return +} + +func Delete(id int64) (affected int64, err error) { + affected, err = models.GetInstance().Delete(id) + return +} diff --git a/app/services/product/product.go b/app/services/product/product.go new file mode 100644 index 0000000..8659de3 --- /dev/null +++ b/app/services/product/product.go @@ -0,0 +1,48 @@ +package product + +import ( + models "com.snow.auto_monitor/app/models/product" +) + +func GetLimitStart(limit int, page int) (int, int) { + if limit <= 0 { + limit = 20 + } + if page > 0 { + page = (page - 1) * limit + } else { + page = 0 + } + return limit, page +} + +func GetById(id int64) (res *models.Product, err error) { + res, _, err = models.GetInstance().GetById(id) + return +} + +func Search(id int64, name string, startTime string, endTime string, limit int, page int) (res []*models.Product, err error) { + limit, page = GetLimitStart(limit, page) + res, err = models.GetInstance().Search(id, name, startTime, endTime, limit, page) + return +} + +func CountAll(id int64, name string, startTime string, endTime string) (res int64, err error) { + res, err = models.GetInstance().CountAll(id, name, startTime, endTime) + return +} + +func Create(product *models.Product) (affected int64, err error) { + affected, err = models.GetInstance().Create(product) + return +} + +func Update(product *models.Product) (affected int64, err error) { + affected, err = models.GetInstance().Update(product) + return +} + +func Delete(id int64) (affected int64, err error) { + affected, err = models.GetInstance().Delete(id) + return +} diff --git a/app/services/transfersys/transfersys.go b/app/services/transfersys/transfersys.go new file mode 100644 index 0000000..9b207a1 --- /dev/null +++ b/app/services/transfersys/transfersys.go @@ -0,0 +1,39 @@ +package transfersys + +import ( + "errors" + + mermod "com.snow.auto_monitor/app/models/merchant" + models "com.snow.auto_monitor/app/models/orders" + promod "com.snow.auto_monitor/app/models/product" +) + +func GetOrder() (res *models.Orders, err error) { + res, _, err = models.GetInstance().GetIdleOrder() + return +} + +func FinishOrder(orders *models.Orders) (affected int64, err error) { + //验证商户是否存在 + _, has, err := mermod.GetInstance().GetById(orders.MerchantId) + if err != nil { + return + } + if !has { + err = errors.New("商户不存在") + return + } + + //验证产品是否存在 + _, has, err = promod.GetInstance().GetById(orders.ProductId) + if err != nil { + return + } + if !has { + err = errors.New("产品不存在") + return + } + + affected, err = models.GetInstance().SetDealingOrder(orders) + return +} diff --git a/app/services/transfersys/transfersys_rdb.go b/app/services/transfersys/transfersys_rdb.go new file mode 100644 index 0000000..a49295b --- /dev/null +++ b/app/services/transfersys/transfersys_rdb.go @@ -0,0 +1,72 @@ +package transfersys + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + models "com.snow.auto_monitor/app/models/orders" + rdbmq "com.snow.auto_monitor/app/utils/rdbmq" +) + +// func TestRDB() (err error) { +// rdbmq.CreateGroup("orders", "orders_one") +// return +// } + +// func WriteGroup() (err error) { +// err = rdbmq.WriteOne("orders", time.Now().Format("2006-01-02 15:04:05"), "test") +// return +// } + +func ReadGroup() (order *models.Orders, err error) { + res, err := rdbmq.ReadOne("orders", "orders_one", "cosumer_one") + if err != nil { + if err.Error() == "context deadline exceeded" { + return nil, errors.New("no data") + } + return nil, err + } + key := "" + for k := range res { + key = k + break + } + jsonstr := res[key].(string) + json.Unmarshal([]byte(jsonstr), &order) + return order, nil +} + +func WriteOrder() { + fmt.Println("开始执行周期任务:getWxTransferStatus") + // 创建分组 + err := rdbmq.CreateStreamAndGroup("orders", "orders_one") + if err != nil && err.Error() != "BUSYGROUP Consumer Group name already exists" { + fmt.Println("分组失败") + return + } + // 创建一个新的Ticker,每3秒钟触发一次 + ticker := time.NewTicker(3 * time.Second) + defer ticker.Stop() // 在函数结束时停止Ticker + for range ticker.C { + for i := 0; i < 10; i++ { + res, _, err := models.GetInstance().GetIdleOrder() + if err != nil { + fmt.Println(err.Error()) + continue + } + if res != nil { + err = rdbmq.WriteOne("orders", res.OrderNo, res) + if err != nil { + fmt.Println(err.Error()) + continue + } + } + } + } +} + +func init() { + go WriteOrder() +} diff --git a/app/services/whitelist/whitelist.go b/app/services/whitelist/whitelist.go new file mode 100644 index 0000000..4ae4d0a --- /dev/null +++ b/app/services/whitelist/whitelist.go @@ -0,0 +1,48 @@ +package whitelist + +import ( + models "com.snow.auto_monitor/app/models/whitelist" +) + +func GetLimitStart(limit int, page int) (int, int) { + if limit <= 0 { + limit = 20 + } + if page > 0 { + page = (page - 1) * limit + } else { + page = 0 + } + return limit, page +} + +func GetById(id int64) (res *models.Whitelist, err error) { + res, _, err = models.GetInstance().GetById(id) + return +} + +func Search(id int64, merchant_id int64, ip string, startTime string, endTime string, limit int, page int) (res []*models.Whitelist, err error) { + limit, page = GetLimitStart(limit, page) + res, err = models.GetInstance().Search(id, merchant_id, ip, startTime, endTime, limit, page) + return +} + +func CountAll(id int64, merchant_id int64, ip string, startTime string, endTime string) (res int64, err error) { + res, err = models.GetInstance().CountAll(id, merchant_id, ip, startTime, endTime) + return +} + +func Create(whitelist *models.Whitelist) (affected int64, err error) { + affected, err = models.GetInstance().Create(whitelist) + return +} + +func Update(whitelist *models.Whitelist) (affected int64, err error) { + affected, err = models.GetInstance().Update(whitelist) + return +} + +func Delete(id int64) (affected int64, err error) { + affected, err = models.GetInstance().Delete(id) + return +} diff --git a/app/services/zhiliansys/zhiliansys.go b/app/services/zhiliansys/zhiliansys.go new file mode 100644 index 0000000..5f84957 --- /dev/null +++ b/app/services/zhiliansys/zhiliansys.go @@ -0,0 +1,42 @@ +package orders + +import ( + "errors" + + models "com.snow.auto_monitor/app/models/orders" + promod "com.snow.auto_monitor/app/models/product" + "github.com/google/uuid" +) + +func GetByOutTradeNo(out_trade_no string, merchant_id int64) (res *models.Orders, err error) { + res, _, err = models.GetInstance().GetByOutTradeNo(out_trade_no, merchant_id) + return +} + +func CreateOrder(orders *models.Orders) (affected int64, err error) { + //验证商户是否存在 + // _, has, err := mermod.GetInstance().GetById(orders.MerchantId) + // if err != nil { + // return + // } + // if !has { + // err = errors.New("商户不存在") + // return + // } + + //验证产品是否存在 + _, has, err := promod.GetInstance().GetById(orders.ProductId) + if err != nil { + return + } + if !has { + err = errors.New("产品不存在") + return + } + //创建订单 + orders.OrderNo = uuid.New().String() + orders.Status = 2 // 1.成功 2.充值中 3.充值失败 4.异常需要人工处理 + orders.TransferStatus = 3 // 1.成功 2.充值中 3. 等待充值 4.充值失败 5.异常需要人工处理 + affected, err = models.GetInstance().Create(orders) + return +} diff --git a/app/utils/.gitkeep b/app/utils/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/utils/httpclient/httpclient.go b/app/utils/httpclient/httpclient.go new file mode 100644 index 0000000..12d029a --- /dev/null +++ b/app/utils/httpclient/httpclient.go @@ -0,0 +1,190 @@ +package httpclient + +import ( + "context" + "fmt" + "net/http" + "strconv" + "time" + + "com.snow.auto_monitor/app/http/trace" + "com.snow.auto_monitor/config" + + "github.com/SkyAPM/go2sky" + "github.com/SkyAPM/go2sky/propagation" + v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent" + "github.com/go-resty/resty/v2" + "github.com/qit-team/snow-core/log/logger" +) + +const ( + RetryCounts = 2 + RetryInterval = 3 * time.Second +) + +const componentIDGOHttpClient = 5005 + +type ClientConfig struct { + ctx context.Context + client *resty.Client + tracer *go2sky.Tracer + extraTags map[string]string +} + +type ClientOption func(*ClientConfig) + +func WithClientTag(key string, value string) ClientOption { + return func(c *ClientConfig) { + if c.extraTags == nil { + c.extraTags = make(map[string]string) + } + c.extraTags[key] = value + } +} + +func WithClient(client *resty.Client) ClientOption { + return func(c *ClientConfig) { + c.client = client + } +} + +func WithContext(ctx context.Context) ClientOption { + return func(c *ClientConfig) { + c.ctx = ctx + } +} + +type transport struct { + *ClientConfig + delegated http.RoundTripper +} + +func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + span, err := t.tracer.CreateExitSpan(t.ctx, fmt.Sprintf("/%s%s", req.Method, req.URL.Path), req.Host, func(header string) error { + // 将本层的调用链信息写入http头部, 传入到下一层调用, 当前使用v3版本的协议 + // https://github.com/apache/skywalking/blob/master/docs/en/protocols/Skywalking-Cross-Process-Propagation-Headers-Protocol-v3.md + req.Header.Set(propagation.Header, header) + return nil + }) + if err != nil { + return t.delegated.RoundTrip(req) + } + defer span.End() + span.SetComponent(componentIDGOHttpClient) + for k, v := range t.extraTags { + span.Tag(go2sky.Tag(k), v) + } + span.Tag(go2sky.TagHTTPMethod, req.Method) + span.Tag(go2sky.TagURL, req.URL.String()) + span.SetSpanLayer(v3.SpanLayer_Http) + resp, err = t.delegated.RoundTrip(req) + if err != nil { + span.Error(time.Now(), err.Error()) + return + } + span.Tag(go2sky.TagStatusCode, strconv.Itoa(resp.StatusCode)) + if resp.StatusCode >= http.StatusBadRequest { + span.Error(time.Now(), "Errors on handling client") + } + return resp, nil +} + +func NewClient(ctx context.Context, options ...ClientOption) (client *resty.Client) { + client = resty.New() + if config.IsDebug() { + client.SetDebug(true).EnableTrace() + } + + var ( + tracer *go2sky.Tracer + err error + ) + if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) { + tracer, err = trace.Tracer() + if err != nil { + logger.Error(ctx, "NewClient:Tracer", err.Error()) + } + } + if tracer != nil { + co := &ClientConfig{ctx: ctx, tracer: tracer} + for _, option := range options { + option(co) + } + if co.client == nil { + co.client = client + } + tp := &transport{ + ClientConfig: co, + delegated: http.DefaultTransport, + } + if co.client.GetClient().Transport != nil { + tp.delegated = co.client.GetClient().Transport + } + co.client.SetTransport(tp) + } + + client.OnBeforeRequest(func(ct *resty.Client, req *resty.Request) error { + //req.SetContext(c) + logger.Info(ctx, "OnBeforeRequest", logger.NewWithField("url", req.URL)) + return nil // if its success otherwise return error + }) + // Registering Response Middleware + client.OnAfterResponse(func(ct *resty.Client, resp *resty.Response) error { + logger.Info(ctx, "OnAfterResponse", logger.NewWithField("url", resp.Request.URL), logger.NewWithField("request", resp.Request.RawRequest), logger.NewWithField("response", resp.String())) + return nil + }) + return client +} + +func NewClientWithRetry(ctx context.Context, retryCounts int, retryInterval time.Duration, options ...ClientOption) (client *resty.Client) { + client = resty.New() + if config.IsDebug() { + client.SetDebug(true).EnableTrace() + } + if retryCounts == 0 { + retryCounts = RetryCounts + } + if retryInterval.Seconds() == 0.0 { + retryInterval = RetryInterval + } + client.SetRetryCount(retryCounts).SetRetryMaxWaitTime(retryInterval) + + var ( + tracer *go2sky.Tracer + err error + ) + if len(config.GetConf().SkyWalkingOapServer) > 0 && config.IsEnvEqual(config.ProdEnv) { + tracer, err = trace.Tracer() + if err != nil { + logger.Error(ctx, "NewClient:Tracer", err.Error()) + } + } + if tracer != nil { + co := &ClientConfig{ctx: ctx, tracer: tracer} + for _, option := range options { + option(co) + } + if co.client == nil { + co.client = client + } + tp := &transport{ + ClientConfig: co, + delegated: http.DefaultTransport, + } + if co.client.GetClient().Transport != nil { + tp.delegated = co.client.GetClient().Transport + } + co.client.SetTransport(tp) + } + + client.OnBeforeRequest(func(ct *resty.Client, req *resty.Request) error { + logger.Info(ctx, "OnBeforeRequest", logger.NewWithField("url", req.URL)) + return nil // if its success otherwise return error + }) + // Registering Response Middleware + client.OnAfterResponse(func(ct *resty.Client, resp *resty.Response) error { + logger.Info(ctx, "OnAfterResponse", logger.NewWithField("url", resp.Request.URL), logger.NewWithField("request", resp.Request.RawRequest), logger.NewWithField("response", resp.String())) + return nil + }) + return client +} diff --git a/app/utils/metric/reporter.go b/app/utils/metric/reporter.go new file mode 100644 index 0000000..36f4660 --- /dev/null +++ b/app/utils/metric/reporter.go @@ -0,0 +1,177 @@ +package metric + +// prometheus metric:unique identifier: name and optional key-value pairs called labels +// 1. name regexp: [a-zA-Z_:][a-zA-Z0-9_:]* +// 2. label name regexp: [a-zA-Z_][a-zA-Z0-9_]* +// 3. Label names beginning with __ are reserved for internal use. +// 4. Label values may contain any Unicode characters. +// 5. notation: {