From 0c94c8033a6972846a41060f6115de208ec51f29 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 29 Jul 2026 15:09:54 +0800 Subject: [PATCH] feat separate order creation and contract menus --- backend/api/internal/logic/platform/menu.go | 3 ++- .../api/internal/logic/platform/platform/access.go | 13 ++++++++++++- .../logic/platform/platform/access_test.go | 14 ++++++++++++++ .../src/router/routes/modules/platform.ts | 7 ++++--- .../src/views/shared/CrudListPage.vue | 11 ++++++++--- 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/backend/api/internal/logic/platform/menu.go b/backend/api/internal/logic/platform/menu.go index a2fbb29..123f1e5 100644 --- a/backend/api/internal/logic/platform/menu.go +++ b/backend/api/internal/logic/platform/menu.go @@ -42,6 +42,7 @@ var PlatformMenus = [][]Menu{ {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: "gasorder_contract", ParentIdentity: "user", GroupCode: "user", Name: "合同管理", Path: "/user/contracts", SortNo: 4, Status: common.StatusEnable}, }, { {Identity: "device", GroupCode: "device", Name: "智能气阀管理", Icon: "icon-common", Path: "/product", SortNo: 60, Status: common.StatusEnable}, @@ -51,7 +52,7 @@ var PlatformMenus = [][]Menu{ }, { {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_create", ParentIdentity: "gasorder", GroupCode: "gasorder", Name: "创建订单", Path: "/gasorder/create", 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}, }, diff --git a/backend/api/internal/logic/platform/platform/access.go b/backend/api/internal/logic/platform/platform/access.go index e8a07e1..f601613 100644 --- a/backend/api/internal/logic/platform/platform/access.go +++ b/backend/api/internal/logic/platform/platform/access.go @@ -12,6 +12,10 @@ import ( const platformMenusContextKey = "platform_authorized_menus" func platformMenuAllowsPath(menus []platformbase.Menu, requestPath string) bool { + return platformMenuAllowsRequest(menus, requestPath, "GET") +} + +func platformMenuAllowsRequest(menus []platformbase.Menu, requestPath, method string) bool { marker := "/platform/v1/" index := strings.Index(requestPath, marker) if index < 0 { @@ -20,10 +24,17 @@ func platformMenuAllowsPath(menus []platformbase.Menu, requestPath string) bool relative := strings.Trim(requestPath[index+len(marker):], "/") resource := strings.Split(relative, "/")[0] menuIdentity := platformRouteMenuIdentity(resource) + if resource == "gasorder_basic" && method == "POST" { + menuIdentity = "gasorder_create" + } for _, menu := range menus { if menu.Identity == menuIdentity { return true } + if method == "GET" && menu.Identity == "gasorder_create" && + (menuIdentity == "gasorder_contract" || resource == "user_address") { + return true + } } return false } @@ -75,7 +86,7 @@ func RequirePlatformMenuAccess() gin.HandlerFunc { return } menus, err := platformbase.LoadPlatformMenus(claims.Role) - if err != nil || !platformMenuAllowsPath(menus, ctx.Request.URL.Path) { + if err != nil || !platformMenuAllowsRequest(menus, ctx.Request.URL.Path, ctx.Request.Method) { infra.Response.Error(ctx, errcode.ErrPermissionDenied) ctx.Abort() return diff --git a/backend/api/internal/logic/platform/platform/access_test.go b/backend/api/internal/logic/platform/platform/access_test.go index ebee9f3..560babb 100644 --- a/backend/api/internal/logic/platform/platform/access_test.go +++ b/backend/api/internal/logic/platform/platform/access_test.go @@ -32,6 +32,20 @@ func TestHiddenGasorderResourcesFollowOwningSecondLevelMenu(t *testing.T) { } } +func TestCreateOrderPermissionIsSeparateFromOrderManagement(t *testing.T) { + createMenus := []platformbase.Menu{{Identity: "gasorder_create"}} + if !platformMenuAllowsRequest(createMenus, "/heqi/platform/v1/gasorder_basic", "POST") { + t.Fatal("create-order menu did not grant order creation") + } + if platformMenuAllowsRequest(createMenus, "/heqi/platform/v1/gasorder_basic", "GET") { + t.Fatal("create-order menu granted order list access") + } + orderMenus := []platformbase.Menu{{Identity: "gasorder_basic"}} + if platformMenuAllowsRequest(orderMenus, "/heqi/platform/v1/gasorder_basic", "POST") { + t.Fatal("order-management menu granted order creation") + } +} + func TestLocationScopeValuesAreExplicit(t *testing.T) { if !validLocationScope("standard") || !validLocationScope("precise") { t.Fatal("supported location scopes were rejected") diff --git a/frontend/platform_admin/src/router/routes/modules/platform.ts b/frontend/platform_admin/src/router/routes/modules/platform.ts index 1e3524d..1a2bd85 100644 --- a/frontend/platform_admin/src/router/routes/modules/platform.ts +++ b/frontend/platform_admin/src/router/routes/modules/platform.ts @@ -76,6 +76,9 @@ const routes: AppRouteRecordRaw[] = [ 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'), + child('user', 'contracts', 'contracts', '合同管理', '/gasorder_contract', 'gasorder_contract'), + child('user', 'contract-products', 'contract-products', '合同气瓶', '/gasorder_contract_product', 'gasorder_contract', true, 'user-contracts'), + child('user', 'contract-revisions', 'contract-revisions', '合同修订记录', '/gasorder_contract_revision', 'gasorder_contract', true, 'user-contracts'), ]), group('product', 'product', '智能气阀管理', 'icon-common', 50, [ child('product', 'product-type', 'type', '类型管理', '/product_type', 'product_type'), @@ -85,9 +88,7 @@ const routes: AppRouteRecordRaw[] = [ child('product', 'owner', 'owner', '智能气阀归属记录', '/product_owner', 'product_info', true, 'product-info'), ], 'device'), group('gasorder', 'gasorder', '气体配送订单管理', 'icon-list', 60, [ - 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', 'create', 'create', '创建订单', '/gasorder_basic', 'gasorder_create'), meta: { title: '创建订单', resource: '/gasorder_basic', requiresAuth: true, menuCode: 'gasorder_create', createMode: true } }, 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'), diff --git a/frontend/platform_admin/src/views/shared/CrudListPage.vue b/frontend/platform_admin/src/views/shared/CrudListPage.vue index fc272a8..5ad7f04 100644 --- a/frontend/platform_admin/src/views/shared/CrudListPage.vue +++ b/frontend/platform_admin/src/views/shared/CrudListPage.vue @@ -524,8 +524,13 @@ async function save() { ); else await resourceApi.create(props.definition.resource, payload); Message.success('保存成功'); - formVisible.value = false; - await load(); + if (route.meta.createMode && !editingIdentity.value) { + resetForm(); + formVisible.value = true; + } else { + formVisible.value = false; + await load(); + } } catch (error) { Message.error((error as Error).message); } finally { @@ -699,7 +704,7 @@ function searchRelation(resource: string | undefined, keyword: string) { } onMounted(async () => { - await load(); + if (!route.meta.createMode) await load(); const actionFields = props.definition.detailActions?.flatMap((item) => item.fields ?? []) ?? []; const relationPaths = new Set( [...props.definition.fields, ...actionFields]