From 6c7a2eb6b2690a047fbc73a5d83e929119b42630 Mon Sep 17 00:00:00 2001 From: wujianhua <1107015496@qq.com> Date: Mon, 9 Jan 2023 16:02:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.go | 91 ++++++++++++++++++++++++++++++++++++++++++ client.go | 38 ++++++++++++++++++ client_test.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 ++++++ go.sum | 19 +++++++++ model.go | 30 ++++++++++++++ option.go | 53 +++++++++++++++++++++++++ sign.go | 71 +++++++++++++++++++++++++++++++++ 8 files changed, 418 insertions(+) create mode 100644 api.go create mode 100644 client.go create mode 100644 client_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 model.go create mode 100644 option.go create mode 100644 sign.go diff --git a/api.go b/api.go new file mode 100644 index 0000000..33dd4bc --- /dev/null +++ b/api.go @@ -0,0 +1,91 @@ +package risk + +import ( + "context" + "fmt" + + "github.com/go-resty/resty/v2" +) + +const ( + writeLogUri = "/v1/metric/counter/write/v2" + syncUri = "/v1/config/database/sync" +) + +type api struct { + http *resty.Client + appId string + appSecret string +} + +func NewApi(opts clientOptions) *api { + + client := resty.New() + client.SetBaseURL(opts.host) + client.SetTimeout(opts.timeOut) + client.SetRetryCount(2) + client.SetDebug(opts.debug) + client.SetHeader("Content-Type", "application/json") + + return &api{ + http: client, + appId: opts.appId, + appSecret: opts.appSecret, + } +} + +// SyncDatabase 写入访问日志,访问/v1/metric/counter/write/v2接口 +func (a *api) WriteLog(ctx context.Context, data *LogData) error { + + sign, random, ts := Sign(a.appId, a.appSecret, data) + + req := Request{ + AppID: a.appId, + Time: int64(ts), + Data: data, + Sign: sign, + Random: random, + } + + res, err := a.http.R(). + SetBody(req). + Post(writeLogUri) + + if err != nil { + return err + } + + if res.StatusCode() != 200 { + return fmt.Errorf("write log has error, status_code: %d, response: %s", res.StatusCode(), res.Body()) + } + + return nil +} + +// SyncDatabase 同步数据库数据,访问/v1/config/database/sync接口 +func (a *api) SyncDatabase(ctx context.Context, data []*SyncData) error { + + sign, random, ts := Sign(a.appId, a.appSecret, data) + + req := Request{ + AppID: a.appId, + Time: int64(ts), + Data: data, + Sign: sign, + Random: random, + } + + res, err := a.http.R(). + SetBody(req). + Post(syncUri) + + if err != nil { + return err + } + + if res.StatusCode() != 200 { + return fmt.Errorf("write log has error, status_code: %d, response: %s", res.StatusCode(), res.Body()) + } + + return nil +} diff --git a/client.go b/client.go new file mode 100644 index 0000000..6ea9402 --- /dev/null +++ b/client.go @@ -0,0 +1,38 @@ +package risk + +import "context" + +type API interface { + WriteLog(ctx context.Context, data *LogData) error + SyncDatabase(ctx context.Context, data []*SyncData) error +} + +type Client struct { + opts clientOptions + apis API +} + +// NewClient 创建一个客户端 +func NewClient(opt ...ClientOption) *Client { + opts := defaultClientOptions + for _, o := range opt { + o(&opts) + } + + apis := NewApi(opts) + + return &Client{ + opts: opts, + apis: apis, + } +} + +// SyncDatabase 写入访问日志,访问/v1/metric/counter/write/v2接口 +func (cli *Client) WriteLog(ctx context.Context, data *LogData) error { + return cli.apis.WriteLog(ctx, data) +} + +// SyncDatabase 同步数据库数据,访问/v1/config/database/sync接口 +func (cli *Client) SyncDatabase(ctx context.Context, data []*SyncData) error { + return cli.apis.SyncDatabase(ctx, data) +} diff --git a/client_test.go b/client_test.go new file mode 100644 index 0000000..dd38e76 --- /dev/null +++ b/client_test.go @@ -0,0 +1,105 @@ +package risk + +import ( + "context" + "testing" + "time" +) + +var client = NewClient( + WithAppID("01b60cd2aacaf411"), + WithAppSecret("2D6MDRfFg7EudhW90UXOFrEI9td"), + WithServerHost("http://risk-sensor.drugeyes.vip:7031/"), + WithTimeOut(time.Second*30), + WithDebug(true), +) + +func TestClient_WriteLog(t *testing.T) { + type args struct { + ctx context.Context + data *LogData + } + tests := []struct { + name string + cli *Client + args args + wantErr bool + }{ + { + name: "普通测试", + cli: client, + args: args{ + ctx: context.Background(), + data: &LogData{ + Organize: "测试", + User: "1", + Ip: "127.0.0.1", + UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + PageType: "LIST", + Value: 12, + Request: "测试库dd", + DbCount: []*DBCount{ + { + UniqueKey: "00014", + DB: "db1", + }, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.cli.WriteLog(tt.args.ctx, tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("Client.WriteLog() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestClient_SyncDatabase(t *testing.T) { + type args struct { + ctx context.Context + data []*SyncData + } + tests := []struct { + name string + cli *Client + args args + wantErr bool + }{ + { + name: "普通测试", + cli: client, + args: args{ + ctx: context.Background(), + data: []*SyncData{ + { + DbKey: "db1", + DbName: "测试库aa", + DbCount: 255, + }, + { + DbKey: "db2", + DbName: "测试库bb", + DbCount: 255, + }, + { + DbKey: "db3", + DbName: "测试库cc", + DbCount: 255, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.cli.SyncDatabase(tt.args.ctx, tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("Client.SyncDatabase() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1eeaed2 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module gitea.drugeyes.vip/pharnexbase/risk-sdk-go + +go 1.19 + +require ( + github.com/go-resty/resty/v2 v2.7.0 + github.com/google/uuid v1.3.0 + github.com/spf13/cast v1.5.0 +) + +require golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9ec461b --- /dev/null +++ b/go.sum @@ -0,0 +1,19 @@ +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/model.go b/model.go new file mode 100644 index 0000000..7a7bc51 --- /dev/null +++ b/model.go @@ -0,0 +1,30 @@ +package risk + +type Request struct { + AppID string `json:"app_id"` + Time int64 `json:"time"` + Data interface{} `json:"data"` + Random string `json:"random"` + Sign string `json:"sign"` +} + +type LogData struct { + Organize string `json:"organize"` + User string `json:"user"` + Ip string `json:"ip"` + UserAgent string `json:"user_agent"` + PageType string `json:"page_type"` + Value int64 `json:"value"` + Request string `json:"request"` + DbCount []*DBCount `json:"db_count"` +} +type DBCount struct { + DB string `json:"db"` + UniqueKey string `json:"unique_key"` +} + +type SyncData struct { + DbKey string `json:"db_key"` + DbName string `json:"db_name"` + DbCount int64 `json:"db_count"` +} diff --git a/option.go b/option.go new file mode 100644 index 0000000..da0f41a --- /dev/null +++ b/option.go @@ -0,0 +1,53 @@ +package risk + +import "time" + +type ClientOption func(c *clientOptions) + +var defaultClientOptions = clientOptions{ + host: "http://risk-monitor-api.drugeyes.vip:7031/", + timeOut: 30 * time.Second, +} + +type clientOptions struct { + appId string + appSecret string + host string + timeOut time.Duration + debug bool +} + +// WithAppID 设置APPID +func WithAppID(appId string) ClientOption { + return func(c *clientOptions) { + c.appId = appId + } +} + +// WithAppSecret 设置AppSecret +func WithAppSecret(secret string) ClientOption { + return func(c *clientOptions) { + c.appSecret = secret + } +} + +// WithHost 设置服务器域名,例如http://risk-monitor-api.drugeyes.vip:7031/ +func WithServerHost(host string) ClientOption { + return func(c *clientOptions) { + c.host = host + } +} + +// WithTimeOut 设置请求过期时间 +func WithTimeOut(timeout time.Duration) ClientOption { + return func(c *clientOptions) { + c.timeOut = timeout + } +} + +// WithDebug 设置debug调试模式,打印请求相关内容 +func WithDebug(debug bool) ClientOption { + return func(c *clientOptions) { + c.debug = debug + } +} diff --git a/sign.go b/sign.go new file mode 100644 index 0000000..ba20f4d --- /dev/null +++ b/sign.go @@ -0,0 +1,71 @@ +package risk + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "strings" + "time" + + "github.com/google/uuid" + + "github.com/spf13/cast" +) + +func CheckSign(appID, appKey, random, sign string, ts int, data interface{}) bool { + if int(time.Now().Unix())-ts > 120 { + return false + } + + v, _ := json.Marshal(data) + + m := md5.New() + + sb := strings.Builder{} + sb.WriteString(appID) + sb.WriteByte('-') + sb.WriteString(cast.ToString(ts)) + sb.WriteByte('-') + sb.Write(v) + sb.WriteByte('-') + sb.WriteString(random) + sb.WriteByte('-') + sb.WriteString(appKey) + + m.Write([]byte(sb.String())) + md5str1 := hex.EncodeToString(m.Sum(nil)) + + return md5str1 == sign +} + +func Sign(appID, appKey string, data interface{}) (sign string, random string, ts int) { + + random = newRandom() + + ts = int(time.Now().Unix()) + + v, _ := json.Marshal(data) + + m := md5.New() + + sb := strings.Builder{} + sb.WriteString(appID) + sb.WriteByte('-') + sb.WriteString(cast.ToString(ts)) + sb.WriteByte('-') + sb.Write(v) + sb.WriteByte('-') + sb.WriteString(random) + sb.WriteByte('-') + sb.WriteString(appKey) + + m.Write([]byte(sb.String())) + sign = hex.EncodeToString(m.Sum(nil)) + return +} + +func newRandom() string { + uuid := uuid.Must(uuid.NewRandom()) + random := uuid.String() + return strings.ReplaceAll(random, "-", "") +}