feat(platform): add organization account resources

This commit is contained in:
2026-07-27 02:03:26 +08:00
parent 4367da50fd
commit 8bd2be73f5
10 changed files with 475 additions and 49 deletions

View File

@@ -0,0 +1,93 @@
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/models"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
)
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"`
GasBasicID uint64 `json:"gas_basic_id"`
DeliveryBasicID uint64 `json:"delivery_basic_id"`
}
type accountUpdateRequest struct {
DisplayName string `json:"display_name" binding:"max=64"`
RoleCode string `json:"role_code" binding:"max=64"`
GasBasicID uint64 `json:"gas_basic_id"`
DeliveryBasicID uint64 `json:"delivery_basic_id"`
}
func passwordHash(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(hash), err
}
func ListGasAccount(ctx *gin.Context) { listPage[models.GasAccount](ctx) }
func GetGasAccount(ctx *gin.Context) { getByIdentity[models.GasAccount](ctx) }
func CreateGasAccount(ctx *gin.Context) {
var request accountRequest
if err := ctx.ShouldBindJSON(&request); err != nil || request.GasBasicID == 0 {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
hash, err := passwordHash(request.Password)
if err != nil {
infra.Response.Error(ctx, err)
return
}
account := models.GasAccount{Entity: newEntity("enabled"), GasBasicID: request.GasBasicID, 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
}
infra.Response.Success(ctx, account)
}
func UpdateGasAccount(ctx *gin.Context) {
var request accountUpdateRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.GasAccount{}, gin.H{"gas_basic_id": request.GasBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"gas_basic_id", "display_name", "role_code"})
}
func ListDeliveryAccount(ctx *gin.Context) { listPage[models.DeliveryAccount](ctx) }
func GetDeliveryAccount(ctx *gin.Context) { getByIdentity[models.DeliveryAccount](ctx) }
func CreateDeliveryAccount(ctx *gin.Context) {
var request accountRequest
if err := ctx.ShouldBindJSON(&request); err != nil || request.DeliveryBasicID == 0 {
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: newEntity("enabled"), DeliveryBasicID: request.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
}
infra.Response.Success(ctx, account)
}
func UpdateDeliveryAccount(ctx *gin.Context) {
var request accountUpdateRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.DeliveryAccount{}, gin.H{"delivery_basic_id": request.DeliveryBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"delivery_basic_id", "display_name", "role_code"})
}

View File

@@ -41,5 +41,5 @@ func UpdateDeliveryBasic(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateByIdentity(ctx, &models.DeliveryBasic{}, gin.H{"gas_basic_id": request.GasBasicID, "name": request.Name, "principal": request.Principal, "address": request.Address})
updateAllowedByIdentity(ctx, &models.DeliveryBasic{}, gin.H{"gas_basic_id": request.GasBasicID, "name": request.Name, "principal": request.Principal, "address": request.Address}, []string{"gas_basic_id", "name", "principal", "address"})
}

View File

@@ -43,5 +43,5 @@ func UpdateGasBasic(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateByIdentity(ctx, &models.GasBasic{}, gin.H{"name": request.Name, "credit_code": request.CreditCode, "principal": request.Principal, "address": request.Address, "longitude": request.Longitude, "latitude": request.Latitude})
updateAllowedByIdentity(ctx, &models.GasBasic{}, gin.H{"name": request.Name, "credit_code": request.CreditCode, "principal": request.Principal, "address": request.Address, "longitude": request.Longitude, "latitude": request.Latitude}, []string{"name", "credit_code", "principal", "address", "longitude", "latitude"})
}

View File

