feat: implement gas and delivery admin systems

This commit is contained in:
david
2026-07-30 14:16:58 +08:00
parent 1093385f95
commit f5ecc0d973
252 changed files with 49385 additions and 363 deletions

View File

@@ -0,0 +1,105 @@
package routers
import (
"fmt"
sdkmiddleware "git.apinb.com/bsm-sdk/core/middleware"
deliverylogic "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/delivery"
"github.com/gin-gonic/gin"
)
func RegisterDelivery(serviceKey string, engine *gin.Engine) {
basePath := fmt.Sprintf("/%s/delivery/v1", serviceKey)
anonymous := engine.Group(basePath)
anonymous.POST("/auth/login", deliverylogic.Login)
protected := engine.Group(basePath)
protected.Use(sdkmiddleware.JwtAuth(true))
protected.Use(deliverylogic.RequireDeliveryAdmin())
protected.GET("/auth/profile", deliverylogic.CurrentProfile)
protected.PUT("/auth/password", deliverylogic.ChangePassword)
protected.GET("/delivery_menu", deliverylogic.ListMenu)
protected.GET("/invitation/qrcode", deliverylogic.InvitationQRCode)
protected.GET("/dashboard/overview", deliverylogic.Dashboard)
protected.GET("/dashboard/reports", deliverylogic.DashboardReport)
protected.GET("/delivery_profile", deliverylogic.ListProfile)
staff := protected.Group("/staff_account")
staff.GET("", deliverylogic.ListStaff)
staff.POST("", deliverylogic.CreateStaff)
staff.GET("/:identity", deliverylogic.GetStaff)
staff.PUT("/:identity", deliverylogic.UpdateStaff)
staff.PUT("/:identity/password", deliverylogic.ResetStaffPassword)
staff.PATCH("/:identity/status", deliverylogic.UpdateStaffStatus)
staff.DELETE("/:identity", deliverylogic.ArchiveStaff)
credential := protected.Group("/staff_credential")
credential.GET("", deliverylogic.ListCredential)
credential.POST("", deliverylogic.CreateCredential)
credential.GET("/:identity", deliverylogic.GetCredential)
credential.PUT("/:identity", deliverylogic.UpdateCredential)
credential.PATCH("/:identity/status", deliverylogic.UpdateCredentialStatus)
credential.DELETE("/:identity", deliverylogic.ArchiveCredential)
user := protected.Group("/user_account")
user.GET("", deliverylogic.ListUser)
user.POST("", deliverylogic.CreateUser)
user.GET("/:identity", deliverylogic.GetUser)
user.PUT("/:identity", deliverylogic.UpdateUser)
user.PUT("/:identity/password", deliverylogic.ResetUserPassword)
user.PATCH("/:identity/status", deliverylogic.UpdateUserStatus)
user.DELETE("/:identity", deliverylogic.ArchiveUser)
address := protected.Group("/user_address")
address.GET("", deliverylogic.ListAddress)
address.POST("", deliverylogic.CreateAddress)
address.GET("/:identity", deliverylogic.GetAddress)
address.PUT("/:identity", deliverylogic.UpdateAddress)
address.PATCH("/:identity/status", deliverylogic.UpdateAddressStatus)
address.DELETE("/:identity", deliverylogic.ArchiveAddress)
contract := protected.Group("/gasorder_contract")
contract.GET("", deliverylogic.ListContract)
contract.POST("", deliverylogic.CreateContract)
contract.GET("/:identity", deliverylogic.GetContract)
contract.PUT("/:identity", deliverylogic.UpdateContract)
contract.POST("/:identity/activate", deliverylogic.ActivateContract)
contract.POST("/:identity/renew", deliverylogic.RenewContract)
contract.POST("/:identity/terminate", deliverylogic.TerminateContract)
product := protected.Group("/gasorder_contract_product")
product.GET("", deliverylogic.ListContractProduct)
product.POST("", deliverylogic.BindContractProduct)
product.GET("/:identity", deliverylogic.GetContractProduct)
product.POST("/:identity/unbind", deliverylogic.UnbindContractProduct)
protected.GET("/gasorder_contract_revision", deliverylogic.ListContractRevision)
protected.GET("/gasorder_contract_revision/:identity", deliverylogic.GetContractRevision)
protected.GET("/product_info", deliverylogic.ListProductCandidate)
protected.GET("/product_info/:identity", deliverylogic.GetProductCandidate)
order := protected.Group("/gasorder_basic")
order.GET("", deliverylogic.ListOrder)
order.POST("", deliverylogic.CreateOrder)
order.GET("/:identity", deliverylogic.GetOrder)
order.POST("/:identity/assign", deliverylogic.AssignOrder)
order.POST("/:identity/reclaim", deliverylogic.ReclaimOrder)
order.POST("/:identity/adjust-amount", deliverylogic.AdjustOrderAmount)
order.POST("/:identity/exception", deliverylogic.OrderException)
order.POST("/:identity/recover", deliverylogic.OrderRecover)
order.POST("/:identity/cancel", deliverylogic.OrderCancel)
protected.GET("/wallet_basic", deliverylogic.ListWallet)
protected.GET("/wallet_basic/:identity", deliverylogic.GetWallet)
protected.POST("/wallet_basic/recharge", deliverylogic.Recharge)
protected.POST("/wallet_recharge", deliverylogic.Recharge)
protected.GET("/wallet_bank", deliverylogic.ListBank)
protected.GET("/wallet_bank/:identity", deliverylogic.GetBank)
protected.GET("/wallet_payment", deliverylogic.ListPayment)
protected.GET("/wallet_payment/:identity", deliverylogic.GetPayment)
protected.GET("/wallet_record", deliverylogic.ListRecord)
protected.GET("/wallet_record/:identity", deliverylogic.GetRecord)
protected.GET("/wallet_refund", deliverylogic.ListRefund)
protected.GET("/wallet_refund/:identity", deliverylogic.GetRefund)
protected.GET("/wallet_apply_cash", deliverylogic.ListApplyCash)
protected.POST("/wallet_apply_cash", deliverylogic.CreateApplyCash)
protected.GET("/wallet_apply_cash/:identity", deliverylogic.GetApplyCash)
protected.GET("/fin_settlement", deliverylogic.ListSettlement)
protected.GET("/fin_settlement/:identity", deliverylogic.GetSettlement)
}

