From ad021f2faa6d8d2bd7b58eadc0093a7b94f2f263 Mon Sep 17 00:00:00 2001 From: luorijun Date: Mon, 23 Mar 2026 17:50:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=A7=E5=93=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=92=8C=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3=20&=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A5=97=E9=A4=90=E6=9F=A5=E8=AF=A2=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/core/scopes.go | 4 + web/handlers/product.go | 178 ++++++++++++++++++++++++++++++++++++ web/handlers/resource.go | 58 ++++++++++-- web/routes.go | 11 +++ web/services/product.go | 74 ++++++++++++++- web/services/product_sku.go | 86 +++++++++++++++++ 6 files changed, 402 insertions(+), 9 deletions(-) create mode 100644 web/handlers/product.go create mode 100644 web/services/product_sku.go diff --git a/web/core/scopes.go b/web/core/scopes.go index 3386948..187fa2d 100644 --- a/web/core/scopes.go +++ b/web/core/scopes.go @@ -7,4 +7,8 @@ const ( ScopeAdminRoleWrite = string("admin_role:write") ScopeAdminRead = string("admin:read") ScopeAdminWrite = string("admin:write") + ScopeProductRead = string("product:read") + ScopeProductWrite = string("product:write") + ScopeProductSkuRead = string("product_sku:read") + ScopeProductSkuWrite = string("product_sku:write") ) diff --git a/web/handlers/product.go b/web/handlers/product.go new file mode 100644 index 0000000..b48f0a2 --- /dev/null +++ b/web/handlers/product.go @@ -0,0 +1,178 @@ +package handlers + +import ( + "platform/web/auth" + "platform/web/core" + g "platform/web/globals" + s "platform/web/services" + + "github.com/gofiber/fiber/v2" +) + +func AllProductsByAdmin(c *fiber.Ctx) error { + // 检查权限 + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductRead) + if err != nil { + return err + } + + // 解析请求参数 + // var req AllProductsByAdminReq + // if err := g.Validator.ParseBody(c, &req); err != nil { + // return err + // } + + // 查询产品 + products, err := s.Product.AllProducts() + if err != nil { + return err + } + + return c.JSON(products) +} + +type AllProductsByAdminReq struct { +} + +func CreateProduct(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite) + if err != nil { + return err + } + + var req s.CreateProductData + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.Product.CreateProduct(&req) + if err != nil { + return err + } + + return nil +} + +func UpdateProduct(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite) + if err != nil { + return err + } + + var req s.UpdateProductData + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.Product.UpdateProduct(&req) + if err != nil { + return err + } + + return nil +} + +func DeleteProduct(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductWrite) + if err != nil { + return err + } + + var req core.IdReq + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.Product.DeleteProduct(req.Id) + if err != nil { + return err + } + + return nil +} + +func PageProductSkuByAdmin(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuRead) + if err != nil { + return err + } + + var req PageProductSkuByAdminReq + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + list, total, err := s.ProductSku.Page(&req.PageReq, req.ProductId) + if err != nil { + return err + } + + return c.JSON(core.PageResp{ + Total: int(total), + Page: req.GetPage(), + Size: req.GetSize(), + List: list, + }) +} + +type PageProductSkuByAdminReq struct { + core.PageReq + ProductId *int32 `json:"product_id"` +} + +func CreateProductSku(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite) + if err != nil { + return err + } + + var req s.CreateProductSkuData + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.ProductSku.Create(req) + if err != nil { + return err + } + + return nil +} + +func UpdateProductSku(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite) + if err != nil { + return err + } + + var req s.UpdateProductSkuData + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.ProductSku.Update(req) + if err != nil { + return err + } + + return nil +} + +func DeleteProductSku(c *fiber.Ctx) error { + _, err := auth.GetAuthCtx(c).PermitAdmin(core.ScopeProductSkuWrite) + if err != nil { + return err + } + + var req core.IdReq + if err := g.Validator.ParseBody(c, &req); err != nil { + return err + } + + err = s.ProductSku.Delete(req.Id) + if err != nil { + return err + } + + return nil +} diff --git a/web/handlers/resource.go b/web/handlers/resource.go index b92e0a7..319d392 100644 --- a/web/handlers/resource.go +++ b/web/handlers/resource.go @@ -221,7 +221,7 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error { do := q.Resource.Where() if req.UserPhone != nil { - do = do.Where(q.User.Phone.Eq(*req.UserPhone)) + do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone)) } if req.ResourceNo != nil { do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) @@ -230,7 +230,7 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error { do = do.Where(q.Resource.Active.Is(*req.Active)) } if req.Mode != nil { - do = do.Where(q.Resource.Type.Eq(int(m.ResourceTypeShort))) + do = do.Where(q.ResourceShort.As("Short").Type.Eq(int(*req.Mode))) } if req.CreatedAtStart != nil { time := u.DateHead(*req.CreatedAtStart) @@ -241,13 +241,19 @@ func PageResourceShortByAdmin(c *fiber.Ctx) error { do = do.Where(q.Resource.CreatedAt.Lte(time)) } - list, total, err := q.Resource. + list, total, err := q.Resource.Debug(). Joins(q.Resource.User, q.Resource.Short). Select( q.Resource.ALL, - q.ResourceShort.As("Short").ALL, q.User.As("User").Phone.As("User__phone"), q.User.As("User").Name.As("User__name"), + q.ResourceShort.As("Short").Type.As("Short__type"), + q.ResourceShort.As("Short").Live.As("Short__live"), + q.ResourceShort.As("Short").Quota.As("Short__quota"), + q.ResourceShort.As("Short").Used.As("Short__used"), + q.ResourceShort.As("Short").Daily.As("Short__daily"), + q.ResourceShort.As("Short").LastAt.As("Short__last_at"), + q.ResourceShort.As("Short").ExpireAt.As("Short__expire_at"), ). Where(q.Resource.Type.Eq(int(m.ResourceTypeShort)), do). FindByPage(req.GetOffset(), req.GetLimit()) @@ -277,20 +283,46 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error { return err } - req := new(struct{ core.PageReq }) - if err = g.Validator.ParseBody(c, req); err != nil { + var req PageResourceLongByAdminReq + if err = g.Validator.ParseBody(c, &req); err != nil { return err } + do := q.Resource.Where() + if req.UserPhone != nil { + do = do.Where(q.User.As("User").Phone.Eq(*req.UserPhone)) + } + if req.ResourceNo != nil { + do = do.Where(q.Resource.ResourceNo.Eq(*req.ResourceNo)) + } + if req.Active != nil { + do = do.Where(q.Resource.Active.Is(*req.Active)) + } + if req.Mode != nil { + do = do.Where(q.ResourceLong.As("Long").Type.Eq(*req.Mode)) + } + if req.CreatedAtStart != nil { + do = do.Where(q.Resource.CreatedAt.Gte(*req.CreatedAtStart)) + } + if req.CreatedAtEnd != nil { + do = do.Where(q.Resource.CreatedAt.Lte(*req.CreatedAtEnd)) + } + list, total, err := q.Resource. Joins(q.Resource.User, q.Resource.Long). Select( q.Resource.ALL, - q.ResourceLong.As("Long").ALL, q.User.As("User").Phone.As("User__phone"), q.User.As("User").Name.As("User__name"), + q.ResourceLong.As("Long").Type.As("Long__type"), + q.ResourceLong.As("Long").Live.As("Long__live"), + q.ResourceLong.As("Long").Quota.As("Long__quota"), + q.ResourceLong.As("Long").Used.As("Long__used"), + q.ResourceLong.As("Long").Daily.As("Long__daily"), + q.ResourceLong.As("Long").LastAt.As("Long__last_at"), + q.ResourceLong.As("Long").ExpireAt.As("Long__expire_at"), ). - Where(q.Resource.Type.Eq(int(m.ResourceTypeLong))). + Where(q.Resource.Type.Eq(int(m.ResourceTypeLong)), do). FindByPage(req.GetOffset(), req.GetLimit()) return c.JSON(core.PageResp{ @@ -301,6 +333,16 @@ func PageResourceLongByAdmin(c *fiber.Ctx) error { }) } +type PageResourceLongByAdminReq struct { + core.PageReq + UserPhone *string `json:"user_phone" form:"user_phone"` + ResourceNo *string `json:"resource_no" form:"resource_no"` + Active *bool `json:"active" form:"active"` + Mode *int `json:"mode" form:"mode"` + CreatedAtStart *time.Time `json:"created_at_start" form:"created_at_start"` + CreatedAtEnd *time.Time `json:"created_at_end" form:"created_at_end"` +} + // AllActiveResource 所有可用套餐 func AllActiveResource(c *fiber.Ctx) error { // 检查权限 diff --git a/web/routes.go b/web/routes.go index a869637..1d76d32 100644 --- a/web/routes.go +++ b/web/routes.go @@ -152,4 +152,15 @@ func adminRouter(api fiber.Router) { // bill 账单 var bill = api.Group("/bill") bill.Post("/page", handlers.PageBillByAdmin) + + // product 产品 + var product = api.Group("/product") + product.Post("/all", handlers.AllProductsByAdmin) + product.Post("/create", handlers.CreateProduct) + product.Post("/update", handlers.UpdateProduct) + product.Post("/remove", handlers.DeleteProduct) + product.Post("/sku/page", handlers.PageProductSkuByAdmin) + product.Post("/sku/create", handlers.CreateProductSku) + product.Post("/sku/update", handlers.UpdateProductSku) + product.Post("/sku/remove", handlers.DeleteProductSku) } diff --git a/web/services/product.go b/web/services/product.go index 7c14a3d..3aa1913 100644 --- a/web/services/product.go +++ b/web/services/product.go @@ -1,6 +1,13 @@ package services -import q "platform/web/queries" +import ( + "platform/web/core" + m "platform/web/models" + q "platform/web/queries" + "time" + + "gorm.io/gen/field" +) var Product = &productService{} @@ -10,3 +17,68 @@ type productService struct{} func (s *productService) GetPrice(code string) { q.ProductSku.Where(q.ProductSku.Code.Eq(code)).Find() } + +// 获取所有产品 +func (s *productService) AllProducts() ([]*m.Product, error) { + return q.Product.Find() +} + +// 新增产品 +func (s *productService) CreateProduct(create *CreateProductData) error { + return q.Product.Create(&m.Product{ + Code: create.Code, + Name: create.Name, + Description: create.Description, + Sort: create.Sort, + Status: m.ProductStatus(create.Status), + }) +} + +type CreateProductData struct { + Code string `json:"code"` + Name string `json:"name"` + Description *string `json:"description"` + Sort int32 `json:"sort"` + Status int `json:"status"` +} + +// 更新产品 +func (s *productService) UpdateProduct(update *UpdateProductData) error { + if update == nil { + return core.NewBizErr("更新数据不存在") + } + + do := make([]field.AssignExpr, 0, 5) + if update.Code != nil { + do = append(do, q.Product.Code.Value(*update.Code)) + } + if update.Name != nil { + do = append(do, q.Product.Name.Value(*update.Name)) + } + if update.Description != nil { + do = append(do, q.Product.Description.Value(*update.Description)) + } + if update.Sort != nil { + do = append(do, q.Product.Sort.Value(*update.Sort)) + } + if update.Status != nil { + do = append(do, q.Product.Status.Value(*update.Status)) + } + _, err := q.Product.Where(q.Product.ID.Eq(update.Id)).UpdateSimple(do...) + return err +} + +type UpdateProductData struct { + Id int32 `json:"id"` + Code *string `json:"code"` + Name *string `json:"name"` + Description *string `json:"description"` + Sort *int32 `json:"sort"` + Status *int `json:"status"` +} + +// 删除产品 +func (s *productService) DeleteProduct(id int32) error { + _, err := q.Product.Where(q.Product.ID.Eq(id)).UpdateColumn(q.Product.DeletedAt, time.Now()) + return err +} diff --git a/web/services/product_sku.go b/web/services/product_sku.go new file mode 100644 index 0000000..33e2c8f --- /dev/null +++ b/web/services/product_sku.go @@ -0,0 +1,86 @@ +package services + +import ( + "platform/web/core" + m "platform/web/models" + q "platform/web/queries" + "time" + + "github.com/shopspring/decimal" + "gorm.io/gen" + "gorm.io/gen/field" +) + +var ProductSku = &productSkuService{} + +type productSkuService struct{} + +func (s *productSkuService) Page(req *core.PageReq, productId *int32) (result []*m.ProductSku, count int64, err error) { + do := make([]gen.Condition, 0) + if productId != nil { + do = append(do, q.ProductSku.ProductID.Eq(*productId)) + } + return q.ProductSku. + Where(do...). + FindByPage(req.GetOffset(), req.GetLimit()) +} + +func (s *productSkuService) Create(create CreateProductSkuData) (err error) { + price, err := decimal.NewFromString(create.Price) + if err != nil { + return core.NewBizErr("产品价格的格式不正确", err) + } + + return q.ProductSku.Create(&m.ProductSku{ + ProductID: create.ProductID, + Code: create.Code, + Name: create.Name, + Price: price, + Discount: create.Discount, + }) +} + +type CreateProductSkuData struct { + ProductID int32 `json:"product_id"` + Code string `json:"code"` + Name string `json:"name"` + Price string `json:"price"` + Discount float32 `json:"discount"` +} + +func (s *productSkuService) Update(update UpdateProductSkuData) (err error) { + do := make([]field.AssignExpr, 0) + + if update.Price != nil { + price, err := decimal.NewFromString(*update.Price) + if err != nil { + return core.NewBizErr("产品价格的格式不正确", err) + } + do = append(do, q.ProductSku.Price.Value(price)) + } + if update.Discount != nil { + do = append(do, q.ProductSku.Discount.Value(*update.Discount)) + } + if update.Code != nil { + do = append(do, q.ProductSku.Code.Value(*update.Code)) + } + if update.Name != nil { + do = append(do, q.ProductSku.Name.Value(*update.Name)) + } + + _, err = q.ProductSku.Where(q.ProductSku.ID.Eq(update.ID)).UpdateSimple(do...) + return err +} + +type UpdateProductSkuData struct { + ID int32 `json:"id"` + Code *string `json:"code"` + Name *string `json:"name"` + Price *string `json:"price"` + Discount *float32 `json:"discount"` +} + +func (s *productSkuService) Delete(id int32) (err error) { + _, err = q.ProductSku.Where(q.ProductSku.ID.Eq(id)).UpdateColumn(q.ProductSku.DeletedAt, time.Now()) + return +}