feat(platform): add resource update safeguards

This commit is contained in:
2026-07-27 01:32:44 +08:00
parent e730343093
commit c5600cf1db
9 changed files with 439 additions and 290 deletions

View File

@@ -0,0 +1,45 @@
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"
)
// ListDeliveryBasic 查询配送点分页列表。
func ListDeliveryBasic(ctx *gin.Context) { listPage[models.DeliveryBasic](ctx) }
// GetDeliveryBasic 查询一个配送点。
func GetDeliveryBasic(ctx *gin.Context) { getByIdentity[models.DeliveryBasic](ctx) }
// CreateDeliveryBasic 创建配送点档案。
func CreateDeliveryBasic(ctx *gin.Context) {
var request models.DeliveryBasic
if err := ctx.ShouldBindJSON(&request); err != nil || request.DeliveryCode == "" || request.Name == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("draft")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateDeliveryBasic 更新配送点基础资料。
func UpdateDeliveryBasic(ctx *gin.Context) {
var request struct {
GasBasicID uint64 `json:"gas_basic_id"`
Name string `json:"name" binding:"required,max=128"`
Principal string `json:"principal" binding:"max=64"`
Address string `json:"address" binding:"max=255"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
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})
}

View File

@@ -0,0 +1,47 @@
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"
)
// ListGasBasic 查询可燃气体站分页列表。
func ListGasBasic(ctx *gin.Context) { listPage[models.GasBasic](ctx) }
// GetGasBasic 查询一个可燃气体站。
func GetGasBasic(ctx *gin.Context) { getByIdentity[models.GasBasic](ctx) }
// CreateGasBasic 创建可燃气体站档案。
func CreateGasBasic(ctx *gin.Context) {
var request models.GasBasic
if err := ctx.ShouldBindJSON(&request); err != nil || request.Code == "" || request.Name == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("draft")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateGasBasic 更新可燃气体站基础资料。
func UpdateGasBasic(ctx *gin.Context) {
var request struct {
Name string `json:"name" binding:"required,max=128"`
CreditCode string `json:"credit_code" binding:"max=64"`
Principal string `json:"principal" binding:"max=64"`
Address string `json:"address" binding:"max=255"`
Longitude string `json:"longitude" binding:"max=32"`
Latitude string `json:"latitude" binding:"max=32"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
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})
}

View File

@@ -0,0 +1,22 @@
package platform
import (
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
"github.com/gin-gonic/gin"
)
// PingHello 返回匿名健康状态。
func PingHello(ctx *gin.Context) {
infra.Response.Success(ctx, gin.H{"service": "platform-api", "status": "ok"})
}
// DashboardOverview 返回平台总后台的运营概览数据。
func DashboardOverview(ctx *gin.Context) {
overview, err := models.GetDashboardOverview()
if err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, overview)
}

View File

