diff --git a/app/admin/controller/pkg.go b/app/admin/controller/pkg.go new file mode 100644 index 0000000..056fb82 --- /dev/null +++ b/app/admin/controller/pkg.go @@ -0,0 +1,61 @@ +package controller + +import ( + "net/http" + "strings" + "twin-api/app/admin/request" + "twin-api/app/admin/service" + "twin-api/base/config" + + "git.u8t.cn/open/go-server/session" + "github.com/gin-gonic/gin" + "github.com/spf13/cast" +) + +type Pkg struct{} + +func (pkg *Pkg) Query(ctx *gin.Context) { + var req request.PkgGet + if err := ctx.ShouldBind(&req); err != nil { + panic(config.ErrParam.New().Append(err)) + } + + count, res, err := service.NewPkg().Query(&req) + if err != nil { + panic(err) + } + ctx.JSON(http.StatusOK, session.NewListRsp(count, res)) +} + +func (pkg *Pkg) Create(ctx *gin.Context) { + var req request.PkgCreate + if err := ctx.ShouldBindJSON(&req); err != nil { + panic(config.ErrParam.New().Append(err)) + } + if err := service.NewPkg().Create(&req); err != nil { + panic(err) + } + ctx.JSON(http.StatusOK, session.NewRspOk()) +} + +func (pkg *Pkg) Update(ctx *gin.Context) { + var req request.PkgPut + if err := ctx.ShouldBindJSON(&req); err != nil { + panic(config.ErrParam.New().Append(err)) + } + if err := service.NewPkg().Update(&req); err != nil { + panic(err) + } + ctx.JSON(http.StatusOK, session.NewRspOk()) +} + +func (pkg *Pkg) Delete(ctx *gin.Context) { + var req request.PkgDelete + if err := ctx.ShouldBind(&req); err != nil { + panic(config.ErrParam.New().Append(err)) + } + if err := service.NewPkg().Delete(cast.ToInt64(strings.TrimSpace(req.Id))); err != nil { + panic(err) + } + ctx.JSON(http.StatusOK, session.NewRspOk()) +} diff --git a/app/admin/request/pkg.go b/app/admin/request/pkg.go new file mode 100644 index 0000000..dd70446 --- /dev/null +++ b/app/admin/request/pkg.go @@ -0,0 +1,52 @@ +package request + +import "github.com/spf13/cast" + +const MaxPageSize = 100 + +type PkgGet struct { + Page string `form:"page" json:"page"` + Size string `form:"size" json:"size"` + Sort string `form:"sort" json:"sort"` + Name string `form:"name" json:"name"` + Version string `form:"version" json:"version"` + Status string `form:"status" json:"status"` +} + +type PkgPut struct { + Id string `json:"id"` + Expire string `json:"expire"` + Status string `json:"status"` +} + +type PkgDelete struct { + Id string `form:"id" json:"id"` +} + +type PkgCreate struct { + Name string `json:"package_name"` + Version string `json:"version"` + Expire string `json:"expire"` + Link string `json:"link"` +} + +func (r *PkgGet) Default() { + if cast.ToInt(r.Size) == 0 { + r.Size = "10" + } + if cast.ToInt(r.Page) <= 0 { + r.Page = "1" + } +} + +func (r *PkgGet) PageInt() int { + return cast.ToInt(r.Page) +} + +func (r *PkgGet) SizeInt() int { + size := cast.ToInt(r.Size) + if size > MaxPageSize { + return MaxPageSize + } + return size +} diff --git a/app/common/dao/pkg.go b/app/common/dao/pkg.go index 01a1abc..bf09d97 100644 --- a/app/common/dao/pkg.go +++ b/app/common/dao/pkg.go @@ -3,9 +3,11 @@ package dao import ( "errors" "fmt" + "time" "twin-api/app/common/model" "twin-api/base/global" + "git.u8t.cn/open/go-server/utils" "gorm.io/gorm" ) @@ -31,8 +33,15 @@ func (p *Pkg) Update(m *model.Pkg) error { return p.db.Table(p.TableName()).Save(m).Error } +func (p *Pkg) UpdateCols(m *model.Pkg, cols ...string) error { + return p.db.Table(p.TableName()).Select(cols).Updates(m).Error +} + func (p *Pkg) Delete(m *model.Pkg) error { - return p.db.Table(p.TableName()).Where("id = ?", m.Id).Update("status", model.PkgDelete).Error + return p.db.Table(p.TableName()).Where("id = ?", m.Id).Updates(map[string]any{ + "status": model.PkgDelete, + "update_time": time.Now().Unix(), + }).Error } func (p *Pkg) Get(id int64) (*model.Pkg, error) { @@ -47,13 +56,24 @@ func (p *Pkg) Get(id int64) (*model.Pkg, error) { return &res, nil } +func (p *Pkg) GetById(id int64) (*model.Pkg, error) { + var res model.Pkg + if err := p.db.Table(p.TableName()).Where("id = ?", id).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + func (p *Pkg) GetByPkgName(name string) (*model.Pkg, error) { var res model.Pkg tx := p.db.Table(p.TableName()).Where("status = ?", model.PkgNormal) if name != "" { tx = tx.Where("name = ?", name) } - tx.Order("id desc") + tx = tx.Order("id desc") if err := tx.First(&res).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil @@ -63,6 +83,36 @@ func (p *Pkg) GetByPkgName(name string) (*model.Pkg, error) { return &res, nil } +func (p *Pkg) GetLatestByName(name string) (*model.Pkg, error) { + var list []*model.Pkg + if err := p.db.Table(p.TableName()).Where("name = ? AND status = ?", name, model.PkgNormal).Find(&list).Error; err != nil { + return nil, err + } + if len(list) == 0 { + return nil, nil + } + latest := list[0] + latestVal := utils.VersionToInt(latest.Version) + for _, item := range list[1:] { + if v := utils.VersionToInt(item.Version); v > latestVal { + latest = item + latestVal = v + } + } + return latest, nil +} + +func (p *Pkg) GetByNameVersion(name, version string) (*model.Pkg, error) { + var res model.Pkg + if err := p.db.Table(p.TableName()).Where("name = ? AND version = ?", name, version).First(&res).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &res, nil +} + func (p *Pkg) GetOne(name, version string) (*model.Pkg, error) { var res model.Pkg tx := p.db.Table(p.TableName()).Where("status = ?", model.PkgNormal) @@ -70,7 +120,7 @@ func (p *Pkg) GetOne(name, version string) (*model.Pkg, error) { tx = tx.Where("name = ? AND version = ?", name, version) } - tx.Order("id desc") + tx = tx.Order("id desc") if err := tx.First(&res).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil diff --git a/app/common/dao/user_pkg.go b/app/common/dao/user_pkg.go index 4bc5e18..1adeb3b 100644 --- a/app/common/dao/user_pkg.go +++ b/app/common/dao/user_pkg.go @@ -60,6 +60,29 @@ func (p *UserPkg) GetLatest(userId int64) (*model.UserPkgInfo, error) { return &res, nil } +func (p *UserPkg) CountByPackageIds(packageIds []int64) (map[int64]int64, error) { + res := make(map[int64]int64) + if len(packageIds) == 0 { + return res, nil + } + type countResult struct { + PackageId int64 `gorm:"column:package_id"` + Count int64 `gorm:"column:count"` + } + var rows []countResult + if err := p.db.Table(p.TableName()). + Select("package_id, count(*) as count"). + Where("package_id IN ?", packageIds). + Group("package_id"). + Find(&rows).Error; err != nil { + return nil, err + } + for _, row := range rows { + res[row.PackageId] = row.Count + } + return res, nil +} + func (p *UserPkg) Query(...any) (any, error) { panic("implement me") } diff --git a/app/common/model/pkg.go b/app/common/model/pkg.go index 7295e46..f05d81b 100644 --- a/app/common/model/pkg.go +++ b/app/common/model/pkg.go @@ -1,5 +1,13 @@ package model +import ( + "fmt" + "strings" + + gsUtils "git.u8t.cn/open/go-server/utils" + "github.com/spf13/cast" +) + type Pkg struct { Id int64 `json:"id"` ExpireTime int64 `json:"expire_time"` @@ -15,3 +23,51 @@ const ( PkgNormal = 1 PkgDelete = 2 ) + +func (p *Pkg) Format() map[string]any { + res := make(map[string]any) + res["id"] = p.Id + res["name"] = p.Name + res["version"] = p.Version + res["link"] = p.Link + res["status"] = p.Status + res["expire"] = gsUtils.TimeToDateTime(p.ExpireTime) + res["create_date"] = gsUtils.TimeToDateTime(p.CreateTime) + res["update_date"] = gsUtils.TimeToDateTime(p.UpdateTime) + return res +} + +func (p *Pkg) FormatAdmin(userCount int64) map[string]any { + res := p.Format() + res["user_count"] = userCount + return res +} + +func ParseExpire(expire string) (int64, error) { + expire = strings.TrimSpace(expire) + if expire == "" { + return 0, fmt.Errorf("expire is required") + } + if isUnixTimestamp(expire) { + return cast.ToInt64(expire), nil + } + if ts := gsUtils.DateTimeToTime(expire); ts > 0 { + return ts, nil + } + if ts := gsUtils.DateToTime(expire); ts > 0 { + return ts, nil + } + return 0, fmt.Errorf("invalid expire format, supports timestamp or date") +} + +func isUnixTimestamp(s string) bool { + if len(s) < 10 { + return false + } + for _, c := range s { + if c < '0' || c > '9' { + return false + } + } + return cast.ToInt64(s) > 1e9 +} diff --git a/app/routers/routers.go b/app/routers/routers.go index 4ac17af..bc79674 100644 --- a/app/routers/routers.go +++ b/app/routers/routers.go @@ -2,10 +2,12 @@ package routers import ( "net/http" + adminController "twin-api/app/admin/controller" "twin-api/app/api/controller" "twin-api/app/common/middle" - gsController "git.u8t.cn/open/go-server/api/controller" + gsAdminController "git.u8t.cn/open/go-server/admin/controller" + gsApiController "git.u8t.cn/open/go-server/api/controller" "github.com/gin-gonic/gin" ) @@ -20,7 +22,7 @@ func InitRouter(engine *gin.Engine) { // 客户端接口路由 func initApiRouter(engine *gin.Engine) { - base := new(gsController.BaseController) + base := new(gsApiController.BaseController) userGroup := engine.Group("/api/user").Use(base.Recovery).Use(base.Before).Use(base.Token).Use(base.IpLimit).Use(base.Response).Use(middle.UserPkg) userApi := new(controller.User) @@ -29,4 +31,13 @@ func initApiRouter(engine *gin.Engine) { // 管理接口路由 func initAdminRouter(engine *gin.Engine) { + base := new(gsAdminController.BaseController) + adminGroup := engine.Group("/admin") + adminGroup.Use(base.Recovery).Use(base.Before).Use(base.Token) + + pkgApi := new(adminController.Pkg) + adminGroup.GET("/package", pkgApi.Query) + adminGroup.POST("/package", pkgApi.Create) + adminGroup.PUT("/package", pkgApi.Update) + adminGroup.DELETE("/package", pkgApi.Delete) }