diff --git a/backend/api/internal/logic/common/base.go b/backend/api/internal/logic/common/base.go index ea209ad..a3af534 100644 --- a/backend/api/internal/logic/common/base.go +++ b/backend/api/internal/logic/common/base.go @@ -74,11 +74,19 @@ func ActiveRecords(query *gorm.DB) *gorm.DB { } func ListPage[T any](ctx *gin.Context) { + ListPageFiltered[T](ctx, nil) +} + +// ListPageFiltered applies a resource-specific exact filter before pagination. +func ListPageFiltered[T any](ctx *gin.Context, filter func(*gorm.DB) *gorm.DB) { page, size := PageSize(ctx) var list []T var total int64 model := new(T) databaseQuery := ApplyKeywordFilter(ctx, ActiveRecords(impl.DBService.Model(model)), model) + if filter != nil { + databaseQuery = filter(databaseQuery) + } if err := databaseQuery.Count(&total).Error; err != nil { infra.Response.Error(ctx, err) return @@ -172,7 +180,7 @@ func gormColumn(tag string) string { func GetByIdentity[T any](ctx *gin.Context) { var data T - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&data).Error; err != nil { + if err := ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&data).Error; err != nil { RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/common/resource.go b/backend/api/internal/logic/common/resource.go index 22aca68..8f198c4 100644 --- a/backend/api/internal/logic/common/resource.go +++ b/backend/api/internal/logic/common/resource.go @@ -56,7 +56,7 @@ func ListResource(ctx *gin.Context, model any) { func GetResource(ctx *gin.Context, model any) { data := reflect.New(reflect.TypeOf(model).Elem()) - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(data.Interface()).Error; err != nil { + if err := ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(data.Interface()).Error; err != nil { RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/delivery/account.go b/backend/api/internal/logic/platform/delivery/account.go index 8ef70f4..ab39575 100644 --- a/backend/api/internal/logic/platform/delivery/account.go +++ b/backend/api/internal/logic/platform/delivery/account.go @@ -1,12 +1,15 @@ package delivery import ( + "strings" + "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" + "gorm.io/gorm" ) type accountRequest struct { @@ -23,8 +26,23 @@ type accountUpdateRequest struct { DeliveryBasicIdentity string `json:"delivery_basic_identity"` } -func ListDeliveryAccount(ctx *gin.Context) { common.ListPage[models.DeliveryAccount](ctx) } -func GetDeliveryAccount(ctx *gin.Context) { common.GetByIdentity[models.DeliveryAccount](ctx) } +func ListDeliveryAccount(ctx *gin.Context) { + identities := strings.Split(strings.TrimSpace(ctx.Query("delivery_basic_identities")), ",") + if len(identities) == 1 && identities[0] == "" { + identities = nil + } + if len(identities) > 100 { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.ListPageFiltered[models.DeliveryAccount](ctx, func(query *gorm.DB) *gorm.DB { + if len(identities) == 0 { + return query + } + return query.Where("delivery_basic_id IN (SELECT id FROM delivery_basic WHERE identity IN ? AND status <> ?)", identities, common.StatusArchived) + }) +} +func GetDeliveryAccount(ctx *gin.Context) { common.GetByIdentity[models.DeliveryAccount](ctx) } func CreateDeliveryAccount(ctx *gin.Context) { var request accountRequest diff --git a/backend/api/internal/logic/platform/ec/ec.go b/backend/api/internal/logic/platform/ec/ec.go index b358677..5371bf3 100644 --- a/backend/api/internal/logic/platform/ec/ec.go +++ b/backend/api/internal/logic/platform/ec/ec.go @@ -76,7 +76,7 @@ func ListEcCategory(ctx *gin.Context) { func GetEcCategory(ctx *gin.Context) { var category models.EcCategory - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&category).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&category).Error; err != nil { common.RespondRecordError(ctx, err) return } @@ -114,7 +114,7 @@ func UpdateEcCategory(ctx *gin.Context) { return } var category models.EcCategory - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&category).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&category).Error; err != nil { common.RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/ec/order.go b/backend/api/internal/logic/platform/ec/order.go index 4935e1f..0d3bddc 100644 --- a/backend/api/internal/logic/platform/ec/order.go +++ b/backend/api/internal/logic/platform/ec/order.go @@ -11,7 +11,7 @@ import ( // GetEcOrder returns the order together with its immutable item snapshots. func GetEcOrder(ctx *gin.Context) { var order models.EcOrder - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { common.RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/gas/account.go b/backend/api/internal/logic/platform/gas/account.go index 552f145..3cabae1 100644 --- a/backend/api/internal/logic/platform/gas/account.go +++ b/backend/api/internal/logic/platform/gas/account.go @@ -1,12 +1,15 @@ package gas import ( + "strings" + "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" + "gorm.io/gorm" ) type accountRequest struct { @@ -25,8 +28,20 @@ type accountUpdateRequest struct { DeliveryBasicIdentity string `json:"delivery_basic_identity"` } -func ListGasAccount(ctx *gin.Context) { common.ListPage[models.GasAccount](ctx) } -func GetGasAccount(ctx *gin.Context) { common.GetByIdentity[models.GasAccount](ctx) } +func ListGasAccount(ctx *gin.Context) { + identities := splitOwnerIdentities(ctx.Query("gas_basic_identities")) + if len(identities) > 100 { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + common.ListPageFiltered[models.GasAccount](ctx, func(query *gorm.DB) *gorm.DB { + if len(identities) == 0 { + return query + } + return query.Where("gas_basic_id IN (SELECT id FROM gas_basic WHERE identity IN ? AND status <> ?)", identities, common.StatusArchived) + }) +} +func GetGasAccount(ctx *gin.Context) { common.GetByIdentity[models.GasAccount](ctx) } func CreateGasAccount(ctx *gin.Context) { var request accountRequest @@ -65,3 +80,11 @@ func UpdateGasAccount(ctx *gin.Context) { } common.UpdateAllowedByIdentity(ctx, &models.GasAccount{}, gin.H{"gas_basic_id": gasBasicID, "display_name": request.DisplayName, "role_code": request.RoleCode}, []string{"gas_basic_id", "display_name", "role_code"}) } + +func splitOwnerIdentities(value string) []string { + parts := strings.Split(strings.TrimSpace(value), ",") + if len(parts) == 1 && parts[0] == "" { + return nil + } + return parts +} diff --git a/backend/api/internal/logic/platform/gasorder/gasorder.go b/backend/api/internal/logic/platform/gasorder/gasorder.go index daff882..0c47235 100644 --- a/backend/api/internal/logic/platform/gasorder/gasorder.go +++ b/backend/api/internal/logic/platform/gasorder/gasorder.go @@ -2,6 +2,7 @@ package gasorder import ( "errors" + "math" "strings" "time" @@ -53,7 +54,7 @@ func GetGasorderPayment(ctx *gin.Context) { common.GetResource(ctx, &models. func getGasorderContract(ctx *gin.Context) { var contract models.GasorderContract - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&contract).Error; err != nil { common.RespondRecordError(ctx, err) return } @@ -77,7 +78,7 @@ func getGasorderContract(ctx *gin.Context) { func getGasorderBasic(ctx *gin.Context) { var order models.GasorderBasic - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { common.RespondRecordError(ctx, err) return } @@ -422,12 +423,19 @@ func CreateGasorderBasic(ctx *gin.Context) { product.Status != common.StatusEnable || product.ProductStatus == common.StatusScrapped || product.UserAccountID != contract.UserAccountID { return errors.New("contract product is no longer eligible") } + if binding.UnitPrice <= 0 || productAmount > math.MaxInt64-binding.UnitPrice { + return errors.New("product amount overflow") + } productAmount += binding.UnitPrice } - payable := productAmount + contract.DefaultDeliveryFee - request.DiscountAmount - if payable <= 0 { + if contract.DefaultDeliveryFee < 0 || productAmount > math.MaxInt64-contract.DefaultDeliveryFee { + return errors.New("order amount overflow") + } + subtotal := productAmount + contract.DefaultDeliveryFee + if request.DiscountAmount >= subtotal { return errors.New("invalid payable amount") } + payable := subtotal - request.DiscountAmount order = models.GasorderBasic{ Entity: models.Entity{Identity: models.NewIdentity(), Status: common.StatusEnable}, OrderStatus: common.StatusCreated, OrderNo: models.NewIdentity(), RequestNo: request.RequestNo, GasorderContractID: contract.ID, @@ -477,19 +485,20 @@ func AssignGasorderBasic(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - var delivery models.DeliveryBasic - if err := impl.DBService.Where("identity = ?", request.DeliveryIdentity).First(&delivery).Error; err != nil || delivery.Status != common.StatusEnable { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } - var staff models.StaffAccount - if err := impl.DBService.Where("identity = ?", request.StaffIdentity).First(&staff).Error; err != nil || - staff.Status != common.StatusEnable || staff.WorkStatus == "off_duty" { - infra.Response.Error(ctx, errcode.ErrInvalidArgument) - return - } operatorIdentity, operatorName := common.PlatformOperator(ctx) err := impl.DBService.Transaction(func(tx *gorm.DB) error { + var delivery models.DeliveryBasic + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("identity = ? AND status = ?", request.DeliveryIdentity, common.StatusEnable). + First(&delivery).Error; err != nil { + return err + } + var staff models.StaffAccount + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("identity = ? AND status = ? AND work_status = ?", request.StaffIdentity, common.StatusEnable, "on_duty"). + First(&staff).Error; err != nil { + return err + } var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil { return err @@ -743,7 +752,7 @@ func gasorderStatusRecord(orderID uint64, from, to int, reason, operatorIdentity func getGasorderTrack(ctx *gin.Context) { var track models.GasorderTrack - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&track).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&track).Error; err != nil { common.RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/platform/access.go b/backend/api/internal/logic/platform/platform/access.go index b7a94e6..6b38f1d 100644 --- a/backend/api/internal/logic/platform/platform/access.go +++ b/backend/api/internal/logic/platform/platform/access.go @@ -1,12 +1,19 @@ package platform import ( + "bytes" + "encoding/json" + "io" + "strings" + "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" + "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" platformbase "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform" + "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" - "strings" ) const platformMenusContextKey = "platform_authorized_menus" @@ -35,6 +42,10 @@ func platformMenuAllowsRequest(menus []platformbase.Menu, requestPath, method st (menuIdentity == "gasorder_contract" || resource == "user_address") { return true } + if method == "GET" && menu.Identity == "gasorder_basic" && + (resource == "delivery_basic" || resource == "staff_account") { + return true + } if method == "GET" && relative == "wallet_basic" && (menu.Identity == "gas_basic" || menu.Identity == "delivery_basic" || menu.Identity == "staff" || menu.Identity == "user_account" || @@ -87,12 +98,21 @@ func RequirePlatformMenuAccess() gin.HandlerFunc { ctx.Abort() return } + var account models.PlatformAccount + if err := impl.DBService.Select("id", "platform_role_code"). + Where("identity = ? AND platform_role_code = ? AND status = ?", claims.Identity, claims.Role, common.StatusEnable). + First(&account).Error; err != nil { + infra.Response.Error(ctx, errcode.ErrPermissionDenied) + ctx.Abort() + return + } if claims.Role == "root" { ctx.Next() return } menus, err := platformbase.LoadPlatformMenus(claims.Role) - if err != nil || !platformMenuAllowsRequest(menus, ctx.Request.URL.Path, ctx.Request.Method) { + if err != nil || !platformMenuAllowsRequest(menus, ctx.Request.URL.Path, ctx.Request.Method) || + !platformScopedRequestAllowed(ctx, menus) { infra.Response.Error(ctx, errcode.ErrPermissionDenied) ctx.Abort() return @@ -101,3 +121,104 @@ func RequirePlatformMenuAccess() gin.HandlerFunc { ctx.Next() } } + +func platformScopedRequestAllowed(ctx *gin.Context, menus []platformbase.Menu) bool { + relative := strings.Trim(strings.SplitN(ctx.Request.URL.Path, "/platform/v1/", 2)[1], "/") + parts := strings.Split(relative, "/") + resource := parts[0] + if resource == "wallet_basic" && len(parts) == 1 && ctx.Request.Method == "GET" { + if hasMenuIdentity(menus, "wallet_apply_cash") { + return true + } + required := map[string]string{ + "gas": "gas_basic", "delivery": "delivery_basic", "staff": "staff", "user": "user_account", + }[ctx.Query("owner_type")] + return required != "" && hasMenuIdentity(menus, required) + } + if resource == "staff_credential" { + return staffCredentialRequestAllowed(ctx, menus, parts) + } + if resource != "staff_account" { + return true + } + if len(parts) == 1 { + if ctx.Request.Method == "POST" { + return hasMenuIdentity(menus, "staff_add") + } + if ctx.Request.Method == "GET" { + required := staffMenuIdentity(ctx.Query("role_code")) + return required != "" && (hasMenuIdentity(menus, required) || + (required == "staff_delivery" && hasMenuIdentity(menus, "gasorder_basic"))) + } + return false + } + var staff models.StaffAccount + if err := common.ActiveRecords(impl.DBService).Select("role_code"). + Where("identity = ?", parts[1]).First(&staff).Error; err != nil { + return false + } + return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode)) +} + +func staffCredentialRequestAllowed(ctx *gin.Context, menus []platformbase.Menu, parts []string) bool { + var staffIdentity string + if len(parts) == 1 && ctx.Request.Method == "GET" { + staffIdentity = ctx.Query("staff_account_identity") + } else if (ctx.Request.Method == "POST" || ctx.Request.Method == "PUT") && ctx.Request.Body != nil { + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + return false + } + ctx.Request.Body = io.NopCloser(bytes.NewReader(body)) + var payload struct { + StaffAccountIdentity string `json:"staff_account_identity"` + } + if json.Unmarshal(body, &payload) != nil { + return false + } + staffIdentity = payload.StaffAccountIdentity + } else if len(parts) > 1 { + var credential models.StaffCredential + if err := common.ActiveRecords(impl.DBService).Select("staff_account_id"). + Where("identity = ?", parts[1]).First(&credential).Error; err != nil { + return false + } + var staff models.StaffAccount + if err := common.ActiveRecords(impl.DBService).Select("role_code"). + Where("id = ?", credential.StaffAccountID).First(&staff).Error; err != nil { + return false + } + return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode)) + } + if staffIdentity == "" { + return false + } + var staff models.StaffAccount + if err := common.ActiveRecords(impl.DBService).Select("role_code"). + Where("identity = ?", staffIdentity).First(&staff).Error; err != nil { + return false + } + return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode)) +} + +func staffMenuIdentity(roleCode string) string { + switch roleCode { + case "installer": + return "staff_installer" + case "delivery": + return "staff_delivery" + case "operations": + return "staff_operations" + default: + return "" + } +} + +func hasMenuIdentity(menus []platformbase.Menu, identity string) bool { + for _, menu := range menus { + if menu.Identity == identity { + return true + } + } + return false +} diff --git a/backend/api/internal/logic/platform/platform/account.go b/backend/api/internal/logic/platform/platform/account.go index 3f30ea7..174328f 100644 --- a/backend/api/internal/logic/platform/platform/account.go +++ b/backend/api/internal/logic/platform/platform/account.go @@ -53,7 +53,7 @@ func platformAccountView(account models.PlatformAccount) map[string]any { func GetPlatformAccount(ctx *gin.Context) { var account models.PlatformAccount - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil { common.RespondRecordError(ctx, err) return } @@ -127,6 +127,9 @@ func UpdatePlatformAccountStatus(ctx *gin.Context) { if !common.RequirePlatformRoot(ctx) { return } + if !modifiablePlatformAccount(ctx) { + return + } common.UpdateRecordStatus(ctx, &models.PlatformAccount{}) } @@ -135,9 +138,26 @@ func ArchivePlatformAccount(ctx *gin.Context) { if !common.RequirePlatformRoot(ctx) { return } + if !modifiablePlatformAccount(ctx) { + return + } common.ArchiveRecord(ctx, &models.PlatformAccount{}) } +func modifiablePlatformAccount(ctx *gin.Context) bool { + var account models.PlatformAccount + if err := common.ActiveRecords(impl.DBService).Select("platform_role_code"). + Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil { + common.RespondRecordError(ctx, err) + return false + } + if account.PlatformRoleCode == "root" { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return false + } + return true +} + func platformPasswordHash(password string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(hash), err diff --git a/backend/api/internal/logic/platform/platform/role.go b/backend/api/internal/logic/platform/platform/role.go index 9f29e16..0f88b0e 100644 --- a/backend/api/internal/logic/platform/platform/role.go +++ b/backend/api/internal/logic/platform/platform/role.go @@ -55,7 +55,7 @@ func UpdatePlatformRole(ctx *gin.Context) { return } var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { common.RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/platform/role_menu.go b/backend/api/internal/logic/platform/platform/role_menu.go index 217739e..ab8505b 100644 --- a/backend/api/internal/logic/platform/platform/role_menu.go +++ b/backend/api/internal/logic/platform/platform/role_menu.go @@ -30,7 +30,7 @@ func ReplacePlatformRoleMenus(ctx *gin.Context) { } if err := impl.DBService.Transaction(func(transaction *gorm.DB) error { var role models.PlatformRole - if err := transaction.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + if err := common.ActiveRecords(transaction).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { return err } if role.IsSystem { @@ -77,7 +77,7 @@ func ListPlatformRoleMenuIdentities(ctx *gin.Context) { return } var role models.PlatformRole - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil { common.RespondRecordError(ctx, err) return } diff --git a/backend/api/internal/logic/platform/product/product.go b/backend/api/internal/logic/platform/product/product.go index a665aba..27650a6 100644 --- a/backend/api/internal/logic/platform/product/product.go +++ b/backend/api/internal/logic/platform/product/product.go @@ -149,11 +149,45 @@ func UpdateProductInfoLifecycle(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } - values := gin.H{"product_status": request.ProductStatus} - if request.ProductStatus == common.StatusScrapped { - values["status"] = common.StatusDisable + err := impl.DBService.Transaction(func(tx *gorm.DB) error { + var product models.ProductInfo + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("identity = ? AND status <> ?", ctx.Param("identity"), common.StatusArchived). + First(&product).Error; err != nil { + return err + } + var pendingRepairs int64 + if err := tx.Model(&models.ProductRepair{}). + Where("product_info_id = ? AND result = ? AND status <> ?", product.ID, "pending", common.StatusArchived). + Count(&pendingRepairs).Error; err != nil { + return err + } + if pendingRepairs > 0 && request.ProductStatus != common.StatusRepairing || + pendingRepairs == 0 && request.ProductStatus == common.StatusRepairing { + return errors.New("product lifecycle conflicts with repair state") + } + if request.ProductStatus == common.StatusScrapped { + var activeOrders int64 + if err := tx.Model(&models.GasorderItem{}). + Where("product_info_id = ? AND active = ?", product.ID, true). + Count(&activeOrders).Error; err != nil { + return err + } + if activeOrders != 0 { + return errors.New("product is occupied by an active order") + } + } + values := map[string]any{"product_status": request.ProductStatus} + if request.ProductStatus == common.StatusScrapped { + values["status"] = common.StatusDisable + } + return tx.Model(&product).Updates(values).Error + }) + if err != nil { + common.RespondRecordError(ctx, err) + return } - common.UpdateAllowedByIdentity(ctx, &models.ProductInfo{}, values, []string{"product_status", "status"}) + infra.Response.Success(ctx, gin.H{"updated": true}) } func UpdateProductInfoRecordStatus(ctx *gin.Context) { @@ -202,9 +236,17 @@ func createProductRepair(ctx *gin.Context, fields []string, relations []common.R } err = impl.DBService.Transaction(func(tx *gorm.DB) error { var product models.ProductInfo - if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&product, data.ProductInfoID).Error; err != nil { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("id = ? AND status = ? AND product_status <> ?", data.ProductInfoID, common.StatusEnable, common.StatusScrapped). + First(&product).Error; err != nil { return err } + var activeOrders int64 + if err := tx.Model(&models.GasorderItem{}). + Where("product_info_id = ? AND active = ?", data.ProductInfoID, true). + Count(&activeOrders).Error; err != nil || activeOrders != 0 { + return errors.New("product is occupied by an active order") + } var pending int64 if err := tx.Model(&models.ProductRepair{}).Where("product_info_id = ? AND result = ?", data.ProductInfoID, "pending").Count(&pending).Error; err != nil || !canStartProductRepair(pending) { return errors.New("product already has a pending repair") @@ -234,7 +276,9 @@ func updateProductRepair(ctx *gin.Context, fields []string, relations []common.R } err = impl.DBService.Transaction(func(tx *gorm.DB) error { var current models.ProductRepair - if err := tx.Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("identity = ? AND status <> ?", ctx.Param("identity"), common.StatusArchived). + First(¤t).Error; err != nil { return err } if current.Result != "pending" { diff --git a/backend/api/internal/logic/platform/staff/credential.go b/backend/api/internal/logic/platform/staff/credential.go index 8a6e2d0..3ae8ded 100644 --- a/backend/api/internal/logic/platform/staff/credential.go +++ b/backend/api/internal/logic/platform/staff/credential.go @@ -18,8 +18,40 @@ type staffCredentialRequest struct { ExpiredAt *time.Time `json:"expired_at"` } -func ListStaffCredential(ctx *gin.Context) { common.ListPage[models.StaffCredential](ctx) } -func GetStaffCredential(ctx *gin.Context) { common.GetByIdentity[models.StaffCredential](ctx) } +func ListStaffCredential(ctx *gin.Context) { + staffIdentity := ctx.Query("staff_account_identity") + if staffIdentity == "" { + common.ListPage[models.StaffCredential](ctx) + return + } + staffID, err := common.ResolveIdentityID(&models.StaffAccount{}, staffIdentity, true) + if err != nil { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + page, size := common.PageSize(ctx) + var list []models.StaffCredential + var total int64 + query := common.ApplyKeywordFilter(ctx, + common.ActiveRecords(impl.DBService.Model(&models.StaffCredential{})). + Where("staff_account_id = ?", staffID), + &models.StaffCredential{}) + if err := query.Count(&total).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + response, err := common.PublicResourceResponse(list) + if err != nil { + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"total": total, "list": response}) +} +func GetStaffCredential(ctx *gin.Context) { common.GetByIdentity[models.StaffCredential](ctx) } func CreateStaffCredential(ctx *gin.Context) { var request staffCredentialRequest if err := ctx.ShouldBindJSON(&request); err != nil { @@ -49,5 +81,15 @@ func UpdateStaffCredential(ctx *gin.Context) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } + var current models.StaffCredential + if err := common.ActiveRecords(impl.DBService).Select("staff_account_id"). + Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + if current.StaffAccountID != staffAccountID { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } common.UpdateAllowedByIdentity(ctx, &models.StaffCredential{}, gin.H{"staff_account_id": staffAccountID, "credential_type": request.CredentialType, "credential_no": request.CredentialNo, "expired_at": request.ExpiredAt}, []string{"staff_account_id", "credential_type", "credential_no", "expired_at"}) } diff --git a/backend/api/internal/logic/platform/staff/staff.go b/backend/api/internal/logic/platform/staff/staff.go index 7cb463b..b3b0ad7 100644 --- a/backend/api/internal/logic/platform/staff/staff.go +++ b/backend/api/internal/logic/platform/staff/staff.go @@ -1,6 +1,8 @@ package staff import ( + "strings" + "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/heqiapp/platforms/backend/api/internal/impl" @@ -10,7 +12,37 @@ import ( ) // ListStaff 查询服务人员分页列表。 -func ListStaff(ctx *gin.Context) { common.ListPage[models.StaffAccount](ctx) } +func ListStaff(ctx *gin.Context) { + roleCode := strings.TrimSpace(ctx.Query("role_code")) + if roleCode == "" { + common.ListPage[models.StaffAccount](ctx) + return + } + if !validStaffRole(roleCode) { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + page, size := common.PageSize(ctx) + var list []models.StaffAccount + var total int64 + query := common.ApplyKeywordFilter(ctx, + common.ActiveRecords(impl.DBService.Model(&models.StaffAccount{})).Where("role_code = ?", roleCode), + &models.StaffAccount{}) + if err := query.Count(&total).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + if err := query.Order("created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil { + infra.Response.Error(ctx, err) + return + } + response, err := common.PublicResourceResponse(list) + if err != nil { + infra.Response.Error(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"total": total, "list": common.ProtectPreciseLocation(ctx, &models.StaffAccount{}, response)}) +} // GetStaff 查询一个服务人员档案。 func GetStaff(ctx *gin.Context) { common.GetByIdentity[models.StaffAccount](ctx) } @@ -28,7 +60,7 @@ func CreateStaff(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 || !validStaffRole(request.RoleCode) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } @@ -77,7 +109,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 || !validWorkStatus(request.WorkStatus) { + if err := ctx.ShouldBindJSON(&request); err != nil || !validWorkStatus(request.WorkStatus) || !validStaffRole(request.RoleCode) { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } @@ -99,3 +131,6 @@ func UpdateStaff(ctx *gin.Context) { } func validWorkStatus(status string) bool { return status == "on_duty" || status == "off_duty" } +func validStaffRole(role string) bool { + return role == "installer" || role == "delivery" || role == "operations" +} diff --git a/backend/api/internal/logic/platform/staff/staff_test.go b/backend/api/internal/logic/platform/staff/staff_test.go index 230b8b7..ecb6bf3 100644 --- a/backend/api/internal/logic/platform/staff/staff_test.go +++ b/backend/api/internal/logic/platform/staff/staff_test.go @@ -10,3 +10,16 @@ func TestWorkStatusIsClosedEnumeration(t *testing.T) { t.Fatal("unknown work status was accepted as available") } } + +func TestStaffRoleIsClosedEnumeration(t *testing.T) { + for _, role := range []string{"installer", "delivery", "operations"} { + if !validStaffRole(role) { + t.Fatalf("supported staff role %q was rejected", role) + } + } + for _, role := range []string{"", "admin", "root", "delivery_admin"} { + if validStaffRole(role) { + t.Fatalf("unsupported staff role %q was accepted", role) + } + } +} diff --git a/backend/api/internal/logic/platform/user/relation.go b/backend/api/internal/logic/platform/user/relation.go index b990d9b..3e0c563 100644 --- a/backend/api/internal/logic/platform/user/relation.go +++ b/backend/api/internal/logic/platform/user/relation.go @@ -118,6 +118,16 @@ func UpdateUserServiceRelation(ctx *gin.Context) { if !ok { return } + var current models.UserServiceRelation + if err := common.ActiveRecords(impl.DBService).Select("user_account_id"). + Where("identity = ?", ctx.Param("identity")).First(¤t).Error; err != nil { + common.RespondRecordError(ctx, err) + return + } + if current.UserAccountID != userAccountID { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } common.UpdateAllowedByIdentity(ctx, &models.UserServiceRelation{}, gin.H{"user_account_id": userAccountID, "gas_basic_id": gasBasicID, "delivery_basic_id": deliveryBasicID, "staff_account_id": staffAccountID}, []string{"user_account_id", "gas_basic_id", "delivery_basic_id", "staff_account_id"}) } diff --git a/backend/api/internal/logic/platform/wallet/wallet.go b/backend/api/internal/logic/platform/wallet/wallet.go index 0730359..cd237d2 100644 --- a/backend/api/internal/logic/platform/wallet/wallet.go +++ b/backend/api/internal/logic/platform/wallet/wallet.go @@ -25,25 +25,46 @@ var walletOwnerModels = map[string]any{ "gas": &models.GasBasic{}, } -func ListWalletBasic(ctx *gin.Context) { listWalletPage[models.WalletBasic](ctx) } +func ListWalletBasic(ctx *gin.Context) { + ownerType := strings.TrimSpace(ctx.Query("owner_type")) + if ownerType != "" && ownerType != "user" && ownerType != "staff" && ownerType != "delivery" && ownerType != "gas" && ownerType != "platform" { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + ownerIdentities := strings.Split(strings.TrimSpace(ctx.Query("owner_identities")), ",") + if len(ownerIdentities) == 1 && ownerIdentities[0] == "" { + ownerIdentities = nil + } + if len(ownerIdentities) > 100 { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + listWalletPage[models.WalletBasic](ctx, ownerType, ownerIdentities) +} func GetWalletBasic(ctx *gin.Context) { getWalletByIdentity[models.WalletBasic](ctx) } -func ListWalletBank(ctx *gin.Context) { listWalletPage[models.WalletBank](ctx) } +func ListWalletBank(ctx *gin.Context) { listWalletPage[models.WalletBank](ctx, "", nil) } func GetWalletBank(ctx *gin.Context) { getWalletByIdentity[models.WalletBank](ctx) } -func ListWalletPayment(ctx *gin.Context) { listWalletPage[models.WalletPayment](ctx) } +func ListWalletPayment(ctx *gin.Context) { listWalletPage[models.WalletPayment](ctx, "", nil) } func GetWalletPayment(ctx *gin.Context) { getWalletByIdentity[models.WalletPayment](ctx) } -func ListWalletRecord(ctx *gin.Context) { listWalletPage[models.WalletRecord](ctx) } +func ListWalletRecord(ctx *gin.Context) { listWalletPage[models.WalletRecord](ctx, "", nil) } func GetWalletRecord(ctx *gin.Context) { getWalletByIdentity[models.WalletRecord](ctx) } -func ListWalletRefund(ctx *gin.Context) { listWalletPage[models.WalletRefund](ctx) } +func ListWalletRefund(ctx *gin.Context) { listWalletPage[models.WalletRefund](ctx, "", nil) } func GetWalletRefund(ctx *gin.Context) { getWalletByIdentity[models.WalletRefund](ctx) } -func ListWalletApplyCash(ctx *gin.Context) { listWalletPage[models.WalletApplyCash](ctx) } +func ListWalletApplyCash(ctx *gin.Context) { listWalletPage[models.WalletApplyCash](ctx, "", nil) } func GetWalletApplyCash(ctx *gin.Context) { getWalletByIdentity[models.WalletApplyCash](ctx) } -func listWalletPage[T any](ctx *gin.Context) { +func listWalletPage[T any](ctx *gin.Context, ownerType string, ownerIdentities []string) { page, size := common.PageSize(ctx) var list []T var total int64 model := new(T) query := common.ApplyKeywordFilter(ctx, common.ActiveRecords(impl.DBService.Model(model)), model) + if ownerType != "" { + query = query.Where("owner_type = ?", ownerType) + } + if len(ownerIdentities) > 0 { + query = query.Where("owner_identity IN ?", ownerIdentities) + } if err := query.Count(&total).Error; err != nil { infra.Response.Error(ctx, err) return @@ -63,7 +84,7 @@ func listWalletPage[T any](ctx *gin.Context) { func getWalletByIdentity[T any](ctx *gin.Context) { var data T - if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&data).Error; err != nil { + if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&data).Error; err != nil { common.RespondRecordError(ctx, err) return } @@ -268,9 +289,6 @@ func RejectWalletApplyCash(ctx *gin.Context) { } func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { - if !common.RequirePlatformRoot(ctx) { - return - } var request struct { Reason string `json:"reason" binding:"required,max=2000"` } @@ -282,7 +300,7 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { err := impl.DBService.Transaction(func(tx *gorm.DB) error { var application models.WalletApplyCash if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). - Where("identity = ?", ctx.Param("identity")).First(&application).Error; err != nil { + Where("identity = ? AND status = ?", ctx.Param("identity"), common.StatusEnable).First(&application).Error; err != nil { return err } if application.ApplyStatus == targetStatus { @@ -315,6 +333,45 @@ func reviewWalletApplyCash(ctx *gin.Context, targetStatus int) { infra.Response.Success(ctx, gin.H{"updated": true, "apply_status": targetStatus}) } +// CompleteWalletApplyCash records the external payout result after approval. +func CompleteWalletApplyCash(ctx *gin.Context) { + var request struct { + TradeNo string `json:"trade_no" binding:"required,max=128"` + CallbackMsg string `json:"callback_msg" binding:"max=4000"` + } + if err := ctx.ShouldBindJSON(&request); err != nil || strings.TrimSpace(request.TradeNo) == "" { + infra.Response.Error(ctx, errcode.ErrInvalidArgument) + return + } + err := impl.DBService.Transaction(func(tx *gorm.DB) error { + var application models.WalletApplyCash + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("identity = ? AND status = ?", ctx.Param("identity"), common.StatusEnable). + First(&application).Error; err != nil { + return err + } + if application.ApplyStatus == common.StatusCompleted { + if application.TradeNo == request.TradeNo { + return nil + } + return errors.New("cash application already completed") + } + if application.ApplyStatus != common.StatusApproved { + return errors.New("cash application is not approved") + } + now := time.Now() + return tx.Model(&application).Updates(map[string]any{ + "apply_status": common.StatusCompleted, "trade_no": strings.TrimSpace(request.TradeNo), + "callback_msg": request.CallbackMsg, "completed_at": &now, + }).Error + }) + if err != nil { + common.RespondRecordError(ctx, err) + return + } + infra.Response.Success(ctx, gin.H{"updated": true, "apply_status": common.StatusCompleted}) +} + func dateNumber(value time.Time, layout string) int32 { number, _ := strconv.ParseInt(value.Format(layout), 10, 32) return int32(number) diff --git a/backend/api/internal/models/entity.go b/backend/api/internal/models/entity.go index 9ae8325..b4c3bf6 100644 --- a/backend/api/internal/models/entity.go +++ b/backend/api/internal/models/entity.go @@ -11,9 +11,9 @@ import ( type Entity struct { ID uint64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"` // 数据库自增主键 Identity string `gorm:"column:identity;type:varchar(36);not null;uniqueIndex" json:"identity"` // UUID V7 业务标识 - CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null" json:"created_at"` // 创建时间 + CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null;index" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null" json:"updated_at"` // 更新时间 - Status int `gorm:"column:status;not null;default:0" json:"status"` // 业务状态 + Status int `gorm:"column:status;not null;default:0;index" json:"status"` // 通用记录状态 } // NewIdentity 生成时间有序的 UUID V7 字符串,生成失败属于不可恢复的运行时错误。 diff --git a/backend/api/internal/models/wallet_apply_cash.go b/backend/api/internal/models/wallet_apply_cash.go index e4f0566..936f277 100644 --- a/backend/api/internal/models/wallet_apply_cash.go +++ b/backend/api/internal/models/wallet_apply_cash.go @@ -9,22 +9,22 @@ 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"` // 内部提现单号 - RequestNo string `gorm:"column:request_no;type:varchar(128);not null;uniqueIndex" json:"request_no"` // 申请幂等号 - Amount int64 `gorm:"column:amount;not null;check:amount > 0" json:"amount"` // 提现金额,单位分 - Fee int64 `gorm:"column:fee;not null;default:0;check:fee >= 0" json:"fee"` // 提现手续费,单位分 - Channel string `gorm:"column:channel;type:varchar(32);not null" json:"channel"` // 提现渠道 - TradeNo string `gorm:"column:trade_no;type:varchar(128);not null;default:'';index" json:"trade_no"` // 第三方提现流水号 - Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 申请备注 - CallbackMsg string `gorm:"column:callback_msg;type:text;not null;default:''" json:"callback_msg"` // 提现回调信息 - ReviewerIdentity string `gorm:"column:reviewer_identity;type:varchar(36);not null;default:'';index" json:"reviewer_identity"` // 审核人业务标识 - ReviewerName string `gorm:"column:reviewer_name;type:varchar(64);not null;default:''" json:"reviewer_name"` // 审核人姓名快照 - ReviewedAt *time.Time `gorm:"column:reviewed_at;type:timestamptz" json:"reviewed_at"` // 审核时间 - ReviewReason string `gorm:"column:review_reason;type:text;not null;default:''" json:"review_reason"` // 审核原因 - CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"` // 完成时间 + 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"` // 内部提现单号 + RequestNo string `gorm:"column:request_no;type:varchar(128);not null;uniqueIndex" json:"request_no"` // 申请幂等号 + Amount int64 `gorm:"column:amount;not null;check:amount > 0" json:"amount"` // 提现金额,单位分 + Fee int64 `gorm:"column:fee;not null;default:0;check:fee >= 0" json:"fee"` // 提现手续费,单位分 + Channel string `gorm:"column:channel;type:varchar(32);not null" json:"channel"` // 提现渠道 + TradeNo string `gorm:"column:trade_no;type:varchar(128);not null;default:'';uniqueIndex:idx_wallet_cash_trade,where:trade_no <> ''" json:"trade_no"` // 第三方提现流水号 + Remark string `gorm:"column:remark;type:text;not null;default:''" json:"remark"` // 申请备注 + CallbackMsg string `gorm:"column:callback_msg;type:text;not null;default:''" json:"callback_msg"` // 提现回调信息 + ReviewerIdentity string `gorm:"column:reviewer_identity;type:varchar(36);not null;default:'';index" json:"reviewer_identity"` // 审核人业务标识 + ReviewerName string `gorm:"column:reviewer_name;type:varchar(64);not null;default:''" json:"reviewer_name"` // 审核人姓名快照 + ReviewedAt *time.Time `gorm:"column:reviewed_at;type:timestamptz" json:"reviewed_at"` // 审核时间 + ReviewReason string `gorm:"column:review_reason;type:text;not null;default:''" json:"review_reason"` // 审核原因 + CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"` // 完成时间 } func init() { database.AppendMigrate(&WalletApplyCash{}) } diff --git a/backend/api/internal/routers/platform.go b/backend/api/internal/routers/platform.go index a5f84f9..7d3e3dc 100644 --- a/backend/api/internal/routers/platform.go +++ b/backend/api/internal/routers/platform.go @@ -135,7 +135,7 @@ func registerWalletRoute(group *gin.RouterGroup) { basic.GET("/:identity", wallet.GetWalletBasic) basic.PATCH("/:identity/status", wallet.UpdateWalletBasicStatus) basic.POST("/:identity/recharge", wallet.RechargeWalletBasic) - basic.GET("/owner/:owner_type/:owner_identity", wallet.GetOrCreateOwnerWallet) + basic.POST("/owner/:owner_type/:owner_identity", wallet.GetOrCreateOwnerWallet) bank := group.Group("/wallet_bank") bank.GET("", wallet.ListWalletBank) @@ -158,6 +158,7 @@ func registerWalletRoute(group *gin.RouterGroup) { applyCash.GET("/:identity", wallet.GetWalletApplyCash) applyCash.POST("/:identity/approve", wallet.ApproveWalletApplyCash) applyCash.POST("/:identity/reject", wallet.RejectWalletApplyCash) + applyCash.POST("/:identity/complete", wallet.CompleteWalletApplyCash) } func registerCommerceRoute(group *gin.RouterGroup) { diff --git a/backend/api/internal/routers/platform_test.go b/backend/api/internal/routers/platform_test.go index 0c0c061..3c64f8b 100644 --- a/backend/api/internal/routers/platform_test.go +++ b/backend/api/internal/routers/platform_test.go @@ -212,9 +212,10 @@ func TestPlatformFinanceContentRoutesFollowTheirContracts(t *testing.T) { } assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_basic/:identity/status", http.MethodPatch) assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_basic/:identity/recharge", http.MethodPost) - assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_basic/owner/:owner_type/:owner_identity", http.MethodGet) + assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_basic/owner/:owner_type/:owner_identity", http.MethodPost) assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_apply_cash/:identity/approve", http.MethodPost) assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_apply_cash/:identity/reject", http.MethodPost) + assertRouteMethods(t, routes, "/heqi/platform/v1/wallet_apply_cash/:identity/complete", http.MethodPost) for _, oldPath := range []string{ "/heqi/platform/v1/wallet/wallet", "/heqi/platform/v1/wallet/wallet_ledger", diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index 22371e1..bd09621 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -33,6 +33,7 @@ export type DetailAction = { method?: 'POST' | 'PUT' | 'PATCH'; danger?: boolean; fields?: ResourceField[]; + visibleFor?: { field: string; values: Array }; }; export type ResourceUiDefinition = { @@ -303,24 +304,24 @@ export const resources: ResourceUiDefinition[] = [ 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', [ - { name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason }, - { name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason] }, - { name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason }, + { name: '启用合同', resource: '/gasorder_contract/:identity/activate', fields: reason, visibleFor: { field: 'contract_status', values: [10] } }, + { name: '续签合同', resource: '/gasorder_contract/:identity/renew', fields: [f('effective_at', { required: true }), f('expired_at'), ...reason], visibleFor: { field: 'contract_status', values: [11, 12] } }, + { name: '终止合同', resource: '/gasorder_contract/:identity/terminate', danger: true, fields: reason, visibleFor: { field: 'contract_status', values: [11] } }, ], { canCreate: true, canEdit: true }), define('gasorder_contract_product', '合同气瓶', 'append_only', [relation('gasorder_contract_identity', '/gasorder_contract', true), relation('product_info_identity', '/product_info', true), f('unit_price')], 'list', [ { name: '解绑气瓶', resource: '/gasorder_contract_product/:identity/unbind', danger: true, fields: reason }, ]), define('gasorder_contract_revision', '合同修订记录', 'readonly', []), define('gasorder_basic', '气体配送订单', 'append_only', [f('request_no', { required: true }), relation('gasorder_contract_identity', '/gasorder_contract', true), f('creator_type', { required: true, type: 'select', options: [{ label: '用户', value: 'user' }, { label: '工作人员', value: 'staff' }, { label: '配送站', value: 'delivery' }, { label: '气站', value: 'gas' }] }), f('creator_identity', { required: true }), relation('user_address_identity', '/user_address', true), f('gasorder_contract_product_identities', { required: true, type: 'identity-list', relation: '/gasorder_contract_product' }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [ - { name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true), ...reason] }, - { name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason }, - { name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason }, - { name: '开始配送', resource: '/gasorder_basic/:identity/delivering', fields: reason }, - { name: '等待签收', resource: '/gasorder_basic/:identity/awaiting-confirmation', fields: reason }, - { name: '完成订单', resource: '/gasorder_basic/:identity/complete', fields: [...reason, f('confirm_type', { required: true }), f('recipient_name', { required: true }), f('recipient_phone'), f('proof_uri'), f('remark')] }, - { name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason }, - { name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason }, - { name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason }, + { name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } }, + { name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason, visibleFor: { field: 'order_status', values: [18] } }, + { name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason, visibleFor: { field: 'order_status', values: [19] } }, + { name: '开始配送', resource: '/gasorder_basic/:identity/delivering', fields: reason, visibleFor: { field: 'order_status', values: [20] } }, + { name: '等待签收', resource: '/gasorder_basic/:identity/awaiting-confirmation', fields: reason, visibleFor: { field: 'order_status', values: [33] } }, + { name: '完成订单', resource: '/gasorder_basic/:identity/complete', fields: [...reason, f('confirm_type', { required: true }), f('recipient_name', { required: true }), f('recipient_phone'), f('proof_uri'), f('remark')], visibleFor: { field: 'order_status', values: [34] } }, + { name: '标记异常', resource: '/gasorder_basic/:identity/exception', fields: reason, visibleFor: { field: 'order_status', values: [19, 20, 33, 34] } }, + { name: '恢复订单', resource: '/gasorder_basic/:identity/recover', fields: reason, visibleFor: { field: 'order_status', values: [21] } }, + { name: '取消订单', resource: '/gasorder_basic/:identity/cancel', danger: true, fields: reason, visibleFor: { field: 'order_status', values: [16, 18] } }, ]), define('gasorder_item', '订单明细', 'readonly', []), define('gasorder_assign', '分配记录', 'readonly', []), @@ -349,7 +350,7 @@ export const resources: ResourceUiDefinition[] = [ define('wallet_refund', '退款记录', 'readonly', []), define('wallet_apply_cash', '提现记录', 'readonly', [ f('cash_no'), - relation('wallet_basic_identity', '/wallet_basic'), + f('wallet_basic_identity', { type: 'identity' }), f('amount'), f('apply_status', { type: 'select', options: [ { label: '待处理', value: 10 }, @@ -365,8 +366,9 @@ export const resources: ResourceUiDefinition[] = [ f('trade_no'), f('remark'), ], 'list', [ - { name: '审核通过', resource: '/wallet_apply_cash/:identity/approve', fields: reason }, - { name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason }, + { name: '审核通过', resource: '/wallet_apply_cash/:identity/approve', fields: reason, visibleFor: { field: 'apply_status', values: [10] } }, + { name: '审核驳回', resource: '/wallet_apply_cash/:identity/reject', danger: true, fields: reason, visibleFor: { field: 'apply_status', values: [10] } }, + { name: '标记处理完成', resource: '/wallet_apply_cash/:identity/complete', fields: [f('trade_no', { required: true }), f('callback_msg')], visibleFor: { field: 'apply_status', values: [25] } }, ]), define('fin_payment', '财务支付记录', 'readonly', []), diff --git a/frontend/platform_admin/src/contracts/platform-resources.json b/frontend/platform_admin/src/contracts/platform-resources.json index bc91f49..8570f0c 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":"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"}]} +{"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/: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":"/wallet_apply_cash/:identity/approve"},{"method":"POST","path":"/wallet_apply_cash/:identity/reject"},{"method":"POST","path":"/wallet_apply_cash/:identity/complete"},{"method":"POST","path":"/wallet_basic/owner/:owner_type/:owner_identity"},{"method":"POST","path":"/wallet_basic/:identity/recharge"},{"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":"/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 525a3a4..68a3527 100644 --- a/frontend/platform_admin/src/router/routes/modules/platform.ts +++ b/frontend/platform_admin/src/router/routes/modules/platform.ts @@ -62,8 +62,8 @@ const routes: AppRouteRecordRaw[] = [ group('organization', 'organization', '机构管理', 'icon-storage', 10, [ child('organization', 'gas-basic', 'gas-basic', '气站管理', '/gas_basic', 'gas_basic'), child('organization', 'delivery-basic', 'delivery-basic', '配送点管理', '/delivery_basic', 'delivery_basic'), - child('organization', 'gas-account', 'gas-account', '气站账户', '/gas_account', 'gas_basic', true, 'gas-basic'), - child('organization', 'delivery-account', 'delivery-account', '配送点账户', '/delivery_account', 'delivery_basic', true, 'delivery-basic'), + child('organization', 'gas-account', 'gas-account', '气站账户', '/gas_account', 'gas_basic', true, 'organization-gas-basic'), + child('organization', 'delivery-account', 'delivery-account', '配送点账户', '/delivery_account', 'delivery_basic', true, 'organization-delivery-basic'), ]), group('staff', 'staff', '工作人员管理', 'icon-user-group', 30, [ { ...child('staff', 'add', 'add', '新增工作人员', '/staff_account', 'staff_add'), meta: { title: '新增工作人员', resource: '/staff_account', requiresAuth: true, menuCode: 'staff_add', createMode: true } }, @@ -97,10 +97,10 @@ const routes: AppRouteRecordRaw[] = [ 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, [ + ], 'gasorder'), + group('ec', 'ec', '电商平台管理', 'icon-gift', 70, [ child('ec', 'categories', 'categories', '商品分类', '/ec_category', 'ec_category'), - child('ec', 'products', 'products', '商品', '/ec_product', 'ec_product'), + 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'), diff --git a/frontend/platform_admin/src/views/dashboard/DashboardPage.vue b/frontend/platform_admin/src/views/dashboard/DashboardPage.vue index 314f292..c49b391 100644 --- a/frontend/platform_admin/src/views/dashboard/DashboardPage.vue +++ b/frontend/platform_admin/src/views/dashboard/DashboardPage.vue @@ -68,7 +68,7 @@ const cards: { key: CountKey; label: string; hint: string; money?: boolean }[] = { key: 'paid_amount', label: '累计实收金额', hint: '支付成功口径', money: true }, ]; const actions = [ - { label: '新建气站', route: 'gas-basic', menu: 'gas_basic', icon: IconPlus }, + { label: '新建气站', route: 'organization-gas-basic', menu: 'gas_basic', icon: IconPlus }, { label: '配送订单', route: 'gasorder-orders', menu: 'gasorder_basic', icon: IconFile }, { label: '智能气阀', route: 'product-info', menu: 'product_info', icon: IconStorage }, { label: '用户管理', route: 'user-account', menu: 'user_account', icon: IconUser }, diff --git a/frontend/platform_admin/src/views/shared/CrudListPage.vue b/frontend/platform_admin/src/views/shared/CrudListPage.vue index e053024..821eb11 100644 --- a/frontend/platform_admin/src/views/shared/CrudListPage.vue +++ b/frontend/platform_admin/src/views/shared/CrudListPage.vue @@ -14,19 +14,6 @@ 查询 重置 - - - - 用户 - 工作人员 - 配送站 - 气站 - 平台 - - - - 获取或创建钱包 -