View File

@@ -0,0 +1,51 @@
package routers
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestDeliveryRoutesRequireAuthentication(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
RegisterDelivery("heqi", engine)
response := httptest.NewRecorder()
engine.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/heqi/delivery/v1/delivery_menu", nil))
if response.Code == http.StatusOK {
t.Fatal("delivery menu must reject anonymous access")
}
}
func TestDeliveryOrderActionBoundary(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
RegisterDelivery("heqi", engine)
routes := map[string]bool{}
for _, route := range engine.Routes() {
routes[route.Method+" "+route.Path] = true
}
for _, forbidden := range []string{
"POST /heqi/delivery/v1/gasorder_basic/:identity/filling",
"POST /heqi/delivery/v1/gasorder_basic/:identity/ready",
"POST /heqi/delivery/v1/gasorder_basic/:identity/delivering",
"POST /heqi/delivery/v1/gasorder_basic/:identity/awaiting-confirmation",
"POST /heqi/delivery/v1/gasorder_basic/:identity/complete",
} {
if routes[forbidden] {
t.Fatalf("delivery web must not expose app or gas action %s", forbidden)
}
}
for _, required := range []string{
"POST /heqi/delivery/v1/gasorder_basic",
"POST /heqi/delivery/v1/gasorder_basic/:identity/assign",
"POST /heqi/delivery/v1/gasorder_basic/:identity/reclaim",
"POST /heqi/delivery/v1/gasorder_basic/:identity/adjust-amount",
} {
if !routes[required] {
t.Fatalf("missing confirmed delivery action %s", required)
}
}
}

View File

@@ -0,0 +1,26 @@
package routers
import (
"fmt"
sdkmiddleware "git.apinb.com/bsm-sdk/core/middleware"
gaslogic "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/gas"
"github.com/gin-gonic/gin"
)
// RegisterGas 注册 /heqi/gas/v1 气站后台路由。
func RegisterGas(serviceKey string, engine *gin.Engine) {
basePath := fmt.Sprintf("/%s/gas/v1", serviceKey)
anonymous := engine.Group(basePath)
anonymous.POST("/auth/login", gaslogic.Login)
protected := engine.Group(basePath)
protected.Use(sdkmiddleware.JwtAuth(true))
protected.Use(gaslogic.RequireGasAdmin())
protected.GET("/auth/profile", gaslogic.CurrentProfile)
protected.PUT("/auth/password", gaslogic.ChangePassword)
protected.GET("/gas_menu", gaslogic.ListMenu)
protected.GET("/invitation/qrcode", gaslogic.InvitationQRCode)
registerGasBusinessRoutes(protected)
}

