优化
This commit is contained in:
@@ -12,6 +12,8 @@ import com.baiyue.iam.dto.TenantDtos.ApprovalResponse;
|
|||||||
import com.baiyue.iam.entity.IamTenantReview;
|
import com.baiyue.iam.entity.IamTenantReview;
|
||||||
import com.baiyue.iam.service.TenantGuard;
|
import com.baiyue.iam.service.TenantGuard;
|
||||||
import com.baiyue.iam.service.TenantService;
|
import com.baiyue.iam.service.TenantService;
|
||||||
|
import com.baiyue.iam.service.MemberService;
|
||||||
|
import com.baiyue.iam.dto.PlatformCustomerMemberDtos;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
@@ -38,6 +40,8 @@ import java.util.List;
|
|||||||
public class PlatformTenantController {
|
public class PlatformTenantController {
|
||||||
@Resource
|
@Resource
|
||||||
private TenantService service;
|
private TenantService service;
|
||||||
|
@Resource
|
||||||
|
private MemberService memberService;
|
||||||
|
|
||||||
@GetMapping("/tenants")
|
@GetMapping("/tenants")
|
||||||
@PreAuthorize("hasAuthority('platform:customer:list')")
|
@PreAuthorize("hasAuthority('platform:customer:list')")
|
||||||
@@ -69,6 +73,15 @@ public class PlatformTenantController {
|
|||||||
return ApiResponse.success(service.merchant(tenantId));
|
return ApiResponse.success(service.merchant(tenantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/tenants/{tenantId:\\d+}/members")
|
||||||
|
@PreAuthorize("hasAuthority('platform:customer:view')")
|
||||||
|
@Operation(summary = "查询企业客户成员及账号", description = "平台只读查询指定企业的全部成员,包含 OWNER 企业管理员,并返回登录账号。")
|
||||||
|
public ApiResponse<List<PlatformCustomerMemberDtos.MemberResponse>> members(
|
||||||
|
@PathVariable long tenantId, @AuthenticationPrincipal AuthenticatedUser user) {
|
||||||
|
TenantGuard.tenant(user, "PLATFORM");
|
||||||
|
return ApiResponse.success(memberService.platformList(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/tenants")
|
@PostMapping("/tenants")
|
||||||
@PreAuthorize("hasAuthority('platform:customer:create')")
|
@PreAuthorize("hasAuthority('platform:customer:create')")
|
||||||
@Operation(summary = "Register enterprise tenant / 创建企业客户草稿", description = "Create a PROFILE_REQUIRED merchant draft only. Saving the customer does not create a review ticket. / 仅创建待完善企业客户草稿,保存客户不会创建审核单。")
|
@Operation(summary = "Register enterprise tenant / 创建企业客户草稿", description = "Create a PROFILE_REQUIRED merchant draft only. Saving the customer does not create a review ticket. / 仅创建待完善企业客户草稿,保存客户不会创建审核单。")
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.baiyue.iam.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** 平台查看客户企业成员时使用的只读契约,补充认证账号信息。 */
|
||||||
|
public final class PlatformCustomerMemberDtos {
|
||||||
|
private PlatformCustomerMemberDtos() { }
|
||||||
|
|
||||||
|
public record MemberResponse(Long membershipId, Long userId, Long authUserId,
|
||||||
|
String username, String displayName, String phone, String email,
|
||||||
|
String memberType, String memberNo, String status,
|
||||||
|
List<Long> roleIds, List<String> roleNames) { }
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ import com.baiyue.iam.entity.IamRole;
|
|||||||
import com.baiyue.iam.entity.IamUser;
|
import com.baiyue.iam.entity.IamUser;
|
||||||
import com.baiyue.iam.mapper.IamRoleMapper;
|
import com.baiyue.iam.mapper.IamRoleMapper;
|
||||||
import com.baiyue.iam.repository.MemberRepository;
|
import com.baiyue.iam.repository.MemberRepository;
|
||||||
|
import com.baiyue.iam.repository.TenantRepository;
|
||||||
|
import com.baiyue.iam.dto.PlatformCustomerMemberDtos;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
import feign.FeignException;
|
import feign.FeignException;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -33,6 +35,8 @@ public class MemberService {
|
|||||||
@Resource
|
@Resource
|
||||||
private MemberRepository repository;
|
private MemberRepository repository;
|
||||||
@Resource
|
@Resource
|
||||||
|
private TenantRepository tenantRepository;
|
||||||
|
@Resource
|
||||||
private IamRoleMapper roleMapper;
|
private IamRoleMapper roleMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private AuthAccountClient authClient;
|
private AuthAccountClient authClient;
|
||||||
@@ -43,6 +47,34 @@ public class MemberService {
|
|||||||
return repository.findByTenant(tenantId).stream().map(m -> response(tenantId, m)).toList();
|
return repository.findByTenant(tenantId).stream().map(m -> response(tenantId, m)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 平台只读查询客户成员;显式校验客户主档,并包含 OWNER 管理员。 */
|
||||||
|
public List<PlatformCustomerMemberDtos.MemberResponse> platformList(long tenantId) {
|
||||||
|
var tenant = tenantRepository.findTenant(tenantId);
|
||||||
|
if (tenant == null || !"MERCHANT".equals(tenant.getTenantType())) {
|
||||||
|
throw new BizException(ResultCode.NOT_FOUND, "企业客户不存在");
|
||||||
|
}
|
||||||
|
return repository.findByTenant(tenantId).stream().map(m -> platformResponse(tenantId, m)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlatformCustomerMemberDtos.MemberResponse platformResponse(long tenantId, IamMembership membership) {
|
||||||
|
IamUser user = requiredUser(membership.getUserId());
|
||||||
|
AuthAccountClient.AccountSummary account = accountSummary(user.getAuthUserId());
|
||||||
|
List<Long> roleIds = repository.findRoleIds(tenantId, membership.getId());
|
||||||
|
List<String> roleNames = roleIds.stream()
|
||||||
|
.map(roleMapper::selectById)
|
||||||
|
.filter(role -> role != null && Long.valueOf(tenantId).equals(role.getTenantId()))
|
||||||
|
.map(IamRole::getRoleName)
|
||||||
|
.toList();
|
||||||
|
return new PlatformCustomerMemberDtos.MemberResponse(membership.getId(), user.getId(),
|
||||||
|
user.getAuthUserId(), account.username(), user.getDisplayName(), user.getPhone(), user.getEmail(),
|
||||||
|
membership.getMemberType(), membership.getMemberNo(), membership.getStatus(),
|
||||||
|
roleIds, roleNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthAccountClient.AccountSummary accountSummary(Long authUserId) {
|
||||||
|
return data(call(() -> authClient.summary(internalToken, authUserId)));
|
||||||
|
}
|
||||||
|
|
||||||
public MemberResponse detail(long tenantId, Long membershipId) {
|
public MemberResponse detail(long tenantId, Long membershipId) {
|
||||||
return response(tenantId, required(tenantId, membershipId));
|
return response(tenantId, required(tenantId, membershipId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,22 @@ paths:
|
|||||||
'400': {$ref: '#/components/responses/BadRequest'}
|
'400': {$ref: '#/components/responses/BadRequest'}
|
||||||
'403': {$ref: '#/components/responses/Forbidden'}
|
'403': {$ref: '#/components/responses/Forbidden'}
|
||||||
'404': {$ref: '#/components/responses/NotFound'}
|
'404': {$ref: '#/components/responses/NotFound'}
|
||||||
|
/api/platform/v1/iam/tenants/{tenantId}/members:
|
||||||
|
parameters:
|
||||||
|
- {$ref: '#/components/parameters/TenantId'}
|
||||||
|
get:
|
||||||
|
tags: [Platform Tenant / 平台租户]
|
||||||
|
summary: 查询企业客户成员及账号
|
||||||
|
description: 平台只读查询指定客户的全部企业成员,包含 OWNER 企业管理员并返回登录账号;需要 `platform:customer:view` 权限。
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 企业成员查询成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PlatformCustomerMembersApiResponse'
|
||||||
|
'403': {$ref: '#/components/responses/Forbidden'}
|
||||||
|
'404': {$ref: '#/components/responses/NotFound'}
|
||||||
/api/platform/v1/iam/tenants/{tenantId}/submit-review:
|
/api/platform/v1/iam/tenants/{tenantId}/submit-review:
|
||||||
parameters:
|
parameters:
|
||||||
- {$ref: '#/components/parameters/TenantId'}
|
- {$ref: '#/components/parameters/TenantId'}
|
||||||
@@ -802,6 +818,28 @@ components:
|
|||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
data: {$ref: '#/components/schemas/InternalUserResponse'}
|
data: {$ref: '#/components/schemas/InternalUserResponse'}
|
||||||
|
PlatformCustomerMemberResponse:
|
||||||
|
type: object
|
||||||
|
required: [membershipId, userId, authUserId, username, displayName, memberType, status, roleIds, roleNames]
|
||||||
|
properties:
|
||||||
|
membershipId: {type: string, pattern: '^\d+$'}
|
||||||
|
userId: {type: string, pattern: '^\d+$'}
|
||||||
|
authUserId: {type: string, pattern: '^\d+$'}
|
||||||
|
username: {type: string}
|
||||||
|
displayName: {type: string}
|
||||||
|
phone: {type: string}
|
||||||
|
email: {type: string, format: email}
|
||||||
|
memberType: {type: string, enum: [OWNER, EMPLOYEE]}
|
||||||
|
memberNo: {type: string}
|
||||||
|
status: {type: string, enum: [ACTIVE, DISABLED]}
|
||||||
|
roleIds: {type: array, items: {type: string, pattern: '^\d+$'}}
|
||||||
|
roleNames: {type: array, items: {type: string}}
|
||||||
|
PlatformCustomerMembersApiResponse:
|
||||||
|
allOf:
|
||||||
|
- {$ref: '#/components/schemas/ApiResponse'}
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
data: {type: array, items: {$ref: '#/components/schemas/PlatformCustomerMemberResponse'}}
|
||||||
PlatformManagedUserPageApiResponse:
|
PlatformManagedUserPageApiResponse:
|
||||||
allOf:
|
allOf:
|
||||||
- {$ref: '#/components/schemas/ApiResponse'}
|
- {$ref: '#/components/schemas/ApiResponse'}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import com.baiyue.iam.client.AuthAccountClient;
|
|||||||
import com.baiyue.iam.dto.MemberDtos.*;
|
import com.baiyue.iam.dto.MemberDtos.*;
|
||||||
import com.baiyue.iam.entity.IamMembership;
|
import com.baiyue.iam.entity.IamMembership;
|
||||||
import com.baiyue.iam.entity.IamRole;
|
import com.baiyue.iam.entity.IamRole;
|
||||||
|
import com.baiyue.iam.entity.IamTenant;
|
||||||
import com.baiyue.iam.entity.IamUser;
|
import com.baiyue.iam.entity.IamUser;
|
||||||
import com.baiyue.iam.mapper.IamRoleMapper;
|
import com.baiyue.iam.mapper.IamRoleMapper;
|
||||||
import com.baiyue.iam.repository.MemberRepository;
|
import com.baiyue.iam.repository.MemberRepository;
|
||||||
|
import com.baiyue.iam.repository.TenantRepository;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -28,6 +30,7 @@ import static org.mockito.Mockito.*;
|
|||||||
@TestPropertySource(properties = "baiyue.security.internal-service-token=internal-token")
|
@TestPropertySource(properties = "baiyue.security.internal-service-token=internal-token")
|
||||||
class MemberServiceTest {
|
class MemberServiceTest {
|
||||||
@MockBean private MemberRepository repository;
|
@MockBean private MemberRepository repository;
|
||||||
|
@MockBean private TenantRepository tenantRepository;
|
||||||
@MockBean private IamRoleMapper roleMapper;
|
@MockBean private IamRoleMapper roleMapper;
|
||||||
@MockBean private AuthAccountClient auth;
|
@MockBean private AuthAccountClient auth;
|
||||||
@Resource private MemberService service;
|
@Resource private MemberService service;
|
||||||
@@ -52,6 +55,28 @@ class MemberServiceTest {
|
|||||||
verifyNoInteractions(auth);
|
verifyNoInteractions(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test void shouldReturnOwnerAccountForPlatformCustomerDetail() {
|
||||||
|
IamTenant tenant = new IamTenant();
|
||||||
|
tenant.setId(100L); tenant.setTenantType("MERCHANT");
|
||||||
|
member.setMemberType("OWNER");
|
||||||
|
user.setPhone("13800138000"); user.setEmail("owner@example.com");
|
||||||
|
when(tenantRepository.findTenant(100L)).thenReturn(tenant);
|
||||||
|
when(repository.findByTenant(100L)).thenReturn(List.of(member));
|
||||||
|
IamRole ownerRole = new IamRole();
|
||||||
|
ownerRole.setId(50L); ownerRole.setTenantId(100L); ownerRole.setRoleName("企业管理员");
|
||||||
|
when(roleMapper.selectById(50L)).thenReturn(ownerRole);
|
||||||
|
when(auth.summary("internal-token", 40L)).thenReturn(ApiResponse.success(
|
||||||
|
new AuthAccountClient.AccountSummary(40L, "merchant_owner", "企业管理员", "ACTIVE")));
|
||||||
|
|
||||||
|
var result = service.platformList(100L);
|
||||||
|
|
||||||
|
assertEquals(1, result.size());
|
||||||
|
assertEquals("merchant_owner", result.get(0).username());
|
||||||
|
assertEquals("OWNER", result.get(0).memberType());
|
||||||
|
assertEquals("ACTIVE", result.get(0).status());
|
||||||
|
assertEquals(List.of("企业管理员"), result.get(0).roleNames());
|
||||||
|
}
|
||||||
|
|
||||||
@Test void shouldHandleCase02() {
|
@Test void shouldHandleCase02() {
|
||||||
when(repository.findByTenantAndId(200L, 20L)).thenReturn(null);
|
when(repository.findByTenantAndId(200L, 20L)).thenReturn(null);
|
||||||
assertThrows(BizException.class, () -> service.detail(200L, 20L));
|
assertThrows(BizException.class, () -> service.detail(200L, 20L));
|
||||||
|
|||||||
Reference in New Issue
Block a user