feat(admin): 添加管理后台功能模块

- 新增管理员界面模板 admin.html
- 实现管理员路由处理器 AdminHandler
- 添加产品、分类、订单、数据库管理子模块
- 注册管理后台相关路由处理器
- 修改URL生成函数名为MustGenerateUrl并统一调用方式
- 更新购物车和订单处理中的URL生成方法调用
- 在主程序中配置管理后台路由别名
This commit is contained in:
2025-11-08 23:02:39 +08:00
parent 89ab365887
commit 8bbf0eb835
11 changed files with 126 additions and 15 deletions

View File

@@ -0,0 +1,8 @@
package admin
type CategoriesHandler struct {
}
func (handler CategoriesHandler) GetData() string {
return "This is the categories handler"
}

View File

@@ -0,0 +1,8 @@
package admin
type DatabaseHandler struct {
}
func (handler DatabaseHandler) GetData() string {
return "This is the database handler"
}

View File

@@ -0,0 +1,29 @@
package admin
import (
"platform/http/actionresults"
"platform/http/handing"
"sportstore/store"
)
var sectionNames = []string{"Products", "Categories", "Orders", "Database"}
type AdminHandler struct {
handing.URLGenerator
}
type AdminTemplateContext struct {
Sections []string
ActiveSection string
SectionUrlFunc func(string) string
}
func (handler AdminHandler) GetSection(section string) actionresults.ActionResult {
return actionresults.NewTemplateAction("admin.html", AdminTemplateContext{
Sections: sectionNames,
ActiveSection: section,
SectionUrlFunc: func(sec string) string {
return store.MustGenerateUrl(handler.URLGenerator, AdminHandler.GetSection, sec)
},
})
}

View File

@@ -0,0 +1,8 @@
package admin
type OrdersHandler struct {
}
func (handler OrdersHandler) GetData() string {
return "This is the orders handler"
}

View File

@@ -0,0 +1,8 @@
package admin
type ProductsHandler struct {
}
func (handler ProductsHandler) GetData() string {
return "This is the products handler"
}

View File

@@ -7,6 +7,7 @@ import (
"platform/pipeline/basic"
"platform/services"
"platform/sessions"
"sportstore/admin"
"sportstore/models/repo"
"sportstore/store"
"sportstore/store/cart"
@@ -33,8 +34,14 @@ func createPipeline() pipeline.RequestPipeline {
handing.HandlerEntry{Prefix: "", Handler: store.CategoryHandler{}},
handing.HandlerEntry{Prefix: "", Handler: store.CartHandler{}},
handing.HandlerEntry{Prefix: "", Handler: store.OrderHandler{}},
handing.HandlerEntry{Prefix: "admin", Handler: admin.AdminHandler{}},
handing.HandlerEntry{Prefix: "admin", Handler: admin.ProductsHandler{}},
handing.HandlerEntry{Prefix: "admin", Handler: admin.CategoriesHandler{}},
handing.HandlerEntry{Prefix: "admin", Handler: admin.OrdersHandler{}},
handing.HandlerEntry{Prefix: "admin", Handler: admin.DatabaseHandler{}},
).AddMethodAlias("/", store.ProductHandler.GetProducts, 0, 1).
AddMethodAlias("/products[/]?[A-z0-9]*?", store.ProductHandler.GetProducts, 0, 1),
AddMethodAlias("/products[/]?[A-z0-9]*?", store.ProductHandler.GetProducts, 0, 1).
AddMethodAlias("/admin[/]?", admin.AdminHandler.GetSection, ""),
)
}

View File