View File

@@ -0,0 +1,107 @@
package routers
import (
gaslogic "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/gas"
"github.com/gin-gonic/gin"
)
func registerGasBusinessRoutes(group *gin.RouterGroup) {
group.GET("/dashboard/overview", gaslogic.DashboardOverview)
group.GET("/dashboard/reports", gaslogic.DashboardReport)
delivery := group.Group("/delivery_basic")
delivery.GET("", gaslogic.ListDelivery)
delivery.POST("", gaslogic.CreateDelivery)
delivery.GET("/:identity", gaslogic.GetDelivery)
delivery.PUT("/:identity", gaslogic.UpdateDelivery)
delivery.PATCH("/:identity/status", gaslogic.UpdateDeliveryStatus)
delivery.DELETE("/:identity", gaslogic.ArchiveDelivery)
deliveryAccount := group.Group("/delivery_account")
deliveryAccount.GET("", gaslogic.ListDeliveryAccount)
deliveryAccount.POST("", gaslogic.CreateDeliveryAccount)
deliveryAccount.GET("/:identity", gaslogic.GetDeliveryAccount)
deliveryAccount.PUT("/:identity", gaslogic.UpdateDeliveryAccount)
deliveryAccount.PUT("/:identity/password", gaslogic.ResetDeliveryAccountPassword)
deliveryAccount.PATCH("/:identity/status", gaslogic.UpdateDeliveryAccountStatus)
deliveryAccount.DELETE("/:identity", gaslogic.ArchiveDeliveryAccount)
staff := group.Group("/staff_account")
staff.GET("", gaslogic.ListStaff)
staff.POST("", gaslogic.CreateStaff)
staff.GET("/:identity", gaslogic.GetStaff)
staff.PUT("/:identity", gaslogic.UpdateStaff)
staff.PUT("/:identity/password", gaslogic.ResetStaffPassword)
staff.PATCH("/:identity/status", gaslogic.UpdateStaffStatus)
staff.DELETE("/:identity", gaslogic.ArchiveStaff)
credential := group.Group("/staff_credential")
credential.GET("", gaslogic.ListCredential)
credential.POST("", gaslogic.CreateCredential)
credential.GET("/:identity", gaslogic.GetCredential)
credential.PUT("/:identity", gaslogic.UpdateCredential)
credential.PATCH("/:identity/status", gaslogic.UpdateCredentialStatus)
credential.DELETE("/:identity", gaslogic.ArchiveCredential)
user := group.Group("/user_account")
user.GET("", gaslogic.ListUser)
user.POST("", gaslogic.CreateUser)
user.GET("/:identity", gaslogic.GetUser)
user.PUT("/:identity", gaslogic.UpdateUser)
user.PUT("/:identity/password", gaslogic.ResetUserPassword)
user.PATCH("/:identity/status", gaslogic.UpdateUserStatus)
user.DELETE("/:identity", gaslogic.ArchiveUser)
address := group.Group("/user_address")
address.GET("", gaslogic.ListUserAddress)
address.POST("", gaslogic.CreateUserAddress)
address.GET("/:identity", gaslogic.GetUserAddress)
address.PUT("/:identity", gaslogic.UpdateUserAddress)
address.PATCH("/:identity/status", gaslogic.UpdateUserAddressStatus)
address.DELETE("/:identity", gaslogic.ArchiveUserAddress)
contract := group.Group("/gasorder_contract")
contract.GET("", gaslogic.ListGasorderContract)
contract.POST("", gaslogic.CreateGasorderContract)
contract.GET("/:identity", gaslogic.GetGasorderContract)
contract.PUT("/:identity", gaslogic.UpdateGasorderContract)
contract.POST("/:identity/activate", gaslogic.ActivateGasorderContract)
contract.POST("/:identity/renew", gaslogic.RenewGasorderContract)
contract.POST("/:identity/terminate", gaslogic.TerminateGasorderContract)
contractProduct := group.Group("/gasorder_contract_product")
contractProduct.GET("", gaslogic.ListGasorderContractProduct)
contractProduct.POST("", gaslogic.BindGasorderContractProduct)
contractProduct.POST("/:identity/unbind", gaslogic.UnbindGasorderContractProduct)
group.GET("/gasorder_contract_revision", gaslogic.ListGasorderContractRevision)
group.GET("/product_info", gaslogic.ListContractProductCandidate)
order := group.Group("/gasorder_basic")
order.GET("", gaslogic.ListGasorderBasic)
order.POST("", gaslogic.CreateGasorderBasic)
order.GET("/:identity", gaslogic.GetGasorderBasic)
order.POST("/:identity/assign", gaslogic.AssignGasorderBasic)
order.POST("/:identity/filling", gaslogic.GasorderStartFilling)
order.POST("/:identity/ready", gaslogic.GasorderReady)
order.POST("/:identity/exception", gaslogic.GasorderException)
order.POST("/:identity/recover", gaslogic.GasorderRecover)
order.POST("/:identity/cancel", gaslogic.GasorderCancel)
group.GET("/wallet_basic", gaslogic.ListWalletBasic)
group.GET("/wallet_bank", gaslogic.ListWalletBank)
group.GET("/wallet_payment", gaslogic.ListWalletPayment)
group.GET("/wallet_record", gaslogic.ListWalletRecord)
group.GET("/wallet_refund", gaslogic.ListWalletRefund)
group.GET("/wallet_apply_cash", gaslogic.ListWalletApplyCash)
group.POST("/wallet_apply_cash", gaslogic.CreateWalletApplyCash)
group.GET("/fin_settlement", gaslogic.ListFinSettlement)
group.GET("/fin_reconciliation", gaslogic.ListFinReconciliation)
ticket := group.Group("/cs_ticket")
ticket.GET("", gaslogic.ListTicket)
ticket.POST("", gaslogic.CreateTicket)
ticket.GET("/:identity", gaslogic.GetTicket)
ticket.PUT("/:identity", gaslogic.UpdateTicket)
ticket.PATCH("/:identity/status", gaslogic.UpdateTicketStatus)
ticket.DELETE("/:identity", gaslogic.ArchiveTicket)
}

