feat(project): 为项目活动时间线添加成员角色名称显示功能
- 在 ObjectActivityConstants 中添加 MEMBER_ACTION_UPDATE 类型支持 - 为 ProductActivityQueryService 和 ProductActivityTimelineQueryService 添加角色名称加载和缓存功能 - 实现角色名称解析和 JSON 数据结构扩展 - 添加相关单元测试验证角色名称显示逻辑 - 集成 ObjectPermissionApi 获取角色信息并实现缓存机制
This commit is contained in:
@@ -50,7 +50,7 @@ public final class ObjectActivityConstants {
|
||||
PRODUCT_ACTION_CREATE, PRODUCT_ACTION_CHANGE_MANAGER);
|
||||
|
||||
public static final List<String> MEMBER_TIMELINE_ACTION_TYPES = List.of(
|
||||
MEMBER_ACTION_ADD, MEMBER_ACTION_REMOVE);
|
||||
MEMBER_ACTION_ADD, MEMBER_ACTION_UPDATE, MEMBER_ACTION_REMOVE);
|
||||
|
||||
private static final Set<String> STATUS_ACTION_TYPE_SET = Set.copyOf(STATUS_ACTION_TYPES);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.njcn.rdms.module.project.service.product;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.common.util.object.PageUtils;
|
||||
@@ -12,7 +14,11 @@ import com.njcn.rdms.module.project.dal.dataobject.product.ProductStatusLogDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.product.ProductStatusLogMapper;
|
||||
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
|
||||
import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -20,9 +26,11 @@ import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -31,6 +39,8 @@ public class ProductActivityQueryService {
|
||||
|
||||
private static final String PRODUCT_OBJECT_TYPE = ObjectActivityConstants.PRODUCT_BIZ_TYPE;
|
||||
private static final String MEMBER_BIZ_TYPE = ObjectActivityConstants.MEMBER_BIZ_TYPE;
|
||||
private static final String ACTIVITY_ROLE_NAME_CACHE = "project_activity_role_name#10m";
|
||||
private static final String ROLE_SCOPE_OBJECT = "object";
|
||||
|
||||
@Resource
|
||||
private ProductStatusLogMapper productStatusLogMapper;
|
||||
@@ -38,6 +48,10 @@ public class ProductActivityQueryService {
|
||||
private BizAuditLogMapper bizAuditLogMapper;
|
||||
@Resource
|
||||
private UserObjectRoleMapper userObjectRoleMapper;
|
||||
@Resource
|
||||
private CacheManager cacheManager;
|
||||
@Resource
|
||||
private ObjectPermissionApi objectPermissionApi;
|
||||
|
||||
public PageResult<ProductActivityRespVO> getProductActivities(Long productId, ProductActivityPageReqVO reqVO) {
|
||||
List<ActivityItem> items = new ArrayList<>();
|
||||
@@ -60,7 +74,9 @@ public class ProductActivityQueryService {
|
||||
List<ProductActivityRespVO> activities = items.stream()
|
||||
.map(ActivityItem::respVO)
|
||||
.toList();
|
||||
return buildPageResult(activities, reqVO);
|
||||
PageResult<ProductActivityRespVO> pageResult = buildPageResult(activities, reqVO);
|
||||
fillMemberRoleNames(pageResult.getList());
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
private void appendMemberActivities(Long productId, ProductActivityPageReqVO reqVO, List<ActivityItem> items) {
|
||||
@@ -101,6 +117,131 @@ public class ProductActivityQueryService {
|
||||
return new PageResult<>(activities.subList(start, end), (long) activities.size());
|
||||
}
|
||||
|
||||
private void fillMemberRoleNames(List<ProductActivityRespVO> activities) {
|
||||
if (activities == null || activities.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<Long> roleIds = new LinkedHashSet<>();
|
||||
for (ProductActivityRespVO activity : activities) {
|
||||
if (!Objects.equals(activity.getType(), ObjectActivityConstants.ACTIVITY_TYPE_MEMBER)) {
|
||||
continue;
|
||||
}
|
||||
Long beforeRoleId = getFieldChangeLong(activity.getDetails(), "roleId", "before");
|
||||
Long afterRoleId = getFieldChangeLong(activity.getDetails(), "roleId", "after");
|
||||
if (beforeRoleId != null) {
|
||||
roleIds.add(beforeRoleId);
|
||||
}
|
||||
if (afterRoleId != null) {
|
||||
roleIds.add(afterRoleId);
|
||||
}
|
||||
}
|
||||
if (roleIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, String> roleNameMap = loadRoleNameMap(roleIds);
|
||||
for (ProductActivityRespVO activity : activities) {
|
||||
if (!Objects.equals(activity.getType(), ObjectActivityConstants.ACTIVITY_TYPE_MEMBER)) {
|
||||
continue;
|
||||
}
|
||||
activity.setDetails(appendRoleNames(activity.getDetails(), roleNameMap));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Long, String> loadRoleNameMap(Set<Long> roleIds) {
|
||||
Map<Long, String> roleNameMap = new LinkedHashMap<>();
|
||||
if (roleIds == null || roleIds.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
Cache cache = cacheManager == null ? null : cacheManager.getCache(ACTIVITY_ROLE_NAME_CACHE);
|
||||
Set<Long> missIds = new LinkedHashSet<>();
|
||||
for (Long roleId : roleIds) {
|
||||
String roleName = cache == null ? null : cache.get(roleId, String.class);
|
||||
if (roleName != null) {
|
||||
roleNameMap.put(roleId, roleName);
|
||||
} else {
|
||||
missIds.add(roleId);
|
||||
}
|
||||
}
|
||||
if (missIds.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
Map<Long, ObjectRoleRespDTO> roleMap = objectPermissionApi == null ? Map.of()
|
||||
: objectPermissionApi.getObjectRoleMap(missIds, ROLE_SCOPE_OBJECT, PRODUCT_OBJECT_TYPE);
|
||||
if (roleMap == null || roleMap.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
for (Long roleId : missIds) {
|
||||
ObjectRoleRespDTO role = roleMap.get(roleId);
|
||||
if (role == null || !StringUtils.hasText(role.getName())) {
|
||||
continue;
|
||||
}
|
||||
roleNameMap.put(roleId, role.getName());
|
||||
if (cache != null) {
|
||||
cache.put(roleId, role.getName());
|
||||
}
|
||||
}
|
||||
return roleNameMap;
|
||||
}
|
||||
|
||||
private Long getFieldChangeLong(String fieldChanges, String fieldName, String valueField) {
|
||||
JsonNode valueNode = getFieldChangeNode(fieldChanges, fieldName, valueField);
|
||||
if (valueNode == null || valueNode.isNull()) {
|
||||
return null;
|
||||
}
|
||||
if (valueNode.isNumber()) {
|
||||
return valueNode.longValue();
|
||||
}
|
||||
if (valueNode.isTextual() && StringUtils.hasText(valueNode.textValue())) {
|
||||
return Long.valueOf(valueNode.textValue().trim());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JsonNode getFieldChangeNode(String fieldChanges, String fieldName, String valueField) {
|
||||
if (!StringUtils.hasText(fieldChanges) || !JsonUtils.isJsonObject(fieldChanges)) {
|
||||
return null;
|
||||
}
|
||||
JsonNode fieldNode = JsonUtils.parseTree(fieldChanges).path(fieldName);
|
||||
if (fieldNode.isMissingNode()) {
|
||||
return null;
|
||||
}
|
||||
JsonNode valueNode = fieldNode.path(valueField);
|
||||
return valueNode.isMissingNode() ? null : valueNode;
|
||||
}
|
||||
|
||||
private String appendRoleNames(String details, Map<Long, String> roleNameMap) {
|
||||
if (!StringUtils.hasText(details) || !JsonUtils.isJsonObject(details)) {
|
||||
return details;
|
||||
}
|
||||
Long beforeRoleId = getFieldChangeLong(details, "roleId", "before");
|
||||
Long afterRoleId = getFieldChangeLong(details, "roleId", "after");
|
||||
if (beforeRoleId == null && afterRoleId == null) {
|
||||
return details;
|
||||
}
|
||||
JsonNode detailsNode = JsonUtils.parseTree(details);
|
||||
if (!(detailsNode instanceof ObjectNode)) {
|
||||
return details;
|
||||
}
|
||||
ObjectNode objectNode = ((ObjectNode) detailsNode).deepCopy();
|
||||
ObjectNode roleNameNode = objectNode.putObject("roleName");
|
||||
appendRoleName(roleNameNode, "before", beforeRoleId, roleNameMap);
|
||||
appendRoleName(roleNameNode, "after", afterRoleId, roleNameMap);
|
||||
return JsonUtils.toJsonString(objectNode);
|
||||
}
|
||||
|
||||
private void appendRoleName(ObjectNode roleNameNode, String fieldName, Long roleId, Map<Long, String> roleNameMap) {
|
||||
if (roleId == null) {
|
||||
roleNameNode.putNull(fieldName);
|
||||
return;
|
||||
}
|
||||
String roleName = roleNameMap.get(roleId);
|
||||
if (StringUtils.hasText(roleName)) {
|
||||
roleNameNode.put(fieldName, roleName);
|
||||
return;
|
||||
}
|
||||
roleNameNode.putNull(fieldName);
|
||||
}
|
||||
|
||||
private boolean includeType(String actual, String expected) {
|
||||
return !StringUtils.hasText(actual) || Objects.equals(actual.trim(), expected);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.njcn.rdms.module.project.service.product;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.common.util.object.PageUtils;
|
||||
@@ -13,6 +14,8 @@ import com.njcn.rdms.module.project.dal.dataobject.product.ProductStatusLogDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.product.ProductStatusLogMapper;
|
||||
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
|
||||
import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -40,6 +43,9 @@ public class ProductActivityTimelineQueryService {
|
||||
* 成员名称在读取时间线时再通过缓存转换,避免把昵称快照写进动态记录。
|
||||
*/
|
||||
private static final String TIMELINE_USER_NICKNAME_CACHE = "project_timeline_user_nickname#10m";
|
||||
private static final String TIMELINE_ROLE_NAME_CACHE = "project_timeline_role_name#10m";
|
||||
private static final String ROLE_SCOPE_OBJECT = "object";
|
||||
private static final String PRODUCT_OBJECT_TYPE = ObjectActivityConstants.PRODUCT_BIZ_TYPE;
|
||||
|
||||
@Resource
|
||||
private ProductStatusLogMapper productStatusLogMapper;
|
||||
@@ -51,6 +57,8 @@ public class ProductActivityTimelineQueryService {
|
||||
private CacheManager cacheManager;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private ObjectPermissionApi objectPermissionApi;
|
||||
|
||||
public PageResult<ProductActivityTimelineRespVO> getProductActivityTimelinePage(
|
||||
Long productId, ProductActivityTimelinePageReqVO reqVO) {
|
||||
@@ -68,6 +76,7 @@ public class ProductActivityTimelineQueryService {
|
||||
PageResult<ProductActivityTimelineRespVO> pageResult =
|
||||
buildPageResult(items.stream().map(ActivityItem::respVO).toList(), reqVO);
|
||||
fillTargetUserNames(pageResult.getList());
|
||||
fillMemberRoleNames(pageResult.getList());
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
@@ -309,6 +318,36 @@ public class ProductActivityTimelineQueryService {
|
||||
}
|
||||
}
|
||||
|
||||
private void fillMemberRoleNames(List<ProductActivityTimelineRespVO> activities) {
|
||||
if (activities == null || activities.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<Long> roleIds = new LinkedHashSet<>();
|
||||
for (ProductActivityTimelineRespVO activity : activities) {
|
||||
if (!Objects.equals(activity.getType(), ObjectActivityConstants.ACTIVITY_TYPE_MEMBER)) {
|
||||
continue;
|
||||
}
|
||||
Long beforeRoleId = getFieldChangeLong(activity.getDetails(), "roleId", "before");
|
||||
Long afterRoleId = getFieldChangeLong(activity.getDetails(), "roleId", "after");
|
||||
if (beforeRoleId != null) {
|
||||
roleIds.add(beforeRoleId);
|
||||
}
|
||||
if (afterRoleId != null) {
|
||||
roleIds.add(afterRoleId);
|
||||
}
|
||||
}
|
||||
if (roleIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, String> roleNameMap = loadRoleNameMap(roleIds);
|
||||
for (ProductActivityTimelineRespVO activity : activities) {
|
||||
if (!Objects.equals(activity.getType(), ObjectActivityConstants.ACTIVITY_TYPE_MEMBER)) {
|
||||
continue;
|
||||
}
|
||||
activity.setDetails(appendRoleNames(activity.getDetails(), roleNameMap));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Long, String> loadUserNicknameMap(Set<Long> userIds) {
|
||||
Map<Long, String> nicknameMap = new LinkedHashMap<>();
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
@@ -344,6 +383,75 @@ public class ProductActivityTimelineQueryService {
|
||||
return nicknameMap;
|
||||
}
|
||||
|
||||
private Map<Long, String> loadRoleNameMap(Set<Long> roleIds) {
|
||||
Map<Long, String> roleNameMap = new LinkedHashMap<>();
|
||||
if (roleIds == null || roleIds.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
Cache cache = cacheManager == null ? null : cacheManager.getCache(TIMELINE_ROLE_NAME_CACHE);
|
||||
Set<Long> missIds = new LinkedHashSet<>();
|
||||
for (Long roleId : roleIds) {
|
||||
String roleName = cache == null ? null : cache.get(roleId, String.class);
|
||||
if (roleName != null) {
|
||||
roleNameMap.put(roleId, roleName);
|
||||
} else {
|
||||
missIds.add(roleId);
|
||||
}
|
||||
}
|
||||
if (missIds.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
Map<Long, ObjectRoleRespDTO> roleMap = objectPermissionApi == null ? Map.of()
|
||||
: objectPermissionApi.getObjectRoleMap(missIds, ROLE_SCOPE_OBJECT, PRODUCT_OBJECT_TYPE);
|
||||
if (roleMap == null || roleMap.isEmpty()) {
|
||||
return roleNameMap;
|
||||
}
|
||||
for (Long roleId : missIds) {
|
||||
ObjectRoleRespDTO role = roleMap.get(roleId);
|
||||
if (role == null || !StringUtils.hasText(role.getName())) {
|
||||
continue;
|
||||
}
|
||||
roleNameMap.put(roleId, role.getName());
|
||||
if (cache != null) {
|
||||
cache.put(roleId, role.getName());
|
||||
}
|
||||
}
|
||||
return roleNameMap;
|
||||
}
|
||||
|
||||
private String appendRoleNames(String details, Map<Long, String> roleNameMap) {
|
||||
if (!StringUtils.hasText(details) || !JsonUtils.isJsonObject(details)) {
|
||||
return details;
|
||||
}
|
||||
Long beforeRoleId = getFieldChangeLong(details, "roleId", "before");
|
||||
Long afterRoleId = getFieldChangeLong(details, "roleId", "after");
|
||||
if (beforeRoleId == null && afterRoleId == null) {
|
||||
return details;
|
||||
}
|
||||
JsonNode detailsNode = JsonUtils.parseTree(details);
|
||||
if (!(detailsNode instanceof ObjectNode)) {
|
||||
return details;
|
||||
}
|
||||
ObjectNode objectNode = ((ObjectNode) detailsNode).deepCopy();
|
||||
ObjectNode roleNameNode = objectNode.putObject("roleName");
|
||||
appendRoleName(roleNameNode, "before", beforeRoleId, roleNameMap);
|
||||
appendRoleName(roleNameNode, "after", afterRoleId, roleNameMap);
|
||||
return JsonUtils.toJsonString(objectNode);
|
||||
}
|
||||
|
||||
private void appendRoleName(ObjectNode roleNameNode, String fieldName, Long roleId, Map<Long, String> roleNameMap) {
|
||||
if (roleId == null) {
|
||||
roleNameNode.putNull(fieldName);
|
||||
return;
|
||||
}
|
||||
String roleName = roleNameMap.get(roleId);
|
||||
if (StringUtils.hasText(roleName)) {
|
||||
roleNameNode.put(fieldName, roleName);
|
||||
return;
|
||||
}
|
||||
roleNameNode.putNull(fieldName);
|
||||
}
|
||||
|
||||
private ProductActivityTimelineRespVO toStatusTimeline(ProductStatusLogDO log) {
|
||||
ProductActivityTimelineRespVO respVO = new ProductActivityTimelineRespVO();
|
||||
respVO.setId(ObjectActivityConstants.ACTIVITY_TYPE_STATUS + ":" + log.getId());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.njcn.rdms.module.project.service.product;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.njcn.rdms.module.project.controller.admin.product.vo.activity.ProductActivityPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.product.vo.activity.ProductActivityRespVO;
|
||||
@@ -10,14 +11,23 @@ import com.njcn.rdms.module.project.dal.dataobject.product.ProductStatusLogDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.product.ProductStatusLogMapper;
|
||||
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
|
||||
import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anySet;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ProductActivityQueryServiceTest extends BaseMockitoUnitTest {
|
||||
@@ -30,6 +40,12 @@ class ProductActivityQueryServiceTest extends BaseMockitoUnitTest {
|
||||
private BizAuditLogMapper bizAuditLogMapper;
|
||||
@Mock
|
||||
private UserObjectRoleMapper userObjectRoleMapper;
|
||||
@Mock
|
||||
private CacheManager cacheManager;
|
||||
@Mock
|
||||
private Cache roleNameCache;
|
||||
@Mock
|
||||
private ObjectPermissionApi objectPermissionApi;
|
||||
|
||||
@Test
|
||||
void getProductActivities_shouldMergeStatusProductAndMemberActivities() {
|
||||
@@ -110,4 +126,88 @@ class ProductActivityQueryServiceTest extends BaseMockitoUnitTest {
|
||||
assertEquals(0, result.getList().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductActivities_shouldAppendRoleNameToMemberDetails() {
|
||||
Long productId = 1003L;
|
||||
ProductActivityPageReqVO reqVO = new ProductActivityPageReqVO();
|
||||
reqVO.setPageNo(1);
|
||||
reqVO.setPageSize(10);
|
||||
reqVO.setActivityType("member");
|
||||
|
||||
BizAuditLogDO memberAudit = new BizAuditLogDO();
|
||||
memberAudit.setId(55L);
|
||||
memberAudit.setBizType("rdms_user_object_role");
|
||||
memberAudit.setBizId(9002L);
|
||||
memberAudit.setActionType("update_member");
|
||||
memberAudit.setFieldChanges("{\"roleId\":{\"before\":3201,\"after\":3202}}");
|
||||
memberAudit.setCreateTime(LocalDateTime.of(2026, 4, 21, 13, 0, 0));
|
||||
|
||||
UserObjectRoleDO member = new UserObjectRoleDO();
|
||||
member.setId(9002L);
|
||||
member.setObjectType("product");
|
||||
member.setObjectId(productId);
|
||||
|
||||
when(bizAuditLogMapper.selectListByBizType("rdms_user_object_role", null, null))
|
||||
.thenReturn(List.of(memberAudit));
|
||||
when(userObjectRoleMapper.selectListByIdsAndObject(List.of(9002L), "product", productId))
|
||||
.thenReturn(List.of(member));
|
||||
when(cacheManager.getCache("project_activity_role_name#10m")).thenReturn(roleNameCache);
|
||||
when(roleNameCache.get(3201L, String.class)).thenReturn("产品经理");
|
||||
when(roleNameCache.get(3202L, String.class)).thenReturn("产品成员");
|
||||
|
||||
PageResult<ProductActivityRespVO> result = productActivityQueryService.getProductActivities(productId, reqVO);
|
||||
|
||||
assertEquals("产品经理", JsonUtils.parseTree(result.getList().get(0).getDetails())
|
||||
.path("roleName").path("before").asText());
|
||||
assertEquals("产品成员", JsonUtils.parseTree(result.getList().get(0).getDetails())
|
||||
.path("roleName").path("after").asText());
|
||||
verify(objectPermissionApi, never()).getObjectRoleMap(anySet(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductActivities_shouldLoadAndCacheRoleNameWhenCacheMiss() {
|
||||
Long productId = 1004L;
|
||||
ProductActivityPageReqVO reqVO = new ProductActivityPageReqVO();
|
||||
reqVO.setPageNo(1);
|
||||
reqVO.setPageSize(10);
|
||||
reqVO.setActivityType("member");
|
||||
|
||||
BizAuditLogDO memberAudit = new BizAuditLogDO();
|
||||
memberAudit.setId(66L);
|
||||
memberAudit.setBizType("rdms_user_object_role");
|
||||
memberAudit.setBizId(9003L);
|
||||
memberAudit.setActionType("add_member");
|
||||
memberAudit.setFieldChanges("{\"roleId\":{\"before\":null,\"after\":3203}}");
|
||||
memberAudit.setCreateTime(LocalDateTime.of(2026, 4, 21, 14, 0, 0));
|
||||
|
||||
UserObjectRoleDO member = new UserObjectRoleDO();
|
||||
member.setId(9003L);
|
||||
member.setObjectType("product");
|
||||
member.setObjectId(productId);
|
||||
|
||||
when(bizAuditLogMapper.selectListByBizType("rdms_user_object_role", null, null))
|
||||
.thenReturn(List.of(memberAudit));
|
||||
when(userObjectRoleMapper.selectListByIdsAndObject(List.of(9003L), "product", productId))
|
||||
.thenReturn(List.of(member));
|
||||
when(cacheManager.getCache("project_activity_role_name#10m")).thenReturn(roleNameCache);
|
||||
when(roleNameCache.get(3203L, String.class)).thenReturn(null);
|
||||
when(objectPermissionApi.getObjectRoleMap(java.util.Set.of(3203L), "object", "product"))
|
||||
.thenReturn(Map.of(3203L, buildRole(3203L, "观察者")));
|
||||
|
||||
PageResult<ProductActivityRespVO> result = productActivityQueryService.getProductActivities(productId, reqVO);
|
||||
|
||||
assertEquals("观察者", JsonUtils.parseTree(result.getList().get(0).getDetails())
|
||||
.path("roleName").path("after").asText());
|
||||
verify(roleNameCache).put(3203L, "观察者");
|
||||
}
|
||||
|
||||
private ObjectRoleRespDTO buildRole(Long id, String name) {
|
||||
ObjectRoleRespDTO role = new ObjectRoleRespDTO();
|
||||
role.setId(id);
|
||||
role.setName(name);
|
||||
role.setScopeType("object");
|
||||
role.setObjectType("product");
|
||||
return role;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.njcn.rdms.module.project.service.product;
|
||||
|
||||
import com.njcn.rdms.framework.common.exception.ServiceException;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import com.njcn.rdms.module.project.controller.admin.product.vo.activity.ProductActivityTimelinePageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.product.vo.activity.ProductActivityTimelineRespVO;
|
||||
@@ -11,6 +12,8 @@ import com.njcn.rdms.module.project.dal.dataobject.product.ProductStatusLogDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.product.ProductStatusLogMapper;
|
||||
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
|
||||
import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -51,7 +54,11 @@ class ProductActivityTimelineQueryServiceTest extends BaseMockitoUnitTest {
|
||||
@Mock
|
||||
private Cache userNicknameCache;
|
||||
@Mock
|
||||
private Cache roleNameCache;
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
@Mock
|
||||
private ObjectPermissionApi objectPermissionApi;
|
||||
|
||||
@Test
|
||||
void getProductActivityTimelinePage_whenTimeRangeMissingEnd_shouldThrowInvalidParam() {
|
||||
@@ -289,6 +296,64 @@ class ProductActivityTimelineQueryServiceTest extends BaseMockitoUnitTest {
|
||||
verify(userNicknameCache).put(2005L, "成员戊");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductActivityTimelinePage_shouldReadRoleNameFromCacheFirst() {
|
||||
Long productId = 1009L;
|
||||
ProductActivityTimelinePageReqVO reqVO = new ProductActivityTimelinePageReqVO();
|
||||
reqVO.setPageNo(1);
|
||||
reqVO.setPageSize(10);
|
||||
reqVO.setActivityType("member");
|
||||
|
||||
BizAuditLogDO addMemberLog = buildMemberLog(81L, 9006L, "add_member",
|
||||
"{\"roleId\":{\"before\":null,\"after\":3101},\"userId\":{\"before\":null,\"after\":2006}}",
|
||||
LocalDateTime.of(2026, 4, 21, 19, 0, 0));
|
||||
when(bizAuditLogMapper.selectListByBizTypeAndActions(eq("rdms_user_object_role"), any(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class)))
|
||||
.thenReturn(List.of(addMemberLog));
|
||||
when(userObjectRoleMapper.selectListByIdsAndObject(List.of(9006L), "product", productId))
|
||||
.thenReturn(List.of(buildMember(9006L, productId, 2006L)));
|
||||
when(cacheManager.getCache("project_timeline_user_nickname#10m")).thenReturn(userNicknameCache);
|
||||
when(cacheManager.getCache("project_timeline_role_name#10m")).thenReturn(roleNameCache);
|
||||
when(roleNameCache.get(3101L, String.class)).thenReturn("产品经理");
|
||||
|
||||
PageResult<ProductActivityTimelineRespVO> result =
|
||||
productActivityTimelineQueryService.getProductActivityTimelinePage(productId, reqVO);
|
||||
|
||||
assertEquals("产品经理", JsonUtils.parseTree(result.getList().get(0).getDetails())
|
||||
.path("roleName").path("after").asText());
|
||||
verify(objectPermissionApi, never()).getObjectRoleMap(anySet(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProductActivityTimelinePage_shouldLoadAndCacheRoleNameWhenCacheMiss() {
|
||||
Long productId = 1010L;
|
||||
ProductActivityTimelinePageReqVO reqVO = new ProductActivityTimelinePageReqVO();
|
||||
reqVO.setPageNo(1);
|
||||
reqVO.setPageSize(10);
|
||||
reqVO.setActivityType("member");
|
||||
|
||||
BizAuditLogDO removeMemberLog = buildMemberLog(91L, 9007L, "remove_member",
|
||||
"{\"roleId\":{\"before\":3102,\"after\":3102},\"userId\":{\"before\":2007,\"after\":2007}}",
|
||||
LocalDateTime.of(2026, 4, 21, 20, 0, 0));
|
||||
when(bizAuditLogMapper.selectListByBizTypeAndActions(eq("rdms_user_object_role"), any(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class)))
|
||||
.thenReturn(List.of(removeMemberLog));
|
||||
when(userObjectRoleMapper.selectListByIdsAndObject(List.of(9007L), "product", productId))
|
||||
.thenReturn(List.of(buildMember(9007L, productId, 2007L)));
|
||||
when(cacheManager.getCache("project_timeline_user_nickname#10m")).thenReturn(userNicknameCache);
|
||||
when(cacheManager.getCache("project_timeline_role_name#10m")).thenReturn(roleNameCache);
|
||||
when(roleNameCache.get(3102L, String.class)).thenReturn(null);
|
||||
when(objectPermissionApi.getObjectRoleMap(java.util.Set.of(3102L), "object", "product"))
|
||||
.thenReturn(Map.of(3102L, buildRole(3102L, "产品成员")));
|
||||
|
||||
PageResult<ProductActivityTimelineRespVO> result =
|
||||
productActivityTimelineQueryService.getProductActivityTimelinePage(productId, reqVO);
|
||||
|
||||
assertEquals("产品成员", JsonUtils.parseTree(result.getList().get(0).getDetails())
|
||||
.path("roleName").path("before").asText());
|
||||
verify(roleNameCache).put(3102L, "产品成员");
|
||||
}
|
||||
|
||||
private BizAuditLogDO buildProductLog(Long id, Long productId, String actionType,
|
||||
String fieldChanges, LocalDateTime createTime) {
|
||||
BizAuditLogDO log = new BizAuditLogDO();
|
||||
@@ -347,4 +412,13 @@ class ProductActivityTimelineQueryServiceTest extends BaseMockitoUnitTest {
|
||||
return user;
|
||||
}
|
||||
|
||||
private ObjectRoleRespDTO buildRole(Long id, String name) {
|
||||
ObjectRoleRespDTO role = new ObjectRoleRespDTO();
|
||||
role.setId(id);
|
||||
role.setName(name);
|
||||
role.setScopeType("object");
|
||||
role.setObjectType("product");
|
||||
return role;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user