diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java index 4dc11a7..92a2df5 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -59,6 +59,12 @@ public class SysNoticeController extends BaseController @ResponseBody public TableDataInfo list(SysNotice notice) { + SysUser currentUser = ShiroUtils.getSysUser(); + Long userId = currentUser == null ? null : currentUser.getUserId(); + if (!canManageNotice(userId)) + { + notice.getParams().put("visibleUserId", userId == null ? -1L : userId); + } startPage(); List list = noticeService.selectNoticeList(notice); return getDataTable(list); @@ -208,6 +214,12 @@ public class SysNoticeController extends BaseController private boolean canManageNotice(Long userId) { - return ShiroUtils.isAdmin(userId) || ShiroUtils.getSubject().isPermitted("system:notice:list"); + if (userId == null) + { + return false; + } + return ShiroUtils.isAdmin(userId) || ShiroUtils.getSubject().isPermitted("system:notice:add") + || ShiroUtils.getSubject().isPermitted("system:notice:edit") + || ShiroUtils.getSubject().isPermitted("system:notice:remove"); } } diff --git a/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImpl.java b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImpl.java index c501668..a717e7d 100644 --- a/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImpl.java +++ b/ruoyi-asset/src/main/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImpl.java @@ -1,10 +1,10 @@ package com.ruoyi.asset.service.impl; -import java.text.SimpleDateFormat; import java.util.List; +import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.util.HtmlUtils; import com.ruoyi.asset.constant.BorrowOrderStatus; import com.ruoyi.asset.domain.AmsBorrowOrder; @@ -43,8 +43,10 @@ public class BorrowReturnReminderServiceImpl implements IBorrowReturnReminderSer @Autowired private ISysNoticeReceiverService noticeReceiverService; + @Autowired + private TransactionTemplate transactionTemplate; + @Override - @Transactional(rollbackFor = Exception.class) public int remindOverdueBorrowOrders() { List candidates = amsBorrowOrderMapper.selectOverdueBorrowReturnReminderCandidates( @@ -61,7 +63,11 @@ public class BorrowReturnReminderServiceImpl implements IBorrowReturnReminderSer { continue; } - createdCount += createReminder(order); + Integer created = transactionTemplate.execute(status -> createReminder(order)); + if (created != null) + { + createdCount += created; + } } return createdCount; } @@ -110,7 +116,7 @@ public class BorrowReturnReminderServiceImpl implements IBorrowReturnReminderSer { return ""; } - return new SimpleDateFormat("yyyy-MM-dd").format(order.getExpectedReturnDate()); + return DateFormatUtils.format(order.getExpectedReturnDate(), "yyyy-MM-dd"); } private String escape(String value) diff --git a/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImplTest.java b/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImplTest.java index 37c92a2..66fc6b6 100644 --- a/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImplTest.java +++ b/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/BorrowReturnReminderServiceImplTest.java @@ -25,6 +25,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; @ExtendWith(MockitoExtension.class) class BorrowReturnReminderServiceImplTest @@ -38,6 +40,9 @@ class BorrowReturnReminderServiceImplTest @Mock private ISysNoticeReceiverService noticeReceiverService; + @Mock + private TransactionTemplate transactionTemplate; + @InjectMocks private BorrowReturnReminderServiceImpl service; @@ -45,6 +50,7 @@ class BorrowReturnReminderServiceImplTest @Test void remindShouldCreateDirectedNoticeForDueCandidate() { + mockTransactionTemplate(); AmsBorrowOrder order = buildOrder(); when(amsBorrowOrderMapper.selectOverdueBorrowReturnReminderCandidates( BorrowOrderStatus.BORROWING, BorrowOrderStatus.BORROWING)) @@ -95,6 +101,7 @@ class BorrowReturnReminderServiceImplTest @Test void remindShouldFailFastWhenReceiverInsertFails() { + mockTransactionTemplate(); AmsBorrowOrder order = buildOrder(); when(amsBorrowOrderMapper.selectOverdueBorrowReturnReminderCandidates( BorrowOrderStatus.BORROWING, BorrowOrderStatus.BORROWING)) @@ -113,6 +120,14 @@ class BorrowReturnReminderServiceImplTest assertTrue(exception.getMessage().contains("接收人写入异常")); } + private void mockTransactionTemplate() + { + when(transactionTemplate.execute(any())).thenAnswer(invocation -> { + TransactionCallback callback = invocation.getArgument(0); + return callback.doInTransaction(null); + }); + } + private AmsBorrowOrder buildOrder() { AmsBorrowOrder order = new AmsBorrowOrder(); diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 81730c0..8cf15bb 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -23,6 +23,22 @@ ruoyi-common + + org.springframework.boot + spring-boot-starter-test + test + + - \ No newline at end of file + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + + + + + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReceiverMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReceiverMapper.java index 27a500b..cde80ff 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReceiverMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeReceiverMapper.java @@ -1,5 +1,6 @@ package com.ruoyi.system.mapper; +import java.util.List; import org.apache.ibatis.annotations.Param; import com.ruoyi.system.domain.SysNoticeReceiver; @@ -46,11 +47,13 @@ public interface SysNoticeReceiverMapper public int countBySource(@Param("sourceType") String sourceType, @Param("sourceId") Long sourceId, @Param("userId") Long userId); + public List selectViewableNoticeIds(@Param("noticeIds") Long[] noticeIds, @Param("userId") Long userId); + /** * 通知删除时清理接收人关系 * * @param noticeIds 通知公告ID数组 * @return 结果 */ - public int deleteByNoticeIds(@Param("noticeIds") String[] noticeIds); + public int deleteByNoticeIds(@Param("noticeIds") Long[] noticeIds); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReceiverService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReceiverService.java index 0a88267..76aaed7 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReceiverService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeReceiverService.java @@ -1,5 +1,6 @@ package com.ruoyi.system.service; +import java.util.List; import com.ruoyi.system.domain.SysNoticeReceiver; /** @@ -28,6 +29,8 @@ public interface ISysNoticeReceiverService */ public boolean isSourceNotified(String sourceType, Long sourceId, Long userId); + public List filterViewableNoticeIds(Long[] noticeIds, Long userId); + /** * 新增通知公告接收人 * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java index cfc2b7a..eebe953 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReadServiceImpl.java @@ -2,7 +2,6 @@ package com.ruoyi.system.service.impl; import java.util.List; import java.util.Map; -import java.util.Arrays; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.common.core.text.Convert; @@ -70,14 +69,12 @@ public class SysNoticeReadServiceImpl implements ISysNoticeReadService { return; } - Long[] readableNoticeIds = Arrays.stream(noticeIds) - .filter(noticeId -> noticeReceiverService.canViewNotice(noticeId, userId)) - .toArray(Long[]::new); - if (readableNoticeIds.length == 0) + List readableNoticeIds = noticeReceiverService.filterViewableNoticeIds(noticeIds, userId); + if (readableNoticeIds.isEmpty()) { return; } - noticeReadMapper.insertNoticeReadBatch(userId, readableNoticeIds); + noticeReadMapper.insertNoticeReadBatch(userId, readableNoticeIds.toArray(new Long[0])); } /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImpl.java index be3789d..7334419 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImpl.java @@ -1,5 +1,9 @@ package com.ruoyi.system.service.impl; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.common.core.text.Convert; @@ -43,6 +47,23 @@ public class SysNoticeReceiverServiceImpl implements ISysNoticeReceiverService return noticeReceiverMapper.countBySource(sourceType, sourceId, userId) > 0; } + @Override + public List filterViewableNoticeIds(Long[] noticeIds, Long userId) + { + if (noticeIds == null || noticeIds.length == 0 || userId == null) + { + return Collections.emptyList(); + } + Long[] effectiveNoticeIds = Arrays.stream(noticeIds) + .filter(Objects::nonNull) + .toArray(Long[]::new); + if (effectiveNoticeIds.length == 0) + { + return Collections.emptyList(); + } + return noticeReceiverMapper.selectViewableNoticeIds(effectiveNoticeIds, userId); + } + @Override public int insertNoticeReceiver(SysNoticeReceiver noticeReceiver) { @@ -56,7 +77,7 @@ public class SysNoticeReceiverServiceImpl implements ISysNoticeReceiverService @Override public void deleteByNoticeIds(String ids) { - String[] noticeIds = Convert.toStrArray(ids); + Long[] noticeIds = Convert.toLongArray(ids); if (noticeIds.length == 0) { return; diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml index c8a4d1e..141390f 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -21,6 +21,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark from sys_notice + + + + AND ( + not exists (select 1 from sys_notice_receiver nr_all where nr_all.notice_id = sys_notice.notice_id) + or exists (select 1 from sys_notice_receiver nr_user where nr_user.notice_id = sys_notice.notice_id and nr_user.user_id = #{params.visibleUserId}) + ) + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml index ed13ea4..3cf4585 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeReadMapper.xml @@ -17,14 +17,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" values (#{noticeId}, #{userId}, sysdate()) - - + select count(*) from sys_notice n + where n.status = '0' + and not exists (select 1 from sys_notice_read r where r.notice_id = n.notice_id and r.user_id = #{userId}) @@ -47,10 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" left join sys_notice_read r on r.notice_id = n.notice_id and r.user_id = #{userId} where n.status = '0' - and ( - not exists (select 1 from sys_notice_receiver nr_all where nr_all.notice_id = n.notice_id) - or exists (select 1 from sys_notice_receiver nr_user where nr_user.notice_id = n.notice_id and nr_user.user_id = #{userId}) - ) + order by n.notice_id desc limit #{limit} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeReceiverMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeReceiverMapper.xml index 03327d5..a45fe13 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysNoticeReceiverMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeReceiverMapper.xml @@ -39,6 +39,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and user_id = #{userId} + + delete from sys_notice_receiver where notice_id in diff --git a/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/SysNoticeReceiverServiceImplTest.java b/ruoyi-system/src/test/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImplTest.java similarity index 72% rename from ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/SysNoticeReceiverServiceImplTest.java rename to ruoyi-system/src/test/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImplTest.java index 6b8c454..fb171b9 100644 --- a/ruoyi-asset/src/test/java/com/ruoyi/asset/service/impl/SysNoticeReceiverServiceImplTest.java +++ b/ruoyi-system/src/test/java/com/ruoyi/system/service/impl/SysNoticeReceiverServiceImplTest.java @@ -1,14 +1,18 @@ -package com.ruoyi.asset.service.impl; +package com.ruoyi.system.service.impl; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collections; import com.ruoyi.system.domain.SysNoticeReceiver; import com.ruoyi.system.mapper.SysNoticeReceiverMapper; -import com.ruoyi.system.service.impl.SysNoticeReceiverServiceImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -46,6 +50,21 @@ class SysNoticeReceiverServiceImplTest assertFalse(service.canViewNotice(10L, 3L)); } + /** 批量标记已读前先用一条 SQL 过滤当前用户真正可见的通知,避免逐条查权限。 */ + @Test + void filterViewableNoticeIdsShouldUseBatchMapper() + { + when(noticeReceiverMapper.selectViewableNoticeIds(any(Long[].class), eq(2L))) + .thenReturn(Collections.singletonList(10L)); + + assertEquals(Collections.singletonList(10L), + service.filterViewableNoticeIds(new Long[] { 10L, null, 11L }, 2L)); + + ArgumentCaptor noticeIdsCaptor = ArgumentCaptor.forClass(Long[].class); + verify(noticeReceiverMapper).selectViewableNoticeIds(noticeIdsCaptor.capture(), eq(2L)); + assertArrayEquals(new Long[] { 10L, 11L }, noticeIdsCaptor.getValue()); + } + /** 业务来源唯一键用于判断同一借用单是否已经提醒过该借用人。 */ @Test void isSourceNotifiedShouldUseSourceAndUser()