feat(work-results): support historical project outcomes

This commit is contained in:
2026-07-22 00:29:19 +08:00
parent 49189474c1
commit d7664e45a7
26 changed files with 479 additions and 49 deletions
@@ -1387,9 +1387,9 @@ public class AihrMemoryService {
return new java.util.LinkedHashSet<>(jdbcTemplate.queryForList("""
select distinct project_code
from aihr_org_snapshot
where tenant_id = ? and ext_party_id = ? and employment_status = 'active'
where tenant_id = ? and (ext_party_id = ? or person_phone = ?) and employment_status = 'active'
and position_level in ('主管', '项目经理')
""", String.class, principal.tenantId(), principal.extPartyId()));
""", String.class, principal.tenantId(), principal.extPartyId(), principal.extPartyId()));
}
private ServiceMemoryResponse mapServiceMemory(ResultSet rs, List<ServiceMemoryVersionResponse> versions)
@@ -212,21 +212,26 @@ public class AihrWorkResultService {
if (!"app_user".equals(principal.userType())) return;
Integer rows = jdbcTemplate.queryForObject("""
select count(*) from aihr_org_snapshot
where tenant_id = ? and ext_party_id = ? and project_code = ?
where tenant_id = ? and (ext_party_id = ? or person_phone = ?) and project_code = ?
and employment_status = 'active' and position_level in ('主管', '项目经理')
""", Integer.class, principal.tenantId(), principal.extPartyId(), projectCode);
""", Integer.class, principal.tenantId(), principal.extPartyId(), principal.extPartyId(), projectCode);
if (rows == null || rows < 1) {
throw new ServiceException("当前项目没有主管权限", HttpStatus.FORBIDDEN);
}
}
private static LocalDate workDate(String raw) {
LocalDate date;
if (raw == null || raw.isBlank()) return LocalDate.now(WORK_ZONE);
try {
return LocalDate.parse(raw.trim());
date = LocalDate.parse(raw.trim());
} catch (Exception ex) {
throw new ServiceException("workDate 必须为 yyyy-MM-dd", HttpStatus.BAD_REQUEST);
}
if (date.isAfter(LocalDate.now(WORK_ZONE))) {
throw new ServiceException("workDate 不能晚于当天", HttpStatus.BAD_REQUEST);
}
return date;
}
private String json(Object value) {
@@ -9,6 +9,7 @@ import org.dromara.aihr.memory.AihrMemoryDto.ServiceMemoryVersionResponse;
import org.dromara.common.core.exception.ServiceException;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
@@ -25,6 +26,7 @@ import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -32,6 +34,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@Tag("dev")
class AihrMemoryServiceTest {
@@ -159,6 +164,23 @@ class AihrMemoryServiceTest {
assertTrue(plan.missingFields().isEmpty());
}
@Test
void phoneMappedSupervisorCanReadProjectCaptureSource() {
JdbcTemplate jdbc = mock(JdbcTemplate.class);
AihrKnowledgePrincipalResolver resolver = mock(AihrKnowledgePrincipalResolver.class);
AihrKnowledgePrincipal supervisor = new AihrKnowledgePrincipal(
"000000", 7L, "app_user", "13900000101", Set.of("supervisor"), Set.of("P2"), "app");
when(resolver.current()).thenReturn(supervisor);
ArgumentCaptor<Object[]> projectsArgs = ArgumentCaptor.forClass(Object[].class);
when(jdbc.queryForList(anyString(), eq(String.class), projectsArgs.capture())).thenReturn(List.of("P2"));
when(jdbc.query(anyString(), any(RowMapper.class), any(Object[].class)))
.thenReturn(List.of("{\"ossId\":123}"));
AihrMemoryService service = new AihrMemoryService(jdbc, new ObjectMapper(), resolver);
assertEquals(123L, service.authorizedCaptureSourceOssId(9L));
assertArrayEquals(new Object[]{"000000", "13900000101", "13900000101"}, projectsArgs.getValue());
}
@Test
void sameConversationLocationFollowupUpgradesOriginalNeedsInputCandidate() throws Exception {
ObjectMapper mapper = new ObjectMapper();
@@ -8,16 +8,21 @@ import org.dromara.aihr.workresult.AihrWorkResultDto.WorkResultResponse;
import org.dromara.common.core.exception.ServiceException;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.time.LocalDate;
import java.time.ZoneId;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
@@ -52,13 +57,46 @@ class AihrWorkResultServiceTest {
AihrKnowledgePrincipalResolver resolver = mock(AihrKnowledgePrincipalResolver.class);
when(resolver.current()).thenReturn(new AihrKnowledgePrincipal(
"000000", 7L, "app_user", "EMP-1", Set.of("employee", "supervisor"), Set.of("P1", "P2"), "app"));
when(jdbc.queryForObject(anyString(), any(Class.class), any(Object[].class))).thenReturn(0);
ArgumentCaptor<String> sql = ArgumentCaptor.forClass(String.class);
when(jdbc.queryForObject(sql.capture(), any(Class.class), any(Object[].class))).thenReturn(0);
AihrWorkResultService service = new AihrWorkResultService(jdbc, new ObjectMapper(), resolver);
ServiceException error = assertThrows(ServiceException.class, () -> service.project("P2", "2026-07-21"));
assertEquals(403, error.getCode());
assertEquals("当前项目没有主管权限", error.getMessage());
assertTrue(sql.getValue().contains("ext_party_id = ? or person_phone = ?"));
}
@Test
void phoneMappedSupervisorCanReadOwnProject() {
JdbcTemplate jdbc = mock(JdbcTemplate.class);
AihrKnowledgePrincipalResolver resolver = mock(AihrKnowledgePrincipalResolver.class);
when(resolver.current()).thenReturn(new AihrKnowledgePrincipal(
"000000", 7L, "app_user", "13900000101", Set.of("supervisor"), Set.of("P2"), "app"));
ArgumentCaptor<Object[]> args = ArgumentCaptor.forClass(Object[].class);
when(jdbc.queryForObject(anyString(), any(Class.class), args.capture())).thenReturn(1);
when(jdbc.queryForList(anyString(), any(Class.class), any(Object[].class))).thenReturn(List.of());
AihrWorkResultService service = new AihrWorkResultService(jdbc, new ObjectMapper(), resolver);
service.project("P2", "2026-07-21");
assertArrayEquals(new Object[]{"000000", "13900000101", "13900000101", "P2"}, args.getValue());
}
@Test
void futureWorkDateIsRejectedBeforeAnyWrite() {
JdbcTemplate jdbc = mock(JdbcTemplate.class);
AihrKnowledgePrincipalResolver resolver = mock(AihrKnowledgePrincipalResolver.class);
when(resolver.current()).thenReturn(new AihrKnowledgePrincipal(
"000000", 7L, "app_user", "EMP-1", Set.of("employee"), Set.of("P1"), "app"));
AihrWorkResultService service = new AihrWorkResultService(jdbc, new ObjectMapper(), resolver);
ServiceException error = assertThrows(ServiceException.class, () -> service.generateMine("P1",
LocalDate.now(ZoneId.of("Asia/Shanghai")).plusDays(1).toString()));
assertEquals(400, error.getCode());
assertEquals("workDate 不能晚于当天", error.getMessage());
}
private static WorkResultResponse response(int version, String generatedAt, String status) {