@@ -1,4 +1,4 @@
// Package platform 汇聚平台总后台的同步 HTTP 业务逻辑。
// Package platform 提供平台总后台的同步 HTTP 业务逻辑。
package platform
import (
@@ -13,277 +13,6 @@ import (
"gorm.io/gorm"
)
// PingHello 返回匿名健康状态。
func PingHello(ctx *gin.Context) {
infra.Response.Success(ctx, gin.H{"service": "platform-api", "status": "ok"})
}
// DashboardOverview 返回平台总后台的运营概览数据。
func DashboardOverview(ctx *gin.Context) {
overview, err := models.GetDashboardOverview()
if err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, overview)
}
// ListGasBasic 查询可燃气体站分页列表。
func ListGasBasic(ctx *gin.Context) { listPage[models.GasBasic](ctx) }
// GetGasBasic 查询一个可燃气体站。
func GetGasBasic(ctx *gin.Context) { getByIdentity[models.GasBasic](ctx) }
// CreateGasBasic 创建可燃气体站档案。
func CreateGasBasic(ctx *gin.Context) {
var request models.GasBasic
if err := ctx.ShouldBindJSON(&request); err != nil || request.Code == "" || request.Name == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("draft")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateGasBasic 更新可燃气体站基础资料。
func UpdateGasBasic(ctx *gin.Context) {
var request struct {
Name string `json:"name" binding:"required,max=128"`
CreditCode string `json:"credit_code" binding:"max=64"`
Principal string `json:"principal" binding:"max=64"`
Address string `json:"address" binding:"max=255"`
Longitude string `json:"longitude" binding:"max=32"`
Latitude string `json:"latitude" binding:"max=32"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
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})
}
// ListDeliveryBasic 查询配送点分页列表。
func ListDeliveryBasic(ctx *gin.Context) { listPage[models.DeliveryBasic](ctx) }
// GetDeliveryBasic 查询一个配送点。
func GetDeliveryBasic(ctx *gin.Context) { getByIdentity[models.DeliveryBasic](ctx) }
// CreateDeliveryBasic 创建配送点档案。
func CreateDeliveryBasic(ctx *gin.Context) {
var request models.DeliveryBasic
if err := ctx.ShouldBindJSON(&request); err != nil || request.DeliveryCode == "" || request.Name == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("draft")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateDeliveryBasic 更新配送点基础资料。
func UpdateDeliveryBasic(ctx *gin.Context) {
var request struct {
GasBasicID uint64 `json:"gas_basic_id"`
Name string `json:"name" binding:"required,max=128"`
Principal string `json:"principal" binding:"max=64"`
Address string `json:"address" binding:"max=255"`
}
if err := ctx.ShouldBindJSON(&request); err != nil {
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})
}
// ListStaff 查询服务人员分页列表。
func ListStaff(ctx *gin.Context) { listPage[models.StaffAccount](ctx) }
// GetStaff 查询一个服务人员档案。
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 == "" {
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 {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateStaff 更新服务人员档案。
func UpdateStaff(ctx *gin.Context) {
var request struct {
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
}
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})
}
// ListUser 查询业主客户分页列表。
func ListUser(ctx *gin.Context) { listPage[models.UserAccount](ctx) }
// GetUser 查询一个业主客户档案。
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 == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("enabled")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateUser 更新业主客户档案。
func UpdateUser(ctx *gin.Context) {
var request struct {
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
}
updateByIdentity(ctx, &models.UserAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "real_name": request.RealName})
}
// ListPlatformRole 查询平台角色分页列表。
func ListPlatformRole(ctx *gin.Context) { listPage[models.PlatformRole](ctx) }
// GetPlatformRole 查询一个平台角色。
func GetPlatformRole(ctx *gin.Context) { getByIdentity[models.PlatformRole](ctx) }
// CreatePlatformRole 创建非内置平台角色。
func CreatePlatformRole(ctx *gin.Context) {
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 = 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
}
infra.Response.Success(ctx, request)
}
// UpdatePlatformRole 更新非内置平台角色。
func UpdatePlatformRole(ctx *gin.Context) {
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 {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
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 {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, role)
}
// UpdatePlatformRoleStatus 更新非内置平台角色状态root 等系统角色始终受保护。
func UpdatePlatformRoleStatus(ctx *gin.Context) {
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 {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&role).Update("status", request.Status).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
// ArchivePlatformRole 归档非内置平台角色root 等系统角色始终受保护。
func ArchivePlatformRole(ctx *gin.Context) {
var role models.PlatformRole
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&role).Update("status", "archived").Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
// ListPlatformMenu 返回菜单树构建所需的有序菜单列表。
func ListPlatformMenu(ctx *gin.Context) {
var list []models.PlatformMenu
if err := impl.DBService.Order("sort_no asc, id asc").Find(&list).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"total": len(list), "list": list})
}
// UpdateRecordStatus 更新主表状态,停用和归档均保留历史记录。
func UpdateRecordStatus(ctx *gin.Context, model any) {
var request struct {
@@ -293,27 +22,12 @@ func UpdateRecordStatus(ctx *gin.Context, model any) {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
updateByIdentity(ctx, model, gin.H{"status": request.Status})
updateAllowedByIdentity(ctx, model, gin.H{"status": request.Status}, []string{"status"})
}
// ArchiveRecord 通过 archived 状态实现逻辑删除,不物理删除主数据。
func ArchiveRecord(ctx *gin.Context, model any) {
updateByIdentity(ctx, model, gin.H{"status": "archived"})
}
// ListPlatfromAccount 查询平台账号列表,手机号在展示层脱敏。
func ListPlatfromAccount(ctx *gin.Context) {
page, size := pageSize(ctx)
list, total, err := models.ListPlatfromAccount(page, size)
if err != nil {
infra.Response.Error(ctx, err)
return
}
views := make([]gin.H, 0, len(list))
for _, item := range list {
views = append(views, gin.H{"identity": item.Identity, "username": item.Username, "display_name": item.DisplayName, "avatar": item.Avatar, "phone_masked": maskPhone(item.Phone), "platform_role_code": item.PlatformRoleCode, "status": item.Status})
}
infra.Response.Success(ctx, gin.H{"total": total, "list": views})
updateAllowedByIdentity(ctx, model, archiveValues(), []string{"status"})
}
func newEntity(status string) models.Entity {
@@ -345,6 +59,25 @@ func getByIdentity[T any](ctx *gin.Context) {
infra.Response.Success(ctx, data)
}
func updateAllowedByIdentity(ctx *gin.Context, model any, values map[string]any, allowedFields []string) {
values = filterFields(values, allowedFields)
if len(values) == 0 {
infra.Response.Success(ctx, gin.H{"updated": false})
return
}
result := impl.DBService.Model(model).Where("identity = ?", ctx.Param("identity")).Updates(values)
if result.Error != nil {
infra.Response.Error(ctx, result.Error)
return
}
if result.RowsAffected == 0 {
infra.Response.Error(ctx, errcode.ErrRecordNotFound)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
func updateByIdentity(ctx *gin.Context, model any, values map[string]any) {
result := impl.DBService.Model(model).Where("identity = ?", ctx.Param("identity")).Updates(values)
if result.Error != nil {

View File

@@ -1,5 +1,11 @@
package platform
import (
"net/http"
"github.com/gin-gonic/gin"
)
// ResourceMode describes the operations a platform-admin resource supports.
type ResourceMode string
@@ -17,6 +23,49 @@ type ResourceContract struct {
Mode ResourceMode
}
// ResourceDefinition describes a route resource and the fields it may change.
type ResourceDefinition struct {
Domain string
Name string
Model func() any
Mode ResourceMode
CreateFields []string
UpdateFields []string
}
// Allows reports whether a resource mode supports an HTTP method.
func (d ResourceDefinition) Allows(method string) bool {
switch d.Mode {
case ReadOnly:
return method == http.MethodGet
case AppendOnly:
return method == http.MethodGet || method == http.MethodPost
case Writable:
return method == http.MethodGet || method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch || method == http.MethodDelete
default:
return false
}
}
func archiveValues() gin.H {
return gin.H{"status": "archived"}
}
func filterFields(values map[string]any, allowedFields []string) gin.H {
allowed := make(map[string]struct{}, len(allowedFields))
for _, field := range allowedFields {
allowed[field] = struct{}{}
}
filtered := make(gin.H, len(allowed))
for field, value := range values {
if _, ok := allowed[field]; ok {
filtered[field] = value
}
}
return filtered
}
// ExpectedResources returns the complete platform-admin resource catalogue.
func ExpectedResources() []ResourceContract {
return []ResourceContract{

View File

@@ -1,6 +1,9 @@
package platform
import "testing"
import (
"net/http"
"testing"
)
func TestExpectedResources(t *testing.T) {
assertContract(t, ExpectedResources(), "gas", "gas_basic", Writable, "list")
@@ -8,6 +11,32 @@ func TestExpectedResources(t *testing.T) {
assertContract(t, ExpectedResources(), "wallet", "wallet_ledger", ReadOnly, "list")
}
func TestResourceDefinitionAllowsOnlySupportedMethods(t *testing.T) {
if (ResourceDefinition{Mode: ReadOnly}).Allows(http.MethodPost) {
t.Fatal("readonly allows POST")
}
if !(ResourceDefinition{Mode: AppendOnly}).Allows(http.MethodPost) {
t.Fatal("append-only rejects POST")
}
if (ResourceDefinition{Mode: AppendOnly}).Allows(http.MethodDelete) {
t.Fatal("append-only allows DELETE")
}
}
func TestArchiveValuesOnlyArchives(t *testing.T) {
got := archiveValues()
if len(got) != 1 || got["status"] != "archived" {
t.Fatalf("unexpected archive values: %#v", got)
}
}
func TestFilterFieldsKeepsOnlyAllowedKeys(t *testing.T) {
got := filterFields(map[string]any{"name": "n", "password_hash": "x"}, []string{"name"})
if len(got) != 1 || got["name"] != "n" {
t.Fatalf("unexpected filtered fields: %#v", got)
}
}
func assertContract(t *testing.T, contracts []ResourceContract, domain, name string, mode ResourceMode, pageKind string) {
t.Helper()
for _, contract := range contracts {

View File

@@ -0,0 +1,128 @@
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"
)
// ListPlatformRole 查询平台角色分页列表。
func ListPlatformRole(ctx *gin.Context) { listPage[models.PlatformRole](ctx) }
// GetPlatformRole 查询一个平台角色。
func GetPlatformRole(ctx *gin.Context) { getByIdentity[models.PlatformRole](ctx) }
// CreatePlatformRole 创建非内置平台角色。
func CreatePlatformRole(ctx *gin.Context) {
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 = 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
}
infra.Response.Success(ctx, request)
}
// UpdatePlatformRole 更新非内置平台角色。
func UpdatePlatformRole(ctx *gin.Context) {
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 {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
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 {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, role)
}
// UpdatePlatformRoleStatus 更新非内置平台角色状态,系统角色始终受保护。
func UpdatePlatformRoleStatus(ctx *gin.Context) {
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 {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&role).Update("status", request.Status).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
// ArchivePlatformRole 归档非内置平台角色,系统角色始终受保护。
func ArchivePlatformRole(ctx *gin.Context) {
var role models.PlatformRole
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
respondRecordError(ctx, err)
return
}
if role.IsSystem {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
if err := impl.DBService.Model(&role).Update("status", "archived").Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"updated": true})
}
// ListPlatformMenu 返回菜单树构建所需的有序菜单列表。
func ListPlatformMenu(ctx *gin.Context) {
var list []models.PlatformMenu
if err := impl.DBService.Order("sort_no asc, id asc").Find(&list).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, gin.H{"total": len(list), "list": list})
}
// ListPlatfromAccount 查询平台账号列表,手机号在展示层脱敏。
func ListPlatfromAccount(ctx *gin.Context) {
page, size := pageSize(ctx)
list, total, err := models.ListPlatfromAccount(page, size)
if err != nil {
infra.Response.Error(ctx, err)
return
}
views := make([]gin.H, 0, len(list))
for _, item := range list {
views = append(views, gin.H{"identity": item.Identity, "username": item.Username, "display_name": item.DisplayName, "avatar": item.Avatar, "phone_masked": maskPhone(item.Phone), "platform_role_code": item.PlatformRoleCode, "status": item.Status})
}
infra.Response.Success(ctx, gin.H{"total": total, "list": views})
}

View File

@@ -0,0 +1,51 @@
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"
)
// ListStaff 查询服务人员分页列表。
func ListStaff(ctx *gin.Context) { listPage[models.StaffAccount](ctx) }
// GetStaff 查询一个服务人员档案。
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 == "" {
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 {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateStaff 更新服务人员档案。
func UpdateStaff(ctx *gin.Context) {
var request struct {
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
}
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})
}

View File

@@ -0,0 +1,45 @@
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"
)
// ListUser 查询业主客户分页列表。
func ListUser(ctx *gin.Context) { listPage[models.UserAccount](ctx) }
// GetUser 查询一个业主客户档案。
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 == "" {
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
return
}
request.Entity = newEntity("enabled")
if err := impl.DBService.Create(&request).Error; err != nil {
infra.Response.Error(ctx, err)
return
}
infra.Response.Success(ctx, request)
}
// UpdateUser 更新业主客户档案。
func UpdateUser(ctx *gin.Context) {
var request struct {
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
}
updateByIdentity(ctx, &models.UserAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "real_name": request.RealName})
}