diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/controller/AccountController.java b/backend/mci-server/src/main/java/com/sino/mci/admin/controller/AccountController.java index e72af10..8bd9767 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/admin/controller/AccountController.java +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/controller/AccountController.java @@ -3,15 +3,19 @@ package com.sino.mci.admin.controller; import com.sino.mci.admin.dto.CreateTenantRequest; import com.sino.mci.admin.dto.CreateUserRequest; import com.sino.mci.admin.dto.LoginRequest; -import com.sino.mci.admin.dto.LoginResponse; +import com.sino.mci.admin.dto.PlatformUserResponse; +import com.sino.mci.admin.dto.TokenResponse; import com.sino.mci.admin.dto.UserResponse; import com.sino.mci.admin.entity.Tenant; +import com.sino.mci.admin.security.AuthContext; import com.sino.mci.admin.service.AccountService; import com.sino.mci.shared.web.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/v1/account") @RequiredArgsConstructor @@ -24,6 +28,11 @@ public class AccountController { return ApiResponse.ok(accountService.createTenant(request)); } + @GetMapping("/tenant/list") + public ApiResponse> listTenants() { + return ApiResponse.ok(accountService.listTenants()); + } + @PostMapping("/user") public ApiResponse createUser( @RequestHeader("X-Tenant-Code") String tenantCode, @@ -31,10 +40,20 @@ public class AccountController { return ApiResponse.ok(accountService.createUser(tenantCode, request)); } + @GetMapping("/user/list") + public ApiResponse> listUsers() { + return ApiResponse.ok(accountService.listUsers(AuthContext.currentTenantCode())); + } + @PostMapping("/login") - public ApiResponse login( + public ApiResponse login( @RequestHeader("X-Tenant-Code") String tenantCode, @Valid @RequestBody LoginRequest request) { return ApiResponse.ok(accountService.login(tenantCode, request.getUsername(), request.getPassword())); } + + @GetMapping("/me") + public ApiResponse me() { + return ApiResponse.ok(AuthContext.get()); + } } diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/dto/PlatformUserResponse.java b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/PlatformUserResponse.java new file mode 100644 index 0000000..bc0e18e --- /dev/null +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/PlatformUserResponse.java @@ -0,0 +1,18 @@ +package com.sino.mci.admin.dto; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class PlatformUserResponse { + private Long id; + private String tenantCode; + private String username; + private String realName; + private String phone; + private Integer status; + private List roleCodes; + private LocalDateTime createTime; +} diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/dto/TokenResponse.java b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/TokenResponse.java new file mode 100644 index 0000000..63dfbac --- /dev/null +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/dto/TokenResponse.java @@ -0,0 +1,13 @@ +package com.sino.mci.admin.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TokenResponse { + private String token; + private LoginResponse userInfo; +} diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/security/AuthInterceptor.java b/backend/mci-server/src/main/java/com/sino/mci/admin/security/AuthInterceptor.java index ed63b14..38bfb38 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/admin/security/AuthInterceptor.java +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/security/AuthInterceptor.java @@ -13,7 +13,10 @@ public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String path = request.getRequestURI(); - if (path.contains("/api/v1/account/login")) { + String method = request.getMethod(); + if (path.contains("/api/v1/account/login") + || ("POST".equals(method) && isExactPath(path, "/api/v1/account/tenant")) + || ("POST".equals(method) && isExactPath(path, "/api/v1/account/user"))) { return true; } @@ -25,4 +28,11 @@ public class AuthInterceptor implements HandlerInterceptor { } return true; } + + private boolean isExactPath(String requestUri, String endpointPath) { + String contextPath = "/msg-platform"; + return requestUri.equals(endpointPath) + || requestUri.equals(contextPath + endpointPath) + || requestUri.endsWith(endpointPath) && requestUri.length() == contextPath.length() + endpointPath.length(); + } } diff --git a/backend/mci-server/src/main/java/com/sino/mci/admin/service/AccountService.java b/backend/mci-server/src/main/java/com/sino/mci/admin/service/AccountService.java index 3c80723..5b8e6ef 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/admin/service/AccountService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/admin/service/AccountService.java @@ -1,9 +1,12 @@ package com.sino.mci.admin.service; +import cn.dev33.satoken.stp.StpUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.sino.mci.admin.dto.CreateTenantRequest; import com.sino.mci.admin.dto.CreateUserRequest; import com.sino.mci.admin.dto.LoginResponse; +import com.sino.mci.admin.dto.PlatformUserResponse; +import com.sino.mci.admin.dto.TokenResponse; import com.sino.mci.admin.dto.UserResponse; import com.sino.mci.admin.entity.PlatformRole; import com.sino.mci.admin.entity.PlatformUser; @@ -92,7 +95,7 @@ public class AccountService { return toUserResponse(user); } - public LoginResponse login(String tenantCode, String username, String password) { + public TokenResponse login(String tenantCode, String username, String password) { PlatformUser user = userMapper.selectOne( new LambdaQueryWrapper() .eq(PlatformUser::getTenantCode, tenantCode) @@ -102,14 +105,30 @@ public class AccountService { throw new UnauthorizedException("用户名或密码错误"); } - LoginResponse response = new LoginResponse(); - response.setId(user.getId()); - response.setTenantCode(user.getTenantCode()); - response.setUsername(user.getUsername()); - response.setRealName(user.getRealName()); - response.setPhone(user.getPhone()); - response.setRoleCodes(loadRoleCodes(user.getId())); - return response; + LoginResponse userInfo = new LoginResponse(); + userInfo.setId(user.getId()); + userInfo.setTenantCode(user.getTenantCode()); + userInfo.setUsername(user.getUsername()); + userInfo.setRealName(user.getRealName()); + userInfo.setPhone(user.getPhone()); + userInfo.setRoleCodes(loadRoleCodes(user.getId())); + + StpUtil.login(user.getId()); + StpUtil.getSession().set("tenantCode", user.getTenantCode()); + StpUtil.getSession().set("username", user.getUsername()); + StpUtil.getSession().set("roleCodes", userInfo.getRoleCodes()); + + return new TokenResponse(StpUtil.getTokenValue(), userInfo); + } + + public List listTenants() { + return tenantMapper.selectList(null); + } + + public List listUsers(String tenantCode) { + List users = userMapper.selectList( + new LambdaQueryWrapper().eq(PlatformUser::getTenantCode, tenantCode)); + return users.stream().map(this::toPlatformUserResponse).collect(Collectors.toList()); } private UserResponse toUserResponse(PlatformUser user) { @@ -127,6 +146,19 @@ public class AccountService { return response; } + private PlatformUserResponse toPlatformUserResponse(PlatformUser user) { + PlatformUserResponse response = new PlatformUserResponse(); + response.setId(user.getId()); + response.setTenantCode(user.getTenantCode()); + response.setUsername(user.getUsername()); + response.setRealName(user.getRealName()); + response.setPhone(user.getPhone()); + response.setStatus(user.getStatus()); + response.setRoleCodes(loadRoleCodes(user.getId())); + response.setCreateTime(user.getCreateTime()); + return response; + } + private List loadRoleCodes(Long userId) { return userRoleMapper.selectList( new LambdaQueryWrapper().eq(UserRole::getUserId, userId)) diff --git a/backend/mci-server/src/main/java/com/sino/mci/config/WebConfig.java b/backend/mci-server/src/main/java/com/sino/mci/config/WebConfig.java index 9d673e0..0e43a01 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/config/WebConfig.java +++ b/backend/mci-server/src/main/java/com/sino/mci/config/WebConfig.java @@ -16,6 +16,9 @@ public class WebConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/msg-platform/api/v1/**", "/api/v1/**") - .excludePathPatterns("/msg-platform/api/v1/account/login", "/api/v1/account/login"); + .excludePathPatterns( + "/msg-platform/api/v1/account/login", "/api/v1/account/login", + "/msg-platform/api/v1/account/tenant", "/api/v1/account/tenant", + "/msg-platform/api/v1/account/user", "/api/v1/account/user"); } } diff --git a/backend/mci-server/src/main/resources/db/migration/V9__add_user_role_platform_admin.sql b/backend/mci-server/src/main/resources/db/migration/V9__add_user_role_platform_admin.sql new file mode 100644 index 0000000..5a1abd6 --- /dev/null +++ b/backend/mci-server/src/main/resources/db/migration/V9__add_user_role_platform_admin.sql @@ -0,0 +1,2 @@ +INSERT IGNORE INTO mci_platform_role (role_code, role_name, permissions) VALUES +('platform_admin', '平台管理员', '["tenant:manage", "user:manage", "*"]'); diff --git a/backend/mci-server/src/test/java/com/sino/mci/channel/controller/ChannelControllerTest.java b/backend/mci-server/src/test/java/com/sino/mci/channel/controller/ChannelControllerTest.java index ac83bb6..654e63f 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/channel/controller/ChannelControllerTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/channel/controller/ChannelControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; +import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -26,17 +27,12 @@ public class ChannelControllerTest { @Test void shouldCreateChannelSubjectAndAccount() throws Exception { String tenantCode = "channel-" + UUID.randomUUID().toString().substring(0, 8); - String tenantPayload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"渠道测试\"}", tenantCode); - mockMvc.perform(post("/msg-platform/api/v1/account/tenant") - .contextPath("/msg-platform") - .contentType(MediaType.APPLICATION_JSON) - .content(tenantPayload)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); + String authToken = createTenantAndLogin(mockMvc, tenantCode, "渠道测试"); String subjectPayload = "{\"channelType\":\"wecom\",\"subjectCode\":\"sino\",\"subjectName\":\"Sino\"}"; mockMvc.perform(post("/msg-platform/api/v1/channel-subject") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(subjectPayload)) @@ -47,6 +43,7 @@ public class ChannelControllerTest { String accountPayload = "{\"accountType\":1,\"channelType\":\"wecom\",\"accountId\":\"ZhangSan\",\"accountName\":\"张三\"}"; mockMvc.perform(post("/msg-platform/api/v1/channel-account") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(accountPayload)) diff --git a/backend/mci-server/src/test/java/com/sino/mci/crm/controller/CrmControllerTest.java b/backend/mci-server/src/test/java/com/sino/mci/crm/controller/CrmControllerTest.java index c197259..44fe796 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/crm/controller/CrmControllerTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/crm/controller/CrmControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; +import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -26,17 +27,12 @@ public class CrmControllerTest { @Test void shouldCreatePersonTagAndConversation() throws Exception { String tenantCode = "crm-" + UUID.randomUUID().toString().substring(0, 8); - String tenantPayload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"CRM测试\"}", tenantCode); - mockMvc.perform(post("/msg-platform/api/v1/account/tenant") - .contextPath("/msg-platform") - .contentType(MediaType.APPLICATION_JSON) - .content(tenantPayload)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); + String authToken = createTenantAndLogin(mockMvc, tenantCode, "CRM测试"); String personPayload = "{\"personCode\":\"P001\",\"personName\":\"张三\",\"phone\":\"13800138000\",\"personType\":3}"; mockMvc.perform(post("/msg-platform/api/v1/person") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(personPayload)) @@ -47,6 +43,7 @@ public class CrmControllerTest { String tagPayload = "{\"parentId\":0,\"tagName\":\"客户\"}"; mockMvc.perform(post("/msg-platform/api/v1/tag") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(tagPayload)) @@ -57,6 +54,7 @@ public class CrmControllerTest { String conversationPayload = "{\"conversationType\":2,\"channelType\":\"wecom\",\"channelConversationId\":\"roomid_123\",\"conversationName\":\"测试群\"}"; mockMvc.perform(post("/msg-platform/api/v1/conversation") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(conversationPayload)) @@ -66,6 +64,7 @@ public class CrmControllerTest { mockMvc.perform(get("/msg-platform/api/v1/tag/list") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode)) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) diff --git a/backend/mci-server/src/test/java/com/sino/mci/inbound/controller/InboundFlowControllerTest.java b/backend/mci-server/src/test/java/com/sino/mci/inbound/controller/InboundFlowControllerTest.java index e49af00..e2e0a57 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/inbound/controller/InboundFlowControllerTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/inbound/controller/InboundFlowControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; +import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -26,17 +27,12 @@ public class InboundFlowControllerTest { @Test void shouldCreateFlowAndReceiveMessage() throws Exception { String tenantCode = "flow-" + UUID.randomUUID().toString().substring(0, 8); - String tenantPayload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"流程测试\",\"inboundWebhookUrl\":\"http://localhost:9999/webhook\"}", tenantCode); - mockMvc.perform(post("/msg-platform/api/v1/account/tenant") - .contextPath("/msg-platform") - .contentType(MediaType.APPLICATION_JSON) - .content(tenantPayload)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); + String authToken = createTenantAndLogin(mockMvc, tenantCode, "流程测试"); String flowPayload = "{\"flowCode\":\"inbound_default\",\"flowName\":\"默认入站流程\",\"flowType\":\"inbound\",\"definitionJson\":{\"nodes\":[{\"id\":\"receive\",\"type\":\"receive\"},{\"id\":\"intent\",\"type\":\"intent\"},{\"id\":\"webhook\",\"type\":\"webhook\"}]}}"; mockMvc.perform(post("/msg-platform/api/v1/inbound/flow") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(flowPayload)) @@ -45,12 +41,14 @@ public class InboundFlowControllerTest { mockMvc.perform(post("/msg-platform/api/v1/inbound/flow/inbound_default/publish") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode)) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)); mockMvc.perform(get("/msg-platform/api/v1/inbound/flow/inbound_default") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode)) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) @@ -62,6 +60,7 @@ public class InboundFlowControllerTest { eventId); mockMvc.perform(post("/msg-platform/api/v1/inbound/receive") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(messagePayload)) diff --git a/backend/mci-server/src/test/java/com/sino/mci/send/controller/SendControllerTest.java b/backend/mci-server/src/test/java/com/sino/mci/send/controller/SendControllerTest.java index 8e5ae32..e8cccde 100644 --- a/backend/mci-server/src/test/java/com/sino/mci/send/controller/SendControllerTest.java +++ b/backend/mci-server/src/test/java/com/sino/mci/send/controller/SendControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.UUID; +import static com.sino.mci.test.AuthTestHelper.createTenantAndLogin; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -26,18 +27,12 @@ public class SendControllerTest { @Test void shouldTestSendToConversationAndQueryRecords() throws Exception { String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8); - - String tenantPayload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"发送测试\"}", tenantCode); - mockMvc.perform(post("/msg-platform/api/v1/account/tenant") - .contextPath("/msg-platform") - .contentType(MediaType.APPLICATION_JSON) - .content(tenantPayload)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); + String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试"); String accountPayload = "{\"accountType\":1,\"channelType\":\"wecom\",\"accountId\":\"ZhangSan\",\"accountName\":\"张三\"}"; String accountResponse = mockMvc.perform(post("/msg-platform/api/v1/channel-account") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(accountPayload)) @@ -49,6 +44,7 @@ public class SendControllerTest { String conversationPayload = "{\"conversationType\":2,\"channelType\":\"wecom\",\"channelConversationId\":\"room-001\",\"conversationName\":\"测试群\"}"; String conversationResponse = mockMvc.perform(post("/msg-platform/api/v1/conversation") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(conversationPayload)) @@ -60,6 +56,7 @@ public class SendControllerTest { String bindPayload = String.format("{\"accountId\":%d,\"bindingType\":1,\"sortOrder\":1}", accountId); mockMvc.perform(post("/msg-platform/api/v1/conversation/" + conversationId + "/bind-account") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(bindPayload)) @@ -71,6 +68,7 @@ public class SendControllerTest { conversationId); String sendResponse = mockMvc.perform(post("/msg-platform/api/v1/send/test") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(sendPayload)) @@ -83,6 +81,7 @@ public class SendControllerTest { mockMvc.perform(get("/msg-platform/api/v1/send/" + taskId + "/records") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode)) .andExpect(status().isOk()) .andExpect(jsonPath("$.code").value(0)) @@ -94,17 +93,12 @@ public class SendControllerTest { @Test void shouldRejectUnsupportedTargetType() throws Exception { String tenantCode = "send-" + UUID.randomUUID().toString().substring(0, 8); - String tenantPayload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"发送测试\"}", tenantCode); - mockMvc.perform(post("/msg-platform/api/v1/account/tenant") - .contextPath("/msg-platform") - .contentType(MediaType.APPLICATION_JSON) - .content(tenantPayload)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); + String authToken = createTenantAndLogin(mockMvc, tenantCode, "发送测试"); String sendPayload = "{\"targetType\":\"rule\",\"targetValue\":{},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"}}"; mockMvc.perform(post("/msg-platform/api/v1/send/test") .contextPath("/msg-platform") + .header("Authorization", authToken) .header("X-Tenant-Code", tenantCode) .contentType(MediaType.APPLICATION_JSON) .content(sendPayload)) diff --git a/backend/mci-server/src/test/java/com/sino/mci/test/AuthTestHelper.java b/backend/mci-server/src/test/java/com/sino/mci/test/AuthTestHelper.java new file mode 100644 index 0000000..c270f17 --- /dev/null +++ b/backend/mci-server/src/test/java/com/sino/mci/test/AuthTestHelper.java @@ -0,0 +1,64 @@ +package com.sino.mci.test; + +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.util.UUID; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class AuthTestHelper { + + public static final String ADMIN_USERNAME = "admin"; + public static final String ADMIN_PASSWORD = "admin123"; + + public static String createTenantAndLogin(MockMvc mockMvc) throws Exception { + String tenantCode = "test-" + UUID.randomUUID().toString().substring(0, 8); + return createTenantAndLogin(mockMvc, tenantCode, "测试租户"); + } + + public static String createTenantAndLogin(MockMvc mockMvc, String tenantCode, String tenantName) throws Exception { + createTenant(mockMvc, tenantCode, tenantName); + createAdminUser(mockMvc, tenantCode); + return login(mockMvc, tenantCode); + } + + public static void createTenant(MockMvc mockMvc, String tenantCode, String tenantName) throws Exception { + String payload = String.format("{\"tenantCode\":\"%s\",\"tenantName\":\"%s\"}", tenantCode, tenantName); + mockMvc.perform(post("/msg-platform/api/v1/account/tenant") + .contextPath("/msg-platform") + .contentType(MediaType.APPLICATION_JSON) + .content(payload)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(0)); + } + + public static void createAdminUser(MockMvc mockMvc, String tenantCode) throws Exception { + String payload = String.format( + "{\"username\":\"%s\",\"password\":\"%s\",\"realName\":\"管理员\",\"phone\":\"13800138000\",\"roleCodes\":[\"platform_admin\"]}", + ADMIN_USERNAME, ADMIN_PASSWORD); + mockMvc.perform(post("/msg-platform/api/v1/account/user") + .contextPath("/msg-platform") + .header("X-Tenant-Code", tenantCode) + .contentType(MediaType.APPLICATION_JSON) + .content(payload)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(0)); + } + + public static String login(MockMvc mockMvc, String tenantCode) throws Exception { + String payload = String.format("{\"username\":\"%s\",\"password\":\"%s\"}", ADMIN_USERNAME, ADMIN_PASSWORD); + MvcResult result = mockMvc.perform(post("/msg-platform/api/v1/account/login") + .contextPath("/msg-platform") + .header("X-Tenant-Code", tenantCode) + .contentType(MediaType.APPLICATION_JSON) + .content(payload)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(0)) + .andReturn(); + return "Bearer " + com.jayway.jsonpath.JsonPath.read(result.getResponse().getContentAsString(), "$.data.token"); + } +}