@@ -159,6 +159,24 @@ func TestPlatformRoleStatusAndArchiveReturnNotFoundWhenUpdateAffectsZeroRows(t *
}
}
func TestReplacePlatformRoleMenusAllowsAnEmptySetToClearAssignmentsTransactionally(t *testing.T) {
_, mock := setupPlatformRoleDatabase(t)
mock.ExpectBegin()
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "platform_role" WHERE identity = $1 ORDER BY "platform_role"."id" LIMIT $2`)).
WithArgs("role-a", 1).
WillReturnRows(platformRoleRows("role-a"))
mock.ExpectExec(regexp.QuoteMeta(`DELETE FROM "platform_role_menu_relation" WHERE platform_role_id = $1`)).
WithArgs(uint64(1)).
WillReturnResult(sqlmock.NewResult(0, 2))
mock.ExpectCommit()
ctx, recorder := updateContext(http.MethodPut, "/roles/role-a/menus", "role-a", []byte(`{"menu_identities":[]}`))
ReplacePlatformRoleMenus(ctx)
assertResponseCode(t, recorder, 0)
assertMockExpectations(t, mock)
}
type responseBody struct {
Code int32 `json:"code"`
Message string `json:"message"`

View File

@@ -1,11 +1,14 @@
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/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// ListPlatformRole 查询平台角色分页列表。
@@ -52,11 +55,87 @@ func UpdatePlatformRole(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&role).Updates(gin.H{"name": request.Name, "data_scope": request.DataScope}).Error; err != nil {
updateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"name": request.Name, "data_scope": request.DataScope}, []string{"name", "data_scope"})
}
type platformMenuRequest struct {
ParentID uint64 `json:"parent_id"`
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"`
}
func GetPlatformMenu(ctx *gin.Context) { getByIdentity[models.PlatformMenu](ctx) }
func CreatePlatformMenu(ctx *gin.Context) {
var request platformMenuRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
menu := models.PlatformMenu{Entity: newEntity("enabled"), ParentID: request.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
}
infra.Response.Success(ctx, role)
infra.Response.Success(ctx, menu)
}
func UpdatePlatformMenu(ctx *gin.Context) {
var request platformMenuRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.PlatformMenu{}, gin.H{"parent_id": request.ParentID, "name": request.Name, "icon": request.Icon, "path": request.Path, "sort_no": request.SortNo}, []string{"parent_id", "name", "icon", "path", "sort_no"})
}
type platformRoleMenusRequest struct {
MenuIdentities []string `json:"menu_identities" binding:"required"`
}
// ReplacePlatformRoleMenus replaces every menu assignment for a role atomically.
func ReplacePlatformRoleMenus(ctx *gin.Context) {
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
}
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.PlatformRoleMenuRelation{}).Error; err != nil {
return err
}
for _, menu := range menus {
relation := models.PlatformRoleMenuRelation{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
}
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
// UpdatePlatformRoleStatus 更新非内置平台角色状态,系统角色始终受保护。
@@ -118,3 +197,71 @@ func ListPlatfromAccount(ctx *gin.Context) {
}
infra.Response.Success(ctx, gin.H{"total": total, "list": views})
}
type platfromAccountRequest 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:"max=64"`
Phone string `json:"phone" binding:"max=32"`
}
type platfromAccountView struct {
Identity string `json:"identity"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
Avatar string `json:"avatar"`
PhoneMasked string `json:"phone_masked"`
PlatformRoleCode string `json:"platform_role_code"`
Status string `json:"status"`
}
func platformAccountView(account models.PlatfromAccount) platfromAccountView {
return platfromAccountView{Identity: account.Identity, Username: account.Username, DisplayName: account.DisplayName, Avatar: account.Avatar, PhoneMasked: maskPhone(account.Phone), PlatformRoleCode: account.PlatformRoleCode, Status: account.Status}
}
func GetPlatfromAccount(ctx *gin.Context) {
var account models.PlatfromAccount
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil {
respondRecordError(ctx, err)
return
}
infra.Response.Success(ctx, platformAccountView(account))
}
func CreatePlatfromAccount(ctx *gin.Context) {
var request platfromAccountRequest
if err := ctx.ShouldBindJSON(&request); 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.PlatfromAccount{Entity: newEntity("enabled"), Username: request.Username, DisplayName: request.DisplayName, Avatar: request.Avatar, PasswordHash: hash, PlatformRoleCode: request.PlatformRoleCode, Phone: request.Phone}
if account.PlatformRoleCode == "" {
account.PlatformRoleCode = "root"
}
if err := impl.DBService.Create(&account).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, platformAccountView(account))
}
func UpdatePlatfromAccount(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:"max=64"`
Phone string `json:"phone" binding:"max=32"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.PlatfromAccount{}, gin.H{"display_name": request.DisplayName, "avatar": request.Avatar, "platform_role_code": request.PlatformRoleCode, "phone": request.Phone}, []string{"display_name", "avatar", "platform_role_code", "phone"})
}

View File

