调整项目结构
This commit is contained in:
parent
eb7ef01bf3
commit
6b7c774080
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.idea
|
||||
youtu_ecpm
|
||||
youtu_ecpm.exe
|
||||
data/logs
|
||||
data/logs
|
||||
data/mysql
|
12
README.md
12
README.md
@ -0,0 +1,12 @@
|
||||
## database dsn
|
||||
```
|
||||
root:youtu!0113@tcp(localhost:3306)/ecpm?charset=utf8&parseTime=True&loc=Local
|
||||
```
|
||||
|
||||
|
||||
## dao生成
|
||||
#### 通过gentool生成,model和query目录下的文件
|
||||
|
||||
```shell
|
||||
gentool -dsn "root:youtu!0113@tcp(localhost:3306)/ecpm?charset=utf8&parseTime=True&loc=Local" -fieldNullable -fieldWithIndexTag -fieldWithTypeTag -withUnitTest -fieldSignable
|
||||
```
|
7
api/gin/controller.go
Normal file
7
api/gin/controller.go
Normal file
@ -0,0 +1,7 @@
|
||||
package ecpm_httpserver
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Controller interface {
|
||||
InitRoutes(r *gin.Engine)
|
||||
}
|
108
api/gin/controller/douyin_open_api.go
Normal file
108
api/gin/controller/douyin_open_api.go
Normal file
@ -0,0 +1,108 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gitea.youtukeji.com.cn/xiabin/douyin-openapi/cache"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"time"
|
||||
"youtu_ecpm/dao/query"
|
||||
viperConfig "youtu_ecpm/pkg/config"
|
||||
"youtu_ecpm/pkg/douyinapi"
|
||||
)
|
||||
|
||||
type DouyinOpenApiController struct {
|
||||
logger *zap.Logger
|
||||
douyinCli *douyinapi.DouYinOpenApiClient
|
||||
q *query.Query
|
||||
}
|
||||
|
||||
// NewDouyinOpenApiController 实例化控制器
|
||||
// logger: 日志
|
||||
// q: 数据库
|
||||
// 将数据库中的数据存储到内存中
|
||||
func NewDouyinOpenApiController(logger *zap.Logger, q *query.Query) *DouyinOpenApiController {
|
||||
// 创建抖音客户端
|
||||
douyinCli := douyinapi.NewDouYinOpenApiClient()
|
||||
// 获取数据库中的数据
|
||||
list, err := q.Douyin.WithContext(context.Background()).Find()
|
||||
if err != nil {
|
||||
logger.Sugar().Error("获取数据失败", err)
|
||||
}
|
||||
// 将数据库中的数据存储到内存中
|
||||
for _, v := range list {
|
||||
douyinCli.NewAndStoreDouYinOpenApi(v.AppID, v.Secret, cache.NewMemory())
|
||||
}
|
||||
return &DouyinOpenApiController{
|
||||
logger: logger,
|
||||
douyinCli: douyinCli,
|
||||
}
|
||||
}
|
||||
|
||||
// GetEcpm 获取ECPM,返回true或false
|
||||
func (ctl *DouyinOpenApiController) GetEcpm(c *gin.Context) {
|
||||
var req struct {
|
||||
AppId string `json:"app_id" form:"app_id"`
|
||||
OpenId string `json:"open_id" form:"open_id"`
|
||||
}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.JSON(http.StatusOK, false)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := ctl.douyinCli.GetEcpmData(req.AppId, req.OpenId, time.Now().Format(time.DateOnly))
|
||||
if err != nil {
|
||||
ctl.logger.Sugar().Error("获取ecpm失败", err)
|
||||
c.JSON(http.StatusOK, false)
|
||||
return
|
||||
}
|
||||
|
||||
ecpm, err := ctl.douyinCli.GetEcpm(res)
|
||||
if err != nil {
|
||||
ctl.logger.Sugar().Error("计算ecpm失败", err)
|
||||
c.JSON(http.StatusOK, false)
|
||||
return
|
||||
}
|
||||
|
||||
// 打印日志
|
||||
ctl.logger.Sugar().Info("ECPM:", zap.Float64("ecpm", ecpm))
|
||||
// 根据 ECPM 值判断并返回 "true" 或 "false"
|
||||
var result bool
|
||||
if ecpm > float64(viperConfig.GetEcpmValue()) && len(res) > viperConfig.GetEcpmView() {
|
||||
result = true
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// Code2OpenId 获取openId
|
||||
func (ctl *DouyinOpenApiController) Code2OpenId(c *gin.Context) {
|
||||
code := c.Query("code")
|
||||
anonymousOpenid := c.Query("anonymous_openid")
|
||||
mpid := c.Query("mpid")
|
||||
|
||||
douyinCli, err := ctl.douyinCli.GetDouYinOpenApi(mpid)
|
||||
if err != nil {
|
||||
ctl.logger.Sugar().Error("获取小程序登录地址失败", err)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
res, err := douyinCli.Api.Code2Session(code, anonymousOpenid)
|
||||
if err != nil {
|
||||
ctl.logger.Sugar().Error("获取小程序登录地址失败", err)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": http.StatusOK,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, res.Data.Openid)
|
||||
}
|
64
api/gin/gin.go
Normal file
64
api/gin/gin.go
Normal file
@ -0,0 +1,64 @@
|
||||
package ecpm_httpserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
ginzap "github.com/gin-contrib/zap"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
"youtu_ecpm/api/gin/controller"
|
||||
"youtu_ecpm/dao/query"
|
||||
)
|
||||
|
||||
type HttpServer struct {
|
||||
engine *gin.Engine
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func NewHttpServer(logger *zap.Logger, q *query.Query) *HttpServer {
|
||||
r := gin.New()
|
||||
r.Use(ginzap.Ginzap(logger, time.RFC3339, true), ginzap.RecoveryWithZap(logger, true))
|
||||
//初始化路由
|
||||
InitRouter(r, logger, q)
|
||||
return &HttpServer{
|
||||
engine: r,
|
||||
}
|
||||
}
|
||||
|
||||
// Run 启动http服务
|
||||
func (s *HttpServer) Run() {
|
||||
srv := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: s.engine,
|
||||
}
|
||||
|
||||
go func() {
|
||||
// 服务连接
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.log.Sugar().Fatalf("listen: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
<-quit
|
||||
s.log.Sugar().Info("Shutdown Server ...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(ctx); err != nil {
|
||||
s.log.Sugar().Fatal("Server Shutdown:", err)
|
||||
}
|
||||
s.log.Sugar().Info("Server exiting")
|
||||
}
|
||||
|
||||
func InitRouter(r *gin.Engine, logger *zap.Logger, q *query.Query) {
|
||||
douyinCtl := controller.NewDouyinOpenApiController(logger, q)
|
||||
r.GET("/get-ecpm", douyinCtl.GetEcpm)
|
||||
r.GET("/code2openId", douyinCtl.Code2OpenId)
|
||||
}
|
82
api/gin/middleware/log.go
Normal file
82
api/gin/middleware/log.go
Normal file
@ -0,0 +1,82 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GinLogger 接收gin框架默认的日志
|
||||
func GinLogger(logger *zap.Logger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
query := c.Request.URL.RawQuery
|
||||
c.Next()
|
||||
|
||||
cost := time.Since(start)
|
||||
logger.Info(path,
|
||||
zap.Int("status", c.Writer.Status()),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("path", path),
|
||||
zap.String("query", query),
|
||||
zap.String("ip", c.ClientIP()),
|
||||
zap.String("user-agent", c.Request.UserAgent()),
|
||||
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
|
||||
zap.Duration("cost", cost),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// GinRecovery recover掉项目可能出现的panic
|
||||
func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// Check for a broken connection, as it is not really a
|
||||
// condition that warrants a panic stack trace.
|
||||
var brokenPipe bool
|
||||
if ne, ok := err.(*net.OpError); ok {
|
||||
if se, ok := ne.Err.(*os.SyscallError); ok {
|
||||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
|
||||
brokenPipe = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpRequest, _ := httputil.DumpRequest(c.Request, false)
|
||||
if brokenPipe {
|
||||
logger.Error(c.Request.URL.Path,
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
)
|
||||
// If the connection is dead, we can't write a status to it.
|
||||
c.Error(err.(error)) // nolint: errcheck
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if stack {
|
||||
logger.Error("[Recovery from panic]",
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
zap.String("stack", string(debug.Stack())),
|
||||
)
|
||||
} else {
|
||||
logger.Error("[Recovery from panic]",
|
||||
zap.Any("error", err),
|
||||
zap.String("request", string(httpRequest)),
|
||||
)
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
24
cmd/main.go
Normal file
24
cmd/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"youtu_ecpm/dao/query"
|
||||
"youtu_ecpm/pkg/config"
|
||||
"youtu_ecpm/pkg/db"
|
||||
"youtu_ecpm/pkg/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// 初始化日志
|
||||
zapLog := log.NewLog(config.GetLogFormat(), config.GetAppEnv(), config.GetLogLevel(), config.GetLogPath())
|
||||
|
||||
// 初始化数据库
|
||||
q := query.Use(db.Db)
|
||||
|
||||
app, _, err := wireApp(zapLog, q)
|
||||
if err != nil {
|
||||
zapLog.Sugar().Fatal("运行失败", err)
|
||||
return
|
||||
}
|
||||
app.Run()
|
||||
}
|
22
cmd/wire.go
Normal file
22
cmd/wire.go
Normal file
@ -0,0 +1,22 @@
|
||||
//go:build wireinject
|
||||
// +build wireinject
|
||||
|
||||
// The build tag makes sure the stub is not built in the final build.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"go.uber.org/zap"
|
||||
ecpm_httpserver "youtu_ecpm/api/gin"
|
||||
"youtu_ecpm/dao/query"
|
||||
"youtu_ecpm/server"
|
||||
)
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(log *zap.Logger, q *query.Query) (*server.EcpmApp, func(), error) {
|
||||
panic(wire.Build(
|
||||
wire.Value(q),
|
||||
server.NewEcpmApp,
|
||||
ecpm_httpserver.NewHttpServer,
|
||||
))
|
||||
}
|
24
cmd/wire_gen.go
Normal file
24
cmd/wire_gen.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"youtu_ecpm/api/gin"
|
||||
"youtu_ecpm/dao/query"
|
||||
"youtu_ecpm/server"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(log *zap.Logger, q *query.Query) (*server.EcpmApp, func(), error) {
|
||||
httpServer := ecpm_httpserver.NewHttpServer(log, q)
|
||||
ecpmApp := server.NewEcpmApp(httpServer)
|
||||
return ecpmApp, func() {
|
||||
}, nil
|
||||
}
|
21
dao/model/douyin.gen.go
Normal file
21
dao/model/douyin.gen.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
const TableNameDouyin = "douyin"
|
||||
|
||||
// Douyin mapped from table <douyin>
|
||||
type Douyin struct {
|
||||
ID uint32 `gorm:"column:id;type:int unsigned;primaryKey;autoIncrement:true" json:"id"`
|
||||
AppID string `gorm:"column:app_id;type:varchar(20);not null" json:"app_id"`
|
||||
Secret string `gorm:"column:secret;type:varchar(40);not null" json:"secret"`
|
||||
EcpmValue int32 `gorm:"column:ecpm_value;type:int;not null" json:"ecpm_value"`
|
||||
EcpmView int32 `gorm:"column:ecpm_view;type:int;not null" json:"ecpm_view"`
|
||||
}
|
||||
|
||||
// TableName Douyin's table name
|
||||
func (*Douyin) TableName() string {
|
||||
return TableNameDouyin
|
||||
}
|
343
dao/query/douyin.gen.go
Normal file
343
dao/query/douyin.gen.go
Normal file
@ -0,0 +1,343 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"youtu_ecpm/dao/model"
|
||||
)
|
||||
|
||||
func newDouyin(db *gorm.DB, opts ...gen.DOOption) douyin {
|
||||
_douyin := douyin{}
|
||||
|
||||
_douyin.douyinDo.UseDB(db, opts...)
|
||||
_douyin.douyinDo.UseModel(&model.Douyin{})
|
||||
|
||||
tableName := _douyin.douyinDo.TableName()
|
||||
_douyin.ALL = field.NewAsterisk(tableName)
|
||||
_douyin.ID = field.NewUint32(tableName, "id")
|
||||
_douyin.AppID = field.NewString(tableName, "app_id")
|
||||
_douyin.Secret = field.NewString(tableName, "secret")
|
||||
_douyin.EcpmValue = field.NewInt32(tableName, "ecpm_value")
|
||||
_douyin.EcpmView = field.NewInt32(tableName, "ecpm_view")
|
||||
|
||||
_douyin.fillFieldMap()
|
||||
|
||||
return _douyin
|
||||
}
|
||||
|
||||
type douyin struct {
|
||||
douyinDo douyinDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Uint32
|
||||
AppID field.String
|
||||
Secret field.String
|
||||
EcpmValue field.Int32
|
||||
EcpmView field.Int32
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (d douyin) Table(newTableName string) *douyin {
|
||||
d.douyinDo.UseTable(newTableName)
|
||||
return d.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (d douyin) As(alias string) *douyin {
|
||||
d.douyinDo.DO = *(d.douyinDo.As(alias).(*gen.DO))
|
||||
return d.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (d *douyin) updateTableName(table string) *douyin {
|
||||
d.ALL = field.NewAsterisk(table)
|
||||
d.ID = field.NewUint32(table, "id")
|
||||
d.AppID = field.NewString(table, "app_id")
|
||||
d.Secret = field.NewString(table, "secret")
|
||||
d.EcpmValue = field.NewInt32(table, "ecpm_value")
|
||||
d.EcpmView = field.NewInt32(table, "ecpm_view")
|
||||
|
||||
d.fillFieldMap()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *douyin) WithContext(ctx context.Context) *douyinDo { return d.douyinDo.WithContext(ctx) }
|
||||
|
||||
func (d douyin) TableName() string { return d.douyinDo.TableName() }
|
||||
|
||||
func (d douyin) Alias() string { return d.douyinDo.Alias() }
|
||||
|
||||
func (d douyin) Columns(cols ...field.Expr) gen.Columns { return d.douyinDo.Columns(cols...) }
|
||||
|
||||
func (d *douyin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := d.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (d *douyin) fillFieldMap() {
|
||||
d.fieldMap = make(map[string]field.Expr, 5)
|
||||
d.fieldMap["id"] = d.ID
|
||||
d.fieldMap["app_id"] = d.AppID
|
||||
d.fieldMap["secret"] = d.Secret
|
||||
d.fieldMap["ecpm_value"] = d.EcpmValue
|
||||
d.fieldMap["ecpm_view"] = d.EcpmView
|
||||
}
|
||||
|
||||
func (d douyin) clone(db *gorm.DB) douyin {
|
||||
d.douyinDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d douyin) replaceDB(db *gorm.DB) douyin {
|
||||
d.douyinDo.ReplaceDB(db)
|
||||
return d
|
||||
}
|
||||
|
||||
type douyinDo struct{ gen.DO }
|
||||
|
||||
func (d douyinDo) Debug() *douyinDo {
|
||||
return d.withDO(d.DO.Debug())
|
||||
}
|
||||
|
||||
func (d douyinDo) WithContext(ctx context.Context) *douyinDo {
|
||||
return d.withDO(d.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (d douyinDo) ReadDB() *douyinDo {
|
||||
return d.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (d douyinDo) WriteDB() *douyinDo {
|
||||
return d.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (d douyinDo) Session(config *gorm.Session) *douyinDo {
|
||||
return d.withDO(d.DO.Session(config))
|
||||
}
|
||||
|
||||
func (d douyinDo) Clauses(conds ...clause.Expression) *douyinDo {
|
||||
return d.withDO(d.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Returning(value interface{}, columns ...string) *douyinDo {
|
||||
return d.withDO(d.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Not(conds ...gen.Condition) *douyinDo {
|
||||
return d.withDO(d.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Or(conds ...gen.Condition) *douyinDo {
|
||||
return d.withDO(d.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Select(conds ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Where(conds ...gen.Condition) *douyinDo {
|
||||
return d.withDO(d.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Order(conds ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Distinct(cols ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Omit(cols ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Join(table schema.Tabler, on ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (d douyinDo) LeftJoin(table schema.Tabler, on ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (d douyinDo) RightJoin(table schema.Tabler, on ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Group(cols ...field.Expr) *douyinDo {
|
||||
return d.withDO(d.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Having(conds ...gen.Condition) *douyinDo {
|
||||
return d.withDO(d.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Limit(limit int) *douyinDo {
|
||||
return d.withDO(d.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (d douyinDo) Offset(offset int) *douyinDo {
|
||||
return d.withDO(d.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (d douyinDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *douyinDo {
|
||||
return d.withDO(d.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Unscoped() *douyinDo {
|
||||
return d.withDO(d.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (d douyinDo) Create(values ...*model.Douyin) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.DO.Create(values)
|
||||
}
|
||||
|
||||
func (d douyinDo) CreateInBatches(values []*model.Douyin, batchSize int) error {
|
||||
return d.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (d douyinDo) Save(values ...*model.Douyin) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.DO.Save(values)
|
||||
}
|
||||
|
||||
func (d douyinDo) First() (*model.Douyin, error) {
|
||||
if result, err := d.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.Douyin), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d douyinDo) Take() (*model.Douyin, error) {
|
||||
if result, err := d.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.Douyin), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d douyinDo) Last() (*model.Douyin, error) {
|
||||
if result, err := d.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.Douyin), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d douyinDo) Find() ([]*model.Douyin, error) {
|
||||
result, err := d.DO.Find()
|
||||
return result.([]*model.Douyin), err
|
||||
}
|
||||
|
||||
func (d douyinDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Douyin, err error) {
|
||||
buf := make([]*model.Douyin, 0, batchSize)
|
||||
err = d.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (d douyinDo) FindInBatches(result *[]*model.Douyin, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return d.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (d douyinDo) Attrs(attrs ...field.AssignExpr) *douyinDo {
|
||||
return d.withDO(d.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Assign(attrs ...field.AssignExpr) *douyinDo {
|
||||
return d.withDO(d.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (d douyinDo) Joins(fields ...field.RelationField) *douyinDo {
|
||||
for _, _f := range fields {
|
||||
d = *d.withDO(d.DO.Joins(_f))
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d douyinDo) Preload(fields ...field.RelationField) *douyinDo {
|
||||
for _, _f := range fields {
|
||||
d = *d.withDO(d.DO.Preload(_f))
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d douyinDo) FirstOrInit() (*model.Douyin, error) {
|
||||
if result, err := d.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.Douyin), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d douyinDo) FirstOrCreate() (*model.Douyin, error) {
|
||||
if result, err := d.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.Douyin), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d douyinDo) FindByPage(offset int, limit int) (result []*model.Douyin, count int64, err error) {
|
||||
result, err = d.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = d.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (d douyinDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = d.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = d.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (d douyinDo) Scan(result interface{}) (err error) {
|
||||
return d.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (d douyinDo) Delete(models ...*model.Douyin) (result gen.ResultInfo, err error) {
|
||||
return d.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (d *douyinDo) withDO(do gen.Dao) *douyinDo {
|
||||
d.DO = *do.(*gen.DO)
|
||||
return d
|
||||
}
|
146
dao/query/douyin.gen_test.go
Normal file
146
dao/query/douyin.gen_test.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"youtu_ecpm/dao/model"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func init() {
|
||||
InitializeDB()
|
||||
err := _gen_test_db.AutoMigrate(&model.Douyin{})
|
||||
if err != nil {
|
||||
fmt.Printf("Error: AutoMigrate(&model.Douyin{}) fail: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_douyinQuery(t *testing.T) {
|
||||
douyin := newDouyin(_gen_test_db)
|
||||
douyin = *douyin.As(douyin.TableName())
|
||||
_do := douyin.WithContext(context.Background()).Debug()
|
||||
|
||||
primaryKey := field.NewString(douyin.TableName(), clause.PrimaryKey)
|
||||
_, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
|
||||
if err != nil {
|
||||
t.Error("clean table <douyin> fail:", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, ok := douyin.GetFieldByName("")
|
||||
if ok {
|
||||
t.Error("GetFieldByName(\"\") from douyin success")
|
||||
}
|
||||
|
||||
err = _do.Create(&model.Douyin{})
|
||||
if err != nil {
|
||||
t.Error("create item in table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
err = _do.Save(&model.Douyin{})
|
||||
if err != nil {
|
||||
t.Error("create item in table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
err = _do.CreateInBatches([]*model.Douyin{{}, {}}, 10)
|
||||
if err != nil {
|
||||
t.Error("create item in table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Select(douyin.ALL).Take()
|
||||
if err != nil {
|
||||
t.Error("Take() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.First()
|
||||
if err != nil {
|
||||
t.Error("First() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Last()
|
||||
if err != nil {
|
||||
t.Error("First() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gen.Dao, batch int) error { return nil })
|
||||
if err != nil {
|
||||
t.Error("FindInBatch() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*model.Douyin{}, 10, func(tx gen.Dao, batch int) error { return nil })
|
||||
if err != nil {
|
||||
t.Error("FindInBatches() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Select(douyin.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
|
||||
if err != nil {
|
||||
t.Error("Find() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Distinct(primaryKey).Take()
|
||||
if err != nil {
|
||||
t.Error("select Distinct() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Select(douyin.ALL).Omit(primaryKey).Take()
|
||||
if err != nil {
|
||||
t.Error("Omit() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Group(primaryKey).Find()
|
||||
if err != nil {
|
||||
t.Error("Group() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Scopes(func(dao gen.Dao) gen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
|
||||
if err != nil {
|
||||
t.Error("Scopes() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, _, err = _do.FindByPage(0, 1)
|
||||
if err != nil {
|
||||
t.Error("FindByPage() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.ScanByPage(&model.Douyin{}, 0, 1)
|
||||
if err != nil {
|
||||
t.Error("ScanByPage() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
|
||||
if err != nil {
|
||||
t.Error("FirstOrInit() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
|
||||
if err != nil {
|
||||
t.Error("FirstOrCreate() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
var _a _another
|
||||
var _aPK = field.NewString(_a.TableName(), "id")
|
||||
|
||||
err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Error("Join() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
|
||||
if err != nil {
|
||||
t.Error("LeftJoin() on table <douyin> fail:", err)
|
||||
}
|
||||
|
||||
_, err = _do.Not().Or().Clauses().Take()
|
||||
if err != nil {
|
||||
t.Error("Not/Or/Clauses on table <douyin> fail:", err)
|
||||
}
|
||||
}
|
93
dao/query/gen.go
Normal file
93
dao/query/gen.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gorm.io/gen"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
Douyin: newDouyin(db, opts...),
|
||||
}
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
db *gorm.DB
|
||||
|
||||
Douyin douyin
|
||||
}
|
||||
|
||||
func (q *Query) Available() bool { return q.db != nil }
|
||||
|
||||
func (q *Query) clone(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
Douyin: q.Douyin.clone(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) ReadDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
|
||||
}
|
||||
|
||||
func (q *Query) WriteDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
|
||||
}
|
||||
|
||||
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
Douyin: q.Douyin.replaceDB(db),
|
||||
}
|
||||
}
|
||||
|
||||
type queryCtx struct {
|
||||
Douyin *douyinDo
|
||||
}
|
||||
|
||||
func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
return &queryCtx{
|
||||
Douyin: q.Douyin.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
|
||||
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
|
||||
}
|
||||
|
||||
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
|
||||
tx := q.db.Begin(opts...)
|
||||
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
|
||||
}
|
||||
|
||||
type QueryTx struct {
|
||||
*Query
|
||||
Error error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Commit() error {
|
||||
return q.db.Commit().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Rollback() error {
|
||||
return q.db.Rollback().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) SavePoint(name string) error {
|
||||
return q.db.SavePoint(name).Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) RollbackTo(name string) error {
|
||||
return q.db.RollbackTo(name).Error
|
||||
}
|
118
dao/query/gen_test.go
Normal file
118
dao/query/gen_test.go
Normal file
@ -0,0 +1,118 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
Args []interface{}
|
||||
}
|
||||
|
||||
type Expectation struct {
|
||||
Ret []interface{}
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
Input
|
||||
Expectation
|
||||
}
|
||||
|
||||
const _gen_test_db_name = "gen_test.db"
|
||||
|
||||
var _gen_test_db *gorm.DB
|
||||
var _gen_test_once sync.Once
|
||||
|
||||
func init() {
|
||||
InitializeDB()
|
||||
_gen_test_db.AutoMigrate(&_another{})
|
||||
}
|
||||
|
||||
func InitializeDB() {
|
||||
_gen_test_once.Do(func() {
|
||||
var err error
|
||||
_gen_test_db, err = gorm.Open(sqlite.Open(_gen_test_db_name), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("open sqlite %q fail: %w", _gen_test_db_name, err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func assert(t *testing.T, methodName string, res, exp interface{}) {
|
||||
if !reflect.DeepEqual(res, exp) {
|
||||
t.Errorf("%v() gotResult = %v, want %v", methodName, res, exp)
|
||||
}
|
||||
}
|
||||
|
||||
type _another struct {
|
||||
ID uint64 `gorm:"primaryKey"`
|
||||
}
|
||||
|
||||
func (*_another) TableName() string { return "another_for_unit_test" }
|
||||
|
||||
func Test_Available(t *testing.T) {
|
||||
if !Use(_gen_test_db).Available() {
|
||||
t.Errorf("query.Available() == false")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_WithContext(t *testing.T) {
|
||||
query := Use(_gen_test_db)
|
||||
if !query.Available() {
|
||||
t.Errorf("query Use(_gen_test_db) fail: query.Available() == false")
|
||||
}
|
||||
|
||||
type Content string
|
||||
var key, value Content = "gen_tag", "unit_test"
|
||||
qCtx := query.WithContext(context.WithValue(context.Background(), key, value))
|
||||
|
||||
for _, ctx := range []context.Context{
|
||||
qCtx.Douyin.UnderlyingDB().Statement.Context,
|
||||
} {
|
||||
if v := ctx.Value(key); v != value {
|
||||
t.Errorf("get value from context fail, expect %q, got %q", value, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Transaction(t *testing.T) {
|
||||
query := Use(_gen_test_db)
|
||||
if !query.Available() {
|
||||
t.Errorf("query Use(_gen_test_db) fail: query.Available() == false")
|
||||
}
|
||||
|
||||
err := query.Transaction(func(tx *Query) error { return nil })
|
||||
if err != nil {
|
||||
t.Errorf("query.Transaction execute fail: %s", err)
|
||||
}
|
||||
|
||||
tx := query.Begin()
|
||||
|
||||
err = tx.SavePoint("point")
|
||||
if err != nil {
|
||||
t.Errorf("query tx SavePoint fail: %s", err)
|
||||
}
|
||||
err = tx.RollbackTo("point")
|
||||
if err != nil {
|
||||
t.Errorf("query tx RollbackTo fail: %s", err)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Errorf("query tx Commit fail: %s", err)
|
||||
}
|
||||
|
||||
err = query.Begin().Rollback()
|
||||
if err != nil {
|
||||
t.Errorf("query tx Rollback fail: %s", err)
|
||||
}
|
||||
}
|
31
docker-compose.yaml
Normal file
31
docker-compose.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
version: "3"
|
||||
services:
|
||||
mysql:
|
||||
image: mysql
|
||||
container_name: mysql
|
||||
restart: always
|
||||
command: [
|
||||
'--character-set-server=utf8mb4',
|
||||
'--collation-server=utf8mb4_general_ci',
|
||||
'--explicit_defaults_for_timestamp=true',
|
||||
'--lower_case_table_names=1'
|
||||
]
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: youtu!0113
|
||||
MYSQL_INITDB_SKIP_TZINFO: "Asia/Shanghai"
|
||||
#MYSQL_DATABASE: data_sys
|
||||
volumes:
|
||||
#数据目录,要确保先创建好
|
||||
- ./data/mysql/data:/var/lib/mysql
|
||||
- ./data/mysql/logs:/var/log/mysql
|
||||
##初始化的脚本,初始化我们存放的init.sql文件
|
||||
- ./data/mysql/initdb:/docker-entrypoint-initdb.d/
|
||||
- ./data/mysql/conf:/etc/mysql/conf.d
|
||||
ports:
|
||||
- "3306:3306"
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-uyoutu", "-pyoutu!0113" ]
|
||||
interval: 6s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
#network_mode: host
|
49
go.mod
49
go.mod
@ -2,37 +2,45 @@ module youtu_ecpm
|
||||
|
||||
go 1.23.4
|
||||
|
||||
replace gitea.youtukeji.com.cn/xiabin/douyin-openapi => ../douyin-openapi
|
||||
|
||||
require (
|
||||
github.com/bytedance/douyin-openapi-credential-go v0.0.0-20240627133153-7f4587ca06ce
|
||||
github.com/bytedance/douyin-openapi-sdk-go v0.0.0-20240925072830-12f094544623
|
||||
gitea.youtukeji.com.cn/xiabin/douyin-openapi v0.0.1
|
||||
github.com/gin-contrib/zap v1.1.4
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/spf13/viper v1.19.0
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/driver/mysql v1.4.4
|
||||
gorm.io/driver/sqlite v1.4.3
|
||||
gorm.io/gen v0.3.26
|
||||
gorm.io/gorm v1.25.9
|
||||
gorm.io/plugin/dbresolver v1.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alibabacloud-go/debug v1.0.0 // indirect
|
||||
github.com/alibabacloud-go/tea v1.2.2 // indirect
|
||||
github.com/bytedance/douyin-openapi-util-go v0.0.0-20240627134255-db766d8741c8 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.1 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.12.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
@ -47,13 +55,18 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/arch v0.9.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
|
||||
gorm.io/hints v1.1.0 // indirect
|
||||
)
|
||||
|
157
go.sum
157
go.sum
@ -1,17 +1,8 @@
|
||||
github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA=
|
||||
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
|
||||
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
|
||||
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
|
||||
github.com/bytedance/douyin-openapi-credential-go v0.0.0-20240627133153-7f4587ca06ce h1:Mn9pJpYYR5tNQZEUorCj2NZoBWbCDjRZTbgRB4hMQXI=
|
||||
github.com/bytedance/douyin-openapi-credential-go v0.0.0-20240627133153-7f4587ca06ce/go.mod h1:OKJKotnRJazXhZzj4dwUvdw9OuupYNJmctz70MoJme8=
|
||||
github.com/bytedance/douyin-openapi-sdk-go v0.0.0-20240925072830-12f094544623 h1:NxYsIQCpexUPrzMnotXbTiCJKmhH9IsvjOGGOv+iztk=
|
||||
github.com/bytedance/douyin-openapi-sdk-go v0.0.0-20240925072830-12f094544623/go.mod h1:dsLFkIt2aKodjL5Y+JDS0wK0PWb9/I7RwNu8JAixtPs=
|
||||
github.com/bytedance/douyin-openapi-util-go v0.0.0-20240627134255-db766d8741c8 h1:71WIUeJE02/oi/sgrIseKSGBQtiA2Dofl/pV7oe4TZk=
|
||||
github.com/bytedance/douyin-openapi-util-go v0.0.0-20240627134255-db766d8741c8/go.mod h1:GPiogxOAuOSzXMhJ+akYQBLnb6+lGv24kf8JBMtBb2Y=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
@ -24,10 +15,12 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-contrib/zap v1.1.4 h1:xvxTybg6XBdNtcQLH3Tf0lFr4vhDkwzgLLrIGlNTqIo=
|
||||
github.com/gin-contrib/zap v1.1.4/go.mod h1:7lgEpe91kLbeJkwBTPgtVBy4zMa6oSBEcvj662diqKQ=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@ -36,22 +29,53 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA=
|
||||
github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
|
||||
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
|
||||
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
|
||||
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@ -63,6 +87,11 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -116,33 +145,38 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -151,35 +185,36 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -190,5 +225,29 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c h1:jWdr7cHgl8c/ua5vYbR2WhSp+NQmzhsj0xoY3foTzW8=
|
||||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE=
|
||||
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ=
|
||||
gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM=
|
||||
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
|
||||
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
|
||||
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
|
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
|
||||
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
|
||||
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
|
||||
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw=
|
||||
gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y=
|
||||
gorm.io/plugin/dbresolver v1.5.0 h1:XVHLxh775eP0CqVh3vcfJtYqja3uFl5Wr3cKlY8jgDY=
|
||||
gorm.io/plugin/dbresolver v1.5.0/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
37
main.go
37
main.go
@ -1,37 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"strconv"
|
||||
"time"
|
||||
viperConfig "youtu_ecpm/config"
|
||||
"youtu_ecpm/log"
|
||||
"youtu_ecpm/router"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery(), zapMiddleware(log.ZapLog))
|
||||
router.InitGin(r)
|
||||
r.Run(":" + strconv.Itoa(viperConfig.GetPort())) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
|
||||
}
|
||||
|
||||
// zapMiddleware 记录每个请求的基本信息
|
||||
func zapMiddleware(zapLogger *log.Logger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 请求开始时间
|
||||
start := time.Now()
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
|
||||
// 记录请求信息
|
||||
zapLogger.Info("HTTP Request",
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("status", strconv.Itoa(c.Writer.Status())),
|
||||
zap.String("duration", time.Since(start).String()),
|
||||
)
|
||||
}
|
||||
}
|
13
model/douyin.go
Normal file
13
model/douyin.go
Normal file
@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
type Douyin struct {
|
||||
Id uint `gorm:"column:id;type:int(11) unsigned;primary_key;AUTO_INCREMENT" json:"id"`
|
||||
AppId string `gorm:"column:app_id;type:varchar(20);NOT NULL" json:"app_id"`
|
||||
Secret string `gorm:"column:secret;type:varchar(40);NOT NULL" json:"secret"`
|
||||
EcpmValue int `gorm:"column:ecpm_value;type:int(11);NOT NULL" json:"ecpm_value"`
|
||||
EcpmView int `gorm:"column:ecpm_view;type:int(11);NOT NULL" json:"ecpm_view"`
|
||||
}
|
||||
|
||||
func (m *Douyin) TableName() string {
|
||||
return "douyin"
|
||||
}
|
26
pkg/db/gorm.go
Normal file
26
pkg/db/gorm.go
Normal file
@ -0,0 +1,26 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var Db *gorm.DB
|
||||
|
||||
func init() {
|
||||
//配置MySQL连接参数
|
||||
username := "root" //账号
|
||||
password := "youtu!0113" //密码
|
||||
host := "localhost" //数据库地址,可以是Ip或者域名
|
||||
port := 3306 //数据库端口
|
||||
Dbname := "ecpm" //数据库名
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
|
||||
fmt.Println(dsn)
|
||||
db, err := gorm.Open(mysql.Open(dsn))
|
||||
if err != nil {
|
||||
panic("连接数据库失败, error=" + err.Error())
|
||||
}
|
||||
Db = db
|
||||
|
||||
}
|
114
pkg/douyinapi/client.go
Normal file
114
pkg/douyinapi/client.go
Normal file
@ -0,0 +1,114 @@
|
||||
package douyinapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
douyinopenapi "gitea.youtukeji.com.cn/xiabin/douyin-openapi"
|
||||
access_token "gitea.youtukeji.com.cn/xiabin/douyin-openapi/access-token"
|
||||
"gitea.youtukeji.com.cn/xiabin/douyin-openapi/cache"
|
||||
"gorm.io/gorm"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var DouyinCli *DouYinOpenApiClient
|
||||
|
||||
type DouYinOpenApiClient struct {
|
||||
db *gorm.DB
|
||||
m *sync.Map
|
||||
}
|
||||
|
||||
type DouYinApi struct {
|
||||
Api *douyinopenapi.DouYinOpenApi
|
||||
Token *access_token.DefaultAccessToken
|
||||
}
|
||||
|
||||
func NewDouYinOpenApiClient() *DouYinOpenApiClient {
|
||||
//DouYinOpenApi = douyinopenapi.NewDouYinOpenApi(douyinopenapi.DouYinOpenApiConfig{
|
||||
// AppId: "1259819191",
|
||||
// AppSecret: "0b7d7c0d0c8c0a0b0a0c0d0e0f0f0e0d0c0b0a090807060504030201000",
|
||||
// IsSandbox: true,
|
||||
// AccessToken: nil,
|
||||
// Cache: nil,
|
||||
//})
|
||||
return &DouYinOpenApiClient{
|
||||
m: &sync.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
// GetDouYinOpenApi 获取抖音client
|
||||
// appId: 小程序id
|
||||
func (d *DouYinOpenApiClient) GetDouYinOpenApi(appId string) (api *DouYinApi, err error) {
|
||||
if v, ok := d.m.Load(appId); !ok {
|
||||
err = ErrCacheNotFound
|
||||
return
|
||||
} else {
|
||||
api = v.(*DouYinApi)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetDouYinOpenApi 存储抖音client
|
||||
// appId: 小程序id
|
||||
func (d *DouYinOpenApiClient) SetDouYinOpenApi(appId string, api *DouYinApi) {
|
||||
d.m.Store(appId, api)
|
||||
}
|
||||
|
||||
// NewAndStoreDouYinOpenApi 创建抖音client并存储
|
||||
// appId: 小程序id
|
||||
// appSecret: 小程序secret
|
||||
// cache: 缓存
|
||||
func (d *DouYinOpenApiClient) NewAndStoreDouYinOpenApi(appId, appSecret string, cache cache.Cache) {
|
||||
api := douyinopenapi.NewDouYinOpenApi(douyinopenapi.DouYinOpenApiConfig{})
|
||||
token := access_token.NewDefaultAccessToken(appId, appSecret, cache, false)
|
||||
d.SetDouYinOpenApi(appId, &DouYinApi{
|
||||
Api: api,
|
||||
Token: token,
|
||||
})
|
||||
}
|
||||
|
||||
// GetEcpmData 获取ECPM数据
|
||||
// appId: 小程序id
|
||||
// openId: 抖音openId
|
||||
// dateHour: 日期
|
||||
func (d *DouYinOpenApiClient) GetEcpmData(appId, openId, dateHour string) (list []douyinopenapi.Record, err error) {
|
||||
douyin, err := d.GetDouYinOpenApi(appId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//获取accessToken
|
||||
accessToken, err := douyin.Token.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err = douyin.Api.GetEcpm(douyinopenapi.GetEcpmParams{
|
||||
AppId: appId,
|
||||
OpenId: openId,
|
||||
AccessToken: accessToken,
|
||||
DateHour: dateHour,
|
||||
PageSize: 500,
|
||||
PageNo: 1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetEcpm 计算ECPM
|
||||
// https://bytedance.larkoffice.com/docx/Vg4yd0RDSovZINxJDyIc6THhnod
|
||||
func (d *DouYinOpenApiClient) GetEcpm(res []douyinopenapi.Record) (ecpm float64, err error) {
|
||||
// 计算 ECPM
|
||||
totalCost := 0
|
||||
totalRecords := len(res)
|
||||
|
||||
for _, record := range res {
|
||||
totalCost += record.Cost
|
||||
}
|
||||
|
||||
// 如果没有记录,则返回错误
|
||||
if totalRecords == 0 {
|
||||
err = errors.New("未找到记录,无法计算 ECPM")
|
||||
return
|
||||
}
|
||||
// 总 cost / 100000 * 1000 / 总记录数
|
||||
ecpm = float64(totalCost) / 100000 * 1000 / float64(totalRecords)
|
||||
return
|
||||
}
|
6
pkg/douyinapi/errors.go
Normal file
6
pkg/douyinapi/errors.go
Normal file
@ -0,0 +1,6 @@
|
||||
package douyinapi
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrCacheNotFound 缓存未找到
|
||||
var ErrCacheNotFound = errors.New("cache not found")
|
17
pkg/log/logger.go
Normal file
17
pkg/log/logger.go
Normal file
@ -0,0 +1,17 @@
|
||||
package log
|
||||
|
||||
//
|
||||
//type Log interface {
|
||||
// Debug(v ...interface{})
|
||||
// Info(v ...interface{})
|
||||
// Warn(v ...interface{})
|
||||
// Error(v ...interface{})
|
||||
// Fatal(v ...interface{})
|
||||
// Panic(v ...interface{})
|
||||
//
|
||||
// Debugf(format string, v ...interface{})
|
||||
// Infof(template string, args ...interface{})
|
||||
// Errorf(format string, v ...interface{})
|
||||
// Warnf(format string, v ...interface{})
|
||||
// Panicf(format string, v ...interface{})
|
||||
//}
|
@ -1,8 +1,6 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
@ -10,19 +8,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var ZapLog *Logger
|
||||
|
||||
func init() {
|
||||
ZapLog = NewLog("json", "", "debug", "./data/logs")
|
||||
}
|
||||
|
||||
const ctxLoggerKey = "zapLogger"
|
||||
|
||||
type Logger struct {
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
func NewLog(console, env, lv, lp string) *Logger {
|
||||
func NewLog(console, env, lv, lp string) *zap.Logger {
|
||||
//目录不存在则创建
|
||||
if _, err := os.Stat(lp); err != nil {
|
||||
err := os.MkdirAll(lp, os.ModePerm)
|
||||
@ -30,7 +16,7 @@ func NewLog(console, env, lv, lp string) *Logger {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// log address "out.log" User-defined
|
||||
|
||||
var level zapcore.Level
|
||||
//debug<info<warn<error<fatal<panic
|
||||
switch lv {
|
||||
@ -97,12 +83,18 @@ func NewLog(console, env, lv, lp string) *Logger {
|
||||
)
|
||||
//定义一个环境生产环境还是开发环境
|
||||
//env := "local"
|
||||
var log *zap.Logger
|
||||
|
||||
if env != "prod" {
|
||||
// 开发模式下 Zap 会记录更详细的日志信息,包括调用者信息和错误级别的堆栈跟踪
|
||||
return &Logger{zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
||||
log = zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))
|
||||
} else {
|
||||
//如果是生产环境,创建一个 Logger 实例,仅记录调用者信息和错误级别的堆栈跟踪
|
||||
log = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))
|
||||
}
|
||||
//如果是生产环境,创建一个 Logger 实例,仅记录调用者信息和错误级别的堆栈跟踪
|
||||
return &Logger{zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
|
||||
zap.ReplaceGlobals(log)
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
// timeEncoder 格式化一下当前实际
|
||||
@ -110,34 +102,3 @@ func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
//enc.AppendString(t.Format("2006-01-02 15:04:05"))
|
||||
enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
|
||||
}
|
||||
|
||||
// WithValue 将 Zap 日志字段和当前的 context.Context 绑定,以便在 HTTP 请求处理过程中可以方便地记录日志。
|
||||
func (l *Logger) WithValue(ctx context.Context, fields ...zapcore.Field) context.Context {
|
||||
// 检查传入的 context 是否为 *gin.Context 类型
|
||||
if c, ok := ctx.(*gin.Context); ok {
|
||||
// 将 gin.Context 转换为标准的 context.Context
|
||||
ctx = c.Request.Context()
|
||||
// 使用 context.WithValue 将 Zap 日志字段绑定到 context
|
||||
// 这样在请求处理过程中就可以通过 context 获取日志记录器实例
|
||||
c.Request = c.Request.WithContext(context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...)))
|
||||
return c
|
||||
}
|
||||
// 如果 context 不是 *gin.Context 类型,直接绑定 Zap 日志字段
|
||||
return context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...))
|
||||
}
|
||||
|
||||
// WithContext Returns a zap instance from the specified context
|
||||
func (l *Logger) WithContext(ctx context.Context) *Logger {
|
||||
//检查传入的 context 是否为 *gin.Context 类型
|
||||
//将 gin.Context 转换为标准的 context.Context
|
||||
if c, ok := ctx.(*gin.Context); ok {
|
||||
ctx = c.Request.Context()
|
||||
}
|
||||
//从上下文中提取键为ctxLoggerKey的值,并尝试将其转换为*zap.Logger。
|
||||
zl := ctx.Value(ctxLoggerKey)
|
||||
ctxLogger, ok := zl.(*zap.Logger)
|
||||
if ok {
|
||||
return &Logger{ctxLogger}
|
||||
}
|
||||
return l
|
||||
}
|
255
router/router.go
255
router/router.go
@ -1,255 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
credential "github.com/bytedance/douyin-openapi-credential-go/client"
|
||||
openApiSdkClient "github.com/bytedance/douyin-openapi-sdk-go/client"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
viperConfig "youtu_ecpm/config"
|
||||
"youtu_ecpm/log"
|
||||
)
|
||||
|
||||
func InitGin(r *gin.Engine) *gin.Engine {
|
||||
r.GET("/get-ecpm", getEcpm)
|
||||
r.GET("/code2openId", code2openId)
|
||||
return r
|
||||
}
|
||||
|
||||
// 定义抖音 API 响应的结构体
|
||||
type APIResponse struct {
|
||||
BaseResp struct {
|
||||
StatusCode int `json:"StatusCode"`
|
||||
StatusMessage string `json:"StatusMessage"`
|
||||
} `json:"BaseResp"`
|
||||
Data struct {
|
||||
Records []Record `json:"records"`
|
||||
Total int `json:"total"`
|
||||
} `json:"data"`
|
||||
ErrMsg string `json:"err_msg"`
|
||||
ErrNo int `json:"err_no"`
|
||||
LogID string `json:"log_id"`
|
||||
}
|
||||
|
||||
type Record struct {
|
||||
Aid string `json:"aid"`
|
||||
Cost int `json:"cost"`
|
||||
Did string `json:"did"`
|
||||
EventName string `json:"event_name"`
|
||||
EventTime string `json:"event_time"`
|
||||
OpenID string `json:"open_id"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type EcpmReq struct {
|
||||
OpenId string `json:"open_id" form:"open_id"`
|
||||
MpId string `json:"mp_id" form:"mp_id" binding:"required"`
|
||||
}
|
||||
|
||||
func code2openId(c *gin.Context) {
|
||||
code := c.Query("code")
|
||||
anonymousOpenid := c.Query("anonymous_openid")
|
||||
openId, err := AppsJsCode2session(code, anonymousOpenid)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": http.StatusOK,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.String(http.StatusOK, openId)
|
||||
}
|
||||
|
||||
func getEcpm(c *gin.Context) {
|
||||
var req EcpmReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if req.OpenId == "" {
|
||||
c.JSON(http.StatusBadRequest, false)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取动态的 access_token
|
||||
accessToken, err := GetAccessToken()
|
||||
if err != nil {
|
||||
errorMessage := fmt.Sprintf("获取 access_token 失败: %v", err)
|
||||
fmt.Println(errorMessage)
|
||||
c.String(http.StatusInternalServerError, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := getEcpmFromApi(req, accessToken)
|
||||
|
||||
// 计算 ECPM
|
||||
totalCost := 0
|
||||
totalRecords := len(res)
|
||||
|
||||
for _, record := range res {
|
||||
totalCost += record.Cost
|
||||
}
|
||||
|
||||
// 如果没有记录,则返回错误
|
||||
if totalRecords == 0 {
|
||||
errorMessage := "未找到记录,无法计算 ECPM"
|
||||
fmt.Println(errorMessage)
|
||||
c.JSON(http.StatusOK, false)
|
||||
return
|
||||
}
|
||||
// 总 cost / 100000 * 1000 / 总记录数
|
||||
ecpm := float64(totalCost) / 100000 * 1000 / float64(totalRecords)
|
||||
// 打印日志
|
||||
log.ZapLog.Info("ECPM:", zap.Float64("ecpm", ecpm))
|
||||
// 根据 ECPM 值判断并返回 "true" 或 "false"
|
||||
var result bool
|
||||
if ecpm > float64(viperConfig.GetEcpmValue()) && viperConfig.GetEcpmView() > 2 {
|
||||
result = true
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
|
||||
// 返回结果
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func getEcpmFromApi(req EcpmReq, accessToken string) (list []Record, err error) {
|
||||
// 构造抖音 API 请求的 URL
|
||||
apiURL := "https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm"
|
||||
params := url.Values{}
|
||||
params.Add("open_id", req.OpenId)
|
||||
params.Add("mp_id", req.MpId)
|
||||
|
||||
fullURL := fmt.Sprintf("%s?open_id=%s&mp_id=%s&access_token=%s&date_hour=%s&page_size=500&page_no=", apiURL, req.OpenId, req.MpId, accessToken, time.Now().Format(time.DateOnly))
|
||||
pageNo := 1
|
||||
for {
|
||||
fullURL += strconv.Itoa(pageNo)
|
||||
var responseBody []byte
|
||||
func() {
|
||||
// 调用抖音 API
|
||||
var resp *http.Response
|
||||
resp, err = http.Get(fullURL)
|
||||
if err != nil {
|
||||
err = errors.New("调用抖音 API 失败")
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// 读取抖音 API 响应数据
|
||||
responseBody, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
err = errors.New("读取抖音 API 响应失败")
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// 解析抖音 API 响应数据
|
||||
var apiResponse APIResponse
|
||||
err = json.Unmarshal(responseBody, &apiResponse)
|
||||
if err != nil {
|
||||
err = errors.New("解析 API 响应数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查 API 是否返回错误
|
||||
if apiResponse.ErrNo != 0 {
|
||||
errorMessage := fmt.Sprintf("抖音 API 返回错误: %s (错误码: %d)", apiResponse.ErrMsg, apiResponse.ErrNo)
|
||||
err = errors.New(errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
list = append(list, apiResponse.Data.Records...)
|
||||
if len(apiResponse.Data.Records) <= 500 {
|
||||
return
|
||||
}
|
||||
|
||||
pageNo++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func AppsJsCode2session(code, anonymousOpenid string) (openId string, err error) {
|
||||
|
||||
/* 构建请求参数,该代码示例中只给出部分参数,请用户根据需要自行构建参数值
|
||||
token:
|
||||
1.若用户自行维护token,将用户维护的token赋值给该参数即可
|
||||
2.SDK包中有获取token的函数,请根据接口path在《OpenAPI SDK 总览》文档中查找获取token函数的名字
|
||||
在使用过程中,请注意token互刷问题
|
||||
header:
|
||||
sdk中默认填充content-type请求头,若不需要填充除content-type之外的请求头,删除该参数即可
|
||||
*/
|
||||
sdkRequest := &openApiSdkClient.AppsJscode2sessionRequest{}
|
||||
sdkRequest.SetAppid(viperConfig.GetAppId())
|
||||
if code == "" && anonymousOpenid == "" {
|
||||
err = errors.New("code can not be empty")
|
||||
return
|
||||
}
|
||||
if code == "" {
|
||||
sdkRequest.SetAnonymousCode(anonymousOpenid)
|
||||
} else {
|
||||
sdkRequest.SetCode(code)
|
||||
}
|
||||
sdkRequest.SetSecret(viperConfig.GetSecret())
|
||||
|
||||
// sdk调用
|
||||
sdkResponse, err := sdkClient.AppsJscode2session(sdkRequest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sdkResponse.Error != nil && *sdkResponse.Error != 0 {
|
||||
if sdkResponse.Errmsg != nil {
|
||||
err = errors.New(*sdkResponse.Errmsg)
|
||||
} else {
|
||||
err = errors.New("api 未知错误")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if sdkResponse.Openid == nil {
|
||||
err = errors.New("open id is nil")
|
||||
return
|
||||
}
|
||||
return *sdkResponse.Openid, nil
|
||||
}
|
||||
|
||||
var sdkClient *openApiSdkClient.Client
|
||||
|
||||
func init() {
|
||||
// 初始化 SDK 客户端,设置 app_id 和 secret
|
||||
opt := new(credential.Config).
|
||||
SetClientKey(viperConfig.GetAppId()). // 替换为你的 app_id
|
||||
SetClientSecret(viperConfig.GetSecret()) // 替换为你的 secret
|
||||
var err error
|
||||
// 创建 SDK 客户端
|
||||
sdkClient, err = openApiSdkClient.NewClient(opt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 access_token 的函数
|
||||
func GetAccessToken() (accecksToken string, err error) {
|
||||
|
||||
// 构造获取 token 的请求参数
|
||||
sdkRequest := &openApiSdkClient.AppsV2TokenRequest{}
|
||||
sdkRequest.SetAppid(viperConfig.GetAppId()) // 设置应用 ID
|
||||
sdkRequest.SetGrantType("client_credential") // 设置授权类型(client_credentials 模式)
|
||||
sdkRequest.SetSecret(viperConfig.GetSecret()) // 设置密钥
|
||||
|
||||
// 调用 SDK 获取 access_token
|
||||
sdkResponse, err := sdkClient.AppsV2Token(sdkRequest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("SDK 调用失败: %v", err)
|
||||
}
|
||||
|
||||
// 返回 access_token
|
||||
return *sdkResponse.Data.AccessToken, nil
|
||||
}
|
19
server/ecpm.go
Normal file
19
server/ecpm.go
Normal file
@ -0,0 +1,19 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
ecpmhttpserver "youtu_ecpm/api/gin"
|
||||
)
|
||||
|
||||
type EcpmApp struct {
|
||||
httpserver *ecpmhttpserver.HttpServer
|
||||
}
|
||||
|
||||
func (s *EcpmApp) Run() {
|
||||
s.httpserver.Run()
|
||||
}
|
||||
|
||||
func NewEcpmApp(srv *ecpmhttpserver.HttpServer) *EcpmApp {
|
||||
return &EcpmApp{
|
||||
srv,
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user