feat(personal): derive ownership from login context
This commit is contained in:
+72
@@ -0,0 +1,72 @@
|
||||
package org.dromara.aihr.personal.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public final class PersonalAssistantDto {
|
||||
|
||||
private PersonalAssistantDto() {
|
||||
}
|
||||
|
||||
public record TextItemRequest(String title, String content, LocalDateTime capturedAt, List<String> tags) {
|
||||
}
|
||||
|
||||
public record UrlItemRequest(String url, String title, LocalDateTime capturedAt) {
|
||||
}
|
||||
|
||||
public record ItemCreatedResponse(long itemId, String status, Long duplicateOf) {
|
||||
}
|
||||
|
||||
public enum SearchScope {
|
||||
PERSONAL,
|
||||
ENTERPRISE
|
||||
}
|
||||
|
||||
public record AskRequest(Long sessionId, String queryText, List<SearchScope> scope, LocalDate dateFrom,
|
||||
LocalDate dateTo, List<Long> itemIds, String outputFormat) {
|
||||
}
|
||||
|
||||
public record CitationResponse(String domain, String sourceId, String title, String excerpt,
|
||||
LocalDateTime capturedAt) {
|
||||
}
|
||||
|
||||
public record AskResponse(long sessionId, String answer, List<CitationResponse> citations, String model,
|
||||
String promptVersion) {
|
||||
}
|
||||
|
||||
public record SpaceResponse(long spaceId, String status, long quotaBytes, long usedBytes, int itemCount) {
|
||||
}
|
||||
|
||||
public record ItemResponse(long id, String sourceType, String title, String originalUrl, String mimeType,
|
||||
long sizeBytes, String status, String errorCode, String errorMessage, String summary,
|
||||
List<String> tags, LocalDateTime capturedAt, LocalDateTime parsedAt) {
|
||||
}
|
||||
|
||||
public record PageResponse<T>(List<T> rows, long total, int pageNum, int pageSize) {
|
||||
}
|
||||
|
||||
public record DownloadUrlResponse(String url, LocalDateTime expiresAt) {
|
||||
}
|
||||
|
||||
public record PersonalSearchRequest(String queryText, List<SearchScope> scope, LocalDate dateFrom,
|
||||
LocalDate dateTo, List<Long> itemIds, Integer limit) {
|
||||
}
|
||||
|
||||
public record SearchHitResponse(String domain, String sourceId, String title, String excerpt,
|
||||
LocalDateTime capturedAt, double score) {
|
||||
}
|
||||
|
||||
public record PersonalSearchResponse(String queryText, List<SearchHitResponse> hits) {
|
||||
}
|
||||
|
||||
public record SessionResponse(long id, String title, String defaultScope, LocalDateTime updatedAt) {
|
||||
}
|
||||
|
||||
public record SessionDetailResponse(long id, String title, List<ChatMessageResponse> messages) {
|
||||
}
|
||||
|
||||
public record ChatMessageResponse(long id, String role, String content, List<CitationResponse> citations,
|
||||
LocalDateTime createdAt) {
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package org.dromara.aihr.personal.support;
|
||||
|
||||
public record PersonalOwner(String tenantId, long userId, String extPartyId) {
|
||||
|
||||
public PersonalOwner {
|
||||
if (tenantId == null || tenantId.isBlank() || userId <= 0) {
|
||||
throw new IllegalStateException("个人知识空间需要有效登录身份");
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.dromara.aihr.personal.support;
|
||||
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PersonalOwnerProvider {
|
||||
|
||||
public PersonalOwner current() {
|
||||
LoginUser user = LoginHelper.getLoginUser();
|
||||
if (user == null) {
|
||||
throw new IllegalStateException("请先登录");
|
||||
}
|
||||
long userId = user.getUserId() == null ? 0L : user.getUserId();
|
||||
return new PersonalOwner(user.getTenantId(), userId, null);
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package org.dromara.aihr.personal;
|
||||
|
||||
import org.dromara.aihr.personal.domain.PersonalAssistantDto;
|
||||
import org.dromara.aihr.personal.support.PersonalOwner;
|
||||
import org.dromara.aihr.personal.support.PersonalOwnerProvider;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@Tag("dev")
|
||||
class PersonalOwnerProviderTest {
|
||||
|
||||
private static final Set<String> FORBIDDEN_OWNER_FIELDS = Set.of(
|
||||
"tenantId", "ownerUserId", "ownerExtPartyId"
|
||||
);
|
||||
|
||||
@Test
|
||||
void requestDtosDoNotExposeOwnerOrTenantFields() {
|
||||
var requestTypes = Arrays.stream(PersonalAssistantDto.class.getDeclaredClasses())
|
||||
.filter(Class::isRecord)
|
||||
.filter(type -> type.getSimpleName().endsWith("Request"))
|
||||
.toList();
|
||||
|
||||
assertEquals(4, requestTypes.size());
|
||||
requestTypes.forEach(type -> {
|
||||
var componentNames = Arrays.stream(type.getRecordComponents())
|
||||
.map(component -> component.getName())
|
||||
.collect(Collectors.toSet());
|
||||
assertEquals(Set.of(), componentNames.stream()
|
||||
.filter(FORBIDDEN_OWNER_FIELDS::contains)
|
||||
.collect(Collectors.toSet()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void personalOwnerRejectsInvalidLoginIdentity() {
|
||||
var blankTenant = assertThrows(IllegalStateException.class,
|
||||
() -> new PersonalOwner(" ", 1L, null));
|
||||
assertEquals("个人知识空间需要有效登录身份", blankTenant.getMessage());
|
||||
|
||||
var missingUser = assertThrows(IllegalStateException.class,
|
||||
() -> new PersonalOwner("000000", 0L, null));
|
||||
assertEquals("个人知识空间需要有效登录身份", missingUser.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void providerRequiresLoginAndDerivesOwnerFromLoginUser() {
|
||||
var provider = new PersonalOwnerProvider();
|
||||
try (MockedStatic<LoginHelper> loginHelper = Mockito.mockStatic(LoginHelper.class)) {
|
||||
loginHelper.when(LoginHelper::getLoginUser).thenReturn(null);
|
||||
var notLoggedIn = assertThrows(IllegalStateException.class, provider::current);
|
||||
assertEquals("请先登录", notLoggedIn.getMessage());
|
||||
|
||||
var loginUser = new LoginUser();
|
||||
loginHelper.when(LoginHelper::getLoginUser).thenReturn(loginUser);
|
||||
var invalidIdentity = assertThrows(IllegalStateException.class, provider::current);
|
||||
assertEquals("个人知识空间需要有效登录身份", invalidIdentity.getMessage());
|
||||
|
||||
loginUser.setTenantId("000000");
|
||||
loginUser.setUserId(42L);
|
||||
loginUser.setUsername("13800000000");
|
||||
loginHelper.when(LoginHelper::getLoginUser).thenReturn(loginUser);
|
||||
|
||||
assertEquals(new PersonalOwner("000000", 42L, null), provider.current());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user