@@ -0,0 +1,105 @@
package platform
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/models"
"github.com/gin-gonic/gin"
)
type staffCredentialRequest struct {
StaffAccountID uint64 `json:"staff_account_id" 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) { listPage[models.StaffCredential](ctx) }
func GetStaffCredential(ctx *gin.Context) { 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
}
credential := models.StaffCredential{Entity: newEntity("enabled"), StaffAccountID: request.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
}
infra.Response.Success(ctx, credential)
}
func UpdateStaffCredential(ctx *gin.Context) {
var request staffCredentialRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.StaffCredential{}, gin.H{"staff_account_id": request.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 {
UserAccountID uint64 `json:"user_account_id" binding:"required"`
Address string `json:"address" binding:"required,max=255"`
Longitude string `json:"longitude" binding:"max=32"`
Latitude string `json:"latitude" binding:"max=32"`
IsDefault bool `json:"is_default"`
}
func ListUserAddress(ctx *gin.Context) { listPage[models.UserAddress](ctx) }
func GetUserAddress(ctx *gin.Context) { getByIdentity[models.UserAddress](ctx) }
func CreateUserAddress(ctx *gin.Context) {
var request userAddressRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
address := models.UserAddress{Entity: newEntity("enabled"), UserAccountID: request.UserAccountID, Address: request.Address, Longitude: request.Longitude, Latitude: request.Latitude, IsDefault: request.IsDefault}
if err := impl.DBService.Create(&address).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, address)
}
func UpdateUserAddress(ctx *gin.Context) {
var request userAddressRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.UserAddress{}, gin.H{"user_account_id": request.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 {
UserAccountID uint64 `json:"user_account_id" binding:"required"`
GasBasicID uint64 `json:"gas_basic_id"`
DeliveryBasicID uint64 `json:"delivery_basic_id"`
StaffAccountID uint64 `json:"staff_account_id"`
}
func ListUserServiceRelation(ctx *gin.Context) { listPage[models.UserServiceRelation](ctx) }
func GetUserServiceRelation(ctx *gin.Context) { getByIdentity[models.UserServiceRelation](ctx) }
func CreateUserServiceRelation(ctx *gin.Context) {
var request userServiceRelationRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
relation := models.UserServiceRelation{Entity: newEntity("enabled"), UserAccountID: request.UserAccountID, GasBasicID: request.GasBasicID, DeliveryBasicID: request.DeliveryBasicID, StaffAccountID: request.StaffAccountID}
if err := impl.DBService.Create(&relation).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, relation)
}
func UpdateUserServiceRelation(ctx *gin.Context) {
var request userServiceRelationRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateAllowedByIdentity(ctx, &models.UserServiceRelation{}, gin.H{"user_account_id": request.UserAccountID, "gas_basic_id": request.GasBasicID, "delivery_basic_id": request.DeliveryBasicID, "staff_account_id": request.StaffAccountID}, []string{"user_account_id", "gas_basic_id", "delivery_basic_id", "staff_account_id"})
}

View File

@@ -16,20 +16,35 @@ func GetStaff(ctx *gin.Context) { getByIdentity[models.StaffAccount](ctx) }
// CreateStaff 创建服务人员档案。
func CreateStaff(ctx *gin.Context) {
var request models.StaffAccount
if err := ctx.ShouldBindJSON(&request); err != nil || request.Name == "" {
var request struct {
Username string `json:"username" binding:"required,max=64"`
Password string `json:"password" binding:"required,min=8,max=128"`
Name string `json:"name" binding:"required,max=64"`
Phone string `json:"phone" binding:"max=32"`
Avatar string `json:"avatar" binding:"max=512"`
RoleCode string `json:"role_code" binding:"max=64"`
GasBasicID uint64 `json:"gas_basic_id"`
DeliveryBasicID uint64 `json:"delivery_basic_id"`
WorkStatus string `json:"work_status" binding:"max=32"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("draft")
if request.WorkStatus == "" {
request.WorkStatus = "off_duty"
}
if err := impl.DBService.Create(&request).Error; err != nil {
hash, err := passwordHash(request.Password)
if err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
staff := models.StaffAccount{Entity: newEntity("draft"), Username: request.Username, PasswordHash: hash, Name: request.Name, Phone: request.Phone, Avatar: request.Avatar, RoleCode: request.RoleCode, GasBasicID: request.GasBasicID, DeliveryBasicID: request.DeliveryBasicID, WorkStatus: request.WorkStatus}
if staff.WorkStatus == "" {
staff.WorkStatus = "off_duty"
}
if err := impl.DBService.Create(&staff).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, staff)
}
// UpdateStaff 更新服务人员档案。
@@ -47,5 +62,5 @@ func UpdateStaff(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateByIdentity(ctx, &models.StaffAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "role_code": request.RoleCode, "gas_basic_id": request.GasBasicID, "delivery_basic_id": request.DeliveryBasicID, "work_status": request.WorkStatus})
updateAllowedByIdentity(ctx, &models.StaffAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "role_code": request.RoleCode, "gas_basic_id": request.GasBasicID, "delivery_basic_id": request.DeliveryBasicID, "work_status": request.WorkStatus}, []string{"name", "phone", "avatar", "role_code", "gas_basic_id", "delivery_basic_id", "work_status"})
}