@@ -28,9 +28,9 @@ type CardProductReference struct {
func (handler CartHandler) GetCart() actionresults.ActionResult {
context := CartTemplateContext{
Cart: handler.Cart,
ProductListUrl: mustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, 0, 1),
RemoveUrl: mustGenerateUrl(handler.URLGenerator, CartHandler.PostRemoveFromCart),
CheckoutUrl: mustGenerateUrl(handler.URLGenerator, OrderHandler.GetCheckout),
ProductListUrl: MustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, 0, 1),
RemoveUrl: MustGenerateUrl(handler.URLGenerator, CartHandler.PostRemoveFromCart),
CheckoutUrl: MustGenerateUrl(handler.URLGenerator, OrderHandler.GetCheckout),
}
return actionresults.NewTemplateAction("cart.html", context)
}
@@ -38,7 +38,7 @@ func (handler CartHandler) GetCart() actionresults.ActionResult {
func (handler CartHandler) GetWidget() actionresults.ActionResult {
context := CartTemplateContext{
Cart: handler.Cart,
CartUrl: mustGenerateUrl(handler.URLGenerator, CartHandler.GetCart),
CartUrl: MustGenerateUrl(handler.URLGenerator, CartHandler.GetCart),
}
return actionresults.NewTemplateAction("cart_widget.html", context)
}
@@ -46,10 +46,10 @@ func (handler CartHandler) GetWidget() actionresults.ActionResult {
func (handler CartHandler) PostAddToCart(ref CardProductReference) actionresults.ActionResult {
p := handler.Repository.GetProduct(ref.ID)
handler.Cart.AddProduct(p)
return actionresults.NewRedirectAction(mustGenerateUrl(handler.URLGenerator, CartHandler.GetCart))
return actionresults.NewRedirectAction(MustGenerateUrl(handler.URLGenerator, CartHandler.GetCart))
}
func (handler CartHandler) PostRemoveFromCart(ref CardProductReference) actionresults.ActionResult {
handler.Cart.RemoveLineForProduct(ref.ID)
return actionresults.NewRedirectAction(mustGenerateUrl(handler.URLGenerator, CartHandler.GetCart))
return actionresults.NewRedirectAction(MustGenerateUrl(handler.URLGenerator, CartHandler.GetCart))
}

View File

@@ -27,11 +27,11 @@ func (handler CategoryHandler) GetButtons(selected int) actionresults.ActionResu
func (handler CategoryHandler) createCategoryUrlFunction() func(int) string {
return func(category int) string {
return mustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, category, 1)
return MustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, category, 1)
}
}
func mustGenerateUrl(generator handing.URLGenerator, method interface{}, data ...interface{}) string {
func MustGenerateUrl(generator handing.URLGenerator, method interface{}, data ...interface{}) string {
url, err := generator.GenerateUrl(method, data...)
if err != nil {
panic(err)

View File

@@ -31,7 +31,7 @@ func (handler OrderHandler) GetCheckout() actionresults.ActionResult {
if jsonData != nil {
json.NewDecoder(strings.NewReader(jsonData.(string))).Decode(&context)
}
context.CancelUrl = mustGenerateUrl(handler.URLGenerator, CartHandler.GetCart)
context.CancelUrl = MustGenerateUrl(handler.URLGenerator, CartHandler.GetCart)
return actionresults.NewTemplateAction("checkout.html", context)
}
@@ -48,7 +48,7 @@ func (handler OrderHandler) PostCheckout(details models.ShippingDetails) actionr
builder := strings.Builder{}
json.NewEncoder(&builder).Encode(ctx)
handler.Session.SetValue("checkout_details", builder.String())
redirectUrl := mustGenerateUrl(handler.URLGenerator, OrderHandler.GetCheckout)
redirectUrl := MustGenerateUrl(handler.URLGenerator, OrderHandler.GetCheckout)
return actionresults.NewRedirectAction(redirectUrl)
} else {
handler.Session.SetValue("checkout_details", "")
@@ -68,12 +68,12 @@ func (handler OrderHandler) PostCheckout(details models.ShippingDetails) actionr
handler.Repository.SaveOrder(&order)
handler.Cart.Reset()
targetUrl := mustGenerateUrl(handler.URLGenerator, OrderHandler.GetSummary, order.Id)
targetUrl := MustGenerateUrl(handler.URLGenerator, OrderHandler.GetSummary, order.Id)
return actionresults.NewRedirectAction(targetUrl)
}
func (handler OrderHandler) GetSummary(orderId int) actionresults.ActionResult {
targetUrl := mustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, 0, 1)
targetUrl := MustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, 0, 1)
return actionresults.NewTemplateAction("checkout_summary.html", struct {
Id int
TargetUrl string

View File

@@ -35,14 +35,14 @@ func (handler ProductHandler) GetProducts(category, page int) actionresults.Acti
PageNumbers: handler.generatePageNumbers(pageCount),
PageUrlFunc: handler.createPageUrlFunction(category),
SelectedCategory: category,
AddToCartUrl: mustGenerateUrl(handler.URLGenerator, CartHandler.PostAddToCart),
AddToCartUrl: MustGenerateUrl(handler.URLGenerator, CartHandler.PostAddToCart),
}
return actionresults.NewTemplateAction("product_list.html", context)
}
func (handler ProductHandler) createPageUrlFunction(category int) func(int) string {
return func(page int) string {
return mustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, category, page)
return MustGenerateUrl(handler.URLGenerator, ProductHandler.GetProducts, category, page)
}
}

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="ch">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"></meta>
<title>Sports Store</title>
<link rel="stylesheet" href="/files/bootstrap.min.css"/>
</head>
<body>
{{ $context := . }}
<div class="bg-info text-white p-2">
<div class="container-fluid">
<div class="row">
<div class="col navbar-brand">Sports Store Administration</div>
</div>
</div>
</div>
<div class="row m-1 p-1">
<div id="sidebar" class="col-3">
<div class="d-grid gap-2">
{{ range $context.Sections }}
<a href="{{ call $context.SectionUrlFunc . }}"
{{ if eq . $context.ActiveSection }}
class="btn btn-info"
{{ else }}
class="btn btn-outline-info"
{{ end }}
> {{ . }}</a>
{{ end }}
</div>
</div>
<div class="col-9">
{{ if eq $context.ActiveSection "" }}
<h6 class="p-2">
Welcome to the SportStore Administrations Features
</h6>
{{ else }}
{{ handler $context.ActiveSection "GetData" }}
{{ end }}
</div>
</div>
</body>
</html>