From 35a52c4b0627260c536a29feef78ad8c8d19ded6 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 29 Jul 2026 14:25:38 +0800 Subject: [PATCH] refactor platform domain states and permissions --- backend/api/internal/initdb/platform.go | 10 +- backend/api/internal/initdb/platform_test.go | 4 +- backend/api/internal/logic/common/base.go | 10 +- .../api/internal/logic/common/organization.go | 31 ++++++ backend/api/internal/logic/common/status.go | 5 +- backend/api/internal/logic/platform/auth.go | 16 +-- .../logic/platform/gasorder/gasorder.go | 83 ++++++++------- .../logic/platform/gasorder/gasorder_test.go | 4 +- backend/api/internal/logic/platform/menu.go | 100 +++++++++--------- .../api/internal/logic/platform/menu_test.go | 2 +- .../logic/platform/platform/access_test.go | 9 ++ .../internal/logic/platform/platform/role.go | 20 ++-- .../logic/platform/product/product.go | 86 ++++++++------- .../logic/platform/product/product_test.go | 6 +- .../logic/platform/resource_contract.go | 4 +- .../internal/logic/platform/staff/staff.go | 16 ++- .../logic/platform/staff/staff_test.go | 12 +++ .../internal/logic/platform/user/relation.go | 10 ++ .../internal/logic/platform/wallet/wallet.go | 10 +- backend/api/internal/models/cs_ticket.go | 1 + backend/api/internal/models/ec_order.go | 1 + backend/api/internal/models/fin_payment.go | 11 +- .../api/internal/models/fin_reconciliation.go | 9 +- backend/api/internal/models/gasorder_basic.go | 49 ++++----- .../api/internal/models/gasorder_contract.go | 23 ++-- backend/api/internal/models/platform_role.go | 10 +- backend/api/internal/models/product_info.go | 2 +- backend/api/internal/models/product_repair.go | 22 ++-- .../api/internal/models/product_warehouse.go | 13 ++- .../internal/models/status_separation_test.go | 37 +++++++ .../internal/models/user_service_relation.go | 8 +- .../api/internal/models/wallet_apply_cash.go | 1 + backend/api/internal/models/wallet_payment.go | 1 + backend/api/internal/models/wallet_refund.go | 1 + backend/api/internal/routers/platform.go | 19 ++-- backend/api/internal/routers/platform_test.go | 19 +++- backend/api/internal/seed/mock.go | 58 +++++----- frontend/platform_admin/src/api/platform.ts | 4 +- frontend/platform_admin/src/api/resources.ts | 42 +++++--- .../src/contracts/platform-resources.json | 2 +- .../src/router/routes/modules/platform.ts | 98 ++++++++--------- .../src/views/shared/TreePage.vue | 2 +- 42 files changed, 507 insertions(+), 364 deletions(-) create mode 100644 backend/api/internal/logic/common/organization.go create mode 100644 backend/api/internal/logic/platform/staff/staff_test.go create mode 100644 backend/api/internal/models/status_separation_test.go diff --git a/backend/api/internal/initdb/platform.go b/backend/api/internal/initdb/platform.go index 4c56aeb..5c983c5 100644 --- a/backend/api/internal/initdb/platform.go +++ b/backend/api/internal/initdb/platform.go @@ -22,11 +22,11 @@ const ( // InitPlatformAccess 幂等初始化 root 角色;菜单定义位于逻辑层静态数据中。 func InitPlatformAccess(database *gorm.DB) error { rootRole := models.PlatformRole{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, - RoleCode: PlatformRootRoleCode, - Name: "系统管理员", - DataScope: "global", - IsSystem: true, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, + RoleCode: PlatformRootRoleCode, + Name: "系统管理员", + LocationScope: "precise", + IsSystem: true, } return database.Where("role_code = ?", rootRole.RoleCode).FirstOrCreate(&rootRole).Error } diff --git a/backend/api/internal/initdb/platform_test.go b/backend/api/internal/initdb/platform_test.go index 5d9ee37..1ceb703 100644 --- a/backend/api/internal/initdb/platform_test.go +++ b/backend/api/internal/initdb/platform_test.go @@ -22,8 +22,8 @@ func TestInitPlatformAccessSeedsRootRole(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "platform_role" WHERE role_code = $1 ORDER BY "platform_role"."id" LIMIT $2`)). WithArgs("root", 1). - WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "status", "role_code", "name", "data_scope", "is_system"}). - AddRow(uint64(1), "root-role", 1, "root", "Root", "global", true)) + WillReturnRows(sqlmock.NewRows([]string{"id", "identity", "status", "role_code", "name", "location_scope", "is_system"}). + AddRow(uint64(1), "root-role", 1, "root", "Root", "precise", true)) if err := InitPlatformAccess(database); err != nil { t.Fatal(err) diff --git a/backend/api/internal/logic/common/base.go b/backend/api/internal/logic/common/base.go index a136902..ea209ad 100644 --- a/backend/api/internal/logic/common/base.go +++ b/backend/api/internal/logic/common/base.go @@ -105,14 +105,14 @@ var keywordSafeColumns = map[string]bool{ "channel": true, "settlement_no": true, "subject_type": true, "content_type": true, "publish_status": true, "template_code": true, "ticket_no": true, "category": true, "priority": true, - "platform_role_code": true, "data_scope": true, "menu_code": true, + "platform_role_code": true, "location_scope": true, "group_code": true, "path": true, "resource_type": true, "owner_type": true, "owner_identity": true, "payment_no": true, "record_no": true, "request_no": true, "refund_no": true, "cash_no": true, "trade_no": true, "trade_type": true, "pay_channel": true, "payment_type": true, - "contract_no": true, "creator_type": true, "from_status": true, - "to_status": true, "confirm_type": true, "product_type_name": true, + "contract_no": true, "creator_type": true, + "confirm_type": true, "product_type_name": true, } func ApplyKeywordFilter(ctx *gin.Context, query *gorm.DB, model any) *gorm.DB { @@ -191,7 +191,7 @@ func UpdateAllowedByIdentity(ctx *gin.Context, model any, values map[string]any, return } - result := impl.DBService.Model(model).Where("identity = ?", ctx.Param("identity")).Updates(values) + result := ActiveRecords(impl.DBService.Model(model)).Where("identity = ?", ctx.Param("identity")).Updates(values) if result.Error != nil { infra.Response.Error(ctx, result.Error) return @@ -204,7 +204,7 @@ func UpdateAllowedByIdentity(ctx *gin.Context, model any, values map[string]any, } func UpdateByIdentity(ctx *gin.Context, model any, values map[string]any) { - result := impl.DBService.Model(model).Where("identity = ?", ctx.Param("identity")).Updates(values) + result := ActiveRecords(impl.DBService.Model(model)).Where("identity = ?", ctx.Param("identity")).Updates(values) if result.Error != nil { infra.Response.Error(ctx, result.Error) return diff --git a/backend/api/internal/logic/common/organization.go b/backend/api/internal/logic/common/organization.go new file mode 100644 index 0000000..ac7a318 --- /dev/null +++ b/backend/api/internal/logic/common/organization.go @@ -0,0 +1,31 @@ +package common + +import ( + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" +) + +// ValidateOrganizationIDs verifies that an optional delivery point and staff +// member belong to the requested gas organization. +func ValidateOrganizationIDs(gasID, deliveryID, staffID uint64) bool { + var delivery models.DeliveryBasic + if deliveryID != 0 { + if err := impl.DBService.First(&delivery, deliveryID).Error; err != nil { + return false + } + if gasID != 0 && delivery.GasBasicID != 0 && delivery.GasBasicID != gasID { + return false + } + } + if staffID == 0 { + return true + } + var staff models.StaffAccount + if err := impl.DBService.First(&staff, staffID).Error; err != nil { + return false + } + if deliveryID != 0 && staff.DeliveryBasicID != deliveryID { + return false + } + return gasID == 0 || staff.GasBasicID == 0 || staff.GasBasicID == gasID +} diff --git a/backend/api/internal/logic/common/status.go b/backend/api/internal/logic/common/status.go index eb2b01f..e57bbbe 100644 --- a/backend/api/internal/logic/common/status.go +++ b/backend/api/internal/logic/common/status.go @@ -1,13 +1,16 @@ package common -// 公共实体状态。所有嵌入 models.Entity 的模型统一使用这些整数值。 +// 通用记录状态。仅用于 models.Entity.Status。 const ( StatusDraft = 0 // 草稿 StatusEnable = 1 // 启用 StatusDisable = 2 // 停用 StatusArchived = 3 // 已归档 StatusFrozen = 4 // 已冻结 +) +// 领域业务状态。必须写入各模型的专用状态字段,禁止写入 Entity.Status。 +const ( StatusPending = 10 // 待处理 StatusActive = 11 // 生效中 StatusExpired = 12 // 已过期 diff --git a/backend/api/internal/logic/platform/auth.go b/backend/api/internal/logic/platform/auth.go index 0bf02c8..b5646ff 100644 --- a/backend/api/internal/logic/platform/auth.go +++ b/backend/api/internal/logic/platform/auth.go @@ -63,8 +63,8 @@ func Login(ctx *gin.Context) { extend["location_scope"] = "precise" } else { var role models.PlatformRole - if impl.DBService.Select("data_scope").Where("role_code = ? AND status = ?", account.PlatformRoleCode, common.StatusEnable).First(&role).Error == nil && - role.DataScope == "precise" { + if impl.DBService.Select("location_scope").Where("role_code = ? AND status = ?", account.PlatformRoleCode, common.StatusEnable).First(&role).Error == nil && + role.LocationScope == "precise" { extend["location_scope"] = "precise" } } @@ -109,15 +109,9 @@ func CurrentProfile(ctx *gin.Context) { menuCodes := make([]string, 0, len(menus)) seenMenuCodes := make(map[string]bool, len(menus)) for _, menu := range menus { - codes := []string{menu.Identity} - if path := strings.Trim(menu.Path, "/"); path != "" { - codes = append(codes, strings.Split(path, "/")[0]) - } - for _, code := range codes { - if code != "" && !seenMenuCodes[code] { - menuCodes = append(menuCodes, code) - seenMenuCodes[code] = true - } + if menu.Identity != "" && !seenMenuCodes[menu.Identity] { + menuCodes = append(menuCodes, menu.Identity) + seenMenuCodes[menu.Identity] = true } } infra.Response.Success(ctx, gin.H{ diff --git a/backend/api/internal/logic/platform/gasorder/gasorder.go b/backend/api/internal/logic/platform/gasorder/gasorder.go index 9a90f6c..daff882 100644 --- a/backend/api/internal/logic/platform/gasorder/gasorder.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder.go @@ -152,7 +152,7 @@ func CreateGasorderContract(ctx *gin.Context) { return } contract := models.GasorderContract{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusDraft}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, ContractStatus: common.StatusDraft, ContractNo: request.ContractNo, UserAccountID: userID, GasBasicID: gasID, DeliveryBasicID: deliveryID, Title: request.Title, Terms: request.Terms, FileURI: request.FileURI, DefaultDeliveryFee: request.DefaultDeliveryFee, SignedAt: request.SignedAt, EffectiveAt: request.EffectiveAt, ExpiredAt: request.ExpiredAt, @@ -192,7 +192,7 @@ func UpdateGasorderContract(ctx *gin.Context) { return } result := impl.DBService.Model(&models.GasorderContract{}). - Where("identity = ? AND status = ?", ctx.Param("identity"), common.StatusDraft). + Where("identity = ? AND contract_status = ?", ctx.Param("identity"), common.StatusDraft). Updates(map[string]any{"delivery_basic_id": deliveryID, "title": request.Title, "terms": request.Terms, "file_uri": request.FileURI, "default_delivery_fee": request.DefaultDeliveryFee, "signed_at": request.SignedAt, "effective_at": request.EffectiveAt, "expired_at": request.ExpiredAt}) @@ -228,13 +228,13 @@ func RenewGasorderContract(ctx *gin.Context) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { return err } - if contract.Status != common.StatusActive && contract.Status != common.StatusExpired && contract.Status != common.StatusTerminated { + if contract.ContractStatus != common.StatusActive && contract.ContractStatus != common.StatusExpired && contract.ContractStatus != common.StatusTerminated { return errors.New("contract cannot be renewed") } - if err := tx.Model(&contract).Updates(map[string]any{"status": common.StatusActive, "effective_at": request.EffectiveAt, "expired_at": request.ExpiredAt}).Error; err != nil { + if err := tx.Model(&contract).Updates(map[string]any{"contract_status": common.StatusActive, "effective_at": request.EffectiveAt, "expired_at": request.ExpiredAt}).Error; err != nil { return err } - contract.Status, contract.EffectiveAt, contract.ExpiredAt = common.StatusActive, request.EffectiveAt, request.ExpiredAt + contract.ContractStatus, contract.EffectiveAt, contract.ExpiredAt = common.StatusActive, request.EffectiveAt, request.ExpiredAt return tx.Create(contractRevision(contract, "renew", request.Reason, operatorIdentity, operatorName)).Error }) if err != nil { @@ -258,10 +258,10 @@ func changeGasorderContract(ctx *gin.Context, action string, target int) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { return err } - if action == "activate" && contract.Status != common.StatusDraft && contract.Status != common.StatusTerminated { + if action == "activate" && contract.ContractStatus != common.StatusDraft && contract.ContractStatus != common.StatusTerminated { return errors.New("contract cannot be activated") } - if action == "terminate" && contract.Status != common.StatusActive { + if action == "terminate" && contract.ContractStatus != common.StatusActive { return errors.New("contract cannot be terminated") } if target == common.StatusActive { @@ -275,23 +275,23 @@ func changeGasorderContract(ctx *gin.Context, action string, target int) { return errors.New("contract has no active product") } } - if err := tx.Model(&contract).Update("status", target).Error; err != nil { + if err := tx.Model(&contract).Update("contract_status", target).Error; err != nil { return err } - contract.Status = target + contract.ContractStatus = target return tx.Create(contractRevision(contract, action, request.Reason, operatorIdentity, operatorName)).Error }) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - infra.Response.Success(ctx, gin.H{"updated": true, "status": target}) + infra.Response.Success(ctx, gin.H{"updated": true, "contract_status": target}) } func contractRevision(contract models.GasorderContract, action, reason, operatorIdentity, operatorName string) *models.GasorderContractRevision { return &models.GasorderContractRevision{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusRecorded}, - GasorderContractID: contract.ID, Action: action, ContractStatus: contract.Status, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, + GasorderContractID: contract.ID, Action: action, ContractStatus: contract.ContractStatus, EffectiveAt: contract.EffectiveAt, ExpiredAt: contract.ExpiredAt, OperatorIdentity: operatorIdentity, OperatorName: operatorName, OccurredAt: time.Now(), Reason: reason, } @@ -312,13 +312,13 @@ func BindGasorderContractProduct(ctx *gin.Context) { common.RespondRecordError(ctx, err) return } - if contract.Status != common.StatusDraft && contract.Status != common.StatusActive { + if contract.ContractStatus != common.StatusDraft && contract.ContractStatus != common.StatusActive { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } var product models.ProductInfo if err := impl.DBService.Where("identity = ?", request.ProductIdentity).First(&product).Error; err != nil || - !product.IsEnabled || product.Status == common.StatusScrapped || product.UserAccountID != contract.UserAccountID { + product.Status != common.StatusEnable || product.ProductStatus == common.StatusScrapped || product.UserAccountID != contract.UserAccountID { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } @@ -328,7 +328,7 @@ func BindGasorderContractProduct(ctx *gin.Context) { return } binding := models.GasorderContractProduct{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusBound}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderContractID: contract.ID, ProductInfoID: product.ID, ProductCode: product.Code, ProductTypeName: productType.Name, ProductParams: product.Params, UnitPrice: request.UnitPrice, BoundAt: time.Now(), } @@ -350,6 +350,7 @@ func UnbindGasorderContractProduct(ctx *gin.Context) { now := time.Now() result := impl.DBService.Model(&models.GasorderContractProduct{}). Where("identity = ? AND unbound_at IS NULL", ctx.Param("identity")). + Where("NOT EXISTS (SELECT 1 FROM gasorder_item WHERE gasorder_item.gasorder_contract_product_id = gasorder_contract_product.id AND gasorder_item.active = true)"). Updates(gasorderUnbindUpdates(request.Reason, now)) if result.Error != nil || result.RowsAffected != 1 { infra.Response.Error(ctx, errcode.ErrInvalidArgument) @@ -389,11 +390,11 @@ func CreateGasorderBasic(ctx *gin.Context) { return err } var contract models.GasorderContract - if err := tx.Where("identity = ?", request.ContractIdentity).First(&contract).Error; err != nil { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", request.ContractIdentity).First(&contract).Error; err != nil { return err } now := time.Now() - if contract.Status != common.StatusActive || contract.EffectiveAt.After(now) || (contract.ExpiredAt != nil && !contract.ExpiredAt.After(now)) { + if contract.ContractStatus != common.StatusActive || contract.EffectiveAt.After(now) || (contract.ExpiredAt != nil && !contract.ExpiredAt.After(now)) { return errors.New("contract is not active") } if request.CreatorType == "user" && creatorID != contract.UserAccountID { @@ -417,8 +418,8 @@ func CreateGasorderBasic(ctx *gin.Context) { var productAmount int64 for _, binding := range bindings { var product models.ProductInfo - if err := tx.Where("id = ?", binding.ProductInfoID).First(&product).Error; err != nil || - !product.IsEnabled || product.Status == common.StatusScrapped || product.UserAccountID != contract.UserAccountID { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", binding.ProductInfoID).First(&product).Error; err != nil || + product.Status != common.StatusEnable || product.ProductStatus == common.StatusScrapped || product.UserAccountID != contract.UserAccountID { return errors.New("contract product is no longer eligible") } productAmount += binding.UnitPrice @@ -428,7 +429,7 @@ func CreateGasorderBasic(ctx *gin.Context) { return errors.New("invalid payable amount") } order = models.GasorderBasic{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusCreated}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, OrderStatus: common.StatusCreated, OrderNo: models.NewIdentity(), RequestNo: request.RequestNo, GasorderContractID: contract.ID, UserAccountID: contract.UserAccountID, CreatorType: request.CreatorType, CreatorID: creatorID, CreatorIdentity: request.CreatorIdentity, GasBasicID: contract.GasBasicID, DeliveryBasicID: contract.DeliveryBasicID, @@ -443,7 +444,7 @@ func CreateGasorderBasic(ctx *gin.Context) { } for _, binding := range bindings { item := models.GasorderItem{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusOrdered}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderBasicID: order.ID, GasorderContractProductID: binding.ID, ProductInfoID: binding.ProductInfoID, Active: true, ProductCode: binding.ProductCode, ProductTypeName: binding.ProductTypeName, @@ -493,20 +494,20 @@ func AssignGasorderBasic(ctx *gin.Context) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { return err } - if order.Status != common.StatusCreated && order.Status != common.StatusAssigned { + if order.OrderStatus != common.StatusCreated && order.OrderStatus != common.StatusAssigned { return errors.New("order cannot be assigned") } if !validGasorderAssignment(order, delivery, staff) { return errors.New("delivery or staff does not belong to the order organization") } - previous := order.Status + previous := order.OrderStatus if err := tx.Model(&order).Updates(map[string]any{ - "delivery_basic_id": delivery.ID, "staff_account_id": staff.ID, "status": common.StatusAssigned, + "delivery_basic_id": delivery.ID, "staff_account_id": staff.ID, "order_status": common.StatusAssigned, }).Error; err != nil { return err } assignment := models.GasorderAssign{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusRecorded}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderBasicID: order.ID, GasBasicID: order.GasBasicID, DeliveryBasicID: delivery.ID, StaffAccountID: staff.ID, AssignerIdentity: operatorIdentity, AssignerName: operatorName, AssignedAt: time.Now(), Reason: request.Reason, @@ -523,7 +524,7 @@ func AssignGasorderBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - infra.Response.Success(ctx, gin.H{"updated": true, "status": common.StatusAssigned}) + infra.Response.Success(ctx, gin.H{"updated": true, "order_status": common.StatusAssigned}) } func GasorderStartFilling(ctx *gin.Context) { @@ -561,14 +562,14 @@ func GasorderComplete(ctx *gin.Context) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { return err } - if order.Status != common.StatusAwaitingConfirmation { + if order.OrderStatus != common.StatusAwaitingConfirmation { return errors.New("order cannot be completed") } - if err := tx.Model(&order).Update("status", common.StatusCompleted).Error; err != nil { + if err := tx.Model(&order).Update("order_status", common.StatusCompleted).Error; err != nil { return err } confirm := models.GasorderConfirm{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusRecorded}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderBasicID: order.ID, ConfirmType: request.ConfirmType, RecipientName: request.RecipientName, RecipientPhone: request.RecipientPhone, ProofURI: request.ProofURI, ConfirmedAt: time.Now(), Remark: request.Remark, } @@ -578,13 +579,13 @@ func GasorderComplete(ctx *gin.Context) { if err := releaseGasorderProducts(tx, order.ID); err != nil { return err } - return tx.Create(gasorderStatusRecord(order.ID, order.Status, common.StatusCompleted, request.Reason, operatorIdentity, operatorName)).Error + return tx.Create(gasorderStatusRecord(order.ID, order.OrderStatus, common.StatusCompleted, request.Reason, operatorIdentity, operatorName)).Error }) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - infra.Response.Success(ctx, gin.H{"updated": true, "status": common.StatusCompleted}) + infra.Response.Success(ctx, gin.H{"updated": true, "order_status": common.StatusCompleted}) } func GasorderException(ctx *gin.Context) { @@ -605,11 +606,11 @@ func GasorderRecover(ctx *gin.Context) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { return err } - if order.Status != common.StatusException || order.PreviousStatus == common.StatusDraft { + if order.OrderStatus != common.StatusException || order.PreviousOrderStatus == common.StatusDraft { return errors.New("order cannot recover") } - target := order.PreviousStatus - if err := tx.Model(&order).Updates(map[string]any{"status": target, "previous_status": common.StatusDraft}).Error; err != nil { + target := order.PreviousOrderStatus + if err := tx.Model(&order).Updates(map[string]any{"order_status": target, "previous_order_status": common.StatusDraft}).Error; err != nil { return err } return tx.Create(gasorderStatusRecord(order.ID, common.StatusException, target, request.Reason, operatorIdentity, operatorName)).Error @@ -635,12 +636,12 @@ func transitionGasorder(ctx *gin.Context, target int) { if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { return err } - if !gasorderTransitionAllowed(order.Status, target) || (target == common.StatusFilling && order.DeliveryBasicID == 0) { + if !gasorderTransitionAllowed(order.OrderStatus, target) || (target == common.StatusFilling && order.DeliveryBasicID == 0) { return errors.New("invalid order transition") } - updates := map[string]any{"status": target} + updates := map[string]any{"order_status": target} if target == common.StatusException { - updates["previous_status"] = order.Status + updates["previous_order_status"] = order.OrderStatus } if err := tx.Model(&order).Updates(updates).Error; err != nil { return err @@ -652,7 +653,7 @@ func transitionGasorder(ctx *gin.Context, target int) { return err } track := models.GasorderTrack{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusActive}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderBasicID: order.ID, StaffAccountID: order.StaffAccountID, AttemptNo: attempt + 1, StartedAt: time.Now(), } @@ -674,13 +675,13 @@ func transitionGasorder(ctx *gin.Context, target int) { return err } } - return tx.Create(gasorderStatusRecord(order.ID, order.Status, target, request.Reason, operatorIdentity, operatorName)).Error + return tx.Create(gasorderStatusRecord(order.ID, order.OrderStatus, target, request.Reason, operatorIdentity, operatorName)).Error }) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - infra.Response.Success(ctx, gin.H{"updated": true, "status": target}) + infra.Response.Success(ctx, gin.H{"updated": true, "order_status": target}) } func gasorderTransitionAllowed(from, target int) bool { @@ -734,7 +735,7 @@ func deliveryBelongsToGas(deliveryID, gasID uint64) bool { func gasorderStatusRecord(orderID uint64, from, to int, reason, operatorIdentity, operatorName string) *models.GasorderStatus { return &models.GasorderStatus{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusRecorded}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, GasorderBasicID: orderID, FromStatus: from, ToStatus: to, OperatorIdentity: operatorIdentity, OperatorName: operatorName, OccurredAt: time.Now(), Reason: reason, } diff --git a/backend/api/internal/logic/platform/gasorder/gasorder_test.go b/backend/api/internal/logic/platform/gasorder/gasorder_test.go index 4c05a82..1579ce3 100644 --- a/backend/api/internal/logic/platform/gasorder/gasorder_test.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder_test.go @@ -91,14 +91,14 @@ func TestGasorderStatusRecordIsImmutableSnapshot(t *testing.T) { if record.GasorderBasicID != 7 || record.FromStatus != common.StatusAssigned || record.ToStatus != common.StatusFilling { t.Fatalf("unexpected status record: %#v", record) } - if record.Status != common.StatusRecorded || record.OccurredAt.IsZero() { + if record.Status != common.StatusEnable || record.OccurredAt.IsZero() { t.Fatalf("status record lacks immutable metadata: %#v", record) } } func TestContractRevisionKeepsSingleContractHistory(t *testing.T) { contract := models.GasorderContract{ - Entity: models.Entity{ID: 9, Status: common.StatusActive}, + Entity: models.Entity{ID: 9, Status: common.StatusEnable}, ContractStatus: common.StatusActive, } revision := contractRevision(contract, "renew", "annual renewal", "operator-a", "Operator") if revision.GasorderContractID != 9 || revision.Action != "renew" || revision.ContractStatus != common.StatusActive { diff --git a/backend/api/internal/logic/platform/menu.go b/backend/api/internal/logic/platform/menu.go index 2f3e4b2..61d1a7a 100644 --- a/backend/api/internal/logic/platform/menu.go +++ b/backend/api/internal/logic/platform/menu.go @@ -10,7 +10,7 @@ import ( type Menu struct { Identity string `json:"identity"` ParentIdentity string `json:"parent_identity,omitempty"` - MenuCode string `json:"menu_code"` + GroupCode string `json:"group_code"` Name string `json:"name"` Icon string `json:"icon"` Path string `json:"path"` @@ -21,79 +21,79 @@ type Menu struct { // PlatformMenus 使用二维数据组织菜单;每一行表示一个菜单分组。 var PlatformMenus = [][]Menu{ { - {Identity: "dashboard", MenuCode: "dashboard", Name: "工作台", Icon: "icon-dashboard", Path: "/dashboard", SortNo: 10, Status: common.StatusEnable}, - {Identity: "dashboard_overview", ParentIdentity: "dashboard", MenuCode: "dashboard", Name: "数据概览", Path: "/dashboard/overview", SortNo: 1, Status: common.StatusEnable}, - {Identity: "dashboard_reports", ParentIdentity: "dashboard", MenuCode: "dashboard", Name: "统计报表", Path: "/dashboard/reports", SortNo: 2, Status: common.StatusEnable}, + {Identity: "dashboard", GroupCode: "dashboard", Name: "工作台", Icon: "icon-dashboard", Path: "/dashboard", SortNo: 10, Status: common.StatusEnable}, + {Identity: "dashboard_overview", ParentIdentity: "dashboard", GroupCode: "dashboard", Name: "数据概览", Path: "/dashboard/overview", SortNo: 1, Status: common.StatusEnable}, + {Identity: "dashboard_reports", ParentIdentity: "dashboard", GroupCode: "dashboard", Name: "统计报表", Path: "/dashboard/reports", SortNo: 2, Status: common.StatusEnable}, }, { - {Identity: "gas", MenuCode: "gas", Name: "气站管理", Icon: "icon-storage", Path: "/gas", SortNo: 20, Status: common.StatusEnable}, - {Identity: "gas_basic", ParentIdentity: "gas", MenuCode: "gas", Name: "气站", Path: "/gas/gas-basic", SortNo: 1, Status: common.StatusEnable}, - {Identity: "gas_account", ParentIdentity: "gas", MenuCode: "gas", Name: "气站账户", Path: "/gas/gas-account", SortNo: 2, Status: common.StatusEnable}, + {Identity: "gas", GroupCode: "gas", Name: "气站管理", Icon: "icon-storage", Path: "/gas", SortNo: 20, Status: common.StatusEnable}, + {Identity: "gas_basic", ParentIdentity: "gas", GroupCode: "gas", Name: "气站", Path: "/gas/gas-basic", SortNo: 1, Status: common.StatusEnable}, + {Identity: "gas_account", ParentIdentity: "gas", GroupCode: "gas", Name: "气站账户", Path: "/gas/gas-account", SortNo: 2, Status: common.StatusEnable}, }, { - {Identity: "delivery", MenuCode: "delivery", Name: "配送站管理", Icon: "icon-send", Path: "/delivery", SortNo: 30, Status: common.StatusEnable}, - {Identity: "delivery_basic", ParentIdentity: "delivery", MenuCode: "delivery", Name: "配送站", Path: "/delivery/delivery-basic", SortNo: 1, Status: common.StatusEnable}, - {Identity: "delivery_account", ParentIdentity: "delivery", MenuCode: "delivery", Name: "配送站账户", Path: "/delivery/delivery-account", SortNo: 2, Status: common.StatusEnable}, + {Identity: "delivery", GroupCode: "delivery", Name: "配送站管理", Icon: "icon-send", Path: "/delivery", SortNo: 30, Status: common.StatusEnable}, + {Identity: "delivery_basic", ParentIdentity: "delivery", GroupCode: "delivery", Name: "配送站", Path: "/delivery/delivery-basic", SortNo: 1, Status: common.StatusEnable}, + {Identity: "delivery_account", ParentIdentity: "delivery", GroupCode: "delivery", Name: "配送站账户", Path: "/delivery/delivery-account", SortNo: 2, Status: common.StatusEnable}, }, { - {Identity: "staff", MenuCode: "staff", Name: "工作人员管理", Icon: "icon-user-group", Path: "/staff", SortNo: 40, Status: common.StatusEnable}, - {Identity: "staff_account", ParentIdentity: "staff", MenuCode: "staff", Name: "工作人员", Path: "/staff/staff-account", SortNo: 1, Status: common.StatusEnable}, - {Identity: "staff_credential", ParentIdentity: "staff", MenuCode: "staff", Name: "人员资质", Path: "/staff/staff-credential", SortNo: 2, Status: common.StatusEnable}, + {Identity: "staff", GroupCode: "staff", Name: "工作人员管理", Icon: "icon-user-group", Path: "/staff", SortNo: 40, Status: common.StatusEnable}, + {Identity: "staff_account", ParentIdentity: "staff", GroupCode: "staff", Name: "工作人员", Path: "/staff/staff-account", SortNo: 1, Status: common.StatusEnable}, + {Identity: "staff_credential", ParentIdentity: "staff", GroupCode: "staff", Name: "人员资质", Path: "/staff/staff-credential", SortNo: 2, Status: common.StatusEnable}, }, { - {Identity: "user", MenuCode: "user", Name: "用户管理", Icon: "icon-user", Path: "/user", SortNo: 50, Status: common.StatusEnable}, - {Identity: "user_account", ParentIdentity: "user", MenuCode: "user", Name: "用户账户", Path: "/user/user-account", SortNo: 1, Status: common.StatusEnable}, - {Identity: "user_address", ParentIdentity: "user", MenuCode: "user", Name: "用户地址", Path: "/user/user-address", SortNo: 2, Status: common.StatusEnable}, - {Identity: "user_service_relation", ParentIdentity: "user", MenuCode: "user", Name: "服务关系", Path: "/user/service-relation", SortNo: 3, Status: common.StatusEnable}, + {Identity: "user", GroupCode: "user", Name: "用户管理", Icon: "icon-user", Path: "/user", SortNo: 50, Status: common.StatusEnable}, + {Identity: "user_account", ParentIdentity: "user", GroupCode: "user", Name: "用户账户", Path: "/user/user-account", SortNo: 1, Status: common.StatusEnable}, + {Identity: "user_address", ParentIdentity: "user", GroupCode: "user", Name: "用户地址", Path: "/user/user-address", SortNo: 2, Status: common.StatusEnable}, + {Identity: "user_service_relation", ParentIdentity: "user", GroupCode: "user", Name: "服务关系", Path: "/user/service-relation", SortNo: 3, Status: common.StatusEnable}, }, { - {Identity: "device", MenuCode: "device", Name: "产品管理", Icon: "icon-common", Path: "/product", SortNo: 60, Status: common.StatusEnable}, - {Identity: "product_type", ParentIdentity: "device", MenuCode: "device", Name: "产品类型", Path: "/product/product-type", SortNo: 1, Status: common.StatusEnable}, - {Identity: "product_warehouse", ParentIdentity: "device", MenuCode: "device", Name: "库房", Path: "/product/warehouse", SortNo: 2, Status: common.StatusEnable}, - {Identity: "product_info", ParentIdentity: "device", MenuCode: "device", Name: "产品信息", Path: "/product/product-info", SortNo: 3, Status: common.StatusEnable}, + {Identity: "device", GroupCode: "device", Name: "产品管理", Icon: "icon-common", Path: "/product", SortNo: 60, Status: common.StatusEnable}, + {Identity: "product_type", ParentIdentity: "device", GroupCode: "device", Name: "产品类型", Path: "/product/product-type", SortNo: 1, Status: common.StatusEnable}, + {Identity: "product_warehouse", ParentIdentity: "device", GroupCode: "device", Name: "库房", Path: "/product/warehouse", SortNo: 2, Status: common.StatusEnable}, + {Identity: "product_info", ParentIdentity: "device", GroupCode: "device", Name: "产品信息", Path: "/product/product-info", SortNo: 3, Status: common.StatusEnable}, }, { - {Identity: "gasorder", MenuCode: "gasorder", Name: "气体配送订单管理", Icon: "icon-list", Path: "/gasorder", SortNo: 70, Status: common.StatusEnable}, - {Identity: "gasorder_contract", ParentIdentity: "gasorder", MenuCode: "gasorder_contract", Name: "合同管理", Path: "/gasorder/contracts", SortNo: 1, Status: common.StatusEnable}, - {Identity: "gasorder_basic", ParentIdentity: "gasorder", MenuCode: "gasorder_basic", Name: "配送订单", Path: "/gasorder/orders", SortNo: 2, Status: common.StatusEnable}, - {Identity: "gasorder_track", ParentIdentity: "gasorder", MenuCode: "gasorder_track", Name: "运行轨迹", Path: "/gasorder/tracks", SortNo: 3, Status: common.StatusEnable}, + {Identity: "gasorder", GroupCode: "gasorder", Name: "气体配送订单管理", Icon: "icon-list", Path: "/gasorder", SortNo: 70, Status: common.StatusEnable}, + {Identity: "gasorder_contract", ParentIdentity: "gasorder", GroupCode: "gasorder", Name: "合同管理", Path: "/gasorder/contracts", SortNo: 1, Status: common.StatusEnable}, + {Identity: "gasorder_basic", ParentIdentity: "gasorder", GroupCode: "gasorder", Name: "配送订单", Path: "/gasorder/orders", SortNo: 2, Status: common.StatusEnable}, + {Identity: "gasorder_track", ParentIdentity: "gasorder", GroupCode: "gasorder", Name: "运行轨迹", Path: "/gasorder/tracks", SortNo: 3, Status: common.StatusEnable}, }, { - {Identity: "ec", MenuCode: "ec", Name: "商城管理", Icon: "icon-gift", Path: "/ec", SortNo: 80, Status: common.StatusEnable}, - {Identity: "ec_category", ParentIdentity: "ec", MenuCode: "ec", Name: "商品分类", Path: "/ec/categories", SortNo: 1, Status: common.StatusEnable}, - {Identity: "ec_product", ParentIdentity: "ec", MenuCode: "ec", Name: "商品", Path: "/ec/products", SortNo: 2, Status: common.StatusEnable}, - {Identity: "ec_cart", ParentIdentity: "ec", MenuCode: "ec", Name: "购物车", Path: "/ec/carts", SortNo: 3, Status: common.StatusEnable}, - {Identity: "ec_order", ParentIdentity: "ec", MenuCode: "ec", Name: "商城订单", Path: "/ec/orders", SortNo: 4, Status: common.StatusEnable}, - {Identity: "ec_review", ParentIdentity: "ec", MenuCode: "ec", Name: "商品评价", Path: "/ec/reviews", SortNo: 5, Status: common.StatusEnable}, + {Identity: "ec", GroupCode: "ec", Name: "商城管理", Icon: "icon-gift", Path: "/ec", SortNo: 80, Status: common.StatusEnable}, + {Identity: "ec_category", ParentIdentity: "ec", GroupCode: "ec", Name: "商品分类", Path: "/ec/categories", SortNo: 1, Status: common.StatusEnable}, + {Identity: "ec_product", ParentIdentity: "ec", GroupCode: "ec", Name: "商品", Path: "/ec/products", SortNo: 2, Status: common.StatusEnable}, + {Identity: "ec_cart", ParentIdentity: "ec", GroupCode: "ec", Name: "购物车", Path: "/ec/carts", SortNo: 3, Status: common.StatusEnable}, + {Identity: "ec_order", ParentIdentity: "ec", GroupCode: "ec", Name: "商城订单", Path: "/ec/orders", SortNo: 4, Status: common.StatusEnable}, + {Identity: "ec_review", ParentIdentity: "ec", GroupCode: "ec", Name: "商品评价", Path: "/ec/reviews", SortNo: 5, Status: common.StatusEnable}, }, { - {Identity: "wallet", MenuCode: "wallet", Name: "钱包管理", Icon: "icon-safe", Path: "/wallet", SortNo: 90, Status: common.StatusEnable}, - {Identity: "wallet_basic", ParentIdentity: "wallet", MenuCode: "wallet", Name: "钱包", Path: "/wallet/wallet-basic", SortNo: 1, Status: common.StatusEnable}, - {Identity: "wallet_bank", ParentIdentity: "wallet", MenuCode: "wallet", Name: "银行卡", Path: "/wallet/banks", SortNo: 2, Status: common.StatusEnable}, - {Identity: "wallet_payment", ParentIdentity: "wallet", MenuCode: "wallet", Name: "支付记录", Path: "/wallet/payments", SortNo: 3, Status: common.StatusEnable}, - {Identity: "wallet_record", ParentIdentity: "wallet", MenuCode: "wallet", Name: "钱包流水", Path: "/wallet/records", SortNo: 4, Status: common.StatusEnable}, - {Identity: "wallet_refund", ParentIdentity: "wallet", MenuCode: "wallet", Name: "退款记录", Path: "/wallet/refunds", SortNo: 5, Status: common.StatusEnable}, - {Identity: "wallet_apply_cash", ParentIdentity: "wallet", MenuCode: "wallet", Name: "提现申请", Path: "/wallet/apply-cash", SortNo: 6, Status: common.StatusEnable}, + {Identity: "wallet", GroupCode: "wallet", Name: "钱包管理", Icon: "icon-safe", Path: "/wallet", SortNo: 90, Status: common.StatusEnable}, + {Identity: "wallet_basic", ParentIdentity: "wallet", GroupCode: "wallet", Name: "钱包", Path: "/wallet/wallet-basic", SortNo: 1, Status: common.StatusEnable}, + {Identity: "wallet_bank", ParentIdentity: "wallet", GroupCode: "wallet", Name: "银行卡", Path: "/wallet/banks", SortNo: 2, Status: common.StatusEnable}, + {Identity: "wallet_payment", ParentIdentity: "wallet", GroupCode: "wallet", Name: "支付记录", Path: "/wallet/payments", SortNo: 3, Status: common.StatusEnable}, + {Identity: "wallet_record", ParentIdentity: "wallet", GroupCode: "wallet", Name: "钱包流水", Path: "/wallet/records", SortNo: 4, Status: common.StatusEnable}, + {Identity: "wallet_refund", ParentIdentity: "wallet", GroupCode: "wallet", Name: "退款记录", Path: "/wallet/refunds", SortNo: 5, Status: common.StatusEnable}, + {Identity: "wallet_apply_cash", ParentIdentity: "wallet", GroupCode: "wallet", Name: "提现申请", Path: "/wallet/apply-cash", SortNo: 6, Status: common.StatusEnable}, }, { - {Identity: "finance", MenuCode: "finance", Name: "财务管理", Icon: "icon-bar-chart", Path: "/finance", SortNo: 100, Status: common.StatusEnable}, - {Identity: "fin_payment", ParentIdentity: "finance", MenuCode: "finance", Name: "支付记录", Path: "/finance/payments", SortNo: 1, Status: common.StatusEnable}, - {Identity: "fin_settlement", ParentIdentity: "finance", MenuCode: "finance", Name: "财务结算", Path: "/finance/settlements", SortNo: 2, Status: common.StatusEnable}, - {Identity: "fin_reconciliation", ParentIdentity: "finance", MenuCode: "finance", Name: "财务对账", Path: "/finance/reconciliations", SortNo: 3, Status: common.StatusEnable}, + {Identity: "finance", GroupCode: "finance", Name: "财务管理", Icon: "icon-bar-chart", Path: "/finance", SortNo: 100, Status: common.StatusEnable}, + {Identity: "fin_payment", ParentIdentity: "finance", GroupCode: "finance", Name: "支付记录", Path: "/finance/payments", SortNo: 1, Status: common.StatusEnable}, + {Identity: "fin_settlement", ParentIdentity: "finance", GroupCode: "finance", Name: "财务结算", Path: "/finance/settlements", SortNo: 2, Status: common.StatusEnable}, + {Identity: "fin_reconciliation", ParentIdentity: "finance", GroupCode: "finance", Name: "财务对账", Path: "/finance/reconciliations", SortNo: 3, Status: common.StatusEnable}, }, { - {Identity: "content", MenuCode: "content", Name: "内容管理", Icon: "icon-file", Path: "/content", SortNo: 110, Status: common.StatusEnable}, - {Identity: "cms_content", ParentIdentity: "content", MenuCode: "content", Name: "内容", Path: "/content/contents", SortNo: 1, Status: common.StatusEnable}, + {Identity: "content", GroupCode: "content", Name: "内容管理", Icon: "icon-file", Path: "/content", SortNo: 110, Status: common.StatusEnable}, + {Identity: "cms_content", ParentIdentity: "content", GroupCode: "content", Name: "内容", Path: "/content/contents", SortNo: 1, Status: common.StatusEnable}, }, { - {Identity: "customer_service", MenuCode: "customer_service", Name: "客服管理", Icon: "icon-customer-service", Path: "/customer-service", SortNo: 120, Status: common.StatusEnable}, - {Identity: "cs_ticket", ParentIdentity: "customer_service", MenuCode: "customer_service", Name: "客服工单", Path: "/customer-service/tickets", SortNo: 1, Status: common.StatusEnable}, + {Identity: "customer_service", GroupCode: "customer_service", Name: "客服管理", Icon: "icon-customer-service", Path: "/customer-service", SortNo: 120, Status: common.StatusEnable}, + {Identity: "cs_ticket", ParentIdentity: "customer_service", GroupCode: "customer_service", Name: "客服工单", Path: "/customer-service/tickets", SortNo: 1, Status: common.StatusEnable}, }, { - {Identity: "platform", MenuCode: "platform", Name: "平台管理", Icon: "icon-settings", Path: "/platform", SortNo: 130, Status: common.StatusEnable}, - {Identity: "platform_account", ParentIdentity: "platform", MenuCode: "platform", Name: "平台账户", Path: "/platform/accounts", SortNo: 1, Status: common.StatusEnable}, - {Identity: "platform_role", ParentIdentity: "platform", MenuCode: "platform", Name: "平台角色", Path: "/platform/roles", SortNo: 2, Status: common.StatusEnable}, - {Identity: "platform_menu", ParentIdentity: "platform", MenuCode: "platform", Name: "平台菜单", Path: "/platform/menus", SortNo: 3, Status: common.StatusEnable}, + {Identity: "platform", GroupCode: "platform", Name: "平台管理", Icon: "icon-settings", Path: "/platform", SortNo: 130, Status: common.StatusEnable}, + {Identity: "platform_account", ParentIdentity: "platform", GroupCode: "platform", Name: "平台账户", Path: "/platform/accounts", SortNo: 1, Status: common.StatusEnable}, + {Identity: "platform_role", ParentIdentity: "platform", GroupCode: "platform", Name: "平台角色", Path: "/platform/roles", SortNo: 2, Status: common.StatusEnable}, + {Identity: "platform_menu", ParentIdentity: "platform", GroupCode: "platform", Name: "平台菜单", Path: "/platform/menus", SortNo: 3, Status: common.StatusEnable}, }, } diff --git a/backend/api/internal/logic/platform/menu_test.go b/backend/api/internal/logic/platform/menu_test.go index 1b39c93..32d1d5d 100644 --- a/backend/api/internal/logic/platform/menu_test.go +++ b/backend/api/internal/logic/platform/menu_test.go @@ -16,7 +16,7 @@ func TestPlatformMenusAreStaticTwoDimensionalData(t *testing.T) { t.Fatalf("first menu in group must be first-level: %#v", parent) } for index, menu := range group { - if menu.Identity == "" || menu.MenuCode == "" { + if menu.Identity == "" || menu.GroupCode == "" { t.Fatalf("menu identity and code are required: %#v", menu) } if index > 0 && menu.ParentIdentity != parent.Identity { diff --git a/backend/api/internal/logic/platform/platform/access_test.go b/backend/api/internal/logic/platform/platform/access_test.go index c11bca6..b47f437 100644 --- a/backend/api/internal/logic/platform/platform/access_test.go +++ b/backend/api/internal/logic/platform/platform/access_test.go @@ -28,3 +28,12 @@ func TestHiddenGasorderResourcesFollowOwningSecondLevelMenu(t *testing.T) { t.Fatal("order menu granted contract management") } } + +func TestLocationScopeValuesAreExplicit(t *testing.T) { + if !validLocationScope("standard") || !validLocationScope("precise") { + t.Fatal("supported location scopes were rejected") + } + if validLocationScope("global") || validLocationScope("anything") { + t.Fatal("ambiguous location scope was accepted") + } +} diff --git a/backend/api/internal/logic/platform/platform/role.go b/backend/api/internal/logic/platform/platform/role.go index b10aaf4..9f29e16 100644 --- a/backend/api/internal/logic/platform/platform/role.go +++ b/backend/api/internal/logic/platform/platform/role.go @@ -27,8 +27,12 @@ func CreatePlatformRole(ctx *gin.Context) { } request.Entity = common.NewEntity(common.StatusEnable) request.IsSystem = false - if request.DataScope == "" { - request.DataScope = "global" + if request.LocationScope == "" { + request.LocationScope = "standard" + } + if !validLocationScope(request.LocationScope) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return } if err := impl.DBService.Create(&request).Error; err != nil { infra.Response.Error(ctx, err) @@ -43,10 +47,10 @@ func UpdatePlatformRole(ctx *gin.Context) { return } var request struct { - Name string `json:"name" binding:"required,max=64"` - DataScope string `json:"data_scope" binding:"required,max=32"` + Name string `json:"name" binding:"required,max=64"` + LocationScope string `json:"location_scope" binding:"required,max=32"` } - if err := ctx.ShouldBindJSON(&request); err != nil { + if err := ctx.ShouldBindJSON(&request); err != nil || !validLocationScope(request.LocationScope) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } @@ -59,9 +63,11 @@ func UpdatePlatformRole(ctx *gin.Context) { 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"}) + common.UpdateAllowedByIdentity(ctx, &models.PlatformRole{}, gin.H{"name": request.Name, "location_scope": request.LocationScope}, []string{"name", "location_scope"}) } +func validLocationScope(scope string) bool { return scope == "standard" || scope == "precise" } + // UpdatePlatformRoleStatus 更新非内置平台角色状态,系统角色始终受保护。 func UpdatePlatformRoleStatus(ctx *gin.Context) { if !common.RequirePlatformRoot(ctx) { @@ -70,7 +76,7 @@ func UpdatePlatformRoleStatus(ctx *gin.Context) { var request struct { Status int `json:"status" binding:"required"` } - if err := ctx.ShouldBindJSON(&request); err != nil { + if err := ctx.ShouldBindJSON(&request); err != nil || !common.IsGenericRecordStatus(request.Status) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } diff --git a/backend/api/internal/logic/platform/product/product.go b/backend/api/internal/logic/platform/product/product.go index 6b4a94e..a665aba 100644 --- a/backend/api/internal/logic/platform/product/product.go +++ b/backend/api/internal/logic/platform/product/product.go @@ -27,7 +27,7 @@ var productLifecycleStatuses = map[int]bool{ } func ProductInfoHandlers(relations ...common.ResourceRelation) (gin.HandlerFunc, gin.HandlerFunc, gin.HandlerFunc, gin.HandlerFunc) { - fields := []string{"code", "name", "params", "produced_at", "is_enabled", "status", "action", "reason", "remark"} + fields := []string{"code", "name", "params", "produced_at", "action", "reason", "remark"} return func(ctx *gin.Context) { common.ListResource(ctx, &models.ProductInfo{}) }, func(ctx *gin.Context) { createProductInfo(ctx, fields, relations) }, func(ctx *gin.Context) { common.GetResource(ctx, &models.ProductInfo{}) }, @@ -43,17 +43,11 @@ func createProductInfo(ctx *gin.Context, fields []string, relations []common.Res delete(values, "action") delete(values, "reason") delete(values, "remark") - data := models.ProductInfo{Entity: common.NewEntity(common.StatusPending), Params: "{}", IsEnabled: false} + data := models.ProductInfo{Entity: common.NewEntity(common.StatusDisable), ProductStatus: common.StatusPending, Params: "{}"} if err := decodeValues(values, &data); err != nil || data.Code == "" || data.Name == "" || data.ProducedAt.IsZero() || !validProductOwnership(data) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - data.Status = common.StatusPending - if data.IsEnabled { - now := time.Now() - data.EnabledAt = &now - data.Status = initialProductStatus(data) - } operatorIdentity, operatorName := productOperator(ctx) err = impl.DBService.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&data).Error; err != nil { @@ -90,36 +84,12 @@ func updateProductInfo(ctx *gin.Context, fields []string, relations []common.Res operatorIdentity, operatorName := productOperator(ctx) err = impl.DBService.Transaction(func(tx *gorm.DB) error { var current models.ProductInfo - if err := tx.Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { return err } if _, changingCode := values["code"]; changingCode { delete(values, "code") } - if rawStatus, supplied := values["status"]; supplied { - status, ok := intValue(rawStatus) - if !ok || !productLifecycleStatuses[status] { - return errors.New("invalid product status") - } - values["status"] = status - } - if enabled, ok := values["is_enabled"].(bool); ok && enabled && !current.IsEnabled { - if current.Status == common.StatusScrapped { - return errors.New("scrapped product cannot be enabled") - } - if current.EnabledAt == nil { - now := time.Now() - values["enabled_at"] = &now - if _, supplied := values["status"]; !supplied { - preview := current - _ = decodeValues(values, &preview) - values["status"] = initialProductStatus(preview) - } - } - } - if enabled, ok := values["is_enabled"].(bool); ok && !enabled { - values["status"] = common.StatusPending - } preview := current if err := decodeValues(values, &preview); err != nil || !validProductOwnership(preview) { return errors.New("product can have at most one current owner") @@ -171,23 +141,48 @@ func listProductOwners(ctx *gin.Context) { infra.Response.Success(ctx, gin.H{"total": total, "list": response}) } -func UpdateProductInfoStatus(ctx *gin.Context) { +func UpdateProductInfoLifecycle(ctx *gin.Context) { + var request struct { + ProductStatus int `json:"product_status" binding:"required"` + } + if err := ctx.ShouldBindJSON(&request); err != nil || !productLifecycleStatuses[request.ProductStatus] { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + values := gin.H{"product_status": request.ProductStatus} + if request.ProductStatus == common.StatusScrapped { + values["status"] = common.StatusDisable + } + common.UpdateAllowedByIdentity(ctx, &models.ProductInfo{}, values, []string{"product_status", "status"}) +} + +func UpdateProductInfoRecordStatus(ctx *gin.Context) { var request struct { Status int `json:"status" binding:"required"` } - if err := ctx.ShouldBindJSON(&request); err != nil || !productLifecycleStatuses[request.Status] { + if err := ctx.ShouldBindJSON(&request); err != nil || !common.IsGenericRecordStatus(request.Status) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + var product models.ProductInfo + if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&product).Error; err != nil || + (request.Status == common.StatusEnable && product.ProductStatus == common.StatusScrapped) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } values := gin.H{"status": request.Status} - if request.Status == common.StatusScrapped { - values["is_enabled"] = false + if request.Status == common.StatusEnable && product.EnabledAt == nil { + now := time.Now() + values["enabled_at"] = &now + if product.ProductStatus == common.StatusPending { + values["product_status"] = initialProductStatus(product) + } } - common.UpdateAllowedByIdentity(ctx, &models.ProductInfo{}, values, []string{"status", "is_enabled"}) + common.UpdateAllowedByIdentity(ctx, &models.ProductInfo{}, values, []string{"status", "enabled_at", "product_status"}) } func ProductRepairHandlers(relations ...common.ResourceRelation) (gin.HandlerFunc, gin.HandlerFunc, gin.HandlerFunc, gin.HandlerFunc) { - fields := []string{"repair_no", "repair_type", "started_at", "completed_at", "result", "target_status", "content", "operator", "remark"} + fields := []string{"repair_no", "repair_type", "started_at", "completed_at", "result", "target_product_status", "content", "operator", "remark"} return func(ctx *gin.Context) { common.ListResource(ctx, &models.ProductRepair{}) }, func(ctx *gin.Context) { createProductRepair(ctx, fields, relations) }, func(ctx *gin.Context) { common.GetResource(ctx, &models.ProductRepair{}) }, @@ -200,7 +195,7 @@ func createProductRepair(ctx *gin.Context, fields []string, relations []common.R infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - data := models.ProductRepair{Entity: common.NewEntity(common.StatusActive), Result: "pending"} + data := models.ProductRepair{Entity: common.NewEntity(common.StatusEnable), Result: "pending"} if err := decodeValues(values, &data); err != nil || data.Result != "pending" || !validRepair(data) || data.ProductInfoID == 0 || data.RepairNo == "" { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return @@ -217,7 +212,7 @@ func createProductRepair(ctx *gin.Context, fields []string, relations []common.R if err := tx.Create(&data).Error; err != nil { return err } - return tx.Model(&models.ProductInfo{}).Where("id = ?", data.ProductInfoID).Update("status", common.StatusRepairing).Error + return tx.Model(&models.ProductInfo{}).Where("id = ?", data.ProductInfoID).Update("product_status", common.StatusRepairing).Error }) if err != nil { infra.Response.Error(ctx, err) @@ -254,6 +249,9 @@ func updateProductRepair(ctx *gin.Context, fields []string, relations []common.R if err := decodeValues(values, &preview); err != nil || !validRepair(preview) { return errors.New("invalid repair") } + if preview.ProductInfoID != current.ProductInfoID { + return errors.New("repair product cannot be changed") + } if err := tx.Model(¤t).Updates(values).Error; err != nil { return err } @@ -271,7 +269,7 @@ func updateProductRepair(ctx *gin.Context, fields []string, relations []common.R if remaining != 0 { return nil } - return tx.Model(&models.ProductInfo{}).Where("id = ?", preview.ProductInfoID).Update("status", preview.TargetStatus).Error + return tx.Model(&models.ProductInfo{}).Where("id = ?", preview.ProductInfoID).Update("product_status", preview.TargetProductStatus).Error } return nil }) @@ -291,7 +289,7 @@ func validRepair(repair models.ProductRepair) bool { return repair.CompletedAt == nil case "passed", "failed": return repair.CompletedAt != nil && !repair.CompletedAt.Before(repair.StartedAt) && - productLifecycleStatuses[repair.TargetStatus] && repair.TargetStatus != common.StatusRepairing + productLifecycleStatuses[repair.TargetProductStatus] && repair.TargetProductStatus != common.StatusRepairing default: return false } @@ -375,7 +373,7 @@ func intValue(value any) (int, bool) { func newProductOwner(product models.ProductInfo, action, reason, remark, operatorIdentity, operatorName string, occurredAt time.Time) *models.ProductOwner { return &models.ProductOwner{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusRecorded}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, ProductInfoID: product.ID, WarehouseID: product.WarehouseID, GasBasicID: product.GasBasicID, DeliveryBasicID: product.DeliveryBasicID, UserAccountID: product.UserAccountID, Action: action, OccurredAt: occurredAt, Reason: reason, Remark: remark, diff --git a/backend/api/internal/logic/platform/product/product_test.go b/backend/api/internal/logic/platform/product/product_test.go index 929fe84..cb4f4eb 100644 --- a/backend/api/internal/logic/platform/product/product_test.go +++ b/backend/api/internal/logic/platform/product/product_test.go @@ -56,13 +56,13 @@ func TestValidRepairEnforcesCompletionRules(t *testing.T) { if !validRepair(models.ProductRepair{StartedAt: started, Result: "pending"}) { t.Fatal("pending repair was rejected") } - if !validRepair(models.ProductRepair{StartedAt: started, CompletedAt: &completed, Result: "passed", TargetStatus: common.StatusInStock}) { + if !validRepair(models.ProductRepair{StartedAt: started, CompletedAt: &completed, Result: "passed", TargetProductStatus: common.StatusInStock}) { t.Fatal("completed repair was rejected") } - if validRepair(models.ProductRepair{StartedAt: started, Result: "passed", TargetStatus: common.StatusInStock}) { + if validRepair(models.ProductRepair{StartedAt: started, Result: "passed", TargetProductStatus: common.StatusInStock}) { t.Fatal("completed result without completion time was accepted") } - if validRepair(models.ProductRepair{StartedAt: completed, CompletedAt: &started, Result: "failed", TargetStatus: common.StatusInStock}) { + if validRepair(models.ProductRepair{StartedAt: completed, CompletedAt: &started, Result: "failed", TargetProductStatus: common.StatusInStock}) { t.Fatal("completion before start was accepted") } } diff --git a/backend/api/internal/logic/platform/resource_contract.go b/backend/api/internal/logic/platform/resource_contract.go index d063833..4df8c2c 100644 --- a/backend/api/internal/logic/platform/resource_contract.go +++ b/backend/api/internal/logic/platform/resource_contract.go @@ -82,11 +82,11 @@ func ExpectedResources() []ResourceContract { resourceContract("staff", "staff_account", Writable, "list"), resourceContract("staff", "staff_credential", Writable, "list"), resourceContract("user", "user_account", Writable, "list"), resourceContract("user", "user_address", Writable, "list"), resourceContract("user", "user_service_relation", Writable, "list"), resourceContract("product", "product_type", Editable, "list"), resourceContract("product", "product_warehouse", Editable, "list"), resourceContract("product", "product_info", Editable, "list"), resourceContract("product", "product_repair", Editable, "list"), resourceContract("product", "product_owner", ReadOnly, "list"), - resourceContract("ec", "ec_category", Writable, "list"), resourceContract("ec", "ec_product", Writable, "list"), resourceContract("ec", "ec_product_attribute", Writable, "list"), resourceContract("ec", "ec_product_image", Writable, "list"), resourceContract("ec", "ec_cart", Writable, "list"), resourceContract("ec", "ec_order", Writable, "list"), resourceContract("ec", "ec_order_item", Writable, "list"), resourceContract("ec", "ec_review", Writable, "list"), + resourceContract("ec", "ec_category", Writable, "list"), resourceContract("ec", "ec_product", Writable, "list"), resourceContract("ec", "ec_product_attribute", Writable, "list"), resourceContract("ec", "ec_product_image", Writable, "list"), resourceContract("ec", "ec_cart", ReadOnly, "list"), resourceContract("ec", "ec_order", ReadOnly, "list"), resourceContract("ec", "ec_order_item", ReadOnly, "list"), resourceContract("ec", "ec_review", ReadOnly, "list"), resourceContract("gasorder", "gasorder_contract", Managed, "list"), resourceContract("gasorder", "gasorder_contract_product", AppendOnly, "list"), resourceContract("gasorder", "gasorder_contract_revision", ReadOnly, "list"), resourceContract("gasorder", "gasorder_basic", AppendOnly, "list"), resourceContract("gasorder", "gasorder_item", ReadOnly, "list"), resourceContract("gasorder", "gasorder_assign", ReadOnly, "list"), resourceContract("gasorder", "gasorder_status", ReadOnly, "list"), resourceContract("gasorder", "gasorder_track", ReadOnly, "list"), resourceContract("gasorder", "gasorder_track_point", ReadOnly, "list"), resourceContract("gasorder", "gasorder_confirm", ReadOnly, "list"), resourceContract("gasorder", "gasorder_payment", ReadOnly, "list"), - resourceContract("finance", "fin_payment", Writable, "list"), resourceContract("finance", "fin_settlement", Writable, "list"), resourceContract("finance", "fin_reconciliation", Writable, "list"), + resourceContract("finance", "fin_payment", ReadOnly, "list"), resourceContract("finance", "fin_settlement", Writable, "list"), resourceContract("finance", "fin_reconciliation", ReadOnly, "list"), resourceContract("content", "cms_content", Writable, "list"), resourceContract("customer_service", "cs_ticket", Writable, "list"), resourceContract("platform", "platform_account", Writable, "list"), resourceContract("platform", "platform_role", Writable, "list"), resourceContract("platform", "platform_menu", ReadOnly, "tree"), resourceContract("wallet", "wallet_basic", ReadOnly, "list"), resourceContract("wallet", "wallet_bank", ReadOnly, "list"), resourceContract("wallet", "wallet_payment", ReadOnly, "list"), resourceContract("wallet", "wallet_record", ReadOnly, "list"), resourceContract("wallet", "wallet_refund", ReadOnly, "list"), resourceContract("wallet", "wallet_apply_cash", ReadOnly, "list"), diff --git a/backend/api/internal/logic/platform/staff/staff.go b/backend/api/internal/logic/platform/staff/staff.go index 34aed05..7cb463b 100644 --- a/backend/api/internal/logic/platform/staff/staff.go +++ b/backend/api/internal/logic/platform/staff/staff.go @@ -47,10 +47,18 @@ func CreateStaff(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } + if !common.ValidateOrganizationIDs(gasBasicID, deliveryBasicID, 0) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } staff := models.StaffAccount{Entity: common.NewEntity(common.StatusDraft), Username: request.Username, PasswordHash: hash, Name: request.Name, Phone: request.Phone, Avatar: request.Avatar, RoleCode: request.RoleCode, GasBasicID: gasBasicID, DeliveryBasicID: deliveryBasicID, WorkStatus: request.WorkStatus} if staff.WorkStatus == "" { staff.WorkStatus = "off_duty" } + if !validWorkStatus(staff.WorkStatus) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } if err := impl.DBService.Create(&staff).Error; err != nil { infra.Response.Error(ctx, err) return @@ -69,7 +77,7 @@ func UpdateStaff(ctx *gin.Context) { DeliveryBasicIdentity string `json:"delivery_basic_identity"` WorkStatus string `json:"work_status" binding:"max=32"` } - if err := ctx.ShouldBindJSON(&request); err != nil { + if err := ctx.ShouldBindJSON(&request); err != nil || !validWorkStatus(request.WorkStatus) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } @@ -83,5 +91,11 @@ func UpdateStaff(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } + if !common.ValidateOrganizationIDs(gasBasicID, deliveryBasicID, 0) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } common.UpdateAllowedByIdentity(ctx, &models.StaffAccount{}, gin.H{"name": request.Name, "phone": request.Phone, "avatar": request.Avatar, "role_code": request.RoleCode, "gas_basic_id": gasBasicID, "delivery_basic_id": deliveryBasicID, "work_status": request.WorkStatus}, []string{"name", "phone", "avatar", "role_code", "gas_basic_id", "delivery_basic_id", "work_status"}) } + +func validWorkStatus(status string) bool { return status == "on_duty" || status == "off_duty" } diff --git a/backend/api/internal/logic/platform/staff/staff_test.go b/backend/api/internal/logic/platform/staff/staff_test.go new file mode 100644 index 0000000..230b8b7 --- /dev/null +++ b/backend/api/internal/logic/platform/staff/staff_test.go @@ -0,0 +1,12 @@ +package staff + +import "testing" + +func TestWorkStatusIsClosedEnumeration(t *testing.T) { + if !validWorkStatus("on_duty") || !validWorkStatus("off_duty") { + t.Fatal("supported work status was rejected") + } + if validWorkStatus("") || validWorkStatus("available") { + t.Fatal("unknown work status was accepted as available") + } +} diff --git a/backend/api/internal/logic/platform/user/relation.go b/backend/api/internal/logic/platform/user/relation.go index d85d01a..b990d9b 100644 --- a/backend/api/internal/logic/platform/user/relation.go +++ b/backend/api/internal/logic/platform/user/relation.go @@ -1,6 +1,8 @@ package user 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" @@ -61,6 +63,9 @@ func UpdateUserAddress(ctx *gin.Context) { if err := tx.Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { return err } + if current.UserAccountID != userAccountID { + return errors.New("address owner cannot be changed") + } if request.IsDefault { if err := tx.Model(&models.UserAddress{}). Where("user_account_id = ? AND id <> ?", userAccountID, current.ID). @@ -137,5 +142,10 @@ func resolveServiceRelation(ctx *gin.Context, request serviceRelationRequest) (u infra.Response.Error(ctx, errcode.ErrInvalidArgument) return 0, 0, 0, 0, false } + if gasBasicID == 0 && deliveryBasicID == 0 && staffAccountID == 0 || + !common.ValidateOrganizationIDs(gasBasicID, deliveryBasicID, staffAccountID) { + 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/wallet/wallet.go b/backend/api/internal/logic/platform/wallet/wallet.go index a0f52b7..0730359 100644 --- a/backend/api/internal/logic/platform/wallet/wallet.go +++ b/backend/api/internal/logic/platform/wallet/wallet.go @@ -227,7 +227,7 @@ func RechargeWalletBasic(ctx *gin.Context) { } now := time.Now() record = models.WalletRecord{ - Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusPosted}, + Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, WalletBasicID: wallet.ID, RecordNo: models.NewIdentity(), RequestNo: request.RequestNo, Direction: "income", TradeType: "recharge", Amount: request.Amount, BalanceAfter: wallet.Balance, WithdrawalBalanceAfter: wallet.WithdrawalBalance, @@ -285,10 +285,10 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { Where("identity = ?", ctx.Param("identity")).First(&application).Error; err != nil { return err } - if application.Status == targetStatus { + if application.ApplyStatus == targetStatus { return nil } - if application.Status != common.StatusPending { + if application.ApplyStatus != common.StatusPending { return errors.New("cash application is not pending") } now := time.Now() @@ -304,7 +304,7 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { } } return tx.Model(&application).Updates(map[string]any{ - "status": targetStatus, "reviewer_identity": operatorIdentity, "reviewer_name": operatorName, + "apply_status": targetStatus, "reviewer_identity": operatorIdentity, "reviewer_name": operatorName, "reviewed_at": &now, "review_reason": request.Reason, }).Error }) @@ -312,7 +312,7 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { common.RespondRecordError(ctx, err) return } - infra.Response.Success(ctx, gin.H{"updated": true, "status": targetStatus}) + infra.Response.Success(ctx, gin.H{"updated": true, "apply_status": targetStatus}) } func dateNumber(value time.Time, layout string) int32 { diff --git a/backend/api/internal/models/cs_ticket.go b/backend/api/internal/models/cs_ticket.go index ed6cc0e..866aa4d 100644 --- a/backend/api/internal/models/cs_ticket.go +++ b/backend/api/internal/models/cs_ticket.go @@ -5,6 +5,7 @@ import "git.apinb.com/bsm-sdk/core/database" // CsTicket 对应 cs_ticket,保存客服工单。 type CsTicket struct { Entity // 公共实体字段 + TicketStatus int `gorm:"column:ticket_status;not null;default:32;index" json:"ticket_status"` // 工单业务状态 TicketNo string `gorm:"column:ticket_no;type:varchar(64);not null;uniqueIndex" json:"ticket_no"` // ticket_no 业务字段 UserAccountID uint64 `gorm:"column:user_account_id;not null;index" json:"user_account_id"` // user_account_id 业务字段 Category string `gorm:"column:category;type:varchar(64);not null" json:"category"` // category 业务字段 diff --git a/backend/api/internal/models/ec_order.go b/backend/api/internal/models/ec_order.go index bf64cb6..78cf1c9 100644 --- a/backend/api/internal/models/ec_order.go +++ b/backend/api/internal/models/ec_order.go @@ -5,6 +5,7 @@ import "git.apinb.com/bsm-sdk/core/database" // EcOrder 对应 ec_order,保存电商订单与组织快照。 type EcOrder struct { Entity // 公共实体字段 + OrderStatus int `gorm:"column:order_status;not null;default:16;index" json:"order_status"` // 商城订单业务状态 OrderNo string `gorm:"column:order_no;type:varchar(64);not null;uniqueIndex" json:"order_no"` // order_no 业务字段 UserAccountID uint64 `gorm:"column:user_account_id;not null;index" json:"user_account_id"` // user_account_id 业务字段 GasStationID uint64 `gorm:"column:gas_station_id;not null;default:0;index" json:"gas_station_id"` // gas_station_id 业务字段 diff --git a/backend/api/internal/models/fin_payment.go b/backend/api/internal/models/fin_payment.go index 167b93c..d48798d 100644 --- a/backend/api/internal/models/fin_payment.go +++ b/backend/api/internal/models/fin_payment.go @@ -7,11 +7,12 @@ import ( // FinPayment 对应 fin_payment,保存支付与退款记录。 type FinPayment struct { - Entity // 公共实体字段 - EcOrderID uint64 `gorm:"column:ec_order_id;not null;index" json:"ec_order_id"` // ec_order_id 业务字段 - Channel string `gorm:"column:channel;type:varchar(32);not null" json:"channel"` // channel 业务字段 - Amount int64 `gorm:"column:amount;not null;default:0;check:amount > 0" json:"amount"` // amount 业务字段 - PaidAt *time.Time `gorm:"column:paid_at;type:timestamptz" json:"paid_at"` // paid_at 业务字段 + Entity // 公共实体字段 + PaymentStatus int `gorm:"column:payment_status;not null;default:10;index" json:"payment_status"` // 支付业务状态 + EcOrderID uint64 `gorm:"column:ec_order_id;not null;index" json:"ec_order_id"` // ec_order_id 业务字段 + Channel string `gorm:"column:channel;type:varchar(32);not null" json:"channel"` // channel 业务字段 + Amount int64 `gorm:"column:amount;not null;default:0;check:amount > 0" json:"amount"` // amount 业务字段 + PaidAt *time.Time `gorm:"column:paid_at;type:timestamptz" json:"paid_at"` // paid_at 业务字段 } func init() { database.AppendMigrate(&FinPayment{}) } diff --git a/backend/api/internal/models/fin_reconciliation.go b/backend/api/internal/models/fin_reconciliation.go index f61df69..c531826 100644 --- a/backend/api/internal/models/fin_reconciliation.go +++ b/backend/api/internal/models/fin_reconciliation.go @@ -7,10 +7,11 @@ import ( // FinReconciliation 对应 fin_reconciliation,保存渠道对账记录。 type FinReconciliation struct { - Entity // 公共实体字段 - Channel string `gorm:"column:channel;type:varchar(32);not null;index" json:"channel"` // channel 业务字段 - BillDate time.Time `gorm:"column:bill_date;type:date;not null" json:"bill_date"` // bill_date 业务字段 - DifferenceAmount int64 `gorm:"column:difference_amount;not null;default:0" json:"difference_amount"` // difference_amount 业务字段 + Entity // 公共实体字段 + ReconciliationStatus int `gorm:"column:reconciliation_status;not null;default:10;index" json:"reconciliation_status"` // 对账业务状态 + Channel string `gorm:"column:channel;type:varchar(32);not null;index" json:"channel"` // channel 业务字段 + BillDate time.Time `gorm:"column:bill_date;type:date;not null" json:"bill_date"` // bill_date 业务字段 + DifferenceAmount int64 `gorm:"column:difference_amount;not null;default:0" json:"difference_amount"` // difference_amount 业务字段 } func init() { database.AppendMigrate(&FinReconciliation{}) } diff --git a/backend/api/internal/models/gasorder_basic.go b/backend/api/internal/models/gasorder_basic.go index 3e1037c..4351129 100644 --- a/backend/api/internal/models/gasorder_basic.go +++ b/backend/api/internal/models/gasorder_basic.go @@ -4,30 +4,31 @@ import "git.apinb.com/bsm-sdk/core/database" // GasorderBasic 对应 gasorder_basic,保存气体配送订单当前快照。 type GasorderBasic struct { - Entity // 公共实体字段 - OrderNo string `gorm:"column:order_no;type:varchar(64);not null;uniqueIndex" json:"order_no"` // 订单编号 - RequestNo string `gorm:"column:request_no;type:varchar(128);not null;uniqueIndex" json:"request_no"` // 创建幂等号 - GasorderContractID uint64 `gorm:"column:gasorder_contract_id;not null;index" json:"gasorder_contract_id"` // 合同自增主键 - UserAccountID uint64 `gorm:"column:user_account_id;not null;index" json:"user_account_id"` // 服务用户自增主键 - CreatorType string `gorm:"column:creator_type;type:varchar(32);not null" json:"creator_type"` // 业务创建方类型 - CreatorID uint64 `gorm:"column:creator_id;not null;default:0;index" json:"creator_id"` // 业务创建方自增主键 - CreatorIdentity string `gorm:"column:creator_identity;type:varchar(36);not null;index" json:"creator_identity"` // 业务创建方标识 - GasBasicID uint64 `gorm:"column:gas_basic_id;not null;index" json:"gas_basic_id"` // 履约气站自增主键 - DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // 当前配送点自增主键 - StaffAccountID uint64 `gorm:"column:staff_account_id;not null;default:0;index" json:"staff_account_id"` // 当前配送人员自增主键 - Address string `gorm:"column:address;type:varchar(255);not null" json:"address"` // 配送地址快照 - Longitude string `gorm:"column:longitude;type:varchar(32);not null;default:''" json:"longitude"` // 配送经度快照 - Latitude string `gorm:"column:latitude;type:varchar(32);not null;default:''" json:"latitude"` // 配送纬度快照 - ContactName string `gorm:"column:contact_name;type:varchar(64);not null" json:"contact_name"` // 联系人快照 - ContactPhone string `gorm:"column:contact_phone;type:varchar(32);not null" json:"contact_phone"` // 联系电话快照 - ProductAmount int64 `gorm:"column:product_amount;not null;check:product_amount >= 0" json:"product_amount"` // 商品金额,单位分 - DeliveryFee int64 `gorm:"column:delivery_fee;not null;default:0;check:delivery_fee >= 0" json:"delivery_fee"` // 配送费,单位分 - DiscountAmount int64 `gorm:"column:discount_amount;not null;default:0;check:discount_amount >= 0" json:"discount_amount"` // 优惠金额,单位分 - PayableAmount int64 `gorm:"column:payable_amount;not null;check:payable_amount > 0" json:"payable_amount"` // 应付金额,单位分 - PreviousStatus int `gorm:"column:previous_status;not null;default:0" json:"previous_status"` // 异常前状态 - OperatorIdentity string `gorm:"column:operator_identity;type:varchar(36);not null;default:''" json:"operator_identity"` // 建单操作人标识 - OperatorName string `gorm:"column:operator_name;type:varchar(64);not null;default:''" json:"operator_name"` // 建单操作人姓名快照 - Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 订单备注 + Entity // 公共实体字段 + OrderStatus int `gorm:"column:order_status;not null;default:16;index" json:"order_status"` // 订单业务状态 + OrderNo string `gorm:"column:order_no;type:varchar(64);not null;uniqueIndex" json:"order_no"` // 订单编号 + RequestNo string `gorm:"column:request_no;type:varchar(128);not null;uniqueIndex" json:"request_no"` // 创建幂等号 + GasorderContractID uint64 `gorm:"column:gasorder_contract_id;not null;index" json:"gasorder_contract_id"` // 合同自增主键 + UserAccountID uint64 `gorm:"column:user_account_id;not null;index" json:"user_account_id"` // 服务用户自增主键 + CreatorType string `gorm:"column:creator_type;type:varchar(32);not null" json:"creator_type"` // 业务创建方类型 + CreatorID uint64 `gorm:"column:creator_id;not null;default:0;index" json:"creator_id"` // 业务创建方自增主键 + CreatorIdentity string `gorm:"column:creator_identity;type:varchar(36);not null;index" json:"creator_identity"` // 业务创建方标识 + GasBasicID uint64 `gorm:"column:gas_basic_id;not null;index" json:"gas_basic_id"` // 履约气站自增主键 + DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // 当前配送点自增主键 + StaffAccountID uint64 `gorm:"column:staff_account_id;not null;default:0;index" json:"staff_account_id"` // 当前配送人员自增主键 + Address string `gorm:"column:address;type:varchar(255);not null" json:"address"` // 配送地址快照 + Longitude string `gorm:"column:longitude;type:varchar(32);not null;default:''" json:"longitude"` // 配送经度快照 + Latitude string `gorm:"column:latitude;type:varchar(32);not null;default:''" json:"latitude"` // 配送纬度快照 + ContactName string `gorm:"column:contact_name;type:varchar(64);not null" json:"contact_name"` // 联系人快照 + ContactPhone string `gorm:"column:contact_phone;type:varchar(32);not null" json:"contact_phone"` // 联系电话快照 + ProductAmount int64 `gorm:"column:product_amount;not null;check:product_amount >= 0" json:"product_amount"` // 商品金额,单位分 + DeliveryFee int64 `gorm:"column:delivery_fee;not null;default:0;check:delivery_fee >= 0" json:"delivery_fee"` // 配送费,单位分 + DiscountAmount int64 `gorm:"column:discount_amount;not null;default:0;check:discount_amount >= 0" json:"discount_amount"` // 优惠金额,单位分 + PayableAmount int64 `gorm:"column:payable_amount;not null;check:payable_amount > 0" json:"payable_amount"` // 应付金额,单位分 + PreviousOrderStatus int `gorm:"column:previous_order_status;not null;default:0" json:"previous_order_status"` // 异常前订单状态 + OperatorIdentity string `gorm:"column:operator_identity;type:varchar(36);not null;default:''" json:"operator_identity"` // 建单操作人标识 + OperatorName string `gorm:"column:operator_name;type:varchar(64);not null;default:''" json:"operator_name"` // 建单操作人姓名快照 + Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 订单备注 } func init() { database.AppendMigrate(&GasorderBasic{}) } diff --git a/backend/api/internal/models/gasorder_contract.go b/backend/api/internal/models/gasorder_contract.go index 25f3f29..f1cb2d5 100644 --- a/backend/api/internal/models/gasorder_contract.go +++ b/backend/api/internal/models/gasorder_contract.go @@ -9,17 +9,18 @@ import ( // GasorderContract 对应 gasorder_contract,保存用户与气站唯一供气合同。 type GasorderContract struct { Entity // 公共实体字段 - ContractNo string `gorm:"column:contract_no;type:varchar(64);not null;uniqueIndex" json:"contract_no"` // 合同编号 - UserAccountID uint64 `gorm:"column:user_account_id;not null;uniqueIndex:idx_gasorder_contract_party" json:"user_account_id"` // 签约用户自增主键 - GasBasicID uint64 `gorm:"column:gas_basic_id;not null;uniqueIndex:idx_gasorder_contract_party" json:"gas_basic_id"` // 签约气站自增主键 - DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // 默认配送点自增主键 - Title string `gorm:"column:title;type:varchar(255);not null" json:"title"` // 合同标题 - Terms string `gorm:"column:terms;type:text;not null;default:''" json:"terms"` // 合同条款 - FileURI string `gorm:"column:file_uri;type:varchar(512);not null;default:''" json:"file_uri"` // 合同文件地址 - DefaultDeliveryFee int64 `gorm:"column:default_delivery_fee;not null;default:0;check:default_delivery_fee >= 0" json:"default_delivery_fee"` // 默认配送费,单位分 - SignedAt time.Time `gorm:"column:signed_at;type:timestamptz;not null" json:"signed_at"` // 签订时间 - EffectiveAt time.Time `gorm:"column:effective_at;type:timestamptz;not null" json:"effective_at"` // 生效时间 - ExpiredAt *time.Time `gorm:"column:expired_at;type:timestamptz" json:"expired_at"` // 到期时间 + ContractStatus int `gorm:"column:contract_status;not null;default:0;index" json:"contract_status"` // 合同业务状态 + ContractNo string `gorm:"column:contract_no;type:varchar(64);not null;uniqueIndex" json:"contract_no"` // 合同编号 + UserAccountID uint64 `gorm:"column:user_account_id;not null;uniqueIndex:idx_gasorder_contract_party,where:contract_status <> 13" json:"user_account_id"` // 签约用户自增主键 + GasBasicID uint64 `gorm:"column:gas_basic_id;not null;uniqueIndex:idx_gasorder_contract_party,where:contract_status <> 13" json:"gas_basic_id"` // 签约气站自增主键 + DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // 默认配送点自增主键 + Title string `gorm:"column:title;type:varchar(255);not null" json:"title"` // 合同标题 + Terms string `gorm:"column:terms;type:text;not null;default:''" json:"terms"` // 合同条款 + FileURI string `gorm:"column:file_uri;type:varchar(512);not null;default:''" json:"file_uri"` // 合同文件地址 + DefaultDeliveryFee int64 `gorm:"column:default_delivery_fee;not null;default:0;check:default_delivery_fee >= 0" json:"default_delivery_fee"` // 默认配送费,单位分 + SignedAt time.Time `gorm:"column:signed_at;type:timestamptz;not null" json:"signed_at"` // 签订时间 + EffectiveAt time.Time `gorm:"column:effective_at;type:timestamptz;not null" json:"effective_at"` // 生效时间 + ExpiredAt *time.Time `gorm:"column:expired_at;type:timestamptz" json:"expired_at"` // 到期时间 } func init() { database.AppendMigrate(&GasorderContract{}) } diff --git a/backend/api/internal/models/platform_role.go b/backend/api/internal/models/platform_role.go index 4e4625e..b572410 100644 --- a/backend/api/internal/models/platform_role.go +++ b/backend/api/internal/models/platform_role.go @@ -4,11 +4,11 @@ import "git.apinb.com/bsm-sdk/core/database" // PlatformRole 对应 platform_role,定义平台总后台的数据范围与菜单权限角色。 type PlatformRole struct { - Entity // 公共实体字段 - RoleCode string `gorm:"column:role_code;type:varchar(64);not null;uniqueIndex" json:"role_code"` // 角色编码 - Name string `gorm:"column:name;type:varchar(64);not null" json:"name"` // 角色名称 - DataScope string `gorm:"column:data_scope;type:varchar(32);not null;default:'global'" json:"data_scope"` // 数据权限范围 - IsSystem bool `gorm:"column:is_system;not null;default:false" json:"is_system"` // 是否系统内置角色 + Entity // 公共实体字段 + RoleCode string `gorm:"column:role_code;type:varchar(64);not null;uniqueIndex" json:"role_code"` // 角色编码 + Name string `gorm:"column:name;type:varchar(64);not null" json:"name"` // 角色名称 + LocationScope string `gorm:"column:location_scope;type:varchar(32);not null;default:'standard'" json:"location_scope"` // 坐标展示范围 + IsSystem bool `gorm:"column:is_system;not null;default:false" json:"is_system"` // 是否系统内置角色 } func init() { database.AppendMigrate(&PlatformRole{}) } diff --git a/backend/api/internal/models/product_info.go b/backend/api/internal/models/product_info.go index fd7289b..9f449e2 100644 --- a/backend/api/internal/models/product_info.go +++ b/backend/api/internal/models/product_info.go @@ -9,6 +9,7 @@ import ( // ProductInfo 对应 product_info,保存一物一码的实体产品档案。 type ProductInfo struct { Entity // 公共实体字段 + ProductStatus int `gorm:"column:product_status;not null;default:10;index" json:"product_status"` // 产品业务状态 Code string `gorm:"column:code;type:varchar(64);not null;uniqueIndex" json:"code"` // 产品唯一标识 Name string `gorm:"column:name;type:varchar(128);not null" json:"name"` // 产品名称 ProductTypeID uint64 `gorm:"column:product_type_id;not null;index" json:"product_type_id"` // 产品类型自增主键 @@ -18,7 +19,6 @@ type ProductInfo struct { DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // 当前归属配送站自增主键 UserAccountID uint64 `gorm:"column:user_account_id;not null;default:0;index" json:"user_account_id"` // 当前归属用户自增主键 ProducedAt time.Time `gorm:"column:produced_at;type:timestamptz;not null" json:"produced_at"` // 生产时间 - IsEnabled bool `gorm:"column:is_enabled;not null;default:false" json:"is_enabled"` // 是否启用 EnabledAt *time.Time `gorm:"column:enabled_at;type:timestamptz" json:"enabled_at"` // 首次启用时间 } diff --git a/backend/api/internal/models/product_repair.go b/backend/api/internal/models/product_repair.go index 7cad870..47dac5c 100644 --- a/backend/api/internal/models/product_repair.go +++ b/backend/api/internal/models/product_repair.go @@ -8,17 +8,17 @@ import ( // ProductRepair 对应 product_repair,保存产品检修过程与结果。 type ProductRepair struct { - Entity // 公共实体字段 - ProductInfoID uint64 `gorm:"column:product_info_id;not null;index;uniqueIndex:idx_pending_product_repair,where:result = 'pending'" json:"product_info_id"` // 产品档案自增主键 - RepairNo string `gorm:"column:repair_no;type:varchar(64);not null;uniqueIndex" json:"repair_no"` // 检修单号 - RepairType string `gorm:"column:repair_type;type:varchar(32);not null" json:"repair_type"` // 检修类型 - StartedAt time.Time `gorm:"column:started_at;type:timestamptz;not null" json:"started_at"` // 开始时间 - CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"` // 完成时间 - Result string `gorm:"column:result;type:varchar(32);not null;default:'pending'" json:"result"` // 检修结果 - TargetStatus int `gorm:"column:target_status;not null;default:0" json:"target_status"` // 完成后的产品状态 - Content string `gorm:"column:content;type:text;not null;default:''" json:"content"` // 检修内容 - Operator string `gorm:"column:operator;type:varchar(64);not null;default:''" json:"operator"` // 检修人员 - Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 备注 + Entity // 公共实体字段 + ProductInfoID uint64 `gorm:"column:product_info_id;not null;index;uniqueIndex:idx_pending_product_repair,where:result = 'pending'" json:"product_info_id"` // 产品档案自增主键 + RepairNo string `gorm:"column:repair_no;type:varchar(64);not null;uniqueIndex" json:"repair_no"` // 检修单号 + RepairType string `gorm:"column:repair_type;type:varchar(32);not null" json:"repair_type"` // 检修类型 + StartedAt time.Time `gorm:"column:started_at;type:timestamptz;not null" json:"started_at"` // 开始时间 + CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"` // 完成时间 + Result string `gorm:"column:result;type:varchar(32);not null;default:'pending'" json:"result"` // 检修结果 + TargetProductStatus int `gorm:"column:target_product_status;not null;default:0" json:"target_product_status"` // 完成后的产品状态 + Content string `gorm:"column:content;type:text;not null;default:''" json:"content"` // 检修内容 + Operator string `gorm:"column:operator;type:varchar(64);not null;default:''" json:"operator"` // 检修人员 + Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 备注 } func init() { database.AppendMigrate(&ProductRepair{}) } diff --git a/backend/api/internal/models/product_warehouse.go b/backend/api/internal/models/product_warehouse.go index 85e6cdd..6e0985d 100644 --- a/backend/api/internal/models/product_warehouse.go +++ b/backend/api/internal/models/product_warehouse.go @@ -4,13 +4,12 @@ import "git.apinb.com/bsm-sdk/core/database" // ProductWarehouse 对应 product_warehouse,保存独立库房档案。 type ProductWarehouse struct { - Entity // 公共实体字段 - Code string `gorm:"column:code;type:varchar(64);not null;uniqueIndex" json:"code"` // 库房编码 - Name string `gorm:"column:name;type:varchar(128);not null" json:"name"` // 库房名称 - Address string `gorm:"column:address;type:varchar(255);not null;default:''" json:"address"` // 库房地址 - Manager string `gorm:"column:manager;type:varchar(64);not null;default:''" json:"manager"` // 库房负责人 - Phone string `gorm:"column:phone;type:varchar(32);not null;default:''" json:"phone"` // 联系电话 - IsEnabled bool `gorm:"column:is_enabled;not null;default:false" json:"is_enabled"` // 是否启用 + Entity // 公共实体字段 + Code string `gorm:"column:code;type:varchar(64);not null;uniqueIndex" json:"code"` // 库房编码 + Name string `gorm:"column:name;type:varchar(128);not null" json:"name"` // 库房名称 + Address string `gorm:"column:address;type:varchar(255);not null;default:''" json:"address"` // 库房地址 + Manager string `gorm:"column:manager;type:varchar(64);not null;default:''" json:"manager"` // 库房负责人 + Phone string `gorm:"column:phone;type:varchar(32);not null;default:''" json:"phone"` // 联系电话 } func init() { database.AppendMigrate(&ProductWarehouse{}) } diff --git a/backend/api/internal/models/status_separation_test.go b/backend/api/internal/models/status_separation_test.go new file mode 100644 index 0000000..750d47c --- /dev/null +++ b/backend/api/internal/models/status_separation_test.go @@ -0,0 +1,37 @@ +package models + +import ( + "reflect" + "testing" +) + +func TestBusinessModelsUseDedicatedStatusFields(t *testing.T) { + for _, test := range []struct { + model any + field string + }{ + {GasorderContract{}, "ContractStatus"}, + {GasorderBasic{}, "OrderStatus"}, + {ProductInfo{}, "ProductStatus"}, + {EcOrder{}, "OrderStatus"}, + {WalletPayment{}, "PaymentStatus"}, + {WalletRefund{}, "RefundStatus"}, + {WalletApplyCash{}, "ApplyStatus"}, + {FinPayment{}, "PaymentStatus"}, + {FinReconciliation{}, "ReconciliationStatus"}, + {CsTicket{}, "TicketStatus"}, + } { + if _, ok := reflect.TypeOf(test.model).FieldByName(test.field); !ok { + t.Fatalf("%T is missing dedicated business status field %s", test.model, test.field) + } + } +} + +func TestProductEnablementHasSingleSource(t *testing.T) { + if _, ok := reflect.TypeOf(ProductInfo{}).FieldByName("IsEnabled"); ok { + t.Fatal("ProductInfo duplicates Entity.Status with IsEnabled") + } + if _, ok := reflect.TypeOf(ProductWarehouse{}).FieldByName("IsEnabled"); ok { + t.Fatal("ProductWarehouse duplicates Entity.Status with IsEnabled") + } +} diff --git a/backend/api/internal/models/user_service_relation.go b/backend/api/internal/models/user_service_relation.go index 581fac1..18d2750 100644 --- a/backend/api/internal/models/user_service_relation.go +++ b/backend/api/internal/models/user_service_relation.go @@ -5,10 +5,10 @@ import "git.apinb.com/bsm-sdk/core/database" // UserServiceRelation 对应 user_service_relation,保存用户服务归属快照。 type UserServiceRelation struct { Entity // 公共实体字段 - UserAccountID uint64 `gorm:"column:user_account_id;not null;index" json:"user_account_id"` // user_account_id 业务字段 - GasBasicID uint64 `gorm:"column:gas_basic_id;not null;default:0;index" json:"gas_basic_id"` // gas_basic_id 业务字段 - DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // delivery_basic_id 业务字段 - StaffAccountID uint64 `gorm:"column:staff_account_id;not null;default:0;index" json:"staff_account_id"` // staff_account_id 业务字段 + UserAccountID uint64 `gorm:"column:user_account_id;not null;index;uniqueIndex:idx_active_user_service,where:status <> 3" json:"user_account_id"` // 用户账户自增主键 + GasBasicID uint64 `gorm:"column:gas_basic_id;not null;default:0;index" json:"gas_basic_id"` // gas_basic_id 业务字段 + DeliveryBasicID uint64 `gorm:"column:delivery_basic_id;not null;default:0;index" json:"delivery_basic_id"` // delivery_basic_id 业务字段 + StaffAccountID uint64 `gorm:"column:staff_account_id;not null;default:0;index" json:"staff_account_id"` // staff_account_id 业务字段 } func init() { database.AppendMigrate(&UserServiceRelation{}) } diff --git a/backend/api/internal/models/wallet_apply_cash.go b/backend/api/internal/models/wallet_apply_cash.go index e3471cd..e4f0566 100644 --- a/backend/api/internal/models/wallet_apply_cash.go +++ b/backend/api/internal/models/wallet_apply_cash.go @@ -9,6 +9,7 @@ import ( // WalletApplyCash 对应 wallet_apply_cash,保存提现申请及审核结果。 type WalletApplyCash struct { Entity // 公共实体字段 + ApplyStatus int `gorm:"column:apply_status;not null;default:10;index" json:"apply_status"` // 提现申请业务状态 WalletBasicID uint64 `gorm:"column:wallet_basic_id;not null;index" json:"wallet_basic_id"` // 钱包自增主键 WalletBankID uint64 `gorm:"column:wallet_bank_id;not null;default:0;index" json:"wallet_bank_id"` // 银行卡自增主键 CashNo string `gorm:"column:cash_no;type:varchar(64);not null;uniqueIndex" json:"cash_no"` // 内部提现单号 diff --git a/backend/api/internal/models/wallet_payment.go b/backend/api/internal/models/wallet_payment.go index b7e3fad..e0a9048 100644 --- a/backend/api/internal/models/wallet_payment.go +++ b/backend/api/internal/models/wallet_payment.go @@ -5,6 +5,7 @@ import "git.apinb.com/bsm-sdk/core/database" // WalletPayment 对应 wallet_payment,保存第三方或余额支付单。 type WalletPayment struct { Entity // 公共实体字段 + PaymentStatus int `gorm:"column:payment_status;not null;default:10;index" json:"payment_status"` // 支付业务状态 WalletBasicID uint64 `gorm:"column:wallet_basic_id;not null;index" json:"wallet_basic_id"` // 钱包自增主键 PaymentNo string `gorm:"column:payment_no;type:varchar(64);not null;uniqueIndex" json:"payment_no"` // 内部支付单号 OrderNo string `gorm:"column:order_no;type:varchar(128);not null;index" json:"order_no"` // 业务订单号 diff --git a/backend/api/internal/models/wallet_refund.go b/backend/api/internal/models/wallet_refund.go index 3cf8d22..e8363c8 100644 --- a/backend/api/internal/models/wallet_refund.go +++ b/backend/api/internal/models/wallet_refund.go @@ -9,6 +9,7 @@ import ( // WalletRefund 对应 wallet_refund,保存支付退款结果。 type WalletRefund struct { Entity // 公共实体字段 + RefundStatus int `gorm:"column:refund_status;not null;default:10;index" json:"refund_status"` // 退款业务状态 WalletBasicID uint64 `gorm:"column:wallet_basic_id;not null;index" json:"wallet_basic_id"` // 钱包自增主键 WalletPaymentID uint64 `gorm:"column:wallet_payment_id;not null;index" json:"wallet_payment_id"` // 原支付记录自增主键 RefundNo string `gorm:"column:refund_no;type:varchar(64);not null;uniqueIndex" json:"refund_no"` // 内部退款单号 diff --git a/backend/api/internal/routers/platform.go b/backend/api/internal/routers/platform.go index 8c0cdcc..a5f84f9 100644 --- a/backend/api/internal/routers/platform.go +++ b/backend/api/internal/routers/platform.go @@ -100,7 +100,7 @@ func registerGasorderRoute(group *gin.RouterGroup) { func registerProductRoute(group *gin.RouterGroup) { registerRestrictedNoDeleteResource(group, "/product_type", &models.ProductType{}, []string{"code", "name"}) - registerRestrictedNoDeleteResource(group, "/product_warehouse", &models.ProductWarehouse{}, []string{"code", "name", "address", "manager", "phone", "is_enabled"}) + registerRestrictedNoDeleteResource(group, "/product_warehouse", &models.ProductWarehouse{}, []string{"code", "name", "address", "manager", "phone"}) infoRelations := []common.ResourceRelation{ requiredRelation("product_type_identity", "product_type_id", &models.ProductType{}), @@ -115,7 +115,8 @@ func registerProductRoute(group *gin.RouterGroup) { info.POST("", infoCreate) info.GET("/:identity", infoGet) info.PUT("/:identity", infoUpdate) - info.PATCH("/:identity/status", product.UpdateProductInfoStatus) + info.PATCH("/:identity/status", product.UpdateProductInfoRecordStatus) + info.PATCH("/:identity/lifecycle", product.UpdateProductInfoLifecycle) repairList, repairCreate, repairGet, repairUpdate := product.ProductRepairHandlers( requiredRelation("product_info_identity", "product_info_id", &models.ProductInfo{}), @@ -164,12 +165,10 @@ func registerCommerceRoute(group *gin.RouterGroup) { 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, 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{})) + registerReadOnlyResource(group, "/ec_cart", &models.EcCart{}) + registerReadOnlyHandlers(group, "/ec_order", func(ctx *gin.Context) { common.ListResource(ctx, &models.EcOrder{}) }, ec.GetEcOrder) + registerReadOnlyResource(group, "/ec_order_item", &models.EcOrderItem{}) + registerReadOnlyResource(group, "/ec_review", &models.EcReview{}) } func registerStaffRoute(group *gin.RouterGroup) { @@ -206,10 +205,10 @@ func registerPlatformRoute(group *gin.RouterGroup) { } 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{})) + registerReadOnlyResource(group, "/fin_payment", &models.FinPayment{}) 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"}) + registerReadOnlyResource(group, "/fin_reconciliation", &models.FinReconciliation{}) } diff --git a/backend/api/internal/routers/platform_test.go b/backend/api/internal/routers/platform_test.go index 875e6b7..0c0c061 100644 --- a/backend/api/internal/routers/platform_test.go +++ b/backend/api/internal/routers/platform_test.go @@ -115,12 +115,19 @@ func TestPlatformProductCommerceAndDeliveryRoutesFollowTheirContracts(t *testing } for _, resource := range []string{ - "/ec_category", "/ec_product", "/ec_product_attribute", "/ec_product_image", "/ec_cart", "/ec_order", "/ec_order_item", "/ec_review", + "/ec_category", "/ec_product", "/ec_product_attribute", "/ec_product_image", } { 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) } + for _, resource := range []string{"/ec_cart", "/ec_order", "/ec_order_item", "/ec_review"} { + path := "/heqi/platform/v1" + resource + assertRouteMethods(t, routes, path, http.MethodGet) + assertRouteMethods(t, routes, path+"/:identity", http.MethodGet) + assertNoRouteMethods(t, routes, path, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) + assertNoRouteMethods(t, routes, path+"/:identity", http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) + } for _, resource := range []string{"/gasorder_item", "/gasorder_assign", "/gasorder_status", "/gasorder_track", "/gasorder_track_point", "/gasorder_confirm", "/gasorder_payment", "/gasorder_contract_revision"} { path := "/heqi/platform/v1" + resource @@ -177,13 +184,19 @@ func TestPlatformFinanceContentRoutesFollowTheirContracts(t *testing.T) { } for _, resource := range []string{ - "/fin_payment", "/fin_settlement", "/fin_reconciliation", - "/cms_content", "/cs_ticket", + "/fin_settlement", "/cms_content", "/cs_ticket", } { 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) } + for _, resource := range []string{"/fin_payment", "/fin_reconciliation"} { + path := "/heqi/platform/v1" + resource + assertRouteMethods(t, routes, path, http.MethodGet) + assertRouteMethods(t, routes, path+"/:identity", http.MethodGet) + assertNoRouteMethods(t, routes, path, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) + assertNoRouteMethods(t, routes, path+"/:identity", http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete) + } for _, resource := range []string{ "/wallet_basic", "/wallet_bank", "/wallet_payment", "/wallet_record", "/wallet_refund", "/wallet_apply_cash", diff --git a/backend/api/internal/seed/mock.go b/backend/api/internal/seed/mock.go index bcf88d2..3c8fcff 100644 --- a/backend/api/internal/seed/mock.go +++ b/backend/api/internal/seed/mock.go @@ -113,7 +113,7 @@ func MockData(database *gorm.DB) error { warehouse := models.ProductWarehouse{ Entity: entity(11, common.StatusEnable), Code: "MOCK-WH-001", Name: "示例中心库房", - Address: gas.Address, Manager: "赵库管", Phone: "13700000001", IsEnabled: true, + Address: gas.Address, Manager: "赵库管", Phone: "13700000001", } if err := put(tx, &warehouse); err != nil { return err @@ -122,9 +122,9 @@ func MockData(database *gorm.DB) error { enabledAt := yesterday productInfo := models.ProductInfo{ Entity: entity(12, common.StatusEnable), Code: "MOCK-CYLINDER-001", Name: "示例液化气钢瓶", + ProductStatus: common.StatusInStock, ProductTypeID: productType.ID, Params: `{"weight":"15kg","medium":"LPG"}`, - WarehouseID: warehouse.ID, GasBasicID: gas.ID, ProducedAt: now.AddDate(-1, 0, 0), - IsEnabled: true, EnabledAt: &enabledAt, + WarehouseID: warehouse.ID, ProducedAt: now.AddDate(-1, 0, 0), EnabledAt: &enabledAt, } if err := put(tx, &productInfo); err != nil { return err @@ -132,7 +132,7 @@ func MockData(database *gorm.DB) error { productOwner := models.ProductOwner{ Entity: entity(13, common.StatusEnable), ProductInfoID: productInfo.ID, - WarehouseID: warehouse.ID, GasBasicID: gas.ID, Action: "stock_in", + WarehouseID: warehouse.ID, Action: "stock_in", OccurredAt: yesterday, Reason: "模拟数据初始化", OperatorName: "系统", } if err := put(tx, &productOwner); err != nil { @@ -141,9 +141,9 @@ func MockData(database *gorm.DB) error { completedAt := yesterday.Add(2 * time.Hour) productRepair := models.ProductRepair{ - Entity: entity(14, common.StatusCompleted), ProductInfoID: productInfo.ID, + Entity: entity(14, common.StatusEnable), ProductInfoID: productInfo.ID, RepairNo: "MOCK-REPAIR-001", RepairType: "inspection", StartedAt: yesterday, - CompletedAt: &completedAt, Result: "passed", TargetStatus: common.StatusEnable, + CompletedAt: &completedAt, Result: "passed", TargetProductStatus: common.StatusInStock, Content: "外观、阀门与气密性检查", Operator: staff.Name, } if err := put(tx, &productRepair); err != nil { @@ -151,7 +151,7 @@ func MockData(database *gorm.DB) error { } contract := models.GasorderContract{ - Entity: entity(15, common.StatusActive), ContractNo: "MOCK-CONTRACT-001", + Entity: entity(15, common.StatusEnable), ContractStatus: common.StatusActive, ContractNo: "MOCK-CONTRACT-001", UserAccountID: user.ID, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, Title: "居民瓶装气配送示例合同", Terms: "按需配送,安全使用。", DefaultDeliveryFee: 500, SignedAt: yesterday, EffectiveAt: yesterday, ExpiredAt: &nextYear, @@ -161,7 +161,7 @@ func MockData(database *gorm.DB) error { } contractRevision := models.GasorderContractRevision{ - Entity: entity(16, common.StatusActive), GasorderContractID: contract.ID, Action: "activate", + Entity: entity(16, common.StatusEnable), GasorderContractID: contract.ID, Action: "activate", ContractStatus: common.StatusActive, EffectiveAt: contract.EffectiveAt, ExpiredAt: contract.ExpiredAt, OperatorIdentity: gasAccount.Identity, OperatorName: gasAccount.DisplayName, OccurredAt: yesterday, Reason: "模拟合同启用", @@ -171,7 +171,7 @@ func MockData(database *gorm.DB) error { } contractProduct := models.GasorderContractProduct{ - Entity: entity(17, common.StatusActive), GasorderContractID: contract.ID, + Entity: entity(17, common.StatusEnable), GasorderContractID: contract.ID, ProductInfoID: productInfo.ID, ProductCode: productInfo.Code, ProductTypeName: productType.Name, ProductParams: productInfo.Params, UnitPrice: 9800, BoundAt: yesterday, @@ -181,7 +181,7 @@ func MockData(database *gorm.DB) error { } gasOrder := models.GasorderBasic{ - Entity: entity(18, common.StatusCompleted), OrderNo: "MOCK-GASORDER-001", + Entity: entity(18, common.StatusEnable), OrderStatus: common.StatusCompleted, OrderNo: "MOCK-GASORDER-001", RequestNo: "MOCK-REQ-GASORDER-001", GasorderContractID: contract.ID, UserAccountID: user.ID, CreatorType: "user", CreatorID: user.ID, CreatorIdentity: user.Identity, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, @@ -195,7 +195,7 @@ func MockData(database *gorm.DB) error { } gasOrderItem := models.GasorderItem{ - Entity: entity(19, common.StatusCompleted), GasorderBasicID: gasOrder.ID, + Entity: entity(19, common.StatusEnable), GasorderBasicID: gasOrder.ID, GasorderContractProductID: contractProduct.ID, ProductInfoID: productInfo.ID, ProductCode: productInfo.Code, ProductTypeName: productType.Name, ProductParams: productInfo.Params, UnitPrice: contractProduct.UnitPrice, @@ -205,7 +205,7 @@ func MockData(database *gorm.DB) error { } assignment := models.GasorderAssign{ - Entity: entity(20, common.StatusCompleted), GasorderBasicID: gasOrder.ID, GasBasicID: gas.ID, + Entity: entity(20, common.StatusEnable), GasorderBasicID: gasOrder.ID, GasBasicID: gas.ID, DeliveryBasicID: delivery.ID, StaffAccountID: staff.ID, AssignerIdentity: deliveryAccount.Identity, AssignerName: deliveryAccount.DisplayName, AssignedAt: now.Add(-2 * time.Hour), Reason: "系统示例派单", @@ -215,7 +215,7 @@ func MockData(database *gorm.DB) error { } orderStatus := models.GasorderStatus{ - Entity: entity(21, common.StatusCompleted), GasorderBasicID: gasOrder.ID, + Entity: entity(21, common.StatusEnable), GasorderBasicID: gasOrder.ID, FromStatus: common.StatusDelivering, ToStatus: common.StatusCompleted, OperatorIdentity: staff.Identity, OperatorName: staff.Name, OccurredAt: now, Reason: "用户已签收", @@ -226,7 +226,7 @@ func MockData(database *gorm.DB) error { trackCompletedAt := now.Add(-10 * time.Minute) track := models.GasorderTrack{ - Entity: entity(22, common.StatusCompleted), GasorderBasicID: gasOrder.ID, + Entity: entity(22, common.StatusEnable), GasorderBasicID: gasOrder.ID, StaffAccountID: staff.ID, AttemptNo: 1, StartedAt: now.Add(-90 * time.Minute), CompletedAt: &trackCompletedAt, } @@ -235,7 +235,7 @@ func MockData(database *gorm.DB) error { } trackPoint := models.GasorderTrackPoint{ - Entity: entity(23, common.StatusCompleted), GasorderTrackID: track.ID, + Entity: entity(23, common.StatusEnable), GasorderTrackID: track.ID, Longitude: address.Longitude, Latitude: address.Latitude, OccurredAt: trackCompletedAt, Source: "gps", Accuracy: "10m", } @@ -244,7 +244,7 @@ func MockData(database *gorm.DB) error { } confirmation := models.GasorderConfirm{ - Entity: entity(24, common.StatusCompleted), GasorderBasicID: gasOrder.ID, + Entity: entity(24, common.StatusEnable), GasorderBasicID: gasOrder.ID, ConfirmType: "signature", RecipientName: user.Name, RecipientPhone: user.Phone, ProofURI: "/mock/proofs/gasorder-001.png", ConfirmedAt: now, Remark: "模拟签收", } @@ -293,7 +293,7 @@ func MockData(database *gorm.DB) error { } ecOrder := models.EcOrder{ - Entity: entity(30, common.StatusPaid), OrderNo: "MOCK-ECORDER-001", + Entity: entity(30, common.StatusEnable), OrderStatus: common.StatusPaid, OrderNo: "MOCK-ECORDER-001", UserAccountID: user.ID, GasStationID: gas.ID, DeliveryPointID: delivery.ID, TotalAmount: ecProduct.PriceAmount, } @@ -302,7 +302,7 @@ func MockData(database *gorm.DB) error { } ecOrderItem := models.EcOrderItem{ - Entity: entity(31, common.StatusPaid), EcOrderID: ecOrder.ID, EcProductID: ecProduct.ID, + Entity: entity(31, common.StatusEnable), EcOrderID: ecOrder.ID, EcProductID: ecProduct.ID, ProductSnapshot: `{"code":"MOCK-EC-LPG-001","name":"15kg 液化气配送服务"}`, Quantity: 1, SaleAmount: ecProduct.PriceAmount, } @@ -311,7 +311,7 @@ func MockData(database *gorm.DB) error { } review := models.EcReview{ - Entity: entity(32, common.StatusPublished), EcOrderID: ecOrder.ID, + Entity: entity(32, common.StatusEnable), EcOrderID: ecOrder.ID, EcProductID: ecProduct.ID, UserAccountID: user.ID, Score: 5, Content: "配送及时,服务规范。", } @@ -341,7 +341,7 @@ func MockData(database *gorm.DB) error { } walletPayment := models.WalletPayment{ - Entity: entity(35, common.StatusSuccess), WalletBasicID: wallet.ID, + Entity: entity(35, common.StatusEnable), PaymentStatus: common.StatusSuccess, WalletBasicID: wallet.ID, PaymentNo: "MOCK-PAYMENT-001", OrderNo: gasOrder.OrderNo, TradeNo: "MOCK-TRADE-001", PaymentType: "gasorder", PayChannel: "balance", PayType: "wallet", Amount: gasOrder.PayableAmount, @@ -352,7 +352,7 @@ func MockData(database *gorm.DB) error { } walletRecord := models.WalletRecord{ - Entity: entity(36, common.StatusCompleted), WalletBasicID: wallet.ID, + Entity: entity(36, common.StatusEnable), WalletBasicID: wallet.ID, RecordNo: "MOCK-RECORD-001", RequestNo: "MOCK-REQ-RECORD-001", Direction: "in", TradeType: "recharge", Amount: 50000, BalanceAfter: 50000, WithdrawalBalanceAfter: 30000, @@ -366,7 +366,7 @@ func MockData(database *gorm.DB) error { refundCompletedAt := now refund := models.WalletRefund{ - Entity: entity(37, common.StatusCompleted), WalletBasicID: wallet.ID, + Entity: entity(37, common.StatusEnable), RefundStatus: common.StatusCompleted, WalletBasicID: wallet.ID, WalletPaymentID: walletPayment.ID, RefundNo: "MOCK-REFUND-001", OrderIdentity: gasOrder.Identity, Amount: 1000, Reason: "模拟部分退款", OrderInfo: `{"order_no":"MOCK-GASORDER-001"}`, Result: `{"status":"success"}`, @@ -377,7 +377,7 @@ func MockData(database *gorm.DB) error { } applyCash := models.WalletApplyCash{ - Entity: entity(38, common.StatusApproved), WalletBasicID: wallet.ID, WalletBankID: bank.ID, + Entity: entity(38, common.StatusEnable), ApplyStatus: common.StatusApproved, WalletBasicID: wallet.ID, WalletBankID: bank.ID, CashNo: "MOCK-CASH-001", RequestNo: "MOCK-REQ-CASH-001", Amount: 5000, Channel: "bank", TradeNo: "MOCK-CASH-TRADE-001", Remark: "模拟提现", ReviewerIdentity: gasAccount.Identity, ReviewerName: gasAccount.DisplayName, @@ -388,7 +388,7 @@ func MockData(database *gorm.DB) error { } gasOrderPayment := models.GasorderPayment{ - Entity: entity(39, common.StatusSuccess), GasorderBasicID: gasOrder.ID, + Entity: entity(39, common.StatusEnable), GasorderBasicID: gasOrder.ID, WalletPaymentID: walletPayment.ID, AttemptNo: 1, Amount: gasOrder.PayableAmount, } if err := put(tx, &gasOrderPayment); err != nil { @@ -396,7 +396,7 @@ func MockData(database *gorm.DB) error { } finPayment := models.FinPayment{ - Entity: entity(40, common.StatusPaid), EcOrderID: ecOrder.ID, + Entity: entity(40, common.StatusEnable), PaymentStatus: common.StatusPaid, EcOrderID: ecOrder.ID, Channel: "wallet", Amount: ecOrder.TotalAmount, PaidAt: &now, } if err := put(tx, &finPayment); err != nil { @@ -404,7 +404,7 @@ func MockData(database *gorm.DB) error { } settlement := models.FinSettlement{ - Entity: entity(41, common.StatusCompleted), SettlementNo: "MOCK-SETTLEMENT-001", + Entity: entity(41, common.StatusEnable), SettlementNo: "MOCK-SETTLEMENT-001", SubjectType: "gas", SubjectID: gas.ID, PeriodStart: now.AddDate(0, 0, -30), PeriodEnd: now, } @@ -413,7 +413,7 @@ func MockData(database *gorm.DB) error { } reconciliation := models.FinReconciliation{ - Entity: entity(42, common.StatusMatched), Channel: "wallet", + Entity: entity(42, common.StatusEnable), ReconciliationStatus: common.StatusMatched, Channel: "wallet", BillDate: now, DifferenceAmount: 0, } if err := put(tx, &reconciliation); err != nil { @@ -430,7 +430,7 @@ func MockData(database *gorm.DB) error { } ticket := models.CsTicket{ - Entity: entity(44, common.StatusOpen), TicketNo: "MOCK-TICKET-001", + Entity: entity(44, common.StatusEnable), TicketStatus: common.StatusOpen, TicketNo: "MOCK-TICKET-001", UserAccountID: user.ID, Category: "delivery", Priority: "normal", } if err := put(tx, &ticket); err != nil { @@ -439,7 +439,7 @@ func MockData(database *gorm.DB) error { role := models.PlatformRole{ Entity: entity(45, common.StatusEnable), RoleCode: "mock_operator", - Name: "模拟运营人员", DataScope: "global", IsSystem: false, + Name: "模拟运营人员", LocationScope: "standard", IsSystem: false, } if err := put(tx, &role); err != nil { return err diff --git a/frontend/platform_admin/src/api/platform.ts b/frontend/platform_admin/src/api/platform.ts index 5536ac2..fee8d50 100644 --- a/frontend/platform_admin/src/api/platform.ts +++ b/frontend/platform_admin/src/api/platform.ts @@ -2,8 +2,8 @@ import { request } from './http'; import { resourceApi } from './resource'; /** 平台角色、菜单、账号和工作台接口。 */ -export type PlatformRole = { identity: string; role_code: string; name: string; data_scope: string; is_system: boolean; status: number }; -export type PlatformMenu = { identity: string; parent_identity?: string; menu_code: string; name: string; icon: string; path: string; sort_no: number }; +export type PlatformRole = { identity: string; role_code: string; name: string; location_scope: string; is_system: boolean; status: number }; +export type PlatformMenu = { identity: string; parent_identity?: string; group_code: string; name: string; icon: string; path: string; sort_no: number }; export const platformApi = { overview: () => request>('/dashboard/overview'), diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index dec79cc..71fc270 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -76,13 +76,21 @@ const fieldLabels: Record = { params: '产品参数', produced_at: '生产时间', enabled_at: '启用时间', - is_enabled: '是否启用', + product_status: '产品生命周期', + contract_status: '合同状态', + order_status: '订单状态', + previous_order_status: '异常前订单状态', + payment_status: '支付状态', + refund_status: '退款状态', + apply_status: '提现状态', + ticket_status: '工单状态', + reconciliation_status: '对账状态', repair_no: '检修单号', repair_type: '检修类型', started_at: '开始时间', completed_at: '完成时间', result: '检修结果', - target_status: '目标状态', + target_product_status: '目标产品状态', content: '内容', operator: '操作人员', remark: '备注', @@ -175,9 +183,9 @@ const fieldLabels: Record = { ticket_no: '工单号', category: '分类', priority: '优先级', - data_scope: '数据范围', + location_scope: '坐标权限', parent_identity: '父级', - menu_code: '菜单编码', + group_code: '菜单分组编码', icon: '图标', path: '路由', menu_identities: '菜单权限', @@ -186,7 +194,7 @@ const fieldLabels: Record = { const numbers = new Set([ 'sort_no', 'stock_quantity', 'quantity', 'score', 'version_no', - 'attempt_no', 'target_status', + 'attempt_no', 'target_product_status', ]); const money = new Set([ 'unit_price', 'amount', 'fee', 'balance', 'withdrawal_balance', @@ -194,7 +202,7 @@ const money = new Set([ 'discount_amount', 'total_amount', 'delivery_fee', 'price_amount', 'sale_amount', 'difference_amount', ]); -const booleans = new Set(['is_default', 'is_enabled', 'is_cover', 'selected', 'withdrawable']); +const booleans = new Set(['is_default', 'is_cover', 'selected', 'withdrawable']); const dates = new Set(['bill_date']); const datetimes = new Set([ 'expired_at', 'produced_at', 'enabled_at', 'started_at', 'completed_at', @@ -279,11 +287,11 @@ export const resources: ResourceUiDefinition[] = [ define('user_service_relation', '用户服务关系', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('staff_account_identity', '/staff_account')]), define('product_type', '产品类型', 'editable', [f('code', { required: true }), f('name', { required: true })]), - define('product_warehouse', '库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address'), f('manager'), f('phone'), f('is_enabled')]), - define('product_info', '产品信息', 'editable', [f('code', { required: true }), f('name', { required: true }), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true }), f('is_enabled')], 'list', [ - { name: '修改产品状态', resource: '/product_info/:identity/status', method: 'PATCH', fields: [f('status', { required: true, type: 'select', options: [{ label: '待处理', value: 10 }, { label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '维修中', value: 31 }, { label: '已报废', value: 27 }] })] }, + define('product_warehouse', '库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address'), f('manager'), f('phone')]), + define('product_info', '产品信息', 'editable', [f('code', { required: true }), f('name', { required: true }), relation('product_type_identity', '/product_type', true), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('produced_at', { required: true })], 'list', [ + { name: '修改产品生命周期', resource: '/product_info/:identity/lifecycle', method: 'PATCH', fields: [f('product_status', { required: true, type: 'select', options: [{ label: '待处理', value: 10 }, { label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '维修中', value: 31 }, { label: '已报废', value: 27 }] })] }, ]), - define('product_repair', '产品检修记录', 'editable', [relation('product_info_identity', '/product_info', true), f('repair_no', { required: true }), f('repair_type', { required: true }), f('started_at', { required: true }), f('completed_at'), f('result', { type: 'select', options: [{ label: '待处理', value: 'pending' }, { label: '通过', value: 'passed' }, { label: '未通过', value: 'failed' }] }), f('target_status', { type: 'select', options: [{ label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '报废', value: 27 }] }), f('content'), f('operator'), f('remark')]), + define('product_repair', '产品检修记录', 'editable', [relation('product_info_identity', '/product_info', true), f('repair_no', { required: true }), f('repair_type', { required: true }), f('started_at', { required: true }), f('completed_at'), f('result', { type: 'select', options: [{ label: '待处理', value: 'pending' }, { label: '通过', value: 'passed' }, { label: '未通过', value: 'failed' }] }), f('target_product_status', { type: 'select', options: [{ label: '在库', value: 28 }, { label: '运输中', value: 29 }, { label: '使用中', value: 30 }, { label: '报废', value: 27 }] }), f('content'), f('operator'), f('remark')]), define('product_owner', '产品归属记录', 'readonly', []), define('gasorder_contract', '配送合同', 'managed', [f('contract_no', { required: true }), relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic', true), relation('delivery_basic_identity', '/delivery_basic'), f('title', { required: true }), f('terms'), f('file_uri'), f('default_delivery_fee'), f('signed_at', { required: true }), f('effective_at', { required: true }), f('expired_at')], 'list', [ @@ -318,10 +326,10 @@ export const resources: ResourceUiDefinition[] = [ define('ec_product', '商品', 'writable', [relation('ec_category_identity', '/ec_category', true), f('product_code', { required: true }), f('name', { required: true }), f('price_amount', { required: true }), f('stock_quantity')]), define('ec_product_attribute', '商品属性', 'writable', [relation('ec_product_identity', '/ec_product', true), f('name', { required: true }), f('value', { required: true }), f('sort_no')]), define('ec_product_image', '商品图片', 'writable', [relation('ec_product_identity', '/ec_product', true), f('image_uri', { required: true }), f('sort_no'), f('is_cover')]), - define('ec_cart', '购物车', 'writable', [relation('user_account_identity', '/user_account', true), relation('ec_product_identity', '/ec_product', true), f('quantity', { required: true }), f('selected')]), - define('ec_order', '商城订单', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('order_no', { required: true }), f('total_amount', { required: true })]), - define('ec_order_item', '商城订单明细', 'writable', [relation('ec_order_identity', '/ec_order', true), relation('ec_product_identity', '/ec_product', true), f('product_snapshot', { required: true }), f('quantity', { required: true }), f('sale_amount', { required: true })]), - define('ec_review', '商品评价', 'writable', [relation('ec_order_identity', '/ec_order', true), relation('ec_product_identity', '/ec_product', true), relation('user_account_identity', '/user_account', true), f('score', { required: true }), f('content', { required: true })]), + define('ec_cart', '购物车', 'readonly', []), + define('ec_order', '商城订单', 'readonly', []), + define('ec_order_item', '商城订单明细', 'readonly', []), + define('ec_review', '商品评价', 'readonly', []), define('wallet_basic', '钱包', 'readonly', [f('owner_type'), f('owner_identity'), f('alipay_id'), f('alipay_name'), f('wxpay_id'), f('wxpay_name'), f('balance'), f('withdrawal_balance')], 'list', [ { name: '后台充值', resource: '/wallet_basic/:identity/recharge', fields: [f('request_no', { required: true }), f('amount', { required: true }), f('withdrawable'), ...reason, f('remark')] }, @@ -336,13 +344,13 @@ export const resources: ResourceUiDefinition[] = [ { name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason }, ]), - define('fin_payment', '财务支付记录', 'writable', [relation('ec_order_identity', '/ec_order', true), f('channel', { required: true }), f('amount', { required: true }), f('paid_at')]), + define('fin_payment', '财务支付记录', 'readonly', []), define('fin_settlement', '财务结算', 'writable', [f('settlement_no', { required: true }), f('subject_type', { required: true }), f('subject_identity', { required: true }), f('period_start', { required: true }), f('period_end', { required: true })]), - define('fin_reconciliation', '财务对账', 'writable', [f('channel', { required: true }), f('bill_date', { required: true }), f('difference_amount', { required: true })]), + define('fin_reconciliation', '财务对账', 'readonly', []), define('cms_content', '内容', 'writable', [f('content_type', { required: true }), f('title', { required: true }), f('body', { required: true }), f('version_no'), f('publish_status')]), define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), f('category', { required: true }), f('priority', { required: true })]), define('platform_account', '平台账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('avatar'), f('platform_role_code', { required: true }), f('phone')]), - define('platform_role', '平台角色', 'writable', [f('role_code', { required: true }), f('name', { required: true }), f('data_scope', { required: true })], 'list', [ + define('platform_role', '平台角色', 'writable', [f('role_code', { required: true }), f('name', { required: true }), f('location_scope', { required: true, type: 'select', options: [{ label: '脱敏坐标', value: 'standard' }, { label: '精确坐标', value: 'precise' }] })], 'list', [ { name: '分配菜单', resource: '/platform_role/:identity/menu', method: 'PUT', fields: [f('menu_identities', { type: 'identity-list', relation: '/platform_menu' })] }, ]), define('platform_menu', '平台菜单', 'readonly', [], 'tree'), diff --git a/frontend/platform_admin/src/contracts/platform-resources.json b/frontend/platform_admin/src/contracts/platform-resources.json index 4f4467d..bc91f49 100644 --- a/frontend/platform_admin/src/contracts/platform-resources.json +++ b/frontend/platform_admin/src/contracts/platform-resources.json @@ -1 +1 @@ -{"resources":[{"domain":"gas","name":"gas_basic","path":"/gas_basic","pageKind":"list","mode":"writable"},{"domain":"gas","name":"gas_account","path":"/gas_account","pageKind":"list","mode":"writable"},{"domain":"delivery","name":"delivery_basic","path":"/delivery_basic","pageKind":"list","mode":"writable"},{"domain":"delivery","name":"delivery_account","path":"/delivery_account","pageKind":"list","mode":"writable"},{"domain":"staff","name":"staff_account","path":"/staff_account","pageKind":"list","mode":"writable"},{"domain":"staff","name":"staff_credential","path":"/staff_credential","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_account","path":"/user_account","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_address","path":"/user_address","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_service_relation","path":"/user_service_relation","pageKind":"list","mode":"writable"},{"domain":"product","name":"product_type","path":"/product_type","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_warehouse","path":"/product_warehouse","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_info","path":"/product_info","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_repair","path":"/product_repair","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_owner","path":"/product_owner","pageKind":"list","mode":"readonly"},{"domain":"ec","name":"ec_category","path":"/ec_category","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product","path":"/ec_product","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product_attribute","path":"/ec_product_attribute","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product_image","path":"/ec_product_image","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_cart","path":"/ec_cart","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_order","path":"/ec_order","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_order_item","path":"/ec_order_item","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_review","path":"/ec_review","pageKind":"list","mode":"writable"},{"domain":"gasorder","name":"gasorder_contract","path":"/gasorder_contract","pageKind":"list","mode":"managed"},{"domain":"gasorder","name":"gasorder_contract_product","path":"/gasorder_contract_product","pageKind":"list","mode":"append_only"},{"domain":"gasorder","name":"gasorder_contract_revision","path":"/gasorder_contract_revision","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_basic","path":"/gasorder_basic","pageKind":"list","mode":"append_only"},{"domain":"gasorder","name":"gasorder_item","path":"/gasorder_item","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_assign","path":"/gasorder_assign","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_status","path":"/gasorder_status","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_track","path":"/gasorder_track","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_track_point","path":"/gasorder_track_point","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_confirm","path":"/gasorder_confirm","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_payment","path":"/gasorder_payment","pageKind":"list","mode":"readonly"},{"domain":"finance","name":"fin_payment","path":"/fin_payment","pageKind":"list","mode":"writable"},{"domain":"finance","name":"fin_settlement","path":"/fin_settlement","pageKind":"list","mode":"writable"},{"domain":"finance","name":"fin_reconciliation","path":"/fin_reconciliation","pageKind":"list","mode":"writable"},{"domain":"content","name":"cms_content","path":"/cms_content","pageKind":"list","mode":"writable"},{"domain":"customer_service","name":"cs_ticket","path":"/cs_ticket","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_account","path":"/platform_account","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_role","path":"/platform_role","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_menu","path":"/platform_menu","pageKind":"tree","mode":"readonly"},{"domain":"wallet","name":"wallet_basic","path":"/wallet_basic","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_bank","path":"/wallet_bank","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_payment","path":"/wallet_payment","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_record","path":"/wallet_record","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_refund","path":"/wallet_refund","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_apply_cash","path":"/wallet_apply_cash","pageKind":"list","mode":"readonly"}],"routes":[{"method":"GET","path":"/gasorder_contract"},{"method":"GET","path":"/gasorder_contract_product"},{"method":"GET","path":"/gasorder_contract_product/:identity"},{"method":"GET","path":"/gasorder_contract_revision"},{"method":"GET","path":"/gasorder_contract_revision/:identity"},{"method":"GET","path":"/gasorder_contract/:identity"},{"method":"GET","path":"/gasorder_confirm"},{"method":"GET","path":"/gasorder_confirm/:identity"},{"method":"GET","path":"/gasorder_track"},{"method":"GET","path":"/gasorder_track_point"},{"method":"GET","path":"/gasorder_track_point/:identity"},{"method":"GET","path":"/gasorder_track/:identity"},{"method":"GET","path":"/gasorder_basic"},{"method":"GET","path":"/gasorder_basic/:identity"},{"method":"GET","path":"/gasorder_item"},{"method":"GET","path":"/gasorder_item/:identity"},{"method":"GET","path":"/gasorder_assign"},{"method":"GET","path":"/gasorder_assign/:identity"},{"method":"GET","path":"/gasorder_status"},{"method":"GET","path":"/gasorder_status/:identity"},{"method":"GET","path":"/gasorder_payment"},{"method":"GET","path":"/gasorder_payment/:identity"},{"method":"GET","path":"/gas_basic"},{"method":"GET","path":"/gas_basic/:identity"},{"method":"GET","path":"/gas_account"},{"method":"GET","path":"/gas_account/:identity"},{"method":"GET","path":"/product_type"},{"method":"GET","path":"/product_type/:identity"},{"method":"GET","path":"/product_warehouse"},{"method":"GET","path":"/product_warehouse/:identity"},{"method":"GET","path":"/product_info"},{"method":"GET","path":"/product_info/:identity"},{"method":"GET","path":"/product_repair"},{"method":"GET","path":"/product_repair/:identity"},{"method":"GET","path":"/product_owner"},{"method":"GET","path":"/product_owner/:identity"},{"method":"GET","path":"/platform_role"},{"method":"GET","path":"/platform_role/:identity"},{"method":"GET","path":"/platform_role/:identity/menu"},{"method":"GET","path":"/platform_account"},{"method":"GET","path":"/platform_account/:identity"},{"method":"GET","path":"/platform_menu"},{"method":"GET","path":"/platform_menu/:identity"},{"method":"GET","path":"/ping/hello"},{"method":"GET","path":"/ec_product"},{"method":"GET","path":"/ec_product_attribute"},{"method":"GET","path":"/ec_product_attribute/:identity"},{"method":"GET","path":"/ec_product_image"},{"method":"GET","path":"/ec_product_image/:identity"},{"method":"GET","path":"/ec_product/:identity"},{"method":"GET","path":"/ec_category"},{"method":"GET","path":"/ec_category/:identity"},{"method":"GET","path":"/ec_cart"},{"method":"GET","path":"/ec_cart/:identity"},{"method":"GET","path":"/ec_order"},{"method":"GET","path":"/ec_order_item"},{"method":"GET","path":"/ec_order_item/:identity"},{"method":"GET","path":"/ec_order/:identity"},{"method":"GET","path":"/ec_review"},{"method":"GET","path":"/ec_review/:identity"},{"method":"GET","path":"/wallet_basic"},{"method":"GET","path":"/wallet_basic/owner/:owner_type/:owner_identity"},{"method":"GET","path":"/wallet_basic/:identity"},{"method":"GET","path":"/wallet_bank"},{"method":"GET","path":"/wallet_bank/:identity"},{"method":"GET","path":"/wallet_record"},{"method":"GET","path":"/wallet_record/:identity"},{"method":"GET","path":"/wallet_refund"},{"method":"GET","path":"/wallet_refund/:identity"},{"method":"GET","path":"/wallet_payment"},{"method":"GET","path":"/wallet_payment/:identity"},{"method":"GET","path":"/wallet_apply_cash"},{"method":"GET","path":"/wallet_apply_cash/:identity"},{"method":"GET","path":"/user_account"},{"method":"GET","path":"/user_account/:identity"},{"method":"GET","path":"/user_address"},{"method":"GET","path":"/user_address/:identity"},{"method":"GET","path":"/user_service_relation"},{"method":"GET","path":"/user_service_relation/:identity"},{"method":"GET","path":"/fin_payment"},{"method":"GET","path":"/fin_payment/:identity"},{"method":"GET","path":"/fin_settlement"},{"method":"GET","path":"/fin_settlement/:identity"},{"method":"GET","path":"/fin_reconciliation"},{"method":"GET","path":"/fin_reconciliation/:identity"},{"method":"GET","path":"/delivery_basic"},{"method":"GET","path":"/delivery_basic/:identity"},{"method":"GET","path":"/delivery_account"},{"method":"GET","path":"/delivery_account/:identity"},{"method":"GET","path":"/dashboard/overview"},{"method":"GET","path":"/staff_account"},{"method":"GET","path":"/staff_account/:identity"},{"method":"GET","path":"/staff_credential"},{"method":"GET","path":"/staff_credential/:identity"},{"method":"GET","path":"/cms_content"},{"method":"GET","path":"/cms_content/:identity"},{"method":"GET","path":"/cs_ticket"},{"method":"GET","path":"/cs_ticket/:identity"},{"method":"GET","path":"/auth/profile"},{"method":"POST","path":"/gasorder_basic"},{"method":"POST","path":"/gasorder_basic/:identity/assign"},{"method":"POST","path":"/gasorder_basic/:identity/awaiting-confirmation"},{"method":"POST","path":"/gasorder_basic/:identity/ready"},{"method":"POST","path":"/gasorder_basic/:identity/recover"},{"method":"POST","path":"/gasorder_basic/:identity/complete"},{"method":"POST","path":"/gasorder_basic/:identity/cancel"},{"method":"POST","path":"/gasorder_basic/:identity/filling"},{"method":"POST","path":"/gasorder_basic/:identity/delivering"},{"method":"POST","path":"/gasorder_basic/:identity/exception"},{"method":"POST","path":"/gasorder_contract"},{"method":"POST","path":"/gasorder_contract/:identity/activate"},{"method":"POST","path":"/gasorder_contract/:identity/renew"},{"method":"POST","path":"/gasorder_contract/:identity/terminate"},{"method":"POST","path":"/gasorder_contract_product"},{"method":"POST","path":"/gasorder_contract_product/:identity/unbind"},{"method":"POST","path":"/gas_basic"},{"method":"POST","path":"/gas_account"},{"method":"POST","path":"/ec_product"},{"method":"POST","path":"/ec_product_attribute"},{"method":"POST","path":"/ec_product_image"},{"method":"POST","path":"/ec_category"},{"method":"POST","path":"/ec_cart"},{"method":"POST","path":"/ec_order"},{"method":"POST","path":"/ec_order_item"},{"method":"POST","path":"/ec_review"},{"method":"POST","path":"/product_type"},{"method":"POST","path":"/product_warehouse"},{"method":"POST","path":"/product_info"},{"method":"POST","path":"/product_repair"},{"method":"POST","path":"/platform_account"},{"method":"POST","path":"/platform_role"},{"method":"POST","path":"/user_account"},{"method":"POST","path":"/user_address"},{"method":"POST","path":"/user_service_relation"},{"method":"POST","path":"/wallet_apply_cash/:identity/approve"},{"method":"POST","path":"/wallet_apply_cash/:identity/reject"},{"method":"POST","path":"/wallet_basic/:identity/recharge"},{"method":"POST","path":"/fin_payment"},{"method":"POST","path":"/fin_settlement"},{"method":"POST","path":"/fin_reconciliation"},{"method":"POST","path":"/delivery_basic"},{"method":"POST","path":"/delivery_account"},{"method":"POST","path":"/staff_account"},{"method":"POST","path":"/staff_credential"},{"method":"POST","path":"/cms_content"},{"method":"POST","path":"/cs_ticket"},{"method":"POST","path":"/auth/login"},{"method":"PUT","path":"/ec_product_attribute/:identity"},{"method":"PUT","path":"/ec_product_image/:identity"},{"method":"PUT","path":"/ec_product/:identity"},{"method":"PUT","path":"/ec_category/:identity"},{"method":"PUT","path":"/ec_cart/:identity"},{"method":"PUT","path":"/ec_order/:identity"},{"method":"PUT","path":"/ec_order_item/:identity"},{"method":"PUT","path":"/ec_review/:identity"},{"method":"PUT","path":"/product_type/:identity"},{"method":"PUT","path":"/product_warehouse/:identity"},{"method":"PUT","path":"/product_info/:identity"},{"method":"PUT","path":"/product_repair/:identity"},{"method":"PUT","path":"/platform_role/:identity"},{"method":"PUT","path":"/platform_role/:identity/menu"},{"method":"PUT","path":"/platform_account/:identity"},{"method":"PUT","path":"/gas_basic/:identity"},{"method":"PUT","path":"/gas_account/:identity"},{"method":"PUT","path":"/gasorder_contract/:identity"},{"method":"PUT","path":"/user_account/:identity"},{"method":"PUT","path":"/user_address/:identity"},{"method":"PUT","path":"/user_service_relation/:identity"},{"method":"PUT","path":"/fin_payment/:identity"},{"method":"PUT","path":"/fin_settlement/:identity"},{"method":"PUT","path":"/fin_reconciliation/:identity"},{"method":"PUT","path":"/delivery_basic/:identity"},{"method":"PUT","path":"/delivery_account/:identity"},{"method":"PUT","path":"/staff_account/:identity"},{"method":"PUT","path":"/staff_credential/:identity"},{"method":"PUT","path":"/cms_content/:identity"},{"method":"PUT","path":"/cs_ticket/:identity"},{"method":"PUT","path":"/auth/password"},{"method":"PATCH","path":"/ec_product_attribute/:identity/status"},{"method":"PATCH","path":"/ec_product_image/:identity/status"},{"method":"PATCH","path":"/ec_product/:identity/status"},{"method":"PATCH","path":"/ec_category/:identity/status"},{"method":"PATCH","path":"/ec_cart/:identity/status"},{"method":"PATCH","path":"/ec_order/:identity/status"},{"method":"PATCH","path":"/ec_order_item/:identity/status"},{"method":"PATCH","path":"/ec_review/:identity/status"},{"method":"PATCH","path":"/product_type/:identity/status"},{"method":"PATCH","path":"/product_warehouse/:identity/status"},{"method":"PATCH","path":"/product_info/:identity/status"},{"method":"PATCH","path":"/product_repair/:identity/status"},{"method":"PATCH","path":"/platform_account/:identity/status"},{"method":"PATCH","path":"/platform_role/:identity/status"},{"method":"PATCH","path":"/user_account/:identity/status"},{"method":"PATCH","path":"/user_address/:identity/status"},{"method":"PATCH","path":"/user_service_relation/:identity/status"},{"method":"PATCH","path":"/fin_payment/:identity/status"},{"method":"PATCH","path":"/fin_settlement/:identity/status"},{"method":"PATCH","path":"/fin_reconciliation/:identity/status"},{"method":"PATCH","path":"/gas_basic/:identity/status"},{"method":"PATCH","path":"/gas_account/:identity/status"},{"method":"PATCH","path":"/delivery_basic/:identity/status"},{"method":"PATCH","path":"/delivery_account/:identity/status"},{"method":"PATCH","path":"/staff_account/:identity/status"},{"method":"PATCH","path":"/staff_credential/:identity/status"},{"method":"PATCH","path":"/cms_content/:identity/status"},{"method":"PATCH","path":"/cs_ticket/:identity/status"},{"method":"PATCH","path":"/wallet_basic/:identity/status"},{"method":"DELETE","path":"/ec_product_attribute/:identity"},{"method":"DELETE","path":"/ec_product_image/:identity"},{"method":"DELETE","path":"/ec_product/:identity"},{"method":"DELETE","path":"/ec_category/:identity"},{"method":"DELETE","path":"/ec_cart/:identity"},{"method":"DELETE","path":"/ec_order/:identity"},{"method":"DELETE","path":"/ec_order_item/:identity"},{"method":"DELETE","path":"/ec_review/:identity"},{"method":"DELETE","path":"/user_account/:identity"},{"method":"DELETE","path":"/user_address/:identity"},{"method":"DELETE","path":"/user_service_relation/:identity"},{"method":"DELETE","path":"/fin_payment/:identity"},{"method":"DELETE","path":"/fin_settlement/:identity"},{"method":"DELETE","path":"/fin_reconciliation/:identity"},{"method":"DELETE","path":"/gas_basic/:identity"},{"method":"DELETE","path":"/gas_account/:identity"},{"method":"DELETE","path":"/delivery_basic/:identity"},{"method":"DELETE","path":"/delivery_account/:identity"},{"method":"DELETE","path":"/staff_account/:identity"},{"method":"DELETE","path":"/staff_credential/:identity"},{"method":"DELETE","path":"/cms_content/:identity"},{"method":"DELETE","path":"/cs_ticket/:identity"},{"method":"DELETE","path":"/platform_account/:identity"},{"method":"DELETE","path":"/platform_role/:identity"}]} +{"resources":[{"domain":"gas","name":"gas_basic","path":"/gas_basic","pageKind":"list","mode":"writable"},{"domain":"gas","name":"gas_account","path":"/gas_account","pageKind":"list","mode":"writable"},{"domain":"delivery","name":"delivery_basic","path":"/delivery_basic","pageKind":"list","mode":"writable"},{"domain":"delivery","name":"delivery_account","path":"/delivery_account","pageKind":"list","mode":"writable"},{"domain":"staff","name":"staff_account","path":"/staff_account","pageKind":"list","mode":"writable"},{"domain":"staff","name":"staff_credential","path":"/staff_credential","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_account","path":"/user_account","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_address","path":"/user_address","pageKind":"list","mode":"writable"},{"domain":"user","name":"user_service_relation","path":"/user_service_relation","pageKind":"list","mode":"writable"},{"domain":"product","name":"product_type","path":"/product_type","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_warehouse","path":"/product_warehouse","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_info","path":"/product_info","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_repair","path":"/product_repair","pageKind":"list","mode":"editable"},{"domain":"product","name":"product_owner","path":"/product_owner","pageKind":"list","mode":"readonly"},{"domain":"ec","name":"ec_category","path":"/ec_category","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product","path":"/ec_product","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product_attribute","path":"/ec_product_attribute","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_product_image","path":"/ec_product_image","pageKind":"list","mode":"writable"},{"domain":"ec","name":"ec_cart","path":"/ec_cart","pageKind":"list","mode":"readonly"},{"domain":"ec","name":"ec_order","path":"/ec_order","pageKind":"list","mode":"readonly"},{"domain":"ec","name":"ec_order_item","path":"/ec_order_item","pageKind":"list","mode":"readonly"},{"domain":"ec","name":"ec_review","path":"/ec_review","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_contract","path":"/gasorder_contract","pageKind":"list","mode":"managed"},{"domain":"gasorder","name":"gasorder_contract_product","path":"/gasorder_contract_product","pageKind":"list","mode":"append_only"},{"domain":"gasorder","name":"gasorder_contract_revision","path":"/gasorder_contract_revision","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_basic","path":"/gasorder_basic","pageKind":"list","mode":"append_only"},{"domain":"gasorder","name":"gasorder_item","path":"/gasorder_item","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_assign","path":"/gasorder_assign","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_status","path":"/gasorder_status","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_track","path":"/gasorder_track","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_track_point","path":"/gasorder_track_point","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_confirm","path":"/gasorder_confirm","pageKind":"list","mode":"readonly"},{"domain":"gasorder","name":"gasorder_payment","path":"/gasorder_payment","pageKind":"list","mode":"readonly"},{"domain":"finance","name":"fin_payment","path":"/fin_payment","pageKind":"list","mode":"readonly"},{"domain":"finance","name":"fin_settlement","path":"/fin_settlement","pageKind":"list","mode":"writable"},{"domain":"finance","name":"fin_reconciliation","path":"/fin_reconciliation","pageKind":"list","mode":"readonly"},{"domain":"content","name":"cms_content","path":"/cms_content","pageKind":"list","mode":"writable"},{"domain":"customer_service","name":"cs_ticket","path":"/cs_ticket","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_account","path":"/platform_account","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_role","path":"/platform_role","pageKind":"list","mode":"writable"},{"domain":"platform","name":"platform_menu","path":"/platform_menu","pageKind":"tree","mode":"readonly"},{"domain":"wallet","name":"wallet_basic","path":"/wallet_basic","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_bank","path":"/wallet_bank","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_payment","path":"/wallet_payment","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_record","path":"/wallet_record","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_refund","path":"/wallet_refund","pageKind":"list","mode":"readonly"},{"domain":"wallet","name":"wallet_apply_cash","path":"/wallet_apply_cash","pageKind":"list","mode":"readonly"}],"routes":[{"method":"GET","path":"/gasorder_contract"},{"method":"GET","path":"/gasorder_contract_product"},{"method":"GET","path":"/gasorder_contract_product/:identity"},{"method":"GET","path":"/gasorder_contract_revision"},{"method":"GET","path":"/gasorder_contract_revision/:identity"},{"method":"GET","path":"/gasorder_contract/:identity"},{"method":"GET","path":"/gasorder_confirm"},{"method":"GET","path":"/gasorder_confirm/:identity"},{"method":"GET","path":"/gasorder_track"},{"method":"GET","path":"/gasorder_track_point"},{"method":"GET","path":"/gasorder_track_point/:identity"},{"method":"GET","path":"/gasorder_track/:identity"},{"method":"GET","path":"/gasorder_basic"},{"method":"GET","path":"/gasorder_basic/:identity"},{"method":"GET","path":"/gasorder_item"},{"method":"GET","path":"/gasorder_item/:identity"},{"method":"GET","path":"/gasorder_assign"},{"method":"GET","path":"/gasorder_assign/:identity"},{"method":"GET","path":"/gasorder_status"},{"method":"GET","path":"/gasorder_status/:identity"},{"method":"GET","path":"/gasorder_payment"},{"method":"GET","path":"/gasorder_payment/:identity"},{"method":"GET","path":"/gas_basic"},{"method":"GET","path":"/gas_basic/:identity"},{"method":"GET","path":"/gas_account"},{"method":"GET","path":"/gas_account/:identity"},{"method":"GET","path":"/product_type"},{"method":"GET","path":"/product_type/:identity"},{"method":"GET","path":"/product_warehouse"},{"method":"GET","path":"/product_warehouse/:identity"},{"method":"GET","path":"/product_info"},{"method":"GET","path":"/product_info/:identity"},{"method":"GET","path":"/product_repair"},{"method":"GET","path":"/product_repair/:identity"},{"method":"GET","path":"/product_owner"},{"method":"GET","path":"/product_owner/:identity"},{"method":"GET","path":"/platform_role"},{"method":"GET","path":"/platform_role/:identity"},{"method":"GET","path":"/platform_role/:identity/menu"},{"method":"GET","path":"/platform_account"},{"method":"GET","path":"/platform_account/:identity"},{"method":"GET","path":"/platform_menu"},{"method":"GET","path":"/platform_menu/:identity"},{"method":"GET","path":"/ping/hello"},{"method":"GET","path":"/ec_product"},{"method":"GET","path":"/ec_product_attribute"},{"method":"GET","path":"/ec_product_attribute/:identity"},{"method":"GET","path":"/ec_product_image"},{"method":"GET","path":"/ec_product_image/:identity"},{"method":"GET","path":"/ec_product/:identity"},{"method":"GET","path":"/ec_category"},{"method":"GET","path":"/ec_category/:identity"},{"method":"GET","path":"/ec_cart"},{"method":"GET","path":"/ec_cart/:identity"},{"method":"GET","path":"/ec_order"},{"method":"GET","path":"/ec_order_item"},{"method":"GET","path":"/ec_order_item/:identity"},{"method":"GET","path":"/ec_order/:identity"},{"method":"GET","path":"/ec_review"},{"method":"GET","path":"/ec_review/:identity"},{"method":"GET","path":"/wallet_basic"},{"method":"GET","path":"/wallet_basic/owner/:owner_type/:owner_identity"},{"method":"GET","path":"/wallet_basic/:identity"},{"method":"GET","path":"/wallet_bank"},{"method":"GET","path":"/wallet_bank/:identity"},{"method":"GET","path":"/wallet_record"},{"method":"GET","path":"/wallet_record/:identity"},{"method":"GET","path":"/wallet_refund"},{"method":"GET","path":"/wallet_refund/:identity"},{"method":"GET","path":"/wallet_payment"},{"method":"GET","path":"/wallet_payment/:identity"},{"method":"GET","path":"/wallet_apply_cash"},{"method":"GET","path":"/wallet_apply_cash/:identity"},{"method":"GET","path":"/user_account"},{"method":"GET","path":"/user_account/:identity"},{"method":"GET","path":"/user_address"},{"method":"GET","path":"/user_address/:identity"},{"method":"GET","path":"/user_service_relation"},{"method":"GET","path":"/user_service_relation/:identity"},{"method":"GET","path":"/fin_payment"},{"method":"GET","path":"/fin_payment/:identity"},{"method":"GET","path":"/fin_settlement"},{"method":"GET","path":"/fin_settlement/:identity"},{"method":"GET","path":"/fin_reconciliation"},{"method":"GET","path":"/fin_reconciliation/:identity"},{"method":"GET","path":"/delivery_basic"},{"method":"GET","path":"/delivery_basic/:identity"},{"method":"GET","path":"/delivery_account"},{"method":"GET","path":"/delivery_account/:identity"},{"method":"GET","path":"/dashboard/overview"},{"method":"GET","path":"/staff_account"},{"method":"GET","path":"/staff_account/:identity"},{"method":"GET","path":"/staff_credential"},{"method":"GET","path":"/staff_credential/:identity"},{"method":"GET","path":"/cms_content"},{"method":"GET","path":"/cms_content/:identity"},{"method":"GET","path":"/cs_ticket"},{"method":"GET","path":"/cs_ticket/:identity"},{"method":"GET","path":"/auth/profile"},{"method":"POST","path":"/gasorder_basic"},{"method":"POST","path":"/gasorder_basic/:identity/assign"},{"method":"POST","path":"/gasorder_basic/:identity/awaiting-confirmation"},{"method":"POST","path":"/gasorder_basic/:identity/ready"},{"method":"POST","path":"/gasorder_basic/:identity/recover"},{"method":"POST","path":"/gasorder_basic/:identity/complete"},{"method":"POST","path":"/gasorder_basic/:identity/cancel"},{"method":"POST","path":"/gasorder_basic/:identity/filling"},{"method":"POST","path":"/gasorder_basic/:identity/delivering"},{"method":"POST","path":"/gasorder_basic/:identity/exception"},{"method":"POST","path":"/gasorder_contract"},{"method":"POST","path":"/gasorder_contract/:identity/activate"},{"method":"POST","path":"/gasorder_contract/:identity/renew"},{"method":"POST","path":"/gasorder_contract/:identity/terminate"},{"method":"POST","path":"/gasorder_contract_product"},{"method":"POST","path":"/gasorder_contract_product/:identity/unbind"},{"method":"POST","path":"/gas_basic"},{"method":"POST","path":"/gas_account"},{"method":"POST","path":"/product_type"},{"method":"POST","path":"/product_warehouse"},{"method":"POST","path":"/product_info"},{"method":"POST","path":"/product_repair"},{"method":"POST","path":"/platform_account"},{"method":"POST","path":"/platform_role"},{"method":"POST","path":"/ec_product"},{"method":"POST","path":"/ec_product_attribute"},{"method":"POST","path":"/ec_product_image"},{"method":"POST","path":"/ec_category"},{"method":"POST","path":"/user_account"},{"method":"POST","path":"/user_address"},{"method":"POST","path":"/user_service_relation"},{"method":"POST","path":"/wallet_apply_cash/:identity/approve"},{"method":"POST","path":"/wallet_apply_cash/:identity/reject"},{"method":"POST","path":"/wallet_basic/:identity/recharge"},{"method":"POST","path":"/delivery_basic"},{"method":"POST","path":"/delivery_account"},{"method":"POST","path":"/staff_account"},{"method":"POST","path":"/staff_credential"},{"method":"POST","path":"/cms_content"},{"method":"POST","path":"/cs_ticket"},{"method":"POST","path":"/auth/login"},{"method":"POST","path":"/fin_settlement"},{"method":"PUT","path":"/product_type/:identity"},{"method":"PUT","path":"/product_warehouse/:identity"},{"method":"PUT","path":"/product_info/:identity"},{"method":"PUT","path":"/product_repair/:identity"},{"method":"PUT","path":"/platform_role/:identity"},{"method":"PUT","path":"/platform_role/:identity/menu"},{"method":"PUT","path":"/platform_account/:identity"},{"method":"PUT","path":"/ec_product_attribute/:identity"},{"method":"PUT","path":"/ec_product_image/:identity"},{"method":"PUT","path":"/ec_product/:identity"},{"method":"PUT","path":"/ec_category/:identity"},{"method":"PUT","path":"/gas_basic/:identity"},{"method":"PUT","path":"/gas_account/:identity"},{"method":"PUT","path":"/gasorder_contract/:identity"},{"method":"PUT","path":"/user_account/:identity"},{"method":"PUT","path":"/user_address/:identity"},{"method":"PUT","path":"/user_service_relation/:identity"},{"method":"PUT","path":"/delivery_basic/:identity"},{"method":"PUT","path":"/delivery_account/:identity"},{"method":"PUT","path":"/staff_account/:identity"},{"method":"PUT","path":"/staff_credential/:identity"},{"method":"PUT","path":"/cms_content/:identity"},{"method":"PUT","path":"/cs_ticket/:identity"},{"method":"PUT","path":"/auth/password"},{"method":"PUT","path":"/fin_settlement/:identity"},{"method":"PATCH","path":"/product_info/:identity/status"},{"method":"PATCH","path":"/product_info/:identity/lifecycle"},{"method":"PATCH","path":"/product_type/:identity/status"},{"method":"PATCH","path":"/product_warehouse/:identity/status"},{"method":"PATCH","path":"/product_repair/:identity/status"},{"method":"PATCH","path":"/platform_account/:identity/status"},{"method":"PATCH","path":"/platform_role/:identity/status"},{"method":"PATCH","path":"/ec_product_attribute/:identity/status"},{"method":"PATCH","path":"/ec_product_image/:identity/status"},{"method":"PATCH","path":"/ec_product/:identity/status"},{"method":"PATCH","path":"/ec_category/:identity/status"},{"method":"PATCH","path":"/user_account/:identity/status"},{"method":"PATCH","path":"/user_address/:identity/status"},{"method":"PATCH","path":"/user_service_relation/:identity/status"},{"method":"PATCH","path":"/gas_basic/:identity/status"},{"method":"PATCH","path":"/gas_account/:identity/status"},{"method":"PATCH","path":"/delivery_basic/:identity/status"},{"method":"PATCH","path":"/delivery_account/:identity/status"},{"method":"PATCH","path":"/staff_account/:identity/status"},{"method":"PATCH","path":"/staff_credential/:identity/status"},{"method":"PATCH","path":"/cms_content/:identity/status"},{"method":"PATCH","path":"/cs_ticket/:identity/status"},{"method":"PATCH","path":"/wallet_basic/:identity/status"},{"method":"PATCH","path":"/fin_settlement/:identity/status"},{"method":"DELETE","path":"/ec_product_attribute/:identity"},{"method":"DELETE","path":"/ec_product_image/:identity"},{"method":"DELETE","path":"/ec_product/:identity"},{"method":"DELETE","path":"/ec_category/:identity"},{"method":"DELETE","path":"/user_account/:identity"},{"method":"DELETE","path":"/user_address/:identity"},{"method":"DELETE","path":"/user_service_relation/:identity"},{"method":"DELETE","path":"/gas_basic/:identity"},{"method":"DELETE","path":"/gas_account/:identity"},{"method":"DELETE","path":"/delivery_basic/:identity"},{"method":"DELETE","path":"/delivery_account/:identity"},{"method":"DELETE","path":"/staff_account/:identity"},{"method":"DELETE","path":"/staff_credential/:identity"},{"method":"DELETE","path":"/cms_content/:identity"},{"method":"DELETE","path":"/cs_ticket/:identity"},{"method":"DELETE","path":"/platform_account/:identity"},{"method":"DELETE","path":"/platform_role/:identity"},{"method":"DELETE","path":"/fin_settlement/:identity"}]} diff --git a/frontend/platform_admin/src/router/routes/modules/platform.ts b/frontend/platform_admin/src/router/routes/modules/platform.ts index 8bc0491..72cb529 100644 --- a/frontend/platform_admin/src/router/routes/modules/platform.ts +++ b/frontend/platform_admin/src/router/routes/modules/platform.ts @@ -55,80 +55,80 @@ const routes: AppRouteRecordRaw[] = [ redirect: '/dashboard/overview', meta: { title: '首页', requiresAuth: true, icon: 'icon-dashboard', order: 0, menuCode: 'dashboard' }, children: [ - { path: 'overview', name: 'dashboard-overview', component: () => import('@/views/dashboard/DashboardPage.vue'), meta: { title: '数据概览', requiresAuth: true, menuCode: 'dashboard' } }, - { path: 'reports', name: 'dashboard-reports', component: () => import('@/views/dashboard/ReportPage.vue'), meta: { title: '统计报表', requiresAuth: true, menuCode: 'dashboard' } }, + { path: 'overview', name: 'dashboard-overview', component: () => import('@/views/dashboard/DashboardPage.vue'), meta: { title: '数据概览', requiresAuth: true, menuCode: 'dashboard_overview' } }, + { path: 'reports', name: 'dashboard-reports', component: () => import('@/views/dashboard/ReportPage.vue'), meta: { title: '统计报表', requiresAuth: true, menuCode: 'dashboard_reports' } }, ], }, group('gas', 'gas', '气站管理', 'icon-storage', 10, [ - child('gas', 'gas-basic', 'basic', '气站', '/gas_basic'), - child('gas', 'gas-account', 'account', '气站账户', '/gas_account'), + child('gas', 'gas-basic', 'basic', '气站', '/gas_basic', 'gas_basic'), + child('gas', 'gas-account', 'account', '气站账户', '/gas_account', 'gas_account'), ]), group('delivery', 'delivery', '配送站管理', 'icon-send', 20, [ - child('delivery', 'delivery-basic', 'basic', '配送站', '/delivery_basic'), - child('delivery', 'delivery-account', 'account', '配送站账户', '/delivery_account'), + child('delivery', 'delivery-basic', 'basic', '配送站', '/delivery_basic', 'delivery_basic'), + child('delivery', 'delivery-account', 'account', '配送站账户', '/delivery_account', 'delivery_account'), ]), group('staff', 'staff', '工作人员管理', 'icon-user-group', 30, [ - child('staff', 'staff-account', 'account', '工作人员', '/staff_account'), - child('staff', 'staff-credential', 'credential', '人员资质', '/staff_credential'), + child('staff', 'staff-account', 'account', '工作人员', '/staff_account', 'staff_account'), + child('staff', 'staff-credential', 'credential', '人员资质', '/staff_credential', 'staff_credential'), ]), group('user', 'user', '用户管理', 'icon-user', 40, [ - child('user', 'user-account', 'account', '用户账户', '/user_account'), - child('user', 'user-address', 'address', '用户地址', '/user_address'), - child('user', 'service-relation', 'service-relation', '服务关系', '/user_service_relation'), + child('user', 'user-account', 'account', '用户账户', '/user_account', 'user_account'), + child('user', 'user-address', 'address', '用户地址', '/user_address', 'user_address'), + child('user', 'service-relation', 'service-relation', '服务关系', '/user_service_relation', 'user_service_relation'), ]), group('product', 'product', '产品管理', 'icon-common', 50, [ - child('product', 'product-type', 'type', '产品类型', '/product_type', 'device'), - child('product', 'warehouse', 'warehouse', '库房', '/product_warehouse', 'device'), - child('product', 'product-info', 'info', '产品信息', '/product_info', 'device'), - child('product', 'repair', 'repair', '检修记录', '/product_repair', 'device', true, 'product-info'), - child('product', 'owner', 'owner', '归属记录', '/product_owner', 'device', true, 'product-info'), + child('product', 'product-type', 'type', '产品类型', '/product_type', 'product_type'), + child('product', 'warehouse', 'warehouse', '库房', '/product_warehouse', 'product_warehouse'), + child('product', 'product-info', 'info', '产品信息', '/product_info', 'product_info'), + child('product', 'repair', 'repair', '检修记录', '/product_repair', 'product_info', true, 'product-info'), + child('product', 'owner', 'owner', '归属记录', '/product_owner', 'product_info', true, 'product-info'), ], 'device'), group('gasorder', 'gasorder', '气体配送订单管理', 'icon-list', 60, [ - child('gasorder', 'contracts', 'contracts', '合同管理', '/gasorder_contract', 'delivery'), - child('gasorder', 'contract-products', 'contract-products', '合同气瓶', '/gasorder_contract_product', 'delivery', true, 'gasorder-contracts'), - child('gasorder', 'contract-revisions', 'contract-revisions', '合同修订记录', '/gasorder_contract_revision', 'delivery', true, 'gasorder-contracts'), - child('gasorder', 'orders', 'orders', '配送订单', '/gasorder_basic', 'delivery'), - child('gasorder', 'order-items', 'order-items', '订单明细', '/gasorder_item', 'delivery', true, 'gasorder-orders'), - child('gasorder', 'assignments', 'assignments', '分配记录', '/gasorder_assign', 'delivery', true, 'gasorder-orders'), - child('gasorder', 'statuses', 'statuses', '状态记录', '/gasorder_status', 'delivery', true, 'gasorder-orders'), - child('gasorder', 'tracks', 'tracks', '运行轨迹', '/gasorder_track', 'delivery'), - child('gasorder', 'track-points', 'track-points', '轨迹点', '/gasorder_track_point', 'delivery', true, 'gasorder-tracks'), - child('gasorder', 'confirms', 'confirms', '确认记录', '/gasorder_confirm', 'delivery', true, 'gasorder-orders'), - child('gasorder', 'payments', 'payments', '订单支付记录', '/gasorder_payment', 'delivery', true, 'gasorder-orders'), + child('gasorder', 'contracts', 'contracts', '合同管理', '/gasorder_contract', 'gasorder_contract'), + child('gasorder', 'contract-products', 'contract-products', '合同气瓶', '/gasorder_contract_product', 'gasorder_contract', true, 'gasorder-contracts'), + child('gasorder', 'contract-revisions', 'contract-revisions', '合同修订记录', '/gasorder_contract_revision', 'gasorder_contract', true, 'gasorder-contracts'), + child('gasorder', 'orders', 'orders', '配送订单', '/gasorder_basic', 'gasorder_basic'), + child('gasorder', 'order-items', 'order-items', '订单明细', '/gasorder_item', 'gasorder_basic', true, 'gasorder-orders'), + child('gasorder', 'assignments', 'assignments', '分配记录', '/gasorder_assign', 'gasorder_basic', true, 'gasorder-orders'), + child('gasorder', 'statuses', 'statuses', '状态记录', '/gasorder_status', 'gasorder_basic', true, 'gasorder-orders'), + child('gasorder', 'tracks', 'tracks', '运行轨迹', '/gasorder_track', 'gasorder_track'), + child('gasorder', 'track-points', 'track-points', '轨迹点', '/gasorder_track_point', 'gasorder_track', true, 'gasorder-tracks'), + child('gasorder', 'confirms', 'confirms', '确认记录', '/gasorder_confirm', 'gasorder_basic', true, 'gasorder-orders'), + child('gasorder', 'payments', 'payments', '订单支付记录', '/gasorder_payment', 'gasorder_basic', true, 'gasorder-orders'), ], 'delivery'), group('ec', 'ec', '商城管理', 'icon-gift', 70, [ - child('ec', 'categories', 'categories', '商品分类', '/ec_category'), - child('ec', 'products', 'products', '商品', '/ec_product'), - child('ec', 'attributes', 'attributes', '商品属性', '/ec_product_attribute', 'ec', true, 'ec-products'), - child('ec', 'images', 'images', '商品图片', '/ec_product_image', 'ec', true, 'ec-products'), - child('ec', 'carts', 'carts', '购物车', '/ec_cart'), - child('ec', 'orders', 'orders', '商城订单', '/ec_order'), - child('ec', 'order-items', 'order-items', '订单明细', '/ec_order_item', 'ec', true, 'ec-orders'), - child('ec', 'reviews', 'reviews', '商品评价', '/ec_review'), + child('ec', 'categories', 'categories', '商品分类', '/ec_category', 'ec_category'), + child('ec', 'products', 'products', '商品', '/ec_product', 'ec_product'), + child('ec', 'attributes', 'attributes', '商品属性', '/ec_product_attribute', 'ec_product', true, 'ec-products'), + child('ec', 'images', 'images', '商品图片', '/ec_product_image', 'ec_product', true, 'ec-products'), + child('ec', 'carts', 'carts', '购物车', '/ec_cart', 'ec_cart'), + child('ec', 'orders', 'orders', '商城订单', '/ec_order', 'ec_order'), + child('ec', 'order-items', 'order-items', '订单明细', '/ec_order_item', 'ec_order', true, 'ec-orders'), + child('ec', 'reviews', 'reviews', '商品评价', '/ec_review', 'ec_review'), ]), group('wallet', 'wallet', '钱包管理', 'icon-safe', 80, [ - child('wallet', 'wallet-basic', 'basic', '钱包', '/wallet_basic'), - child('wallet', 'banks', 'banks', '银行卡', '/wallet_bank'), - child('wallet', 'payments', 'payments', '支付记录', '/wallet_payment'), - child('wallet', 'records', 'records', '钱包流水', '/wallet_record'), - child('wallet', 'refunds', 'refunds', '退款记录', '/wallet_refund'), - child('wallet', 'apply-cash', 'apply-cash', '提现申请', '/wallet_apply_cash'), + child('wallet', 'wallet-basic', 'basic', '钱包', '/wallet_basic', 'wallet_basic'), + child('wallet', 'banks', 'banks', '银行卡', '/wallet_bank', 'wallet_bank'), + child('wallet', 'payments', 'payments', '支付记录', '/wallet_payment', 'wallet_payment'), + child('wallet', 'records', 'records', '钱包流水', '/wallet_record', 'wallet_record'), + child('wallet', 'refunds', 'refunds', '退款记录', '/wallet_refund', 'wallet_refund'), + child('wallet', 'apply-cash', 'apply-cash', '提现申请', '/wallet_apply_cash', 'wallet_apply_cash'), ]), group('finance', 'finance', '财务管理', 'icon-bar-chart', 90, [ - child('finance', 'payments', 'payments', '支付记录', '/fin_payment'), - child('finance', 'settlements', 'settlements', '财务结算', '/fin_settlement'), - child('finance', 'reconciliations', 'reconciliations', '财务对账', '/fin_reconciliation'), + child('finance', 'payments', 'payments', '支付记录', '/fin_payment', 'fin_payment'), + child('finance', 'settlements', 'settlements', '财务结算', '/fin_settlement', 'fin_settlement'), + child('finance', 'reconciliations', 'reconciliations', '财务对账', '/fin_reconciliation', 'fin_reconciliation'), ]), group('content', 'content', '内容管理', 'icon-file', 100, [ - child('content', 'contents', 'contents', '内容', '/cms_content'), + child('content', 'contents', 'contents', '内容', '/cms_content', 'cms_content'), ]), group('customer-service', 'customer_service', '客服管理', 'icon-customer-service', 110, [ - child('customer_service', 'tickets', 'tickets', '客服工单', '/cs_ticket'), + child('customer_service', 'tickets', 'tickets', '客服工单', '/cs_ticket', 'cs_ticket'), ]), group('platform', 'platform', '平台管理', 'icon-settings', 120, [ - child('platform', 'accounts', 'accounts', '平台账户', '/platform_account'), - child('platform', 'roles', 'roles', '平台角色', '/platform_role'), - child('platform', 'menus', 'menus', '平台菜单', '/platform_menu'), + child('platform', 'accounts', 'accounts', '平台账户', '/platform_account', 'platform_account'), + child('platform', 'roles', 'roles', '平台角色', '/platform_role', 'platform_role'), + child('platform', 'menus', 'menus', '平台菜单', '/platform_menu', 'platform_menu'), ]), ]; diff --git a/frontend/platform_admin/src/views/shared/TreePage.vue b/frontend/platform_admin/src/views/shared/TreePage.vue index 5436e70..86a973d 100644 --- a/frontend/platform_admin/src/views/shared/TreePage.vue +++ b/frontend/platform_admin/src/views/shared/TreePage.vue @@ -24,7 +24,7 @@ - {{ option.name ?? option.menu_code ?? option.identity }} + {{ option.name ?? option.group_code ?? option.identity }}