View File

@@ -16,17 +16,29 @@ func GetUser(ctx *gin.Context) { getByIdentity[models.UserAccount](ctx) }
// CreateUser 创建业主客户档案。
func CreateUser(ctx *gin.Context) {
var request models.UserAccount
if err := ctx.ShouldBindJSON(&request); err != nil || request.Name == "" {
var request struct {
Username string `json:"username" binding:"required,max=64"`
Password string `json:"password" binding:"required,min=8,max=128"`
Name string `json:"name" binding:"required,max=64"`
Phone string `json:"phone" binding:"max=32"`
Avatar string `json:"avatar" binding:"max=512"`
RealName string `json:"real_name" binding:"max=64"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("enabled")
if err := impl.DBService.Create(&request).Error; err != nil {
hash, err := passwordHash(request.Password)
if err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
user := models.UserAccount{Entity: newEntity("enabled"), Username: request.Username, PasswordHash: hash, Name: request.Name, Phone: request.Phone, Avatar: request.Avatar, RealName: request.RealName}
if err := impl.DBService.Create(&user).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, user)
}
// UpdateUser 更新业主客户档案。
@@ -41,5 +53,5 @@ func UpdateUser(ctx *gin.Context) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateByIdentity(ctx, &models.UserAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "real_name": request.RealName})
updateAllowedByIdentity(ctx, &models.UserAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "real_name": request.RealName}, []string{"name", "phone", "avatar", "real_name"})
}

View File

@@ -30,47 +30,28 @@ func RegisterPlatform(serviceKey string, engine *gin.Engine) {
}
func registerGasRoute(group *gin.RouterGroup) {
resource := group.Group("/gas/gas_basic")
resource.GET("", platform.ListGasBasic)
resource.POST("", platform.CreateGasBasic)
resource.GET("/:identity", platform.GetGasBasic)
resource.PUT("/:identity", platform.UpdateGasBasic)
resource.PATCH("/:identity/status", func(ctx *gin.Context) { platform.UpdateRecordStatus(ctx, &models.GasBasic{}) })
resource.DELETE("/:identity", func(ctx *gin.Context) { platform.ArchiveRecord(ctx, &models.GasBasic{}) })
registerWritableResource(group, "/gas/gas_basic", platform.ListGasBasic, platform.CreateGasBasic, platform.GetGasBasic, platform.UpdateGasBasic, &models.GasBasic{})
registerWritableResource(group, "/gas/gas_account", platform.ListGasAccount, platform.CreateGasAccount, platform.GetGasAccount, platform.UpdateGasAccount, &models.GasAccount{})
}
func registerDeliveryRoute(group *gin.RouterGroup) {
resource := group.Group("/delivery/delivery_basic")
resource.GET("", platform.ListDeliveryBasic)
resource.POST("", platform.CreateDeliveryBasic)
resource.GET("/:identity", platform.GetDeliveryBasic)
resource.PUT("/:identity", platform.UpdateDeliveryBasic)
resource.PATCH("/:identity/status", func(ctx *gin.Context) { platform.UpdateRecordStatus(ctx, &models.DeliveryBasic{}) })
resource.DELETE("/:identity", func(ctx *gin.Context) { platform.ArchiveRecord(ctx, &models.DeliveryBasic{}) })
registerWritableResource(group, "/delivery/delivery_basic", platform.ListDeliveryBasic, platform.CreateDeliveryBasic, platform.GetDeliveryBasic, platform.UpdateDeliveryBasic, &models.DeliveryBasic{})
registerWritableResource(group, "/delivery/delivery_account", platform.ListDeliveryAccount, platform.CreateDeliveryAccount, platform.GetDeliveryAccount, platform.UpdateDeliveryAccount, &models.DeliveryAccount{})
}
func registerStaffRoute(group *gin.RouterGroup) {
resource := group.Group("/staff")
resource.GET("/account", platform.ListStaff)
resource.POST("/account", platform.CreateStaff)
resource.GET("/:identity", platform.GetStaff)
resource.PUT("/:identity", platform.UpdateStaff)
resource.PATCH("/:identity/status", func(ctx *gin.Context) { platform.UpdateRecordStatus(ctx, &models.StaffAccount{}) })
resource.DELETE("/:identity", func(ctx *gin.Context) { platform.ArchiveRecord(ctx, &models.StaffAccount{}) })
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{})
}
func registerUserRoute(group *gin.RouterGroup) {
resource := group.Group("/user")
resource.GET("/account", platform.ListUser)
resource.POST("/account", platform.CreateUser)
resource.GET("/:identity", platform.GetUser)
resource.PUT("/:identity", platform.UpdateUser)
resource.PATCH("/:identity/status", func(ctx *gin.Context) { platform.UpdateRecordStatus(ctx, &models.UserAccount{}) })
resource.DELETE("/:identity", func(ctx *gin.Context) { platform.ArchiveRecord(ctx, &models.UserAccount{}) })
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{})
}
func registerPlatformRoute(group *gin.RouterGroup) {
group.GET("/platform/platfrom_account", platform.ListPlatfromAccount)
registerWritableResource(group, "/platform/platfrom_account", platform.ListPlatfromAccount, platform.CreatePlatfromAccount, platform.GetPlatfromAccount, platform.UpdatePlatfromAccount, &models.PlatfromAccount{})
role := group.Group("/platform/platform_role")
role.GET("", platform.ListPlatformRole)
role.POST("", platform.CreatePlatformRole)
@@ -78,5 +59,16 @@ func registerPlatformRoute(group *gin.RouterGroup) {
role.PUT("/:identity", platform.UpdatePlatformRole)
role.PATCH("/:identity/status", platform.UpdatePlatformRoleStatus)
role.DELETE("/:identity", platform.ArchivePlatformRole)
group.GET("/platform/platform_menu", platform.ListPlatformMenu)
role.PUT("/:identity/menus", platform.ReplacePlatformRoleMenus)
registerWritableResource(group, "/platform/platform_menu", platform.ListPlatformMenu, platform.CreatePlatformMenu, platform.GetPlatformMenu, platform.UpdatePlatformMenu, &models.PlatformMenu{})
}
func registerWritableResource(group *gin.RouterGroup, path string, list, create, get, update gin.HandlerFunc, model any) {
resource := group.Group(path)
resource.GET("", list)
resource.POST("", create)
resource.GET("/:identity", get)
resource.PUT("/:identity", update)
resource.PATCH("/:identity/status", func(ctx *gin.Context) { platform.UpdateRecordStatus(ctx, model) })
resource.DELETE("/:identity", func(ctx *gin.Context) { platform.ArchiveRecord(ctx, model) })
}

View File

@@ -1,6 +1,7 @@
package routers
import (
"net/http"
"testing"
"github.com/gin-gonic/gin"
@@ -20,3 +21,46 @@ func TestPlatformGasRouteUsesGasBasic(t *testing.T) {
}
t.Fatal("gas_basic list route is not registered")
}
func TestPlatformOrganizationAndAccountRoutesExposeResourceCRUD(t *testing.T) {
engine := gin.New()
RegisterPlatform("heqi", engine)
routes := make(map[string]map[string]bool)
for _, route := range engine.Routes() {
if routes[route.Path] == nil {
routes[route.Path] = make(map[string]bool)
}
routes[route.Path][route.Method] = true
}
for _, resource := range []string{
"/gas/gas_basic",
"/gas/gas_account",
"/delivery/delivery_basic",
"/delivery/delivery_account",
"/staff/account",
"/staff/credential",
"/user/account",
"/user/address",
"/user/service_relation",
"/platform/platfrom_account",
"/platform/platform_role",
"/platform/platform_menu",
} {
assertRouteMethods(t, routes, "/heqi/platform/v1"+resource, http.MethodGet, http.MethodPost)
assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete)
assertRouteMethods(t, routes, "/heqi/platform/v1"+resource+"/:identity/status", http.MethodPatch)
}
assertRouteMethods(t, routes, "/heqi/platform/v1/platform/platform_role/:identity/menus", http.MethodPut)
}
func assertRouteMethods(t *testing.T, routes map[string]map[string]bool, path string, methods ...string) {
t.Helper()
for _, method := range methods {
if !routes[path][method] {
t.Errorf("route %s %s is not registered", method, path)
}
}
}