From a7d318a0132eecda159e1cf344da165ee4c88a42 Mon Sep 17 00:00:00 2001 From: david Date: Wed, 29 Jul 2026 15:29:49 +0800 Subject: [PATCH] feat embed wallets and add withdrawal records --- backend/api/internal/logic/platform/menu.go | 20 +++----- .../logic/platform/platform/access.go | 6 +++ .../logic/platform/platform/access_test.go | 12 +++++ .../scripts/check-backend-contract.mjs | 6 ++- frontend/platform_admin/src/api/resources.ts | 30 +++++++++-- .../src/router/routes/modules/platform.ts | 9 +--- .../src/views/shared/CrudListPage.vue | 51 +++++++++++++++++-- 7 files changed, 102 insertions(+), 32 deletions(-) diff --git a/backend/api/internal/logic/platform/menu.go b/backend/api/internal/logic/platform/menu.go index 123f1e5..e7b9998 100644 --- a/backend/api/internal/logic/platform/menu.go +++ b/backend/api/internal/logic/platform/menu.go @@ -57,27 +57,19 @@ var PlatformMenus = [][]Menu{ {Identity: "gasorder_track", ParentIdentity: "gasorder", GroupCode: "gasorder", Name: "运行轨迹", Path: "/gasorder/tracks", SortNo: 3, Status: common.StatusEnable}, }, { - {Identity: "ec", GroupCode: "ec", Name: "商城管理", Icon: "icon-gift", Path: "/ec", SortNo: 80, Status: common.StatusEnable}, + {Identity: "ec", GroupCode: "ec", Name: "电商平台管理", Icon: "icon-gift", Path: "/ec", SortNo: 80, Status: common.StatusEnable}, {Identity: "ec_category", ParentIdentity: "ec", GroupCode: "ec", Name: "商品分类", Path: "/ec/categories", SortNo: 1, Status: common.StatusEnable}, - {Identity: "ec_product", ParentIdentity: "ec", GroupCode: "ec", Name: "商品", Path: "/ec/products", SortNo: 2, Status: common.StatusEnable}, + {Identity: "ec_product", ParentIdentity: "ec", GroupCode: "ec", Name: "商品管理", Path: "/ec/products", SortNo: 2, Status: common.StatusEnable}, {Identity: "ec_cart", ParentIdentity: "ec", GroupCode: "ec", Name: "购物车", Path: "/ec/carts", SortNo: 3, Status: common.StatusEnable}, {Identity: "ec_order", ParentIdentity: "ec", GroupCode: "ec", Name: "商城订单", Path: "/ec/orders", SortNo: 4, Status: common.StatusEnable}, {Identity: "ec_review", ParentIdentity: "ec", GroupCode: "ec", Name: "商品评价", Path: "/ec/reviews", SortNo: 5, Status: common.StatusEnable}, }, - { - {Identity: "wallet", GroupCode: "wallet", Name: "钱包管理", Icon: "icon-safe", Path: "/wallet", SortNo: 90, Status: common.StatusEnable}, - {Identity: "wallet_basic", ParentIdentity: "wallet", GroupCode: "wallet", Name: "钱包", Path: "/wallet/wallet-basic", SortNo: 1, Status: common.StatusEnable}, - {Identity: "wallet_bank", ParentIdentity: "wallet", GroupCode: "wallet", Name: "银行卡", Path: "/wallet/banks", SortNo: 2, Status: common.StatusEnable}, - {Identity: "wallet_payment", ParentIdentity: "wallet", GroupCode: "wallet", Name: "支付记录", Path: "/wallet/payments", SortNo: 3, Status: common.StatusEnable}, - {Identity: "wallet_record", ParentIdentity: "wallet", GroupCode: "wallet", Name: "钱包流水", Path: "/wallet/records", SortNo: 4, Status: common.StatusEnable}, - {Identity: "wallet_refund", ParentIdentity: "wallet", GroupCode: "wallet", Name: "退款记录", Path: "/wallet/refunds", SortNo: 5, Status: common.StatusEnable}, - {Identity: "wallet_apply_cash", ParentIdentity: "wallet", GroupCode: "wallet", Name: "提现申请", Path: "/wallet/apply-cash", SortNo: 6, Status: common.StatusEnable}, - }, { {Identity: "finance", GroupCode: "finance", Name: "财务管理", Icon: "icon-bar-chart", Path: "/finance", SortNo: 100, Status: common.StatusEnable}, - {Identity: "fin_payment", ParentIdentity: "finance", GroupCode: "finance", Name: "支付记录", Path: "/finance/payments", SortNo: 1, Status: common.StatusEnable}, - {Identity: "fin_settlement", ParentIdentity: "finance", GroupCode: "finance", Name: "财务结算", Path: "/finance/settlements", SortNo: 2, Status: common.StatusEnable}, - {Identity: "fin_reconciliation", ParentIdentity: "finance", GroupCode: "finance", Name: "财务对账", Path: "/finance/reconciliations", SortNo: 3, Status: common.StatusEnable}, + {Identity: "wallet_apply_cash", ParentIdentity: "finance", GroupCode: "finance", Name: "提现记录", Path: "/finance/withdrawals", SortNo: 1, Status: common.StatusEnable}, + {Identity: "fin_payment", ParentIdentity: "finance", GroupCode: "finance", Name: "支付记录", Path: "/finance/payments", SortNo: 2, Status: common.StatusEnable}, + {Identity: "fin_settlement", ParentIdentity: "finance", GroupCode: "finance", Name: "财务结算", Path: "/finance/settlements", SortNo: 3, Status: common.StatusEnable}, + {Identity: "fin_reconciliation", ParentIdentity: "finance", GroupCode: "finance", Name: "财务对账", Path: "/finance/reconciliations", SortNo: 4, Status: common.StatusEnable}, }, { {Identity: "content", GroupCode: "content", Name: "内容管理", Icon: "icon-file", Path: "/content", SortNo: 110, Status: common.StatusEnable}, diff --git a/backend/api/internal/logic/platform/platform/access.go b/backend/api/internal/logic/platform/platform/access.go index f601613..b7a94e6 100644 --- a/backend/api/internal/logic/platform/platform/access.go +++ b/backend/api/internal/logic/platform/platform/access.go @@ -35,6 +35,12 @@ func platformMenuAllowsRequest(menus []platformbase.Menu, requestPath, method st (menuIdentity == "gasorder_contract" || resource == "user_address") { return true } + if method == "GET" && relative == "wallet_basic" && + (menu.Identity == "gas_basic" || menu.Identity == "delivery_basic" || + menu.Identity == "staff" || menu.Identity == "user_account" || + menu.Identity == "wallet_apply_cash") { + return true + } } return false } diff --git a/backend/api/internal/logic/platform/platform/access_test.go b/backend/api/internal/logic/platform/platform/access_test.go index 560babb..598631d 100644 --- a/backend/api/internal/logic/platform/platform/access_test.go +++ b/backend/api/internal/logic/platform/platform/access_test.go @@ -46,6 +46,18 @@ func TestCreateOrderPermissionIsSeparateFromOrderManagement(t *testing.T) { } } +func TestOwnerMenusCanReadEmbeddedWallets(t *testing.T) { + for _, identity := range []string{"gas_basic", "delivery_basic", "staff", "user_account", "wallet_apply_cash"} { + menus := []platformbase.Menu{{Identity: identity}} + if !platformMenuAllowsRequest(menus, "/heqi/platform/v1/wallet_basic", "GET") { + t.Fatalf("%s could not read its embedded wallet", identity) + } + if platformMenuAllowsRequest(menus, "/heqi/platform/v1/wallet_basic/owner/user/test", "GET") { + t.Fatalf("%s unexpectedly received wallet creation access", identity) + } + } +} + func TestLocationScopeValuesAreExplicit(t *testing.T) { if !validLocationScope("standard") || !validLocationScope("precise") { t.Fatal("supported location scopes were rejected") diff --git a/frontend/platform_admin/scripts/check-backend-contract.mjs b/frontend/platform_admin/scripts/check-backend-contract.mjs index baaac1a..d7df63f 100644 --- a/frontend/platform_admin/scripts/check-backend-contract.mjs +++ b/frontend/platform_admin/scripts/check-backend-contract.mjs @@ -13,6 +13,10 @@ const frontendDefinitions = [ ...source.matchAll(/define\('([^']+)',\s*'[^']+',\s*'([^']+)'/g), ].map((match) => ({ name: match[1], mode: match[2] })); const frontendNames = frontendDefinitions.map((item) => item.name); +const embeddedResources = new Set([ + 'wallet_basic', 'wallet_bank', 'wallet_payment', 'wallet_record', + 'wallet_refund', 'wallet_apply_cash', +]); for (const name of frontendNames) { const item = backend.get(name); @@ -29,7 +33,7 @@ for (const item of contract.resources) { throw new Error( `资源模式不一致:${item.name},后端=${item.mode},前端=${frontend?.mode}`, ); - if (!routes.includes(`'/${item.name}'`)) + if (!embeddedResources.has(item.name) && !routes.includes(`'/${item.name}'`)) throw new Error(`缺少后端资源路由:${item.name}`); } diff --git a/frontend/platform_admin/src/api/resources.ts b/frontend/platform_admin/src/api/resources.ts index 8f774a8..22371e1 100644 --- a/frontend/platform_admin/src/api/resources.ts +++ b/frontend/platform_admin/src/api/resources.ts @@ -53,6 +53,7 @@ export type ResourceUiDefinition = { relationKey: string; title: string; }; + walletOwnerType?: 'gas' | 'delivery' | 'staff' | 'user'; }; const fieldLabels: Record = { @@ -136,6 +137,8 @@ const fieldLabels: Record = { channel: '渠道', review_reason: '审核原因', reviewed_at: '审核时间', + reviewer_name: '审核人', + reviewer_identity: '审核人标识', ymd: '日期', ym: '月份', contract_no: '合同编号', @@ -281,13 +284,13 @@ function define( const reason = [f('reason', { required: true })]; export const resources: ResourceUiDefinition[] = [ - { ...define('gas_basic', '气站管理', 'writable', [f('code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('address'), f('longitude'), f('latitude')]), accountManagement: { resource: '/gas_account', relationKey: 'gas_basic_identity', title: '气站账户' } }, + { ...define('gas_basic', '气站管理', 'writable', [f('code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('address'), f('longitude'), f('latitude')]), accountManagement: { resource: '/gas_account', relationKey: 'gas_basic_identity', title: '气站账户' }, walletOwnerType: 'gas' }, define('gas_account', '气站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('gas_basic_identity', '/gas_basic', true)]), - { ...define('delivery_basic', '配送点管理', 'writable', [f('delivery_code', { required: true }), f('name', { required: true }), relation('gas_basic_identity', '/gas_basic'), f('principal'), f('address')]), accountManagement: { resource: '/delivery_account', relationKey: 'delivery_basic_identity', title: '配送点账户' } }, + { ...define('delivery_basic', '配送点管理', 'writable', [f('delivery_code', { required: true }), f('name', { required: true }), relation('gas_basic_identity', '/gas_basic'), f('principal'), f('address')]), accountManagement: { resource: '/delivery_account', relationKey: 'delivery_basic_identity', title: '配送点账户' }, walletOwnerType: 'delivery' }, define('delivery_account', '配送站账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('role_code'), relation('delivery_basic_identity', '/delivery_basic', true)]), - define('staff_account', '工作人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('role_code', { required: true, type: 'select', options: [{ label: '安装人员', value: 'installer' }, { label: '配送人员', value: 'delivery' }, { label: '运维人员', value: 'operations' }] }), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('work_status', { type: 'select', options: [{ label: '在岗', value: 'on_duty' }, { label: '离岗', value: 'off_duty' }] })]), + { ...define('staff_account', '工作人员', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('role_code', { required: true, type: 'select', options: [{ label: '安装人员', value: 'installer' }, { label: '配送人员', value: 'delivery' }, { label: '运维人员', value: 'operations' }] }), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), f('work_status', { type: 'select', options: [{ label: '在岗', value: 'on_duty' }, { label: '离岗', value: 'off_duty' }] })]), walletOwnerType: 'staff' }, define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]), - define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name')]), + { ...define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name')]), walletOwnerType: 'user' }, define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]), define('user_service_relation', '用户服务关系', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('staff_account_identity', '/staff_account')]), @@ -344,7 +347,24 @@ export const resources: ResourceUiDefinition[] = [ define('wallet_payment', '钱包支付记录', 'readonly', []), define('wallet_record', '钱包流水', 'readonly', []), define('wallet_refund', '退款记录', 'readonly', []), - define('wallet_apply_cash', '提现申请', 'readonly', [], 'list', [ + define('wallet_apply_cash', '提现记录', 'readonly', [ + f('cash_no'), + relation('wallet_basic_identity', '/wallet_basic'), + f('amount'), + f('apply_status', { type: 'select', options: [ + { label: '待处理', value: 10 }, + { label: '已通过', value: 25 }, + { label: '已驳回', value: 26 }, + { label: '已完成', value: 23 }, + ] }), + f('reviewer_name'), + f('review_reason'), + f('reviewed_at'), + f('completed_at'), + f('channel'), + 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 }, ]), diff --git a/frontend/platform_admin/src/router/routes/modules/platform.ts b/frontend/platform_admin/src/router/routes/modules/platform.ts index 1a2bd85..525a3a4 100644 --- a/frontend/platform_admin/src/router/routes/modules/platform.ts +++ b/frontend/platform_admin/src/router/routes/modules/platform.ts @@ -108,15 +108,8 @@ const routes: AppRouteRecordRaw[] = [ child('ec', 'order-items', 'order-items', '订单明细', '/ec_order_item', 'ec_order', true, 'ec-orders'), child('ec', 'reviews', 'reviews', '商品评价', '/ec_review', 'ec_review'), ]), - group('wallet', 'wallet', '钱包管理', 'icon-safe', 80, [ - child('wallet', 'wallet-basic', 'basic', '钱包', '/wallet_basic', 'wallet_basic'), - child('wallet', 'banks', 'banks', '银行卡', '/wallet_bank', 'wallet_bank'), - child('wallet', 'payments', 'payments', '支付记录', '/wallet_payment', 'wallet_payment'), - child('wallet', 'records', 'records', '钱包流水', '/wallet_record', 'wallet_record'), - child('wallet', 'refunds', 'refunds', '退款记录', '/wallet_refund', 'wallet_refund'), - child('wallet', 'apply-cash', 'apply-cash', '提现申请', '/wallet_apply_cash', 'wallet_apply_cash'), - ]), group('finance', 'finance', '财务管理', 'icon-bar-chart', 90, [ + child('finance', 'withdrawals', 'withdrawals', '提现记录', '/wallet_apply_cash', 'wallet_apply_cash'), child('finance', 'payments', 'payments', '支付记录', '/fin_payment', 'fin_payment'), child('finance', 'settlements', 'settlements', '财务结算', '/fin_settlement', 'fin_settlement'), child('finance', 'reconciliations', 'reconciliations', '财务对账', '/fin_reconciliation', 'fin_reconciliation'), diff --git a/frontend/platform_admin/src/views/shared/CrudListPage.vue b/frontend/platform_admin/src/views/shared/CrudListPage.vue index 5ad7f04..e053024 100644 --- a/frontend/platform_admin/src/views/shared/CrudListPage.vue +++ b/frontend/platform_admin/src/views/shared/CrudListPage.vue @@ -45,6 +45,14 @@ + + +