diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/user/SysUserRelationMapper.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/user/SysUserRelationMapper.java index 6260e5617..67453f6d0 100755 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/user/SysUserRelationMapper.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/user/SysUserRelationMapper.java @@ -3,8 +3,32 @@ package com.bsd.pjcloud.admin.mapper.user; import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity; import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +/** + * 用户关系数据访问层,负责关系记录的常规持久化及逻辑删除后的安全恢复。 + */ @Mapper public interface SysUserRelationMapper extends PjcloudBaseMapper { -} \ No newline at end of file + /** + * 按唯一键查询已逻辑删除的关系记录,查询范围由租户拦截器限制为当前租户。 + * + * @param subjectUserId 关系主语用户 ID + * @param objectUserId 关系对象用户 ID + * @param relationTypeId 关系类型 ID + * @return 已删除的关系记录,仅返回关系 ID;不存在时返回空 + */ + SysUserRelationEntity selectDeletedByUniqueKey(@Param("subjectUserId") Long subjectUserId, + @Param("objectUserId") Long objectUserId, + @Param("relationTypeId") Long relationTypeId); + + /** + * 恢复指定的已删除关系,并使用本次创建请求覆盖关系时效和备注信息。 + * + * @param relation 包含原关系 ID 以及本次创建请求数据的关系实体 + * @return 实际恢复的记录数,返回 0 表示记录已被并发恢复或不存在 + */ + int restoreDeletedRelation(@Param("relation") SysUserRelationEntity relation); + +} diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java index fa2fe57f4..b8c86fbdf 100755 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java @@ -26,6 +26,7 @@ import com.bsd.pjcloud.common.core.exception.ErrorCodes; import com.bsd.pjcloud.common.core.util.MsgUtils; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; +import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -482,6 +483,12 @@ public class SysUserRelationServiceImpl extends ServiceImpl + + - \ No newline at end of file + + + UPDATE sys_user_relation + SET effective_time = #{relation.effectiveTime}, + expire_time = #{relation.expireTime}, + remark = #{relation.remark}, + del_flag = '0', + update_time = CURRENT_TIMESTAMP + WHERE relation_id = #{relation.relationId} + AND del_flag = '1' + + diff --git a/pjcloud-upms/pjcloud-upms-biz/src/test/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImplTest.java b/pjcloud-upms/pjcloud-upms-biz/src/test/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImplTest.java index 3f02e5e1a..a1cfc2b72 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/test/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImplTest.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/test/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImplTest.java @@ -1,14 +1,18 @@ package com.bsd.pjcloud.admin.service.impl.user; import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.bsd.pjcloud.admin.api.dto.user.SysUserRelationCreateDto; import com.bsd.pjcloud.admin.api.entity.SysUser; import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationEntity; import com.bsd.pjcloud.admin.api.entity.user.SysUserRelationTypeEntity; import com.bsd.pjcloud.admin.api.vo.user.SysUserRelationGraphVo; +import com.bsd.pjcloud.admin.mapper.user.SysUserRelationMapper; import com.bsd.pjcloud.admin.service.SysUserRelationTypeService; import com.bsd.pjcloud.admin.service.SysUserService; import com.bsd.pjcloud.admin.service.SysUserStudentService; +import com.bsd.pjcloud.common.core.constant.CommonConstants; import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; import java.util.List; import java.util.Set; @@ -19,7 +23,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -164,6 +170,43 @@ class SysUserRelationServiceImplTest { verify(studentService).updateGuardianPhones(FIRST_WARD_USER_ID, null); } + /** + * 验证相同关系曾被逻辑删除时,创建接口恢复原记录而不是再次插入触发唯一索引冲突。 + */ + @Test + void shouldRestoreDeletedRelationInsteadOfInsertingDuplicate() { + SysUserService sysUserService = mock(SysUserService.class); + SysUserRelationTypeService relationTypeService = mock(SysUserRelationTypeService.class); + SysUserRelationMapper relationMapper = mock(SysUserRelationMapper.class); + SysUserRelationServiceImpl service = spy(new SysUserRelationServiceImpl( + sysUserService, relationTypeService, mock(SysUserStudentService.class))); + ReflectionTestUtils.setField(service, "baseMapper", relationMapper); + + SysUserRelationTypeEntity relationType = createGuardianRelationType(); + relationType.setStatus(CommonConstants.STATUS_NORMAL); + when(sysUserService.getById(GUARDIAN_USER_ID)).thenReturn(createUser(GUARDIAN_USER_ID, "监护人")); + when(sysUserService.getById(SECOND_WARD_USER_ID)).thenReturn(createUser(SECOND_WARD_USER_ID, "被监护人二")); + when(relationTypeService.getById(RELATION_TYPE_ID)).thenReturn(relationType); + doReturn(false).when(service).exists(org.mockito.ArgumentMatchers.>any()); + doNothing().when(service).syncStudentGuardianPhones(any()); + + SysUserRelationEntity deletedRelation = createRelation(99L, SECOND_WARD_USER_ID); + when(relationMapper.selectDeletedByUniqueKey(GUARDIAN_USER_ID, SECOND_WARD_USER_ID, RELATION_TYPE_ID)) + .thenReturn(deletedRelation); + when(relationMapper.restoreDeletedRelation(any())).thenReturn(1); + SysUserRelationCreateDto dto = new SysUserRelationCreateDto(); + dto.setSubjectUserId(GUARDIAN_USER_ID); + dto.setObjectUserId(SECOND_WARD_USER_ID); + dto.setRelationTypeId(RELATION_TYPE_ID); + + Long relationId = service.createRelation(dto); + + assertEquals(99L, relationId); + verify(relationMapper).restoreDeletedRelation(any()); + verify(relationMapper, never()).insert(any()); + verify(service).syncStudentGuardianPhones(List.of(SECOND_WARD_USER_ID)); + } + private SysUserRelationEntity createRelation(Long relationId, Long wardUserId) { SysUserRelationEntity relation = new SysUserRelationEntity(); relation.setRelationId(relationId);