feat(finance): add customer wallet recharge flow
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
-- 客户资金账户与充值权限。只读访问和资金写操作使用独立权限。
|
||||
USE baiyue_iam;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
DROP PROCEDURE IF EXISTS migrate_wallet_permissions;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE migrate_wallet_permissions()
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM iam_permission
|
||||
WHERE (id = 5105056 AND (tenant_id <> 0 OR app_code <> 'PLATFORM'
|
||||
OR permission_type <> 'BUTTON'
|
||||
OR permission_code <> 'platform:finance:recharge:review'))
|
||||
OR (id = 5105057 AND (tenant_id <> 0 OR app_code <> 'PLATFORM'
|
||||
OR permission_type <> 'BUTTON'
|
||||
OR permission_code <> 'platform:finance:customer-account:adjust'))
|
||||
OR (id = 5305020 AND (tenant_id <> 0 OR app_code <> 'MERCHANT'
|
||||
OR permission_type <> 'MENU'
|
||||
OR permission_code <> 'merchant:finance:account:view'))
|
||||
OR (id = 5305021 AND (tenant_id <> 0 OR app_code <> 'MERCHANT'
|
||||
OR permission_type <> 'BUTTON'
|
||||
OR permission_code <> 'merchant:finance:recharge:create'))
|
||||
OR (permission_code = 'platform:finance:recharge:review'
|
||||
AND (id <> 5105056 OR tenant_id <> 0 OR app_code <> 'PLATFORM'))
|
||||
OR (permission_code = 'platform:finance:customer-account:adjust'
|
||||
AND (id <> 5105057 OR tenant_id <> 0 OR app_code <> 'PLATFORM'))
|
||||
OR (permission_code = 'merchant:finance:account:view'
|
||||
AND (id <> 5305020 OR tenant_id <> 0 OR app_code <> 'MERCHANT'))
|
||||
OR (permission_code = 'merchant:finance:recharge:create'
|
||||
AND (id <> 5305021 OR tenant_id <> 0 OR app_code <> 'MERCHANT'))
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='资金账户权限初始化失败:权限ID、编码或类型冲突';
|
||||
END IF;
|
||||
|
||||
INSERT INTO iam_permission
|
||||
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
|
||||
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
|
||||
VALUES
|
||||
(5105056,0,'PLATFORM','BUTTON',5105040,'platform:finance:recharge:review',
|
||||
'审核客户充值',NULL,'POST',100,'ACTIVE',1,0),
|
||||
(5105057,0,'PLATFORM','BUTTON',5105040,'platform:finance:customer-account:adjust',
|
||||
'调整客户资金账户',NULL,'POST',110,'ACTIVE',1,0),
|
||||
(5305020,0,'MERCHANT','MENU',5300000,'merchant:finance:account:view',
|
||||
'资金账户','/finance/account','GET',50,'ACTIVE',1,0),
|
||||
(5305021,0,'MERCHANT','BUTTON',5305020,'merchant:finance:recharge:create',
|
||||
'提交充值申请',NULL,'POST',10,'ACTIVE',1,0)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
permission_name=VALUES(permission_name), parent_id=VALUES(parent_id),
|
||||
route_path=VALUES(route_path), http_method=VALUES(http_method),
|
||||
sort_no=VALUES(sort_no), status='ACTIVE', built_in=1, deleted=0;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission (tenant_id, role_id, permission_id, created_at)
|
||||
SELECT 0, role.id, permission.id, CURRENT_TIMESTAMP(3)
|
||||
FROM iam_role role
|
||||
JOIN iam_permission permission ON permission.tenant_id=0 AND permission.app_code='PLATFORM'
|
||||
WHERE role.tenant_id=0 AND role.app_code='PLATFORM' AND role.role_code='PLATFORM_ADMIN'
|
||||
AND role.status='ACTIVE' AND role.deleted=0
|
||||
AND permission.permission_code IN (
|
||||
'platform:finance:recharge:review',
|
||||
'platform:finance:customer-account:adjust')
|
||||
AND permission.status='ACTIVE' AND permission.deleted=0;
|
||||
|
||||
INSERT IGNORE INTO iam_role_permission (tenant_id, role_id, permission_id, created_at)
|
||||
SELECT 0, role.id, permission.id, CURRENT_TIMESTAMP(3)
|
||||
FROM iam_role role
|
||||
JOIN iam_permission permission ON permission.tenant_id=0 AND permission.app_code='MERCHANT'
|
||||
WHERE role.tenant_id=0 AND role.app_code='MERCHANT' AND role.role_code='MERCHANT_OWNER'
|
||||
AND role.status='ACTIVE' AND role.deleted=0
|
||||
AND permission.permission_code IN (
|
||||
'merchant:finance:account:view',
|
||||
'merchant:finance:recharge:create')
|
||||
AND permission.status='ACTIVE' AND permission.deleted=0;
|
||||
|
||||
IF (SELECT COUNT(*) FROM iam_permission
|
||||
WHERE tenant_id=0 AND app_code='PLATFORM' AND permission_type='BUTTON'
|
||||
AND parent_id=5105040 AND status='ACTIVE' AND built_in=1 AND deleted=0
|
||||
AND permission_code IN (
|
||||
'platform:finance:recharge:review',
|
||||
'platform:finance:customer-account:adjust')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='资金账户权限初始化失败:平台写权限资源不完整';
|
||||
END IF;
|
||||
|
||||
IF (SELECT COUNT(*) FROM iam_permission
|
||||
WHERE tenant_id=0 AND app_code='MERCHANT' AND status='ACTIVE' AND built_in=1 AND deleted=0
|
||||
AND ((id=5305020 AND permission_type='MENU' AND parent_id=5300000
|
||||
AND permission_code='merchant:finance:account:view')
|
||||
OR (id=5305021 AND permission_type='BUTTON' AND parent_id=5305020
|
||||
AND permission_code='merchant:finance:recharge:create'))) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='资金账户权限初始化失败:客户权限资源不完整';
|
||||
END IF;
|
||||
|
||||
IF (SELECT COUNT(*) FROM iam_role_permission grant_row
|
||||
JOIN iam_role role ON role.id=grant_row.role_id AND role.tenant_id=grant_row.tenant_id
|
||||
JOIN iam_permission permission ON permission.id=grant_row.permission_id
|
||||
WHERE role.tenant_id=0 AND role.app_code='PLATFORM' AND role.role_code='PLATFORM_ADMIN'
|
||||
AND role.status='ACTIVE' AND role.deleted=0
|
||||
AND permission.tenant_id=0 AND permission.app_code='PLATFORM'
|
||||
AND permission.status='ACTIVE' AND permission.deleted=0
|
||||
AND permission.permission_code IN (
|
||||
'platform:finance:recharge:review',
|
||||
'platform:finance:customer-account:adjust')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='资金账户权限初始化失败:平台超级管理员授权不完整';
|
||||
END IF;
|
||||
|
||||
IF (SELECT COUNT(*) FROM iam_role_permission grant_row
|
||||
JOIN iam_role role ON role.id=grant_row.role_id AND role.tenant_id=grant_row.tenant_id
|
||||
JOIN iam_permission permission ON permission.id=grant_row.permission_id
|
||||
WHERE role.tenant_id=0 AND role.app_code='MERCHANT' AND role.role_code='MERCHANT_OWNER'
|
||||
AND role.status='ACTIVE' AND role.deleted=0
|
||||
AND permission.tenant_id=0 AND permission.app_code='MERCHANT'
|
||||
AND permission.status='ACTIVE' AND permission.deleted=0
|
||||
AND permission.permission_code IN (
|
||||
'merchant:finance:account:view',
|
||||
'merchant:finance:recharge:create')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='资金账户权限初始化失败:企业负责人授权不完整';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL migrate_wallet_permissions();
|
||||
DROP PROCEDURE IF EXISTS migrate_wallet_permissions;
|
||||
@@ -124,7 +124,7 @@ paths:
|
||||
get:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 查询当前用户有效权限
|
||||
description: 返回当前有效平台成员权限编码;只要求有效 PLATFORM JWT,不依赖 `platform:role:list`。海外仓返回 `platform:overseas-warehouse:dispatch:list`、`platform:overseas-warehouse:dispatch:edit`、`platform:overseas-warehouse:dispatch:cancel`、`platform:overseas-warehouse:dispatch:transfer-delivery`、`platform:overseas-warehouse:outbound:list`、`platform:overseas-warehouse:outbound:create`、`platform:overseas-warehouse:outbound:cancel`、`platform:overseas-warehouse:delivery:list`、`platform:overseas-warehouse:delivery:return`、`platform:overseas-warehouse:delivery:status`、`platform:overseas-warehouse:exception-order:list`、`platform:overseas-warehouse:exception-order:create`、`platform:overseas-warehouse:customer-visit:list`、`platform:overseas-warehouse:customer-visit:edit`;海外 PDA 盘点、移库、换标和现场查询统一返回 `lastleg:pda:operate`,且仅限 `OVERSEAS_WAREHOUSE_PDA_OPERATOR`。列表权限包含详情和只读辅助接口,写操作独立授权。
|
||||
description: 返回当前有效平台成员权限编码;只要求有效 PLATFORM JWT,不依赖 `platform:role:list`。资金账户充值审核与手工调账分别返回 `platform:finance:recharge:review`、`platform:finance:customer-account:adjust`,并与 `platform:finance:customer-account:list` 只读权限分离。海外仓返回 `platform:overseas-warehouse:dispatch:list`、`platform:overseas-warehouse:dispatch:edit`、`platform:overseas-warehouse:dispatch:cancel`、`platform:overseas-warehouse:dispatch:transfer-delivery`、`platform:overseas-warehouse:outbound:list`、`platform:overseas-warehouse:outbound:create`、`platform:overseas-warehouse:outbound:cancel`、`platform:overseas-warehouse:delivery:list`、`platform:overseas-warehouse:delivery:return`、`platform:overseas-warehouse:delivery:status`、`platform:overseas-warehouse:exception-order:list`、`platform:overseas-warehouse:exception-order:create`、`platform:overseas-warehouse:customer-visit:list`、`platform:overseas-warehouse:customer-visit:edit`;海外 PDA 盘点、移库、换标和现场查询统一返回 `lastleg:pda:operate`,且仅限 `OVERSEAS_WAREHOUSE_PDA_OPERATOR`。列表权限包含详情和只读辅助接口,写操作独立授权。
|
||||
responses:
|
||||
'200':
|
||||
description: 当前平台成员权限查询成功
|
||||
@@ -326,7 +326,7 @@ paths:
|
||||
get:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 查询当前用户有效权限
|
||||
description: 返回当前有效客户成员权限编码。海外采购、本土卡、本土散板和海外入库订单分别返回 `merchant:cross-border:procurement`、`merchant:cross-border:domestic-trucking`、`merchant:cross-border:pallet-shipping`、`merchant:warehouse:inbound:list`;客户派送返回 `merchant:warehouse:delivery:list` 和 `merchant:warehouse:delivery:create`。列表权限包含详情只读,创建权限独立授权。
|
||||
description: 返回当前有效客户成员权限编码。资金账户和流水只读返回 `merchant:finance:account:view`,提交充值申请返回 `merchant:finance:recharge:create`,两者独立授权。海外采购、本土卡、本土散板和海外入库订单分别返回 `merchant:cross-border:procurement`、`merchant:cross-border:domestic-trucking`、`merchant:cross-border:pallet-shipping`、`merchant:warehouse:inbound:list`;客户派送返回 `merchant:warehouse:delivery:list` 和 `merchant:warehouse:delivery:create`。列表权限包含详情只读,创建权限独立授权。
|
||||
responses:
|
||||
'200':
|
||||
description: 当前商户成员权限查询成功
|
||||
|
||||
@@ -42,6 +42,11 @@ class IamOpenApiContractTest {
|
||||
.contains("merchant:warehouse:inbound:list")
|
||||
.contains("lastleg:pda:operate")
|
||||
.contains("OVERSEAS_WAREHOUSE_PDA_OPERATOR")
|
||||
.contains("merchant:finance:account:view")
|
||||
.contains("merchant:finance:recharge:create")
|
||||
.contains("platform:finance:recharge:review")
|
||||
.contains("platform:finance:customer-account:adjust")
|
||||
.contains("两者独立授权")
|
||||
.contains("列表权限包含详情只读")
|
||||
.contains("PermissionCodesApiResponse:")
|
||||
.contains("TenantPageApiResponse:")
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.baiyue.iam.repository;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class WalletPermissionSqlContractTest {
|
||||
@Test
|
||||
void declaresReadAndWritePermissionsAsSeparateResources() throws Exception {
|
||||
String sql = sql();
|
||||
|
||||
assertThat(sql).contains(
|
||||
"(5105056,0,'PLATFORM','BUTTON',5105040,'platform:finance:recharge:review'",
|
||||
"(5105057,0,'PLATFORM','BUTTON',5105040,'platform:finance:customer-account:adjust'",
|
||||
"(5305020,0,'MERCHANT','MENU',5300000,'merchant:finance:account:view'",
|
||||
"(5305021,0,'MERCHANT','BUTTON',5305020,'merchant:finance:recharge:create'",
|
||||
"'资金账户','/finance/account','GET'",
|
||||
"'提交充值申请',NULL,'POST'",
|
||||
"'审核客户充值',NULL,'POST'",
|
||||
"'调整客户资金账户',NULL,'POST'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failsClosedOnPermissionIdentityOrTypeConflicts() throws Exception {
|
||||
String sql = sql();
|
||||
|
||||
assertThat(sql).contains(
|
||||
"migrate_wallet_permissions",
|
||||
"permission_type <> 'MENU'",
|
||||
"permission_type <> 'BUTTON'",
|
||||
"permission_code = 'merchant:finance:account:view'",
|
||||
"permission_code = 'platform:finance:customer-account:adjust'",
|
||||
"资金账户权限初始化失败:权限ID、编码或类型冲突");
|
||||
}
|
||||
|
||||
@Test
|
||||
void grantsMerchantPermissionsOnlyToTheEnterpriseOwner() throws Exception {
|
||||
String sql = sql();
|
||||
int permissionIndex = sql.indexOf("'merchant:finance:account:view'", sql.indexOf("INSERT IGNORE INTO"));
|
||||
int grantStart = sql.lastIndexOf("INSERT IGNORE INTO", permissionIndex);
|
||||
int grantEnd = sql.indexOf(';', permissionIndex);
|
||||
String grant = sql.substring(grantStart, grantEnd);
|
||||
|
||||
assertThat(grant).contains(
|
||||
"role.app_code='MERCHANT'",
|
||||
"role.role_code='MERCHANT_OWNER'",
|
||||
"'merchant:finance:account:view'",
|
||||
"'merchant:finance:recharge:create'");
|
||||
assertThat(grant).doesNotContain("PLATFORM_ADMIN", "PLATFORM_CUSTOM_ADMIN");
|
||||
}
|
||||
|
||||
@Test
|
||||
void grantsPlatformWritesOnlyToTheSuperAdministratorByDefault() throws Exception {
|
||||
String sql = sql();
|
||||
int permissionIndex = sql.indexOf("'platform:finance:recharge:review'", sql.indexOf("INSERT IGNORE INTO"));
|
||||
int grantStart = sql.lastIndexOf("INSERT IGNORE INTO", permissionIndex);
|
||||
int grantEnd = sql.indexOf(';', permissionIndex);
|
||||
String grant = sql.substring(grantStart, grantEnd);
|
||||
|
||||
assertThat(grant).contains(
|
||||
"role.app_code='PLATFORM'",
|
||||
"role.role_code='PLATFORM_ADMIN'",
|
||||
"'platform:finance:recharge:review'",
|
||||
"'platform:finance:customer-account:adjust'");
|
||||
assertThat(grant).doesNotContain(
|
||||
"PLATFORM_CUSTOM_ADMIN",
|
||||
"CUSTOMER_REVIEWER",
|
||||
"ORDER_OPERATOR");
|
||||
}
|
||||
|
||||
@Test
|
||||
void assertsCatalogAndDefaultRoleGrants() throws Exception {
|
||||
assertThat(sql()).contains(
|
||||
"资金账户权限初始化失败:平台写权限资源不完整",
|
||||
"资金账户权限初始化失败:客户权限资源不完整",
|
||||
"资金账户权限初始化失败:平台超级管理员授权不完整",
|
||||
"资金账户权限初始化失败:企业负责人授权不完整",
|
||||
")) <> 2 THEN");
|
||||
}
|
||||
|
||||
private static String sql() throws Exception {
|
||||
return new ClassPathResource("db/wallet-permissions.sql")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,35 @@ class AuthorizationSnapshotServiceTest {
|
||||
"merchant:warehouse:inbound:list");
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesWalletPermissionsFromCurrentRoleAssignments() {
|
||||
when(mapper.countActiveIdentity(42L, 7L, 9L)).thenReturn(1);
|
||||
when(mapper.findRoles(42L, 7L, 9L, "MERCHANT")).thenReturn(List.of("MERCHANT_OWNER"));
|
||||
when(mapper.findPermissions(42L, 7L, 9L, "MERCHANT")).thenReturn(List.of(
|
||||
"merchant:finance:account:view",
|
||||
"merchant:finance:recharge:create"));
|
||||
|
||||
var merchantPermissions = service.currentPermissions(new AuthenticatedUser(42L, 7L, 7L, 9L,
|
||||
"owner", "MERCHANT", "MERCHANT", java.util.Set.of()), "MERCHANT");
|
||||
|
||||
assertThat(merchantPermissions).containsExactlyInAnyOrder(
|
||||
"merchant:finance:account:view",
|
||||
"merchant:finance:recharge:create");
|
||||
|
||||
when(mapper.countActiveIdentity(51L, 0L, 19L)).thenReturn(1);
|
||||
when(mapper.findRoles(51L, 0L, 19L, "PLATFORM")).thenReturn(List.of("PLATFORM_ADMIN"));
|
||||
when(mapper.findPermissions(51L, 0L, 19L, "PLATFORM")).thenReturn(List.of(
|
||||
"platform:finance:recharge:review",
|
||||
"platform:finance:customer-account:adjust"));
|
||||
|
||||
var platformPermissions = service.currentPermissions(new AuthenticatedUser(51L, 0L, 0L, 19L,
|
||||
"platform-admin", "PLATFORM", "PLATFORM", java.util.Set.of()), "PLATFORM");
|
||||
|
||||
assertThat(platformPermissions).containsExactlyInAnyOrder(
|
||||
"platform:finance:recharge:review",
|
||||
"platform:finance:customer-account:adjust");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsIncompleteCurrentIdentityAsBusinessError() {
|
||||
assertThatThrownBy(() -> service
|
||||
|
||||
Reference in New Issue
Block a user