View File

@@ -0,0 +1,54 @@
package routers
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestGasRoutesRequireAuthentication(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
RegisterGas("heqi", engine)
request := httptest.NewRequest(http.MethodGet, "/heqi/gas/v1/gas_menu", nil)
response := httptest.NewRecorder()
engine.ServeHTTP(response, request)
if response.Code == http.StatusOK {
t.Fatal("gas menu must reject anonymous requests")
}
}
func TestGasOrderRoutesDoNotExposeDownstreamActions(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
RegisterGas("heqi", engine)
routes := map[string]bool{}
for _, route := range engine.Routes() {
routes[route.Method+" "+route.Path] = true
}
for _, path := range []string{
"POST /heqi/gas/v1/gasorder_basic/:identity/delivering",
"POST /heqi/gas/v1/gasorder_basic/:identity/awaiting-confirmation",
"POST /heqi/gas/v1/gasorder_basic/:identity/complete",
} {
if routes[path] {
t.Fatalf("gas admin must not expose downstream action %s", path)
}
}
for _, path := range []string{
"POST /heqi/gas/v1/gasorder_basic/:identity/assign",
"POST /heqi/gas/v1/gasorder_basic/:identity/filling",
"POST /heqi/gas/v1/gasorder_basic/:identity/ready",
"POST /heqi/gas/v1/gasorder_basic/:identity/exception",
"POST /heqi/gas/v1/gasorder_basic/:identity/recover",
"POST /heqi/gas/v1/gasorder_basic/:identity/cancel",
} {
if !routes[path] {
t.Fatalf("missing gas order action %s", path)
}
}
}

View File

@@ -6,5 +6,7 @@ import "github.com/gin-gonic/gin"
// Register 注册路由。
func Register(serviceKey string, engine *gin.Engine) {
RegisterPlatform(serviceKey, engine)
RegisterGas(serviceKey, engine)
RegisterDelivery(serviceKey, engine)
registerUploadRoute(serviceKey, engine)
}