feat(platform): add organization account resources
This commit is contained in:
93
backend/api/internal/logic/platform/account.go
Normal file
93
backend/api/internal/logic/platform/account.go
Normal 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"})
|
||||
}
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
105
backend/api/internal/logic/platform/secondary_resource.go
Normal file
105
backend/api/internal/logic/platform/secondary_resource.go
Normal 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"})
|
||||
}
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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"})
|
||||
}
|
||||
|
||||
@@ -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) })
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user