From bc8138f9789594a19fe04be9f859e7f46a065c39 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 28 Jul 2026 13:55:40 +0800 Subject: [PATCH] refactor platform logic modules --- backend/api/internal/logic/common/base.go | 9 +- backend/api/internal/logic/common/operator.go | 21 + .../platform/{ => dashboard}/dashboard.go | 2 +- .../{ => dashboard}/dashboard_test.go | 6 +- .../{ => dashboard}/test_helpers_test.go | 6 +- .../logic/platform/delivery/account.go | 65 +++ .../delivery.go} | 2 +- .../platform/{ec_category.go => ec/ec.go} | 2 +- .../platform/{ec_order.go => ec/order.go} | 2 +- .../{fin_settlement.go => fin/fin.go} | 2 +- .../account.go} | 51 +- .../platform/{gas_basic.go => gas/gas.go} | 2 +- .../gasorder.go} | 14 +- .../gasorder_test.go} | 2 +- .../api/internal/logic/platform/platform.go | 509 ------------------ .../logic/platform/platform/access.go | 82 +++ .../logic/platform/platform/account.go | 142 +++++ .../internal/logic/platform/platform/menu.go | 128 +++++ .../internal/logic/platform/platform/role.go | 104 ++++ .../logic/platform/platform/role_menu.go | 92 ++++ .../product.go} | 2 +- .../product_test.go} | 2 +- .../logic/platform/staff/credential.go | 53 ++ .../{staff_account.go => staff/staff.go} | 4 +- .../relation.go} | 121 ++--- .../{user_account.go => user/user.go} | 4 +- .../{wallet_workflow.go => wallet/wallet.go} | 19 +- .../wallet_test.go} | 2 +- backend/api/internal/routers/platform.go | 187 ++++--- 29 files changed, 861 insertions(+), 776 deletions(-) create mode 100644 backend/api/internal/logic/common/operator.go rename backend/api/internal/logic/platform/{ => dashboard}/dashboard.go (96%) rename backend/api/internal/logic/platform/{ => dashboard}/dashboard_test.go (89%) rename backend/api/internal/logic/platform/{ => dashboard}/test_helpers_test.go (80%) create mode 100644 backend/api/internal/logic/platform/delivery/account.go rename backend/api/internal/logic/platform/{delivery_basic.go => delivery/delivery.go} (99%) rename backend/api/internal/logic/platform/{ec_category.go => ec/ec.go} (99%) rename backend/api/internal/logic/platform/{ec_order.go => ec/order.go} (98%) rename backend/api/internal/logic/platform/{fin_settlement.go => fin/fin.go} (99%) rename backend/api/internal/logic/platform/{gas_account_delivery_account.go => gas/account.go} (57%) rename backend/api/internal/logic/platform/{gas_basic.go => gas/gas.go} (99%) rename backend/api/internal/logic/platform/{gasorder_workflow.go => gasorder/gasorder.go} (98%) rename backend/api/internal/logic/platform/{gasorder_workflow_test.go => gasorder/gasorder_test.go} (99%) delete mode 100644 backend/api/internal/logic/platform/platform.go create mode 100644 backend/api/internal/logic/platform/platform/access.go create mode 100644 backend/api/internal/logic/platform/platform/account.go create mode 100644 backend/api/internal/logic/platform/platform/menu.go create mode 100644 backend/api/internal/logic/platform/platform/role.go create mode 100644 backend/api/internal/logic/platform/platform/role_menu.go rename backend/api/internal/logic/platform/{product_workflow.go => product/product.go} (99%) rename backend/api/internal/logic/platform/{product_workflow_test.go => product/product_test.go} (99%) create mode 100644 backend/api/internal/logic/platform/staff/credential.go rename backend/api/internal/logic/platform/{staff_account.go => staff/staff.go} (98%) rename backend/api/internal/logic/platform/{staff_credential_user_address_user_service_relation.go => user/relation.go} (59%) rename backend/api/internal/logic/platform/{user_account.go => user/user.go} (97%) rename backend/api/internal/logic/platform/{wallet_workflow.go => wallet/wallet.go} (95%) rename backend/api/internal/logic/platform/{wallet_workflow_test.go => wallet/wallet_test.go} (99%) diff --git a/backend/api/internal/logic/common/base.go b/backend/api/internal/logic/common/base.go index e7f3f07..2d8fac5 100644 --- a/backend/api/internal/logic/common/base.go +++ b/backend/api/internal/logic/common/base.go @@ -1,4 +1,4 @@ -// Package platform 提供平台总后台的同步 HTTP 业务逻辑。 +// Package common provides shared HTTP and persistence helpers for business logic modules. package common import ( @@ -12,9 +12,16 @@ import ( "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" + "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) +// PasswordHash creates the shared password representation used by account modules. +func PasswordHash(password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hash), err +} + // FilterFields keeps only explicitly allowed persistence fields. func FilterFields(values map[string]any, allowedFields []string) gin.H { allowed := make(map[string]struct{}, len(allowedFields)) diff --git a/backend/api/internal/logic/common/operator.go b/backend/api/internal/logic/common/operator.go new file mode 100644 index 0000000..767fe71 --- /dev/null +++ b/backend/api/internal/logic/common/operator.go @@ -0,0 +1,21 @@ +package common + +import ( + "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" +) + +// PlatformOperator returns the authenticated platform account identity and display name. +func PlatformOperator(ctx *gin.Context) (string, string) { + claims, err := middleware.ParseAuth(ctx) + if err != nil { + return "", "" + } + var account models.PlatformAccount + if err := impl.DBService.Select("display_name").Where("identity = ?", claims.Identity).First(&account).Error; err != nil { + return claims.Identity, "" + } + return claims.Identity, account.DisplayName +} diff --git a/backend/api/internal/logic/platform/dashboard.go b/backend/api/internal/logic/platform/dashboard/dashboard.go similarity index 96% rename from backend/api/internal/logic/platform/dashboard.go rename to backend/api/internal/logic/platform/dashboard/dashboard.go index ddb4ee2..4e4b881 100644 --- a/backend/api/internal/logic/platform/dashboard.go +++ b/backend/api/internal/logic/platform/dashboard/dashboard.go @@ -1,4 +1,4 @@ -package platform +package dashboard import ( "git.apinb.com/bsm-sdk/core/infra" diff --git a/backend/api/internal/logic/platform/dashboard_test.go b/backend/api/internal/logic/platform/dashboard/dashboard_test.go similarity index 89% rename from backend/api/internal/logic/platform/dashboard_test.go rename to backend/api/internal/logic/platform/dashboard/dashboard_test.go index c21a68b..01c4f10 100644 --- a/backend/api/internal/logic/platform/dashboard_test.go +++ b/backend/api/internal/logic/platform/dashboard/dashboard_test.go @@ -1,4 +1,4 @@ -package platform +package dashboard import ( "regexp" @@ -9,7 +9,7 @@ import ( ) func TestDashboardOverviewReturnsZeroValuesForAnEmptyDatabase(t *testing.T) { - _, mock := setupPlatformRoleDatabase(t) + _, mock := setupDashboardDatabase(t) for _, query := range []string{ `SELECT count\(\*\) FROM "gas_basic" WHERE status = \$1`, `SELECT count\(\*\) FROM "delivery_basic" WHERE status = \$1`, @@ -26,5 +26,5 @@ func TestDashboardOverviewReturnsZeroValuesForAnEmptyDatabase(t *testing.T) { if overview != (models.DashboardOverview{}) { t.Fatalf("empty dashboard = %#v, want zero values", overview) } - assertMockExpectations(t, mock) + assertDashboardMockExpectations(t, mock) } diff --git a/backend/api/internal/logic/platform/test_helpers_test.go b/backend/api/internal/logic/platform/dashboard/test_helpers_test.go similarity index 80% rename from backend/api/internal/logic/platform/test_helpers_test.go rename to backend/api/internal/logic/platform/dashboard/test_helpers_test.go index 4b43b10..d771c39 100644 --- a/backend/api/internal/logic/platform/test_helpers_test.go +++ b/backend/api/internal/logic/platform/dashboard/test_helpers_test.go @@ -1,4 +1,4 @@ -package platform +package dashboard import ( "testing" @@ -9,7 +9,7 @@ import ( "gorm.io/gorm" ) -func setupPlatformRoleDatabase(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) { +func setupDashboardDatabase(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) { t.Helper() previous := impl.DBService sqlDatabase, mock, err := sqlmock.New() @@ -28,7 +28,7 @@ func setupPlatformRoleDatabase(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) { return database, mock } -func assertMockExpectations(t *testing.T, mock sqlmock.Sqlmock) { +func assertDashboardMockExpectations(t *testing.T, mock sqlmock.Sqlmock) { t.Helper() if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) diff --git a/backend/api/internal/logic/platform/delivery/account.go b/backend/api/internal/logic/platform/delivery/account.go new file mode 100644 index 0000000..2762f9e --- /dev/null +++ b/backend/api/internal/logic/platform/delivery/account.go @@ -0,0 +1,65 @@ +package delivery + +import ( + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" +) + +type accountRequest struct { + Username string `json:"username" binding:"required,max=64"` + Password string `json:"password" binding:"required,min=8,max=128"` + DisplayName string `json:"display_name" binding:"max=64"` + RoleCode string `json:"role_code" binding:"max=64"` + DeliveryBasicIdentity string `json:"delivery_basic_identity"` +} + +type accountUpdateRequest struct { + DisplayName string `json:"display_name" binding:"max=64"` + RoleCode string `json:"role_code" binding:"max=64"` + DeliveryBasicIdentity string `json:"delivery_basic_identity"` +} + +func ListDeliveryAccount(ctx *gin.Context) { common.ListPage[models.DeliveryAccount](ctx) } +func GetDeliveryAccount(ctx *gin.Context) { common.GetByIdentity[models.DeliveryAccount](ctx) } + +func CreateDeliveryAccount(ctx *gin.Context) { + var request accountRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + hash, err := common.PasswordHash(request.Password) + if err != nil { + infra.Response.Error(ctx, err) + return + } + account := models.DeliveryAccount{Entity: common.NewEntity("enabled"), DeliveryBasicID: deliveryBasicID, Username: request.Username, DisplayName: request.DisplayName, PasswordHash: hash, RoleCode: request.RoleCode} + if err := impl.DBService.Create(&account).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + common.RespondCreatedResource(ctx, account) +} + +func UpdateDeliveryAccount(ctx *gin.Context) { + var request accountUpdateRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.DeliveryAccount{}, gin.H{"delivery_basic_id": deliveryBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"delivery_basic_id", "display_name", "role_code"}) +} diff --git a/backend/api/internal/logic/platform/delivery_basic.go b/backend/api/internal/logic/platform/delivery/delivery.go similarity index 99% rename from backend/api/internal/logic/platform/delivery_basic.go rename to backend/api/internal/logic/platform/delivery/delivery.go index e29d117..85be42b 100644 --- a/backend/api/internal/logic/platform/delivery_basic.go +++ b/backend/api/internal/logic/platform/delivery/delivery.go @@ -1,4 +1,4 @@ -package platform +package delivery import ( "git.apinb.com/bsm-sdk/core/errcode" diff --git a/backend/api/internal/logic/platform/ec_category.go b/backend/api/internal/logic/platform/ec/ec.go similarity index 99% rename from backend/api/internal/logic/platform/ec_category.go rename to backend/api/internal/logic/platform/ec/ec.go index 406dbfd..68b6b42 100644 --- a/backend/api/internal/logic/platform/ec_category.go +++ b/backend/api/internal/logic/platform/ec/ec.go @@ -1,4 +1,4 @@ -package platform +package ec import ( "git.apinb.com/bsm-sdk/core/infra" diff --git a/backend/api/internal/logic/platform/ec_order.go b/backend/api/internal/logic/platform/ec/order.go similarity index 98% rename from backend/api/internal/logic/platform/ec_order.go rename to backend/api/internal/logic/platform/ec/order.go index 4e38810..4935e1f 100644 --- a/backend/api/internal/logic/platform/ec_order.go +++ b/backend/api/internal/logic/platform/ec/order.go @@ -1,4 +1,4 @@ -package platform +package ec import ( "git.apinb.com/bsm-sdk/core/infra" diff --git a/backend/api/internal/logic/platform/fin_settlement.go b/backend/api/internal/logic/platform/fin/fin.go similarity index 99% rename from backend/api/internal/logic/platform/fin_settlement.go rename to backend/api/internal/logic/platform/fin/fin.go index b68ef31..d789f27 100644 --- a/backend/api/internal/logic/platform/fin_settlement.go +++ b/backend/api/internal/logic/platform/fin/fin.go @@ -1,4 +1,4 @@ -package platform +package fin import ( "bytes" diff --git a/backend/api/internal/logic/platform/gas_account_delivery_account.go b/backend/api/internal/logic/platform/gas/account.go similarity index 57% rename from backend/api/internal/logic/platform/gas_account_delivery_account.go rename to backend/api/internal/logic/platform/gas/account.go index c2a5f3a..28c062e 100644 --- a/backend/api/internal/logic/platform/gas_account_delivery_account.go +++ b/backend/api/internal/logic/platform/gas/account.go @@ -1,4 +1,4 @@ -package platform +package gas import ( "git.apinb.com/bsm-sdk/core/errcode" @@ -7,7 +7,6 @@ import ( "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" - "golang.org/x/crypto/bcrypt" ) type accountRequest struct { @@ -26,11 +25,6 @@ type accountUpdateRequest struct { DeliveryBasicIdentity string `json:"delivery_basic_identity"` } -func passwordHash(password string) (string, error) { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - return string(hash), err -} - func ListGasAccount(ctx *gin.Context) { common.ListPage[models.GasAccount](ctx) } func GetGasAccount(ctx *gin.Context) { common.GetByIdentity[models.GasAccount](ctx) } @@ -45,7 +39,7 @@ func CreateGasAccount(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - hash, err := passwordHash(request.Password) + hash, err := common.PasswordHash(request.Password) if err != nil { infra.Response.Error(ctx, err) return @@ -71,44 +65,3 @@ func UpdateGasAccount(ctx *gin.Context) { } common.UpdateAllowedByIdentity(ctx, &models.GasAccount{}, gin.H{"gas_basic_id": gasBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"gas_basic_id", "display_name", "role_code"}) } - -func ListDeliveryAccount(ctx *gin.Context) { common.ListPage[models.DeliveryAccount](ctx) } -func GetDeliveryAccount(ctx *gin.Context) { common.GetByIdentity[models.DeliveryAccount](ctx) } - -func CreateDeliveryAccount(ctx *gin.Context) { - var request accountRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - hash, err := passwordHash(request.Password) - if err != nil { - infra.Response.Error(ctx, err) - return - } - account := models.DeliveryAccount{Entity: common.NewEntity("enabled"), DeliveryBasicID: deliveryBasicID, Username: request.Username, DisplayName: request.DisplayName, PasswordHash: hash, RoleCode: request.RoleCode} - if err := impl.DBService.Create(&account).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - common.RespondCreatedResource(ctx, account) -} - -func UpdateDeliveryAccount(ctx *gin.Context) { - var request accountUpdateRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.DeliveryAccount{}, gin.H{"delivery_basic_id": deliveryBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"delivery_basic_id", "display_name", "role_code"}) -} diff --git a/backend/api/internal/logic/platform/gas_basic.go b/backend/api/internal/logic/platform/gas/gas.go similarity index 99% rename from backend/api/internal/logic/platform/gas_basic.go rename to backend/api/internal/logic/platform/gas/gas.go index 34c6f73..a092d91 100644 --- a/backend/api/internal/logic/platform/gas_basic.go +++ b/backend/api/internal/logic/platform/gas/gas.go @@ -1,4 +1,4 @@ -package platform +package gas import ( "git.apinb.com/bsm-sdk/core/errcode" diff --git a/backend/api/internal/logic/platform/gasorder_workflow.go b/backend/api/internal/logic/platform/gasorder/gasorder.go similarity index 98% rename from backend/api/internal/logic/platform/gasorder_workflow.go rename to backend/api/internal/logic/platform/gasorder/gasorder.go index fce0eb8..be9d751 100644 --- a/backend/api/internal/logic/platform/gasorder_workflow.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder.go @@ -1,4 +1,4 @@ -package platform +package gasorder import ( "errors" @@ -212,7 +212,7 @@ func RenewGasorderContract(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var contract models.GasorderContract if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { @@ -242,7 +242,7 @@ func changeGasorderContract(ctx *gin.Context, action, target string) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var contract models.GasorderContract if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { @@ -363,7 +363,7 @@ func CreateGasorderBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) var order models.GasorderBasic err = impl.DBService.Transaction(func(tx *gorm.DB) error { if err := tx.Where("request_no = ?", request.RequestNo).First(&order).Error; err == nil { @@ -472,7 +472,7 @@ func AssignGasorderBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { @@ -530,7 +530,7 @@ func GasorderRecover(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { @@ -560,7 +560,7 @@ func transitionGasorder(ctx *gin.Context, target string, allowed map[string]bool infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { diff --git a/backend/api/internal/logic/platform/gasorder_workflow_test.go b/backend/api/internal/logic/platform/gasorder/gasorder_test.go similarity index 99% rename from backend/api/internal/logic/platform/gasorder_workflow_test.go rename to backend/api/internal/logic/platform/gasorder/gasorder_test.go index 0f529cb..c8a9fe4 100644 --- a/backend/api/internal/logic/platform/gasorder_workflow_test.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder_test.go @@ -1,4 +1,4 @@ -package platform +package gasorder import ( "testing" diff --git a/backend/api/internal/logic/platform/platform.go b/backend/api/internal/logic/platform/platform.go deleted file mode 100644 index 2c819d3..0000000 --- a/backend/api/internal/logic/platform/platform.go +++ /dev/null @@ -1,509 +0,0 @@ -package platform - -import ( - "errors" - "strings" - - "git.apinb.com/bsm-sdk/core/errcode" - "git.apinb.com/bsm-sdk/core/infra" - "git.apinb.com/bsm-sdk/core/middleware" - "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" - "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" - "git.apinb.com/heqiapp/platforms/backend/api/internal/models" - "github.com/gin-gonic/gin" - "golang.org/x/crypto/bcrypt" - "gorm.io/gorm" -) - -var errSystemPlatformRole = errors.New("system platform roles cannot be modified") - -const platformMenusContextKey = "platform_authorized_menus" - -func platformMenuAllowsPath(menus []models.PlatformMenu, requestPath string) bool { - marker := "/platform/v1/" - index := strings.Index(requestPath, marker) - if index < 0 { - return false - } - relative := strings.Trim(requestPath[index+len(marker):], "/") - resource := strings.Split(relative, "/")[0] - domain := platformRouteDomain(resource) - for _, menu := range menus { - if menu.MenuCode == domain { - return true - } - menuPath := strings.Trim(menu.Path, "/") - if menuPath != "" && strings.Split(menuPath, "/")[0] == domain { - return true - } - } - return false -} - -func platformRouteDomain(resource string) string { - prefix := strings.Split(resource, "_")[0] - switch prefix { - case "product": - return "device" - case "gasorder": - return "delivery" - case "fin": - return "finance" - case "cms": - return "content" - case "cs": - return "customer_service" - case "platform": - return "platform" - default: - return prefix - } -} - -// RequirePlatformMenuAccess enforces role-menu authorization after JWT authentication. -func RequirePlatformMenuAccess() gin.HandlerFunc { - return func(ctx *gin.Context) { - if strings.Contains(ctx.Request.URL.Path, "/platform/v1/auth/") { - ctx.Next() - return - } - claims, err := middleware.ParseAuth(ctx) - if err != nil { - infra.Response.Error(ctx, err) - ctx.Abort() - return - } - if claims.Role == "root" { - ctx.Next() - return - } - menus, err := common.LoadPlatformMenus(claims.Role) - if err != nil || !platformMenuAllowsPath(menus, ctx.Request.URL.Path) { - infra.Response.Error(ctx, errcode.ErrPermissionDenied) - ctx.Abort() - return - } - ctx.Set(platformMenusContextKey, menus) - ctx.Next() - } -} - -// ListPlatformRole 查询平台角色分页列表。 -func ListPlatformRole(ctx *gin.Context) { common.ListPage[models.PlatformRole](ctx) } - -// GetPlatformRole 查询一个平台角色。 -func GetPlatformRole(ctx *gin.Context) { common.GetByIdentity[models.PlatformRole](ctx) } - -// CreatePlatformRole 创建非内置平台角色。 -func CreatePlatformRole(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request models.PlatformRole - if err := ctx.ShouldBindJSON(&request); err != nil || request.RoleCode == "" || request.Name == "" || request.RoleCode == "root" { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - request.Entity = common.NewEntity("enabled") - request.IsSystem = false - if request.DataScope == "" { - request.DataScope = "global" - } - if err := impl.DBService.Create(&request).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - common.RespondCreatedResource(ctx, request) -} - -// UpdatePlatformRole 更新非内置平台角色。 -func UpdatePlatformRole(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request struct { - Name string `json:"name" binding:"required,max=64"` - DataScope string `json:"data_scope" binding:"required,max=32"` - } - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - if role.IsSystem { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"name": request.Name, "data_scope": request.DataScope}, []string{"name", "data_scope"}) -} - -type platformMenuRequest struct { - ParentIdentity string `json:"parent_identity"` - MenuCode string `json:"menu_code" binding:"required,max=64"` - Name string `json:"name" binding:"required,max=64"` - Icon string `json:"icon" binding:"max=64"` - Path string `json:"path" binding:"max=255"` - SortNo int `json:"sort_no"` -} - -type platformMenuView struct { - Identity string `json:"identity"` - ParentIdentity string `json:"parent_identity,omitempty"` - MenuCode string `json:"menu_code"` - Name string `json:"name"` - Icon string `json:"icon"` - Path string `json:"path"` - SortNo int `json:"sort_no"` - Status string `json:"status"` -} - -func platformMenuViews(list []models.PlatformMenu) []platformMenuView { - identities := make(map[uint64]string, len(list)) - for _, item := range list { - identities[item.ID] = item.Identity - } - views := make([]platformMenuView, 0, len(list)) - for _, item := range list { - views = append(views, platformMenuView{Identity: item.Identity, ParentIdentity: identities[item.ParentID], MenuCode: item.MenuCode, Name: item.Name, Icon: item.Icon, Path: item.Path, SortNo: item.SortNo, Status: item.Status}) - } - return views -} - -func GetPlatformMenu(ctx *gin.Context) { - var menu models.PlatformMenu - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&menu).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - list := []models.PlatformMenu{menu} - if menu.ParentID != 0 { - var parent models.PlatformMenu - if err := impl.DBService.Select("id", "identity").First(&parent, menu.ParentID).Error; err == nil { - list = append(list, parent) - } - } - views := platformMenuViews(list) - infra.Response.Success(ctx, views[0]) -} - -func CreatePlatformMenu(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request platformMenuRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - parentID, err := common.ResolveIdentityID(&models.PlatformMenu{}, request.ParentIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - menu := models.PlatformMenu{Entity: common.NewEntity("enabled"), ParentID: parentID, MenuCode: request.MenuCode, Name: request.Name, Icon: request.Icon, Path: request.Path, SortNo: request.SortNo} - if err := impl.DBService.Create(&menu).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - common.RespondCreatedResource(ctx, menu) -} - -func UpdatePlatformMenu(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request platformMenuRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - parentID, err := common.ResolveIdentityID(&models.PlatformMenu{}, request.ParentIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.PlatformMenu{}, gin.H{"parent_id": parentID, "name": request.Name, "icon": request.Icon, "path": request.Path, "sort_no": request.SortNo}, []string{"parent_id", "name", "icon", "path", "sort_no"}) -} - -func UpdatePlatformMenuStatus(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - common.UpdateRecordStatus(ctx, &models.PlatformMenu{}) -} - -func ArchivePlatformMenu(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - common.ArchiveRecord(ctx, &models.PlatformMenu{}) -} - -type platformRoleMenusRequest struct { - MenuIdentities []string `json:"menu_identities"` -} - -// ReplacePlatformRoleMenus replaces every menu assignment for a role atomically. -func ReplacePlatformRoleMenus(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request platformRoleMenusRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - if err := impl.DBService.Transaction(func(transaction *gorm.DB) error { - var role models.PlatformRole - if err := transaction.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { - return err - } - if role.IsSystem { - return errSystemPlatformRole - } - var menus []models.PlatformMenu - if len(request.MenuIdentities) > 0 { - if err := transaction.Where("identity IN ?", request.MenuIdentities).Find(&menus).Error; err != nil { - return err - } - if len(menus) != len(request.MenuIdentities) { - return gorm.ErrRecordNotFound - } - } - if err := transaction.Where("platform_role_id = ?", role.ID).Delete(&models.PlatformRoleMenu{}).Error; err != nil { - return err - } - for _, menu := range menus { - relation := models.PlatformRoleMenu{PlatformRoleID: role.ID, PlatformMenuID: menu.ID} - if err := transaction.Create(&relation).Error; err != nil { - return err - } - } - return nil - }); err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - infra.Response.Error(ctx, errcode.ErrRecordNotFound) - return - } - if errors.Is(err, errSystemPlatformRole) { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - infra.Response.Error(ctx, err) - return - } - infra.Response.Success(ctx, gin.H{"updated": true}) -} - -// ListPlatformRoleMenuIdentities returns the current assignment for the role editor. -func ListPlatformRoleMenuIdentities(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - var identities []string - if err := impl.DBService.Model(&models.PlatformMenu{}). - Joins("JOIN platform_role_menu ON platform_role_menu.platform_menu_id = platform_menu.id"). - Where("platform_role_menu.platform_role_id = ?", role.ID). - Order("platform_menu.sort_no asc, platform_menu.id asc"). - Pluck("platform_menu.identity", &identities).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - infra.Response.Success(ctx, gin.H{"menu_identities": identities}) -} - -// UpdatePlatformRoleStatus 更新非内置平台角色状态,系统角色始终受保护。 -func UpdatePlatformRoleStatus(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request struct { - Status string `json:"status" binding:"required,max=32"` - } - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - if role.IsSystem { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"status": request.Status}, []string{"status"}) -} - -// ArchivePlatformRole 归档非内置平台角色,系统角色始终受保护。 -func ArchivePlatformRole(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - if role.IsSystem { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"status": "archived"}, []string{"status"}) -} - -// ListPlatformMenu 返回菜单树构建所需的有序菜单列表。 -func ListPlatformMenu(ctx *gin.Context) { - claims, err := middleware.ParseAuth(ctx) - if err != nil { - infra.Response.Error(ctx, err) - return - } - list, err := common.LoadPlatformMenus(claims.Role) - if err != nil { - infra.Response.Error(ctx, err) - return - } - infra.Response.Success(ctx, gin.H{"total": len(list), "list": platformMenuViews(list)}) -} - -// ListPlatformAccount 查询平台账号列表,手机号在展示层脱敏。 -func ListPlatformAccount(ctx *gin.Context) { - page, size := common.PageSize(ctx) - var list []models.PlatformAccount - var total int64 - query := common.ApplyKeywordFilter(ctx, impl.DBService.Model(&models.PlatformAccount{}), &models.PlatformAccount{}) - if err := query.Count(&total).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - views := make([]map[string]any, 0, len(list)) - for _, item := range list { - view := platformAccountView(item) - common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view) - views = append(views, view) - } - infra.Response.Success(ctx, gin.H{"total": total, "list": views}) -} - -type platformAccountRequest struct { - Username string `json:"username" binding:"required,max=64"` - Password string `json:"password" binding:"required,min=8,max=128"` - DisplayName string `json:"display_name" binding:"max=64"` - Avatar string `json:"avatar" binding:"max=512"` - PlatformRoleCode string `json:"platform_role_code" binding:"required,max=64"` - Phone string `json:"phone" binding:"max=32"` -} - -func platformAccountView(account models.PlatformAccount) map[string]any { - return map[string]any{ - "identity": account.Identity, "username": account.Username, - "display_name": account.DisplayName, "avatar": account.Avatar, "phone": account.Phone, - "platform_role_code": account.PlatformRoleCode, "status": account.Status, - } -} - -func GetPlatformAccount(ctx *gin.Context) { - var account models.PlatformAccount - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil { - common.RespondRecordError(ctx, err) - return - } - view := platformAccountView(account) - infra.Response.Success(ctx, common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view)) -} - -func CreatePlatformAccount(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - var request platformAccountRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - if !isAssignablePlatformRole(request.PlatformRoleCode) { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - hash, err := platformPasswordHash(request.Password) - if err != nil { - infra.Response.Error(ctx, err) - return - } - account := models.PlatformAccount{Entity: common.NewEntity("enabled"), Username: request.Username, DisplayName: request.DisplayName, Avatar: request.Avatar, PasswordHash: hash, PlatformRoleCode: request.PlatformRoleCode, Phone: request.Phone} - if err := impl.DBService.Create(&account).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - view := platformAccountView(account) - infra.Response.Success(ctx, common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view)) -} - -func UpdatePlatformAccount(ctx *gin.Context) { - var request struct { - DisplayName string `json:"display_name" binding:"max=64"` - Avatar string `json:"avatar" binding:"max=512"` - PlatformRoleCode *string `json:"platform_role_code" binding:"omitempty,max=64"` - Phone string `json:"phone" binding:"max=32"` - } - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - values := gin.H{"display_name": request.DisplayName, "avatar": request.Avatar, "phone": request.Phone} - if request.PlatformRoleCode != nil { - if !common.RequirePlatformRoot(ctx) { - return - } - if !isAssignablePlatformRole(*request.PlatformRoleCode) { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - values["platform_role_code"] = *request.PlatformRoleCode - } - common.UpdateAllowedByIdentity(ctx, &models.PlatformAccount{}, values, []string{"display_name", "avatar", "platform_role_code", "phone"}) -} - -// UpdatePlatformAccountStatus updates a platform account lifecycle state. -func UpdatePlatformAccountStatus(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - common.UpdateRecordStatus(ctx, &models.PlatformAccount{}) -} - -// ArchivePlatformAccount archives a platform account without deleting audit history. -func ArchivePlatformAccount(ctx *gin.Context) { - if !common.RequirePlatformRoot(ctx) { - return - } - common.ArchiveRecord(ctx, &models.PlatformAccount{}) -} - -func platformPasswordHash(password string) (string, error) { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - return string(hash), err -} - -func isAssignablePlatformRole(roleCode string) bool { - if roleCode == "" || roleCode == "root" { - return false - } - var role models.PlatformRole - return impl.DBService.Where("role_code = ? AND status = ? AND is_system = ?", roleCode, "enabled", false).First(&role).Error == nil -} diff --git a/backend/api/internal/logic/platform/platform/access.go b/backend/api/internal/logic/platform/platform/access.go new file mode 100644 index 0000000..204b1ab --- /dev/null +++ b/backend/api/internal/logic/platform/platform/access.go @@ -0,0 +1,82 @@ +package platform + +import ( + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" + "strings" +) + +const platformMenusContextKey = "platform_authorized_menus" + +func platformMenuAllowsPath(menus []models.PlatformMenu, requestPath string) bool { + marker := "/platform/v1/" + index := strings.Index(requestPath, marker) + if index < 0 { + return false + } + relative := strings.Trim(requestPath[index+len(marker):], "/") + resource := strings.Split(relative, "/")[0] + domain := platformRouteDomain(resource) + for _, menu := range menus { + if menu.MenuCode == domain { + return true + } + menuPath := strings.Trim(menu.Path, "/") + if menuPath != "" && strings.Split(menuPath, "/")[0] == domain { + return true + } + } + return false +} + +func platformRouteDomain(resource string) string { + prefix := strings.Split(resource, "_")[0] + switch prefix { + case "product": + return "device" + case "gasorder": + return "delivery" + case "fin": + return "finance" + case "cms": + return "content" + case "cs": + return "customer_service" + case "platform": + return "platform" + default: + return prefix + } +} + +// RequirePlatformMenuAccess enforces role-menu authorization after JWT authentication. +func RequirePlatformMenuAccess() gin.HandlerFunc { + return func(ctx *gin.Context) { + if strings.Contains(ctx.Request.URL.Path, "/platform/v1/auth/") { + ctx.Next() + return + } + claims, err := middleware.ParseAuth(ctx) + if err != nil { + infra.Response.Error(ctx, err) + ctx.Abort() + return + } + if claims.Role == "root" { + ctx.Next() + return + } + menus, err := common.LoadPlatformMenus(claims.Role) + if err != nil || !platformMenuAllowsPath(menus, ctx.Request.URL.Path) { + infra.Response.Error(ctx, errcode.ErrPermissionDenied) + ctx.Abort() + return + } + ctx.Set(platformMenusContextKey, menus) + ctx.Next() + } +} diff --git a/backend/api/internal/logic/platform/platform/account.go b/backend/api/internal/logic/platform/platform/account.go new file mode 100644 index 0000000..6a45635 --- /dev/null +++ b/backend/api/internal/logic/platform/platform/account.go @@ -0,0 +1,142 @@ +package platform + +import ( + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/bcrypt" +) + +// ListPlatformAccount 查询平台账号列表,手机号在展示层脱敏。 +func ListPlatformAccount(ctx *gin.Context) { + page, size := common.PageSize(ctx) + var list []models.PlatformAccount + var total int64 + query := common.ApplyKeywordFilter(ctx, impl.DBService.Model(&models.PlatformAccount{}), &models.PlatformAccount{}) + if err := query.Count(&total).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + views := make([]map[string]any, 0, len(list)) + for _, item := range list { + view := platformAccountView(item) + common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view) + views = append(views, view) + } + infra.Response.Success(ctx, gin.H{"total": total, "list": views}) +} + +type platformAccountRequest struct { + Username string `json:"username" binding:"required,max=64"` + Password string `json:"password" binding:"required,min=8,max=128"` + DisplayName string `json:"display_name" binding:"max=64"` + Avatar string `json:"avatar" binding:"max=512"` + PlatformRoleCode string `json:"platform_role_code" binding:"required,max=64"` + Phone string `json:"phone" binding:"max=32"` +} + +func platformAccountView(account models.PlatformAccount) map[string]any { + return map[string]any{ + "identity": account.Identity, "username": account.Username, + "display_name": account.DisplayName, "avatar": account.Avatar, "phone": account.Phone, + "platform_role_code": account.PlatformRoleCode, "status": account.Status, + } +} + +func GetPlatformAccount(ctx *gin.Context) { + var account models.PlatformAccount + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + view := platformAccountView(account) + infra.Response.Success(ctx, common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view)) +} + +func CreatePlatformAccount(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request platformAccountRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + if !isAssignablePlatformRole(request.PlatformRoleCode) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + hash, err := platformPasswordHash(request.Password) + if err != nil { + infra.Response.Error(ctx, err) + return + } + account := models.PlatformAccount{Entity: common.NewEntity("enabled"), Username: request.Username, DisplayName: request.DisplayName, Avatar: request.Avatar, PasswordHash: hash, PlatformRoleCode: request.PlatformRoleCode, Phone: request.Phone} + if err := impl.DBService.Create(&account).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + view := platformAccountView(account) + infra.Response.Success(ctx, common.ProtectPreciseLocation(ctx, &models.PlatformAccount{}, view)) +} + +func UpdatePlatformAccount(ctx *gin.Context) { + var request struct { + DisplayName string `json:"display_name" binding:"max=64"` + Avatar string `json:"avatar" binding:"max=512"` + PlatformRoleCode *string `json:"platform_role_code" binding:"omitempty,max=64"` + Phone string `json:"phone" binding:"max=32"` + } + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + values := gin.H{"display_name": request.DisplayName, "avatar": request.Avatar, "phone": request.Phone} + if request.PlatformRoleCode != nil { + if !common.RequirePlatformRoot(ctx) { + return + } + if !isAssignablePlatformRole(*request.PlatformRoleCode) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + values["platform_role_code"] = *request.PlatformRoleCode + } + common.UpdateAllowedByIdentity(ctx, &models.PlatformAccount{}, values, []string{"display_name", "avatar", "platform_role_code", "phone"}) +} + +// UpdatePlatformAccountStatus updates a platform account lifecycle state. +func UpdatePlatformAccountStatus(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + common.UpdateRecordStatus(ctx, &models.PlatformAccount{}) +} + +// ArchivePlatformAccount archives a platform account without deleting audit history. +func ArchivePlatformAccount(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + common.ArchiveRecord(ctx, &models.PlatformAccount{}) +} + +func platformPasswordHash(password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hash), err +} + +func isAssignablePlatformRole(roleCode string) bool { + if roleCode == "" || roleCode == "root" { + return false + } + var role models.PlatformRole + return impl.DBService.Where("role_code = ? AND status = ? AND is_system = ?", roleCode, "enabled", false).First(&role).Error == nil +} diff --git a/backend/api/internal/logic/platform/platform/menu.go b/backend/api/internal/logic/platform/platform/menu.go new file mode 100644 index 0000000..3542650 --- /dev/null +++ b/backend/api/internal/logic/platform/platform/menu.go @@ -0,0 +1,128 @@ +package platform + +import ( + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" +) + +type platformMenuRequest struct { + ParentIdentity string `json:"parent_identity"` + MenuCode string `json:"menu_code" binding:"required,max=64"` + Name string `json:"name" binding:"required,max=64"` + Icon string `json:"icon" binding:"max=64"` + Path string `json:"path" binding:"max=255"` + SortNo int `json:"sort_no"` +} + +type platformMenuView struct { + Identity string `json:"identity"` + ParentIdentity string `json:"parent_identity,omitempty"` + MenuCode string `json:"menu_code"` + Name string `json:"name"` + Icon string `json:"icon"` + Path string `json:"path"` + SortNo int `json:"sort_no"` + Status string `json:"status"` +} + +func platformMenuViews(list []models.PlatformMenu) []platformMenuView { + identities := make(map[uint64]string, len(list)) + for _, item := range list { + identities[item.ID] = item.Identity + } + views := make([]platformMenuView, 0, len(list)) + for _, item := range list { + views = append(views, platformMenuView{Identity: item.Identity, ParentIdentity: identities[item.ParentID], MenuCode: item.MenuCode, Name: item.Name, Icon: item.Icon, Path: item.Path, SortNo: item.SortNo, Status: item.Status}) + } + return views +} + +func GetPlatformMenu(ctx *gin.Context) { + var menu models.PlatformMenu + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&menu).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + list := []models.PlatformMenu{menu} + if menu.ParentID != 0 { + var parent models.PlatformMenu + if err := impl.DBService.Select("id", "identity").First(&parent, menu.ParentID).Error; err == nil { + list = append(list, parent) + } + } + views := platformMenuViews(list) + infra.Response.Success(ctx, views[0]) +} + +func CreatePlatformMenu(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request platformMenuRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + parentID, err := common.ResolveIdentityID(&models.PlatformMenu{}, request.ParentIdentity, false) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + menu := models.PlatformMenu{Entity: common.NewEntity("enabled"), ParentID: parentID, MenuCode: request.MenuCode, Name: request.Name, Icon: request.Icon, Path: request.Path, SortNo: request.SortNo} + if err := impl.DBService.Create(&menu).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + common.RespondCreatedResource(ctx, menu) +} + +func UpdatePlatformMenu(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request platformMenuRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + parentID, err := common.ResolveIdentityID(&models.PlatformMenu{}, request.ParentIdentity, false) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.PlatformMenu{}, gin.H{"parent_id": parentID, "name": request.Name, "icon": request.Icon, "path": request.Path, "sort_no": request.SortNo}, []string{"parent_id", "name", "icon", "path", "sort_no"}) +} + +func UpdatePlatformMenuStatus(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + common.UpdateRecordStatus(ctx, &models.PlatformMenu{}) +} + +func ArchivePlatformMenu(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + common.ArchiveRecord(ctx, &models.PlatformMenu{}) +} + +// ListPlatformMenu 返回菜单树构建所需的有序菜单列表。 +func ListPlatformMenu(ctx *gin.Context) { + claims, err := middleware.ParseAuth(ctx) + if err != nil { + infra.Response.Error(ctx, err) + return + } + list, err := common.LoadPlatformMenus(claims.Role) + if err != nil { + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"total": len(list), "list": platformMenuViews(list)}) +} diff --git a/backend/api/internal/logic/platform/platform/role.go b/backend/api/internal/logic/platform/platform/role.go new file mode 100644 index 0000000..eaa9dbd --- /dev/null +++ b/backend/api/internal/logic/platform/platform/role.go @@ -0,0 +1,104 @@ +package platform + +import ( + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" +) + +// ListPlatformRole 查询平台角色分页列表。 +func ListPlatformRole(ctx *gin.Context) { common.ListPage[models.PlatformRole](ctx) } + +// GetPlatformRole 查询一个平台角色。 +func GetPlatformRole(ctx *gin.Context) { common.GetByIdentity[models.PlatformRole](ctx) } + +// CreatePlatformRole 创建非内置平台角色。 +func CreatePlatformRole(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request models.PlatformRole + if err := ctx.ShouldBindJSON(&request); err != nil || request.RoleCode == "" || request.Name == "" || request.RoleCode == "root" { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + request.Entity = common.NewEntity("enabled") + request.IsSystem = false + if request.DataScope == "" { + request.DataScope = "global" + } + if err := impl.DBService.Create(&request).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + common.RespondCreatedResource(ctx, request) +} + +// UpdatePlatformRole 更新非内置平台角色。 +func UpdatePlatformRole(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request struct { + Name string `json:"name" binding:"required,max=64"` + DataScope string `json:"data_scope" binding:"required,max=32"` + } + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + var role models.PlatformRole + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + if role.IsSystem { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"name": request.Name, "data_scope": request.DataScope}, []string{"name", "data_scope"}) +} + +// UpdatePlatformRoleStatus 更新非内置平台角色状态,系统角色始终受保护。 +func UpdatePlatformRoleStatus(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request struct { + Status string `json:"status" binding:"required,max=32"` + } + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + var role models.PlatformRole + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + if role.IsSystem { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"status": request.Status}, []string{"status"}) +} + +// ArchivePlatformRole 归档非内置平台角色,系统角色始终受保护。 +func ArchivePlatformRole(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var role models.PlatformRole + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + if role.IsSystem { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"status": "archived"}, []string{"status"}) +} diff --git a/backend/api/internal/logic/platform/platform/role_menu.go b/backend/api/internal/logic/platform/platform/role_menu.go new file mode 100644 index 0000000..5bbab3f --- /dev/null +++ b/backend/api/internal/logic/platform/platform/role_menu.go @@ -0,0 +1,92 @@ +package platform + +import ( + "errors" + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +var errSystemPlatformRole = errors.New("system platform roles cannot be modified") + +type platformRoleMenusRequest struct { + MenuIdentities []string `json:"menu_identities"` +} + +// ReplacePlatformRoleMenus replaces every menu assignment for a role atomically. +func ReplacePlatformRoleMenus(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var request platformRoleMenusRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + if err := impl.DBService.Transaction(func(transaction *gorm.DB) error { + var role models.PlatformRole + if err := transaction.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + return err + } + if role.IsSystem { + return errSystemPlatformRole + } + var menus []models.PlatformMenu + if len(request.MenuIdentities) > 0 { + if err := transaction.Where("identity IN ?", request.MenuIdentities).Find(&menus).Error; err != nil { + return err + } + if len(menus) != len(request.MenuIdentities) { + return gorm.ErrRecordNotFound + } + } + if err := transaction.Where("platform_role_id = ?", role.ID).Delete(&models.PlatformRoleMenu{}).Error; err != nil { + return err + } + for _, menu := range menus { + relation := models.PlatformRoleMenu{PlatformRoleID: role.ID, PlatformMenuID: menu.ID} + if err := transaction.Create(&relation).Error; err != nil { + return err + } + } + return nil + }); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + infra.Response.Error(ctx, errcode.ErrRecordNotFound) + return + } + if errors.Is(err, errSystemPlatformRole) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"updated": true}) +} + +// ListPlatformRoleMenuIdentities returns the current assignment for the role editor. +func ListPlatformRoleMenuIdentities(ctx *gin.Context) { + if !common.RequirePlatformRoot(ctx) { + return + } + var role models.PlatformRole + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + var identities []string + if err := impl.DBService.Model(&models.PlatformMenu{}). + Joins("JOIN platform_role_menu ON platform_role_menu.platform_menu_id = platform_menu.id"). + Where("platform_role_menu.platform_role_id = ?", role.ID). + Order("platform_menu.sort_no asc, platform_menu.id asc"). + Pluck("platform_menu.identity", &identities).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"menu_identities": identities}) +} diff --git a/backend/api/internal/logic/platform/product_workflow.go b/backend/api/internal/logic/platform/product/product.go similarity index 99% rename from backend/api/internal/logic/platform/product_workflow.go rename to backend/api/internal/logic/platform/product/product.go index c4152cf..188553d 100644 --- a/backend/api/internal/logic/platform/product_workflow.go +++ b/backend/api/internal/logic/platform/product/product.go @@ -1,4 +1,4 @@ -package platform +package product import ( "encoding/json" diff --git a/backend/api/internal/logic/platform/product_workflow_test.go b/backend/api/internal/logic/platform/product/product_test.go similarity index 99% rename from backend/api/internal/logic/platform/product_workflow_test.go rename to backend/api/internal/logic/platform/product/product_test.go index 37d9b04..7eebc07 100644 --- a/backend/api/internal/logic/platform/product_workflow_test.go +++ b/backend/api/internal/logic/platform/product/product_test.go @@ -1,4 +1,4 @@ -package platform +package product import ( "testing" diff --git a/backend/api/internal/logic/platform/staff/credential.go b/backend/api/internal/logic/platform/staff/credential.go new file mode 100644 index 0000000..b4521c7 --- /dev/null +++ b/backend/api/internal/logic/platform/staff/credential.go @@ -0,0 +1,53 @@ +package staff + +import ( + "time" + + "git.apinb.com/bsm-sdk/core/errcode" + "git.apinb.com/bsm-sdk/core/infra" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" + "github.com/gin-gonic/gin" +) + +type staffCredentialRequest struct { + StaffAccountIdentity string `json:"staff_account_identity" binding:"required"` + CredentialType string `json:"credential_type" binding:"required,max=64"` + CredentialNo string `json:"credential_no" binding:"max=128"` + ExpiredAt *time.Time `json:"expired_at"` +} + +func ListStaffCredential(ctx *gin.Context) { common.ListPage[models.StaffCredential](ctx) } +func GetStaffCredential(ctx *gin.Context) { common.GetByIdentity[models.StaffCredential](ctx) } +func CreateStaffCredential(ctx *gin.Context) { + var request staffCredentialRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + credential := models.StaffCredential{Entity: common.NewEntity("enabled"), StaffAccountID: staffAccountID, CredentialType: request.CredentialType, CredentialNo: request.CredentialNo, ExpiredAt: request.ExpiredAt} + if err := impl.DBService.Create(&credential).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + common.RespondCreatedResource(ctx, credential) +} +func UpdateStaffCredential(ctx *gin.Context) { + var request staffCredentialRequest + if err := ctx.ShouldBindJSON(&request); err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.UpdateAllowedByIdentity(ctx, &models.StaffCredential{}, gin.H{"staff_account_id": staffAccountID, "credential_type": request.CredentialType, "credential_no": request.CredentialNo, "expired_at": request.ExpiredAt}, []string{"staff_account_id", "credential_type", "credential_no", "expired_at"}) +} diff --git a/backend/api/internal/logic/platform/staff_account.go b/backend/api/internal/logic/platform/staff/staff.go similarity index 98% rename from backend/api/internal/logic/platform/staff_account.go rename to backend/api/internal/logic/platform/staff/staff.go index 1149e09..9e98e20 100644 --- a/backend/api/internal/logic/platform/staff_account.go +++ b/backend/api/internal/logic/platform/staff/staff.go @@ -1,4 +1,4 @@ -package platform +package staff import ( "git.apinb.com/bsm-sdk/core/errcode" @@ -32,7 +32,7 @@ func CreateStaff(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - hash, err := passwordHash(request.Password) + hash, err := common.PasswordHash(request.Password) if err != nil { infra.Response.Error(ctx, err) return diff --git a/backend/api/internal/logic/platform/staff_credential_user_address_user_service_relation.go b/backend/api/internal/logic/platform/user/relation.go similarity index 59% rename from backend/api/internal/logic/platform/staff_credential_user_address_user_service_relation.go rename to backend/api/internal/logic/platform/user/relation.go index 779706d..35bd16b 100644 --- a/backend/api/internal/logic/platform/staff_credential_user_address_user_service_relation.go +++ b/backend/api/internal/logic/platform/user/relation.go @@ -1,8 +1,6 @@ -package platform +package user import ( - "time" - "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" @@ -11,48 +9,7 @@ import ( "github.com/gin-gonic/gin" ) -type staffCredentialRequest struct { - StaffAccountIdentity string `json:"staff_account_identity" binding:"required"` - CredentialType string `json:"credential_type" binding:"required,max=64"` - CredentialNo string `json:"credential_no" binding:"max=128"` - ExpiredAt *time.Time `json:"expired_at"` -} - -func ListStaffCredential(ctx *gin.Context) { common.ListPage[models.StaffCredential](ctx) } -func GetStaffCredential(ctx *gin.Context) { common.GetByIdentity[models.StaffCredential](ctx) } -func CreateStaffCredential(ctx *gin.Context) { - var request staffCredentialRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - credential := models.StaffCredential{Entity: common.NewEntity("enabled"), StaffAccountID: staffAccountID, CredentialType: request.CredentialType, CredentialNo: request.CredentialNo, ExpiredAt: request.ExpiredAt} - if err := impl.DBService.Create(&credential).Error; err != nil { - infra.Response.Error(ctx, err) - return - } - common.RespondCreatedResource(ctx, credential) -} -func UpdateStaffCredential(ctx *gin.Context) { - var request staffCredentialRequest - if err := ctx.ShouldBindJSON(&request); err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - common.UpdateAllowedByIdentity(ctx, &models.StaffCredential{}, gin.H{"staff_account_id": staffAccountID, "credential_type": request.CredentialType, "credential_no": request.CredentialNo, "expired_at": request.ExpiredAt}, []string{"staff_account_id", "credential_type", "credential_no", "expired_at"}) -} - -type userAddressRequest struct { +type addressRequest struct { UserAccountIdentity string `json:"user_account_identity" binding:"required"` Address string `json:"address" binding:"required,max=255"` Longitude string `json:"longitude" binding:"max=32"` @@ -63,7 +20,7 @@ type userAddressRequest struct { func ListUserAddress(ctx *gin.Context) { common.ListPage[models.UserAddress](ctx) } func GetUserAddress(ctx *gin.Context) { common.GetByIdentity[models.UserAddress](ctx) } func CreateUserAddress(ctx *gin.Context) { - var request userAddressRequest + var request addressRequest if err := ctx.ShouldBindJSON(&request); err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return @@ -81,7 +38,7 @@ func CreateUserAddress(ctx *gin.Context) { common.RespondCreatedResource(ctx, address) } func UpdateUserAddress(ctx *gin.Context) { - var request userAddressRequest + var request addressRequest if err := ctx.ShouldBindJSON(&request); err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return @@ -94,7 +51,7 @@ func UpdateUserAddress(ctx *gin.Context) { common.UpdateAllowedByIdentity(ctx, &models.UserAddress{}, gin.H{"user_account_id": userAccountID, "address": request.Address, "longitude": request.Longitude, "latitude": request.Latitude, "is_default": request.IsDefault}, []string{"user_account_id", "address", "longitude", "latitude", "is_default"}) } -type userServiceRelationRequest struct { +type serviceRelationRequest struct { UserAccountIdentity string `json:"user_account_identity" binding:"required"` GasBasicIdentity string `json:"gas_basic_identity"` DeliveryBasicIdentity string `json:"delivery_basic_identity"` @@ -104,29 +61,13 @@ type userServiceRelationRequest struct { func ListUserServiceRelation(ctx *gin.Context) { common.ListPage[models.UserServiceRelation](ctx) } func GetUserServiceRelation(ctx *gin.Context) { common.GetByIdentity[models.UserServiceRelation](ctx) } func CreateUserServiceRelation(ctx *gin.Context) { - var request userServiceRelationRequest + var request serviceRelationRequest if err := ctx.ShouldBindJSON(&request); err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - gasBasicID, err := common.ResolveIdentityID(&models.GasBasic{}, request.GasBasicIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) + userAccountID, gasBasicID, deliveryBasicID, staffAccountID, ok := resolveServiceRelation(ctx, request) + if !ok { return } relation := models.UserServiceRelation{Entity: common.NewEntity("enabled"), UserAccountID: userAccountID, GasBasicID: gasBasicID, DeliveryBasicID: deliveryBasicID, StaffAccountID: staffAccountID} @@ -137,30 +78,38 @@ func CreateUserServiceRelation(ctx *gin.Context) { common.RespondCreatedResource(ctx, relation) } func UpdateUserServiceRelation(ctx *gin.Context) { - var request userServiceRelationRequest + var request serviceRelationRequest if err := ctx.ShouldBindJSON(&request); err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - gasBasicID, err := common.ResolveIdentityID(&models.GasBasic{}, request.GasBasicIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, false) - if err != nil { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) + userAccountID, gasBasicID, deliveryBasicID, staffAccountID, ok := resolveServiceRelation(ctx, request) + if !ok { return } common.UpdateAllowedByIdentity(ctx, &models.UserServiceRelation{}, gin.H{"user_account_id": userAccountID, "gas_basic_id": gasBasicID, "delivery_basic_id": deliveryBasicID, "staff_account_id": staffAccountID}, []string{"user_account_id", "gas_basic_id", "delivery_basic_id", "staff_account_id"}) } + +func resolveServiceRelation(ctx *gin.Context, request serviceRelationRequest) (uint64, uint64, uint64, uint64, bool) { + userAccountID, err := common.ResolveIdentityID(&models.UserAccount{}, request.UserAccountIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return 0, 0, 0, 0, false + } + gasBasicID, err := common.ResolveIdentityID(&models.GasBasic{}, request.GasBasicIdentity, false) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return 0, 0, 0, 0, false + } + deliveryBasicID, err := common.ResolveIdentityID(&models.DeliveryBasic{}, request.DeliveryBasicIdentity, false) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return 0, 0, 0, 0, false + } + staffAccountID, err := common.ResolveIdentityID(&models.StaffAccount{}, request.StaffAccountIdentity, false) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return 0, 0, 0, 0, false + } + return userAccountID, gasBasicID, deliveryBasicID, staffAccountID, true +} diff --git a/backend/api/internal/logic/platform/user_account.go b/backend/api/internal/logic/platform/user/user.go similarity index 97% rename from backend/api/internal/logic/platform/user_account.go rename to backend/api/internal/logic/platform/user/user.go index 6927bac..e1345a8 100644 --- a/backend/api/internal/logic/platform/user_account.go +++ b/backend/api/internal/logic/platform/user/user.go @@ -1,4 +1,4 @@ -package platform +package user import ( "git.apinb.com/bsm-sdk/core/errcode" @@ -29,7 +29,7 @@ func CreateUser(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - hash, err := passwordHash(request.Password) + hash, err := common.PasswordHash(request.Password) if err != nil { infra.Response.Error(ctx, err) return diff --git a/backend/api/internal/logic/platform/wallet_workflow.go b/backend/api/internal/logic/platform/wallet/wallet.go similarity index 95% rename from backend/api/internal/logic/platform/wallet_workflow.go rename to backend/api/internal/logic/platform/wallet/wallet.go index befd16a..acdff64 100644 --- a/backend/api/internal/logic/platform/wallet_workflow.go +++ b/backend/api/internal/logic/platform/wallet/wallet.go @@ -1,4 +1,4 @@ -package platform +package wallet import ( "errors" @@ -9,7 +9,6 @@ import ( "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" - "git.apinb.com/bsm-sdk/core/middleware" "git.apinb.com/heqiapp/platforms/backend/api/internal/config" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" @@ -196,7 +195,7 @@ func RechargeWalletBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) var record models.WalletRecord err := impl.DBService.Transaction(func(tx *gorm.DB) error { if err := tx.Where("request_no = ?", request.RequestNo).First(&record).Error; err == nil { @@ -269,7 +268,7 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus string) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - operatorIdentity, operatorName := walletOperator(ctx) + operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { var application models.WalletApplyCash if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). @@ -306,18 +305,6 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus string) { infra.Response.Success(ctx, gin.H{"updated": true, "status": targetStatus}) } -func walletOperator(ctx *gin.Context) (string, string) { - claims, err := middleware.ParseAuth(ctx) - if err != nil { - return "", "" - } - var account models.PlatformAccount - if err := impl.DBService.Select("display_name").Where("identity = ?", claims.Identity).First(&account).Error; err != nil { - return claims.Identity, "" - } - return claims.Identity, account.DisplayName -} - func dateNumber(value time.Time, layout string) int32 { number, _ := strconv.ParseInt(value.Format(layout), 10, 32) return int32(number) diff --git a/backend/api/internal/logic/platform/wallet_workflow_test.go b/backend/api/internal/logic/platform/wallet/wallet_test.go similarity index 99% rename from backend/api/internal/logic/platform/wallet_workflow_test.go rename to backend/api/internal/logic/platform/wallet/wallet_test.go index 321de6f..88ec615 100644 --- a/backend/api/internal/logic/platform/wallet_workflow_test.go +++ b/backend/api/internal/logic/platform/wallet/wallet_test.go @@ -1,4 +1,4 @@ -package platform +package wallet import ( "testing" diff --git a/backend/api/internal/routers/platform.go b/backend/api/internal/routers/platform.go index a9adc84..d3a28e0 100644 --- a/backend/api/internal/routers/platform.go +++ b/backend/api/internal/routers/platform.go @@ -5,7 +5,18 @@ import ( "git.apinb.com/bsm-sdk/core/middleware" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" - "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform" + platformbase "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/dashboard" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/delivery" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/ec" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/fin" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/gas" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/gasorder" + platformlogic "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/platform" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/product" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/staff" + userlogic "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/user" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/wallet" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" ) @@ -14,15 +25,15 @@ import ( func RegisterPlatform(serviceKey string, engine *gin.Engine) { basePath := fmt.Sprintf("/%s/platform/v1", serviceKey) anonymous := engine.Group(basePath) - anonymous.GET("/ping/hello", platform.PingHello) - anonymous.POST("/auth/login", platform.Login) + anonymous.GET("/ping/hello", dashboard.PingHello) + anonymous.POST("/auth/login", platformbase.Login) protected := engine.Group(basePath) protected.Use(middleware.JwtAuth(true)) - protected.Use(platform.RequirePlatformMenuAccess()) - protected.GET("/auth/profile", platform.CurrentProfile) - protected.PUT("/auth/password", platform.ChangePassword) - protected.GET("/dashboard/overview", platform.DashboardOverview) + protected.Use(platformlogic.RequirePlatformMenuAccess()) + protected.GET("/auth/profile", platformbase.CurrentProfile) + protected.PUT("/auth/password", platformbase.ChangePassword) + protected.GET("/dashboard/overview", dashboard.DashboardOverview) registerGasRoute(protected) registerDeliveryRoute(protected) @@ -38,50 +49,50 @@ func RegisterPlatform(serviceKey string, engine *gin.Engine) { } func registerGasRoute(group *gin.RouterGroup) { - registerWritableResource(group, "/gas_basic", platform.ListGasBasic, platform.CreateGasBasic, platform.GetGasBasic, platform.UpdateGasBasic, &models.GasBasic{}) - registerWritableResource(group, "/gas_account", platform.ListGasAccount, platform.CreateGasAccount, platform.GetGasAccount, platform.UpdateGasAccount, &models.GasAccount{}) + registerWritableResource(group, "/gas_basic", gas.ListGasBasic, gas.CreateGasBasic, gas.GetGasBasic, gas.UpdateGasBasic, &models.GasBasic{}) + registerWritableResource(group, "/gas_account", gas.ListGasAccount, gas.CreateGasAccount, gas.GetGasAccount, gas.UpdateGasAccount, &models.GasAccount{}) } func registerDeliveryRoute(group *gin.RouterGroup) { - registerWritableResource(group, "/delivery_basic", platform.ListDeliveryBasic, platform.CreateDeliveryBasic, platform.GetDeliveryBasic, platform.UpdateDeliveryBasic, &models.DeliveryBasic{}) - registerWritableResource(group, "/delivery_account", platform.ListDeliveryAccount, platform.CreateDeliveryAccount, platform.GetDeliveryAccount, platform.UpdateDeliveryAccount, &models.DeliveryAccount{}) + registerWritableResource(group, "/delivery_basic", delivery.ListDeliveryBasic, delivery.CreateDeliveryBasic, delivery.GetDeliveryBasic, delivery.UpdateDeliveryBasic, &models.DeliveryBasic{}) + registerWritableResource(group, "/delivery_account", delivery.ListDeliveryAccount, delivery.CreateDeliveryAccount, delivery.GetDeliveryAccount, delivery.UpdateDeliveryAccount, &models.DeliveryAccount{}) } func registerGasorderRoute(group *gin.RouterGroup) { contract := group.Group("/gasorder_contract") - contract.GET("", platform.ListGasorderContract) - contract.POST("", platform.CreateGasorderContract) - contract.GET("/:identity", platform.GetGasorderContract) - contract.PUT("/:identity", platform.UpdateGasorderContract) - contract.POST("/:identity/activate", platform.ActivateGasorderContract) - contract.POST("/:identity/renew", platform.RenewGasorderContract) - contract.POST("/:identity/terminate", platform.TerminateGasorderContract) + contract.GET("", gasorder.ListGasorderContract) + contract.POST("", gasorder.CreateGasorderContract) + contract.GET("/:identity", gasorder.GetGasorderContract) + contract.PUT("/:identity", gasorder.UpdateGasorderContract) + contract.POST("/:identity/activate", gasorder.ActivateGasorderContract) + contract.POST("/:identity/renew", gasorder.RenewGasorderContract) + contract.POST("/:identity/terminate", gasorder.TerminateGasorderContract) contractProduct := group.Group("/gasorder_contract_product") - contractProduct.GET("", platform.ListGasorderContractProduct) - contractProduct.POST("", platform.BindGasorderContractProduct) - contractProduct.GET("/:identity", platform.GetGasorderContractProduct) - contractProduct.POST("/:identity/unbind", platform.UnbindGasorderContractProduct) - registerReadOnlyHandlers(group, "/gasorder_contract_revision", platform.ListGasorderContractRevision, platform.GetGasorderContractRevision) + contractProduct.GET("", gasorder.ListGasorderContractProduct) + contractProduct.POST("", gasorder.BindGasorderContractProduct) + contractProduct.GET("/:identity", gasorder.GetGasorderContractProduct) + contractProduct.POST("/:identity/unbind", gasorder.UnbindGasorderContractProduct) + registerReadOnlyHandlers(group, "/gasorder_contract_revision", gasorder.ListGasorderContractRevision, gasorder.GetGasorderContractRevision) order := group.Group("/gasorder_basic") - order.GET("", platform.ListGasorderBasic) - order.POST("", platform.CreateGasorderBasic) - order.GET("/:identity", platform.GetGasorderBasic) - order.POST("/:identity/assign", platform.AssignGasorderBasic) - order.POST("/:identity/filling", platform.GasorderStartFilling) - order.POST("/:identity/ready", platform.GasorderReady) - order.POST("/:identity/exception", platform.GasorderException) - order.POST("/:identity/recover", platform.GasorderRecover) - order.POST("/:identity/cancel", platform.GasorderCancel) + order.GET("", gasorder.ListGasorderBasic) + order.POST("", gasorder.CreateGasorderBasic) + order.GET("/:identity", gasorder.GetGasorderBasic) + order.POST("/:identity/assign", gasorder.AssignGasorderBasic) + order.POST("/:identity/filling", gasorder.GasorderStartFilling) + order.POST("/:identity/ready", gasorder.GasorderReady) + order.POST("/:identity/exception", gasorder.GasorderException) + order.POST("/:identity/recover", gasorder.GasorderRecover) + order.POST("/:identity/cancel", gasorder.GasorderCancel) - registerReadOnlyHandlers(group, "/gasorder_item", platform.ListGasorderItem, platform.GetGasorderItem) - registerReadOnlyHandlers(group, "/gasorder_assign", platform.ListGasorderAssign, platform.GetGasorderAssign) - registerReadOnlyHandlers(group, "/gasorder_status", platform.ListGasorderStatus, platform.GetGasorderStatus) - registerReadOnlyHandlers(group, "/gasorder_track", platform.ListGasorderTrack, platform.GetGasorderTrack) - registerReadOnlyHandlers(group, "/gasorder_track_point", platform.ListGasorderTrackPoint, platform.GetGasorderTrackPoint) - registerReadOnlyHandlers(group, "/gasorder_confirm", platform.ListGasorderConfirm, platform.GetGasorderConfirm) - registerReadOnlyHandlers(group, "/gasorder_payment", platform.ListGasorderPayment, platform.GetGasorderPayment) + registerReadOnlyHandlers(group, "/gasorder_item", gasorder.ListGasorderItem, gasorder.GetGasorderItem) + registerReadOnlyHandlers(group, "/gasorder_assign", gasorder.ListGasorderAssign, gasorder.GetGasorderAssign) + registerReadOnlyHandlers(group, "/gasorder_status", gasorder.ListGasorderStatus, gasorder.GetGasorderStatus) + registerReadOnlyHandlers(group, "/gasorder_track", gasorder.ListGasorderTrack, gasorder.GetGasorderTrack) + registerReadOnlyHandlers(group, "/gasorder_track_point", gasorder.ListGasorderTrackPoint, gasorder.GetGasorderTrackPoint) + registerReadOnlyHandlers(group, "/gasorder_confirm", gasorder.ListGasorderConfirm, gasorder.GetGasorderConfirm) + registerReadOnlyHandlers(group, "/gasorder_payment", gasorder.ListGasorderPayment, gasorder.GetGasorderPayment) } func registerProductRoute(group *gin.RouterGroup) { @@ -95,20 +106,20 @@ func registerProductRoute(group *gin.RouterGroup) { optionalRelation("delivery_basic_identity", "delivery_basic_id", &models.DeliveryBasic{}), optionalRelation("user_account_identity", "user_account_id", &models.UserAccount{}), } - infoList, infoCreate, infoGet, infoUpdate := platform.ProductInfoHandlers(infoRelations...) + infoList, infoCreate, infoGet, infoUpdate := product.ProductInfoHandlers(infoRelations...) info := group.Group("/product_info") info.GET("", infoList) info.POST("", infoCreate) info.GET("/:identity", infoGet) info.PUT("/:identity", infoUpdate) - info.PATCH("/:identity/status", platform.UpdateProductInfoStatus) + info.PATCH("/:identity/status", product.UpdateProductInfoStatus) - repairList, repairCreate, repairGet, repairUpdate := platform.ProductRepairHandlers( + repairList, repairCreate, repairGet, repairUpdate := product.ProductRepairHandlers( requiredRelation("product_info_identity", "product_info_id", &models.ProductInfo{}), ) registerNoDeleteResource(group, "/product_repair", repairList, repairCreate, repairGet, repairUpdate, &models.ProductRepair{}) - ownerList, ownerCreate, ownerGet := platform.ProductOwnerHandlers( + ownerList, ownerCreate, ownerGet := product.ProductOwnerHandlers( requiredRelation("product_info_identity", "product_info_id", &models.ProductInfo{}), optionalRelation("warehouse_identity", "warehouse_id", &models.ProductWarehouse{}), optionalRelation("gas_basic_identity", "gas_basic_id", &models.GasBasic{}), @@ -123,90 +134,90 @@ func registerProductRoute(group *gin.RouterGroup) { func registerWalletRoute(group *gin.RouterGroup) { basic := group.Group("/wallet_basic") - basic.GET("", platform.ListWalletBasic) - basic.GET("/:identity", platform.GetWalletBasic) - basic.PATCH("/:identity/status", platform.UpdateWalletBasicStatus) - basic.POST("/:identity/recharge", platform.RechargeWalletBasic) - basic.GET("/owner/:owner_type/:owner_identity", platform.GetOrCreateOwnerWallet) + basic.GET("", wallet.ListWalletBasic) + basic.GET("/:identity", wallet.GetWalletBasic) + basic.PATCH("/:identity/status", wallet.UpdateWalletBasicStatus) + basic.POST("/:identity/recharge", wallet.RechargeWalletBasic) + basic.GET("/owner/:owner_type/:owner_identity", wallet.GetOrCreateOwnerWallet) bank := group.Group("/wallet_bank") - bank.GET("", platform.ListWalletBank) - bank.GET("/:identity", platform.GetWalletBank) + bank.GET("", wallet.ListWalletBank) + bank.GET("/:identity", wallet.GetWalletBank) payment := group.Group("/wallet_payment") - payment.GET("", platform.ListWalletPayment) - payment.GET("/:identity", platform.GetWalletPayment) + payment.GET("", wallet.ListWalletPayment) + payment.GET("/:identity", wallet.GetWalletPayment) record := group.Group("/wallet_record") - record.GET("", platform.ListWalletRecord) - record.GET("/:identity", platform.GetWalletRecord) + record.GET("", wallet.ListWalletRecord) + record.GET("/:identity", wallet.GetWalletRecord) refund := group.Group("/wallet_refund") - refund.GET("", platform.ListWalletRefund) - refund.GET("/:identity", platform.GetWalletRefund) + refund.GET("", wallet.ListWalletRefund) + refund.GET("/:identity", wallet.GetWalletRefund) applyCash := group.Group("/wallet_apply_cash") - applyCash.GET("", platform.ListWalletApplyCash) - applyCash.GET("/:identity", platform.GetWalletApplyCash) - applyCash.POST("/:identity/approve", platform.ApproveWalletApplyCash) - applyCash.POST("/:identity/reject", platform.RejectWalletApplyCash) + applyCash.GET("", wallet.ListWalletApplyCash) + applyCash.GET("/:identity", wallet.GetWalletApplyCash) + applyCash.POST("/:identity/approve", wallet.ApproveWalletApplyCash) + applyCash.POST("/:identity/reject", wallet.RejectWalletApplyCash) } func registerCommerceRoute(group *gin.RouterGroup) { categoryRelations := []common.ResourceRelation{optionalRelation("parent_identity", "parent_id", &models.EcCategory{})} _, categoryCreate, _, categoryUpdate := common.ResourceHandlers(&models.EcCategory{}, []string{"name", "sort_no"}, []string{"name", "sort_no"}, categoryRelations...) - registerWritableResource(group, "/ec_category", platform.ListEcCategory, categoryCreate, platform.GetEcCategory, categoryUpdate, &models.EcCategory{}) + registerWritableResource(group, "/ec_category", ec.ListEcCategory, categoryCreate, ec.GetEcCategory, categoryUpdate, &models.EcCategory{}) registerRestrictedWritableResource(group, "/ec_product", &models.EcProduct{}, []string{"product_code", "name", "price_amount", "stock_quantity"}, requiredRelation("ec_category_identity", "ec_category_id", &models.EcCategory{})) registerRestrictedWritableResource(group, "/ec_product_attribute", &models.EcProductAttribute{}, []string{"name", "value", "sort_no"}, requiredRelation("ec_product_identity", "ec_product_id", &models.EcProduct{})) registerRestrictedWritableResource(group, "/ec_product_image", &models.EcProductImage{}, []string{"image_uri", "sort_no", "is_cover"}, requiredRelation("ec_product_identity", "ec_product_id", &models.EcProduct{})) registerRestrictedWritableResource(group, "/ec_cart", &models.EcCart{}, []string{"quantity", "selected"}, requiredRelation("user_account_identity", "user_account_id", &models.UserAccount{}), requiredRelation("ec_product_identity", "ec_product_id", &models.EcProduct{})) orderRelations := []common.ResourceRelation{requiredRelation("user_account_identity", "user_account_id", &models.UserAccount{}), optionalRelation("gas_basic_identity", "gas_station_id", &models.GasBasic{}), optionalRelation("delivery_basic_identity", "delivery_point_id", &models.DeliveryBasic{})} list, create, _, update := common.ResourceHandlers(&models.EcOrder{}, []string{"order_no", "total_amount"}, []string{"total_amount"}, orderRelations...) - registerWritableResource(group, "/ec_order", list, create, platform.GetEcOrder, update, &models.EcOrder{}) + registerWritableResource(group, "/ec_order", list, create, ec.GetEcOrder, update, &models.EcOrder{}) registerRestrictedWritableResource(group, "/ec_order_item", &models.EcOrderItem{}, []string{"product_snapshot", "quantity", "sale_amount"}, requiredRelation("ec_order_identity", "ec_order_id", &models.EcOrder{}), requiredRelation("ec_product_identity", "ec_product_id", &models.EcProduct{})) registerRestrictedWritableResource(group, "/ec_review", &models.EcReview{}, []string{"score", "content"}, requiredRelation("ec_order_identity", "ec_order_id", &models.EcOrder{}), requiredRelation("ec_product_identity", "ec_product_id", &models.EcProduct{}), requiredRelation("user_account_identity", "user_account_id", &models.UserAccount{})) } func registerStaffRoute(group *gin.RouterGroup) { - registerWritableResource(group, "/staff_account", platform.ListStaff, platform.CreateStaff, platform.GetStaff, platform.UpdateStaff, &models.StaffAccount{}) - registerWritableResource(group, "/staff_credential", platform.ListStaffCredential, platform.CreateStaffCredential, platform.GetStaffCredential, platform.UpdateStaffCredential, &models.StaffCredential{}) + registerWritableResource(group, "/staff_account", staff.ListStaff, staff.CreateStaff, staff.GetStaff, staff.UpdateStaff, &models.StaffAccount{}) + registerWritableResource(group, "/staff_credential", staff.ListStaffCredential, staff.CreateStaffCredential, staff.GetStaffCredential, staff.UpdateStaffCredential, &models.StaffCredential{}) } func registerUserRoute(group *gin.RouterGroup) { - registerWritableResource(group, "/user_account", platform.ListUser, platform.CreateUser, platform.GetUser, platform.UpdateUser, &models.UserAccount{}) - registerWritableResource(group, "/user_address", platform.ListUserAddress, platform.CreateUserAddress, platform.GetUserAddress, platform.UpdateUserAddress, &models.UserAddress{}) - registerWritableResource(group, "/user_service_relation", platform.ListUserServiceRelation, platform.CreateUserServiceRelation, platform.GetUserServiceRelation, platform.UpdateUserServiceRelation, &models.UserServiceRelation{}) + registerWritableResource(group, "/user_account", userlogic.ListUser, userlogic.CreateUser, userlogic.GetUser, userlogic.UpdateUser, &models.UserAccount{}) + registerWritableResource(group, "/user_address", userlogic.ListUserAddress, userlogic.CreateUserAddress, userlogic.GetUserAddress, userlogic.UpdateUserAddress, &models.UserAddress{}) + registerWritableResource(group, "/user_service_relation", userlogic.ListUserServiceRelation, userlogic.CreateUserServiceRelation, userlogic.GetUserServiceRelation, userlogic.UpdateUserServiceRelation, &models.UserServiceRelation{}) } func registerPlatformRoute(group *gin.RouterGroup) { account := group.Group("/platform_account") - account.GET("", platform.ListPlatformAccount) - account.POST("", platform.CreatePlatformAccount) - account.GET("/:identity", platform.GetPlatformAccount) - account.PUT("/:identity", platform.UpdatePlatformAccount) - account.PATCH("/:identity/status", platform.UpdatePlatformAccountStatus) - account.DELETE("/:identity", platform.ArchivePlatformAccount) + account.GET("", platformlogic.ListPlatformAccount) + account.POST("", platformlogic.CreatePlatformAccount) + account.GET("/:identity", platformlogic.GetPlatformAccount) + account.PUT("/:identity", platformlogic.UpdatePlatformAccount) + account.PATCH("/:identity/status", platformlogic.UpdatePlatformAccountStatus) + account.DELETE("/:identity", platformlogic.ArchivePlatformAccount) role := group.Group("/platform_role") - role.GET("", platform.ListPlatformRole) - role.POST("", platform.CreatePlatformRole) - role.GET("/:identity", platform.GetPlatformRole) - role.PUT("/:identity", platform.UpdatePlatformRole) - role.PATCH("/:identity/status", platform.UpdatePlatformRoleStatus) - role.DELETE("/:identity", platform.ArchivePlatformRole) - role.GET("/:identity/menu", platform.ListPlatformRoleMenuIdentities) - role.PUT("/:identity/menu", platform.ReplacePlatformRoleMenus) + role.GET("", platformlogic.ListPlatformRole) + role.POST("", platformlogic.CreatePlatformRole) + role.GET("/:identity", platformlogic.GetPlatformRole) + role.PUT("/:identity", platformlogic.UpdatePlatformRole) + role.PATCH("/:identity/status", platformlogic.UpdatePlatformRoleStatus) + role.DELETE("/:identity", platformlogic.ArchivePlatformRole) + role.GET("/:identity/menu", platformlogic.ListPlatformRoleMenuIdentities) + role.PUT("/:identity/menu", platformlogic.ReplacePlatformRoleMenus) menu := group.Group("/platform_menu") - menu.GET("", platform.ListPlatformMenu) - menu.POST("", platform.CreatePlatformMenu) - menu.GET("/:identity", platform.GetPlatformMenu) - menu.PUT("/:identity", platform.UpdatePlatformMenu) - menu.PATCH("/:identity/status", platform.UpdatePlatformMenuStatus) - menu.DELETE("/:identity", platform.ArchivePlatformMenu) + menu.GET("", platformlogic.ListPlatformMenu) + menu.POST("", platformlogic.CreatePlatformMenu) + menu.GET("/:identity", platformlogic.GetPlatformMenu) + menu.PUT("/:identity", platformlogic.UpdatePlatformMenu) + menu.PATCH("/:identity/status", platformlogic.UpdatePlatformMenuStatus) + menu.DELETE("/:identity", platformlogic.ArchivePlatformMenu) } func registerFinanceRoute(group *gin.RouterGroup) { registerRestrictedWritableResource(group, "/fin_payment", &models.FinPayment{}, []string{"channel", "amount", "paid_at"}, requiredRelation("ec_order_identity", "ec_order_id", &models.EcOrder{})) - settlementList, settlementCreate, settlementGet, settlementUpdate := platform.FinSettlementHandlers() + settlementList, settlementCreate, settlementGet, settlementUpdate := fin.FinSettlementHandlers() registerWritableResource(group, "/fin_settlement", settlementList, settlementCreate, settlementGet, settlementUpdate, &models.FinSettlement{}) registerRestrictedWritableResource(group, "/fin_reconciliation", &models.FinReconciliation{}, []string{"channel", "bill_date", "difference_amount"})