ReportItems

在Reporting Services表达式中使用 ReportItems 集合引用 (转)
2009-03-19 11:49
ReportItems 内置集合是来自报表项(如报表设计图面上的数据区域行或文本框)的文本框集合。 ReportItems 集合包括位于表头、表尾或表体的当前作用域中的文本框。 此集合在运行时由报表处理器和报表呈现器确定。 用户查看报表页面时,如果报表处理器连续组合报表数据和报表项布局元素,则当前作用域将随之变化。 可以使用 ReportItems 内置集合在每个页面中生成显示首项和尾项的字典样式表头。

使用 ReportItems 值属性
ReportItems 集合内的项只有一个属性:Value。 ReportItems 项的值可用于显示或计算报表中其他字段的数据。 若要访问当前文本框的值,可以使用 Visual Basic 内置全局 Me.Value 或仅使用 Value。 在报表函数(如 First)和聚合函数中使用完全限定语法。

例如:

此表达式放置在文本框中时,显示名为 Textbox1 的 ReportItem 文本框的值:
=ReportItems.Textbox1.Value
此表达式放置在 ReportItem 文本框的 Color 属性中,当值为 > 0 时以黑色显示文本;否则将以红色显示该值:
=IIF(Me.Value > 0,"Black","Red")
此表达式放置在表头或表尾的文本框中时,显示所呈现的报表每一页中名为 LastName 的文本框的第一个值:
=First(ReportItems("LastName").Value)
字典样式表头表达式
可以创建一个表头,显示此页第一个客户和最后一个客户。 因为在一个表达式中,表头中的文本框只能引用一次 ReportItems 内置集合,所以需要在表头添加两个文本框:一个用于第一个客户名称 (=First(ReportItems!textboxLastName.Value),另一个用于最后一个客户名称 (=Last(ReportItems!textboxLastName.Value)。

在表头或表尾部分,只有当前页中的文本框才能作为 ReportItems 集合的成员。 例如,在多页数据区域中,如果 ReportItems!textboxLastName.Value 引用一个只显示在首页的文本框,则可以在首页看到一个值,其他页则显示 #Error,表明该表达式不能按本文所述进行计算。

ReportItems 集合的作用域
处理报表时,文本框所在数据集、数据区域和组关联的上下文将计算表体或数据区域中的所有文本框。 对 ReportItems 集合的引用的作用域为当前作用域或高于当前作用域的任何点。

例如,位于父组行的文本框不能包含引用子组行的文本框名称的表达式。 此类表达式不会解析报表中的值,因为子行文本框超出了作用域。 有关详细信息,请参阅在表达式中使用内置报表函数和聚合函数 (Reporting Services)。

From:http://msdn.microsoft.com/zh-cn/library/bb934329.aspx
以下是onlineReportServiceImplTest的代码:package com.jd.jacp.unit.report; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.jd.jacp.client.service.ICicdClient; import com.jd.jacp.client.service.ICmdbClient; import com.jd.jacp.client.service.ItsmClientService; import com.jd.jacp.common.service.UserHelper; import com.jd.jacp.common.vo.UserVO; import com.jd.jacp.common.vo.enums.YesOrNoEnum; import com.jd.jacp.demand.entity.BizDemandUser; import com.jd.jacp.demand.service.IBizDemandUserService; import com.jd.jacp.notify.entity.BizNotifyRule; import com.jd.jacp.notify.entity.BizNotifySetting; import com.jd.jacp.notify.enums.BizNotifySendWayEnum; import com.jd.jacp.notify.service.IBizNotifyRuleService; import com.jd.jacp.notify.service.IBizNotifySettingService; import com.jd.jacp.report.dto.*; import com.jd.jacp.report.entity.BizReport; import com.jd.jacp.report.entity.BizReportItem; import com.jd.jacp.report.enums.AcceptanceReportEnum; import com.jd.jacp.report.service.IBizReportItemService; import com.jd.jacp.report.service.IBizReportService; import com.jd.jacp.report.service.impl.OnlineReportServiceImpl; import com.jd.jacp.report.vo.SprintOnlineReportInfoVO; import com.jd.jacp.space.dto.CardQuery; import com.jd.jacp.space.dto.SubSystemByParamsDTO; import com.jd.jacp.space.entity.BizSpace; import com.jd.jacp.space.entity.BizSpaceCard; import com.jd.jacp.space.entity.BizSpaceSprint; import com.jd.jacp.space.service.IBizSpaceCardService; import com.jd.jacp.space.service.IBizSpaceService; import com.jd.jacp.space.service.IBizSpaceSettingService; import com.jd.jacp.space.service.IBizSpaceSprintService; import com.jd.jacp.space.vo.DetailVO; import com.jd.jacp.space.vo.QueryReportVO; import com.jd.jacp.unit.MockitoBasedTest; import com.msxf.devops.common.PageResult; import com.msxf.fcid.xmsg.reponse.ChatInfoDataResp; import org.apache.velocity.script.VelocityScriptEngine; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import static org.junit.Assert.*; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; import static org.mockito.Mockito.*; import javax.script.ScriptEngineManager; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; public class OnlineReportServiceImplTest extends MockitoBasedTest { @Spy @InjectMocks private OnlineReportServiceImpl onlineReportService; @Mock private IBizSpaceSprintService bizSpaceSprintService; @Mock private IBizSpaceSettingService spaceSettingService; @Mock private ScriptEngineManager scriptEngineManager; @Mock private IBizSpaceService bizSpaceService; @Mock private IBizReportService bizReportService; @Mock private IBizNotifySettingService bizNotifySettingService; @Mock private IBizNotifyRuleService bizNotifyRuleService; @Mock private ItsmClientService itsmClientService; @Mock private ICmdbClient cmdbClient; @Mock private ICicdClient cicdClient; @Mock private IBizSpaceCardService spaceCardService; @Mock private IBizDemandUserService demandUserService; @Mock private IBizReportItemService reportItemService; @Mock private UserHelper userHelper; @Test(expected = Exception.class) public void selectOnlineReportInfo_SprintNotExist_ThrowsException() { when(bizSpaceSprintService.getById(1)).thenReturn(null); onlineReportService.selectOnlineReportInfo(1); } @Test(expected = Exception.class) public void selectOnlineReportInfo_SpaceNotExist_ThrowsException() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setSpaceId(1); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(null); onlineReportService.selectOnlineReportInfo(1); } @Test public void selectOnlineReportInfo_Successful() { // Mock data BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); BizSpace space = new BizSpace().setId(1).setKey("key"); BizReport report = new BizReport().setId(1).setExtendInfo("{\"reportName\":\"Test Report\"}"); BizNotifySetting notifySetting = new BizNotifySetting().setId(1); BizNotifyRule notifyRule = new BizNotifyRule(); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(space); when(bizReportService.selectReport(any(), any(), anyInt())).thenReturn(report); when(bizNotifySettingService.getOneBy(anyString(), anyString())).thenReturn(notifySetting); when(bizNotifyRuleService.getOneByNotifyId(anyInt())).thenReturn(notifyRule); SprintOnlineReportInfoVO result = onlineReportService.selectOnlineReportInfo(1); assertNotNull(result); assertEquals("Test Report", result.getOnlineReport().getReportName()); assertNotNull(result.getNotifySetting()); } @Test public void selectOnlineReportInfo_NoNotifySetting() { // Mock data BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); BizSpace space = new BizSpace().setId(1).setKey("key"); BizReport report = new BizReport().setId(1).setExtendInfo("{\"reportName\":\"Test Report\"}"); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(space); when(bizReportService.selectReport(any(), any(), anyInt())).thenReturn(report); when(bizNotifySettingService.getOneBy(anyString(), anyString())).thenReturn(null); SprintOnlineReportInfoVO result = onlineReportService.selectOnlineReportInfo(1); assertNotNull(result); assertEquals("Test Report", result.getOnlineReport().getReportName()); assertNull(result.getNotifySetting()); } @Test public void getOnlineReportBaseInfoDTO_test() { when(bizReportService.selectReport(any(), any(), any())).thenReturn(new BizReport()); when(bizNotifySettingService.getOneBy(any(), any())).thenReturn(null); onlineReportService.getOnlineReportBaseInfoDTO(new BizSpaceSprint(), "1"); } @Test public void saveSprintNotifySetting_Test() { SprintOnlineReportInfoVO reportInfoVO = new SprintOnlineReportInfoVO(); reportInfoVO.setNotifySetting(new NotifySettingDTO()); onlineReportService.saveSprintNotifySetting(reportInfoVO); } @Test(expected = Exception.class) public void saveSpaceNotifySetting_test() { onlineReportService.saveSpaceNotifySetting(new SprintOnlineReportInfoVO()); } @Test public void saveSpaceNotifySetting_test1() { when(bizSpaceSprintService.getById(any())).thenReturn(new BizSpaceSprint().setSpaceId(1)); SprintOnlineReportInfoVO reportInfoVO = new SprintOnlineReportInfoVO(); reportInfoVO.setNotifySetting(new NotifySettingDTO()); reportInfoVO.setOnlineReport(new OnlineReportBaseInfoDTO()); onlineReportService.saveSpaceNotifySetting(reportInfoVO); } @Test public void selectSprintCardPage_NoRecords_ReturnEmptyPage() { CardQuery query = new CardQuery(); query.setPageNum(1); query.setPageSize(10); Page<BizSpaceCard> emptyPage = new Page<>(); emptyPage.setRecords(Collections.emptyList()); when(spaceCardService.queryCardByCondition(any(CardQuery.class))).thenReturn(emptyPage); PageResult<OnlineReportCardDTO> result = onlineReportService.selectSprintCardPage(query); assertTrue(result.getRecords().isEmpty()); assertEquals(1, result.getCurrent()); assertEquals(10, result.getSize()); } @Test public void selectSprintCardPage_WithRecords_ReturnFilledPage() { CardQuery query = new CardQuery(); query.setPageNum(1); query.setPageSize(10); query.setSprintId(1); // Mock card page BizSpaceCard card = new BizSpaceCard(); card.setId(1); Page<BizSpaceCard> cardPage = new Page<>(); cardPage.setRecords(Arrays.asList(card)); when(spaceCardService.queryCardByCondition(any(CardQuery.class))).thenReturn(cardPage); // Mock demand users List<BizDemandUser> demandUsers = Collections.emptyList(); when(demandUserService.selectByDemandIds(anyList(), any(Integer.class))).thenReturn(demandUsers); // Mock report map BizReport uatReport = new BizReport(); BizReport gradationReport = new BizReport(); Map<String, BizReport> reportMap = new java.util.HashMap<>(); reportMap.put(AcceptanceReportEnum.reportType.UAT.name(), uatReport); reportMap.put(AcceptanceReportEnum.reportType.GRADATION.name(), gradationReport); when(bizReportService.selectReportMap(anyList(), any(AcceptanceReportEnum.relationBizType.class), any(Integer.class))).thenReturn(reportMap); // Mock report items List<BizReportItem> reportItems = Collections.emptyList(); when(reportItemService.listReportItemByReportIds(anyList())).thenReturn(reportItems); // Mock user map Map<String, UserVO> userVOMap = new java.util.HashMap<>(); when(userHelper.getUserVoMapWithFSNameByErps(anyList(), any(Function.class), any(Function.class))).thenReturn(userVOMap); PageResult<OnlineReportCardDTO> result = onlineReportService.selectSprintCardPage(query); assertEquals(1, result.getRecords().size()); } @Test public void selectSprintCardPage_WithRecords_ReturnFilledPage2() { CardQuery query = new CardQuery(); query.setPageNum(1); query.setPageSize(10); query.setSprintId(1); // Mock card page BizSpaceCard card = new BizSpaceCard(); card.setId(1); Page<BizSpaceCard> cardPage = new Page<>(); cardPage.setRecords(Arrays.asList(card)); when(spaceCardService.queryCardByCondition(any(CardQuery.class))).thenReturn(cardPage); // Mock demand users List<BizDemandUser> demandUsers = Collections.emptyList(); when(demandUserService.selectByDemandIds(anyList(), any(Integer.class))).thenReturn(demandUsers); // Mock report map BizReport uatReport = new BizReport(); BizReport gradationReport = new BizReport(); Map<String, BizReport> reportMap = new java.util.HashMap<>(); when(bizReportService.selectReportMap(anyList(), any(AcceptanceReportEnum.relationBizType.class), any(Integer.class))).thenReturn(reportMap); // Mock report items List<BizReportItem> reportItems = Collections.emptyList(); when(reportItemService.listReportItemByReportIds(anyList())).thenReturn(reportItems); // Mock user map Map<String, UserVO> userVOMap = new java.util.HashMap<>(); when(userHelper.getUserVoMapWithFSNameByErps(anyList(), any(Function.class), any(Function.class))).thenReturn(userVOMap); PageResult<OnlineReportCardDTO> result = onlineReportService.selectSprintCardPage(query); assertEquals(1, result.getRecords().size()); } @Test public void selectSprintAppList_SprintNotExist_ReturnEmptyList() { when(bizSpaceSprintService.getById(anyInt())).thenReturn(null); List<OnlineReportAppDTO> result = onlineReportService.selectSprintAppList(1); assertEquals(Collections.emptyList(), result); } @Test public void selectSprintAppList_SpaceNotExist_ReturnEmptyList() { BizSpaceSprint bizSpaceSprint = new BizSpaceSprint(); bizSpaceSprint.setId(1); bizSpaceSprint.setSpaceId(1); when(bizSpaceSprintService.getById(anyInt())).thenReturn(bizSpaceSprint); when(bizSpaceService.getById(anyInt())).thenReturn(null); List<OnlineReportAppDTO> result = onlineReportService.selectSprintAppList(1); assertEquals(Collections.emptyList(), result); } @Test public void selectSprintAppList_SprintAndSpaceExist_ReturnAppList() { BizSpaceSprint bizSpaceSprint = new BizSpaceSprint(); bizSpaceSprint.setId(1); bizSpaceSprint.setSpaceId(1); BizSpace bizSpace = new BizSpace(); bizSpace.setId(1); bizSpace.setKey("key"); when(bizSpaceSprintService.getById(anyInt())).thenReturn(bizSpaceSprint); when(bizSpaceService.getById(anyInt())).thenReturn(bizSpace); List<OnlineReportAppDTO> result = onlineReportService.selectSprintAppList(1); } @Test public void selectSprintAppList_test1() { onlineReportService.selectSprintAppList(1, "2"); } @Test public void selectSprintAppList_test2() { QueryReportVO queryReportVO = new QueryReportVO(); queryReportVO.setDetail(Arrays.asList(new DetailVO())); when(itsmClientService.getQueryReport(any(), any())).thenReturn(queryReportVO); onlineReportService.selectSprintAppList(1, "2"); } @Test public void sendOnlineReportNow_SprintNotExist_ShouldThrowException() { SprintOnlineReportInfoVO param = new SprintOnlineReportInfoVO(); param.setSprintId(1); when(bizSpaceSprintService.getById(1)).thenReturn(null); assertThrows(IllegalArgumentException.class, () -> { onlineReportService.sendOnlineReportNow(param); }); } @Test public void sendOnlineReportNow_SpaceNotConfigured_ShouldThrowException() { SprintOnlineReportInfoVO param = new SprintOnlineReportInfoVO(); param.setSprintId(1); BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setSpaceId(1); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(onlineReportService.selectSpaceSwitch(1)).thenReturn(false); assertThrows(IllegalArgumentException.class, () -> { onlineReportService.sendOnlineReportNow(param); }); } @Test public void sendOnlineReportNow_ValidConditions_ShouldSendReport() { SprintOnlineReportInfoVO param = new SprintOnlineReportInfoVO(); param.setSprintId(1); param.setNotifySetting(new NotifySettingDTO()); param.setOnlineReport(new OnlineReportBaseInfoDTO()); BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setSpaceId(1); sprint.setArchived(1); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(onlineReportService.selectSpaceSwitch(1)).thenReturn(true); onlineReportService.sendOnlineReportNow(param); } @Test public void genDefaultOnlineReportName_test() { onlineReportService.genDefaultOnlineReportName(new BizSpaceSprint(), "1", "2"); } @Test public void genDefaultOnlineReportName_test2() { SubSystemByParamsDTO subSystemByParamsDTO = new SubSystemByParamsDTO(); subSystemByParamsDTO.setDepartment("11"); when(cmdbClient.queryProjectByCode(any())).thenReturn(subSystemByParamsDTO); onlineReportService.genDefaultOnlineReportName(new BizSpaceSprint(), "1", ""); } @Test public void sendOnlineReportByNotifySetting_SprintNotArchived_ShouldThrowException() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(YesOrNoEnum.NO.getNumCode()); NotifySettingDTO notifySetting = new NotifySettingDTO(); OnlineReportBaseInfoDTO reportBaseInfoDTO = new OnlineReportBaseInfoDTO(); onlineReportService.sendOnlineReportByNotifySetting(sprint, notifySetting, reportBaseInfoDTO); } @Test public void sendOnlineReportByNotifySetting_SpaceNotExist_ShouldLogWarning() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(YesOrNoEnum.YES.getNumCode()); sprint.setSpaceId(1); NotifySettingDTO notifySetting = new NotifySettingDTO(); OnlineReportBaseInfoDTO reportBaseInfoDTO = new OnlineReportBaseInfoDTO(); when(bizSpaceService.getById(1)).thenReturn(null); onlineReportService.sendOnlineReportByNotifySetting(sprint, notifySetting, reportBaseInfoDTO); verify(bizSpaceService).getById(1); } @Test public void sendOnlineReportByNotifySetting_ReportBaseInfoIsNull_ShouldCreateNew() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(YesOrNoEnum.YES.getNumCode()); sprint.setSpaceId(1); sprint.setId(1); sprint.setName("sprintName"); BizSpace space = new BizSpace(); space.setId(1); space.setKey("key"); NotifySettingDTO notifySetting = new NotifySettingDTO(); notifySetting.setSendWays(Arrays.asList()); OnlineReportBaseInfoDTO reportBaseInfoDTO = null; when(bizSpaceService.getById(1)).thenReturn(space); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(spaceCardService.queryCardByCondition(any())).thenReturn(new Page<>()); onlineReportService.sendOnlineReportByNotifySetting(sprint, notifySetting, reportBaseInfoDTO); verify(bizSpaceService).getById(1); } @Test public void sendOnlineReportByNotifySetting_SendWayIsLark_ShouldCallWKMethod() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(YesOrNoEnum.YES.getNumCode()); sprint.setSpaceId(1); sprint.setId(1); sprint.setName("sprintName"); BizSpace space = new BizSpace(); space.setId(1); space.setKey("key"); NotifySettingDTO notifySetting = new NotifySettingDTO(); notifySetting.setSendWays(Arrays.asList(BizNotifySendWayEnum.LARK.getCode())); ChatInfoDataResp.ChatData chatData = new ChatInfoDataResp.ChatData(); chatData.setChatId("chatId"); notifySetting.setLarks(Arrays.asList(chatData)); OnlineReportBaseInfoDTO reportBaseInfoDTO = new OnlineReportBaseInfoDTO(); when(bizSpaceService.getById(1)).thenReturn(space); when(spaceCardService.queryCardByCondition(any())).thenReturn(new Page<>()); onlineReportService.sendOnlineReportByNotifySetting(sprint, notifySetting, reportBaseInfoDTO); } @Test public void sendOnlineReportByNotifySetting_SendWayIsEmail_ShouldCallEmailMethod() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(YesOrNoEnum.YES.getNumCode()); sprint.setSpaceId(1); sprint.setId(1); sprint.setName("sprintName"); BizSpace space = new BizSpace(); space.setId(1); space.setKey("key"); NotifySettingDTO notifySetting = new NotifySettingDTO(); notifySetting.setSendWays(Arrays.asList(BizNotifySendWayEnum.EMAIL.getCode())); notifySetting.setEmails("test@example.com"); OnlineReportBaseInfoDTO reportBaseInfoDTO = new OnlineReportBaseInfoDTO(); when(bizSpaceService.getById(1)).thenReturn(space); when(spaceCardService.queryCardByCondition(any())).thenReturn(new Page<>()); when(scriptEngineManager.getEngineByName(any())).thenReturn(new VelocityScriptEngine()); onlineReportService.sendOnlineReportByNotifySetting(sprint, notifySetting, reportBaseInfoDTO); } @Test public void sendOnlineReportForSprints_test() { BizSpaceSprint sprint = new BizSpaceSprint(); when(spaceSettingService.checkRequireKeyIsYesCode(any(), any())).thenReturn(true); when(bizSpaceSprintService.listByIds(any())).thenReturn(Arrays.asList(sprint)); onlineReportService.sendOnlineReportForSprints(Arrays.asList(1)); } @Test public void sendOnlineReportForSprints_test1() { BizSpaceSprint sprint = new BizSpaceSprint(); when(bizSpaceSprintService.listByIds(any())).thenReturn(Arrays.asList(sprint)); onlineReportService.sendOnlineReportForSprints(Arrays.asList(1)); } @Test public void sendOnlineReportForSprints_test2() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(0); when(spaceSettingService.checkRequireKeyIsYesCode(any(), any())).thenReturn(true); when(bizSpaceSprintService.listByIds(any())).thenReturn(Arrays.asList(sprint)); onlineReportService.sendOnlineReportForSprints(Arrays.asList(1)); } @Test public void sendOnlineReportForSprints_test3() { BizSpaceSprint sprint = new BizSpaceSprint(); sprint.setArchived(1); when(spaceSettingService.checkRequireKeyIsYesCode(any(), any())).thenReturn(true); BizNotifySetting bizNotifySetting = new BizNotifySetting(); when(bizNotifySettingService.getOneBy(any(), any())).thenReturn(bizNotifySetting); when(bizSpaceSprintService.listByIds(any())).thenReturn(Arrays.asList(sprint)); onlineReportService.sendOnlineReportForSprints(Arrays.asList(1)); } @Test public void selectSpaceNotifySetting_test() { onlineReportService.selectSpaceNotifySetting(1); } @Test public void selectSpaceNotifySetting_test2() { when(bizSpaceSprintService.getSpaceBySprintId(any())).thenReturn(new BizSpace().setId(1)); onlineReportService.selectSpaceNotifySetting(1); } @Test public void selectSpaceNotifySetting_test3() { when(bizSpaceSprintService.getSpaceBySprintId(any())).thenReturn(new BizSpace().setId(1)); when(bizNotifySettingService.getOneBy(any(), any())).thenReturn(new BizNotifySetting().setId(1)); onlineReportService.selectSpaceNotifySetting(1); } @Test public void queryCheckTest() { BizReport bizReport = new BizReport(); bizReport.setExtendInfo("{\"checkPoint\":\"Test CheckPoint\",\"checkPointResult\":\"Test CheckPointResult\",\"omittedIssuesForRecord\":\"Test OmittedIssuesForRecord\"}"); when(bizReportService.selectReport(AcceptanceReportEnum.reportType.ONLINE, AcceptanceReportEnum.relationBizType.SPRINT, 1)).thenReturn(bizReport); OnlineReportCheckDTO result = onlineReportService.queryCheck(1); assertNotNull(result); assertEquals("Test CheckPoint", result.getCheckPoint()); assertEquals("Test CheckPointResult", result.getCheckPointResult()); assertEquals("Test OmittedIssuesForRecord", result.getOmittedIssuesForRecord()); } @Test public void updateCheckTest() { // Mock data BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); BizSpace space = new BizSpace().setId(1).setKey("key"); OnlineReportCheckDTO onlineReportCheckDTO = new OnlineReportCheckDTO() .setCheckPoint("Test CheckPoint") .setCheckPointResult("Test CheckPointResult") .setOmittedIssuesForRecord("Test OmittedIssuesForRecord"); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(space); when(onlineReportService.getOnlineReportBaseInfoDTO(eq(sprint), eq("key"))) .thenReturn(new OnlineReportBaseInfoDTO()); // Act onlineReportService.updateCheck(onlineReportCheckDTO, 1); // Verify verify(bizReportService, times(1)).saveOrUpdateOnlineReport(1, any(OnlineReportBaseInfoDTO.class)); } } 以下是onlineReportService对应类的代码:package com.jd.jacp.report.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.jd.jacp.client.dto.CICDAppGrayscaleDeployInfo; import com.jd.jacp.client.dto.GetCiCdTaskQuery; import com.jd.jacp.client.service.ICicdClient; import com.jd.jacp.client.service.ICmdbClient; import com.jd.jacp.client.service.ItsmClientService; import com.jd.jacp.common.feishu.FeiShuMessageSender; import com.jd.jacp.common.service.UserHelper; import com.jd.jacp.common.vo.UserVO; import com.jd.jacp.common.vo.enums.YesOrNoEnum; import com.jd.jacp.demand.entity.BizDemandUser; import com.jd.jacp.demand.service.IBizDemandUserService; import com.jd.jacp.demand.vo.DemandUserTypeEnum; import com.jd.jacp.notify.entity.BizNotifyRule; import com.jd.jacp.notify.entity.BizNotifySetting; import com.jd.jacp.notify.enums.BizNotifySendWayEnum; import com.jd.jacp.notify.enums.BizNotifyTypeEnum; import com.jd.jacp.notify.service.IBizNotifyRuleService; import com.jd.jacp.notify.service.IBizNotifySettingService; import com.jd.jacp.report.dto.*; import com.jd.jacp.report.entity.BizReport; import com.jd.jacp.report.entity.BizReportItem; import com.jd.jacp.report.enums.AcceptanceReportEnum; import com.jd.jacp.report.service.IBizReportItemService; import com.jd.jacp.report.service.IBizReportService; import com.jd.jacp.report.service.IOnlineReportService; import com.jd.jacp.report.vo.SprintOnlineReportInfoVO; import com.jd.jacp.space.dto.CardQuery; import com.jd.jacp.space.dto.SubSystemByParamsDTO; import com.jd.jacp.space.entity.BizSpace; import com.jd.jacp.space.entity.BizSpaceCard; import com.jd.jacp.space.entity.BizSpaceSprint; import com.jd.jacp.space.service.IBizSpaceCardService; import com.jd.jacp.space.service.IBizSpaceService; import com.jd.jacp.space.service.IBizSpaceSettingService; import com.jd.jacp.space.service.IBizSpaceSprintService; import com.jd.jacp.space.service.impl.BizSpaceSettingServiceImpl; import com.jd.jacp.space.vo.DetailVO; import com.jd.jacp.space.vo.QueryReportVO; import com.jd.jacp.util.AssertUtil; import com.jd.jacp.util.EmailSender; import com.msxf.devops.common.PageResult; import com.msxf.fcid.xmsg.reponse.ChatInfoDataResp; import com.msxf.fcid.xmsg.sdk.response.FeishuMessageSendResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import java.io.InputStream; import java.io.StringWriter; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author ke.feng-n */ @Slf4j @Service public class OnlineReportServiceImpl implements IOnlineReportService { @Autowired private IBizSpaceSprintService bizSpaceSprintService; @Autowired private ItsmClientService itsmClientService; @Autowired private IBizReportService bizReportService; @Autowired private ICmdbClient cmdbClient; @Autowired private IBizSpaceService bizSpaceService; @Autowired private IBizNotifySettingService bizNotifySettingService; @Autowired private IBizNotifyRuleService bizNotifyRuleService; @Autowired private IBizSpaceCardService spaceCardService; @Autowired private IBizDemandUserService demandUserService; @Autowired private IBizReportItemService reportItemService; @Autowired private UserHelper userHelper; @Resource(name = "scriptEngineManager") private ScriptEngineManager scriptEngineManager; @Autowired private FeiShuMessageSender feiShuMessageSender; @Autowired private ICicdClient cicdClient; @Autowired private IBizSpaceSettingService spaceSettingService; @Value("${env.domain.name}") private String domain; @Resource EmailSender emailSender; @Override public SprintOnlineReportInfoVO selectOnlineReportInfo(Integer sprintId) { BizSpaceSprint sprint = bizSpaceSprintService.getById(sprintId); Assert.notNull(sprint, "迭代不存在"); BizSpace space = bizSpaceService.getById(sprint.getSpaceId()); Assert.notNull(space, "迭代所属子系统不存在"); //获取报告名称和遗漏问题 OnlineReportBaseInfoDTO onlineReportBaseInfoDTO = getOnlineReportBaseInfoDTO(sprint, space.getKey()); SprintOnlineReportInfoVO reportInfoVO = new SprintOnlineReportInfoVO(); reportInfoVO.setOnlineReport(onlineReportBaseInfoDTO); //获取迭代的通知配置信息 BizNotifySetting sprintNotifySetting = getSprintNotifySetting(sprint); if (ObjectUtil.isEmpty(sprintNotifySetting)) { return reportInfoVO; } BizNotifyRule notifyRule = bizNotifyRuleService.getOneByNotifyId(sprintNotifySetting.getId()); NotifySettingDTO notifySetting = NotifySettingDTO.of(sprintNotifySetting, notifyRule); reportInfoVO.setNotifySetting(notifySetting); return reportInfoVO; } /** * 获取迭代的基础配置信息(报告名称,) * * @param sprint * @param spaceKey * @return */ public OnlineReportBaseInfoDTO getOnlineReportBaseInfoDTO(BizSpaceSprint sprint, String spaceKey) { BizReport bizReport = bizReportService.selectReport(AcceptanceReportEnum.reportType.ONLINE, AcceptanceReportEnum.relationBizType.SPRINT, sprint.getId()); OnlineReportBaseInfoDTO onlineReportBaseInfoDTO; if (ObjectUtil.isNotEmpty(bizReport) && StrUtil.isNotBlank(bizReport.getExtendInfo())) { onlineReportBaseInfoDTO = JSONUtil.toBean(bizReport.getExtendInfo(), OnlineReportBaseInfoDTO.class); } else { BizNotifySetting spaceNotifySetting = bizNotifySettingService.getOneBy(String.valueOf(sprint.getSpaceId()), BizNotifyTypeEnum.SPACE_SPRINT_ONLINE_REPORT.getCode()); onlineReportBaseInfoDTO = genDefaultOnlineReportName(sprint, spaceKey, ObjectUtil.isEmpty(spaceNotifySetting) ? null : spaceNotifySetting.getRelatedObjName()); } return onlineReportBaseInfoDTO; } @Override @Transactional(rollbackFor = Exception.class) public void saveSprintNotifySetting(SprintOnlineReportInfoVO param) { bizReportService.saveOrUpdateOnlineReport(param.getSprintId(), param.getOnlineReport()); Integer notifyId = bizNotifySettingService.saveOrUpdateNotifySetting(BizNotifyTypeEnum.SPRINT_ONLINE_REPORT, String.valueOf(param.getSprintId()), param.getNotifySetting(), null); bizNotifyRuleService.saveOrUpdateNotifyRule(notifyId, param.getNotifySetting().getSendTime()); } @Override @Transactional(rollbackFor = Exception.class) public void saveSpaceNotifySetting(SprintOnlineReportInfoVO param) { BizSpaceSprint sprint = bizSpaceSprintService.getById(param.getSprintId()); Assert.notNull(sprint, "迭代不存在"); Integer notifyId = bizNotifySettingService.saveOrUpdateNotifySetting(BizNotifyTypeEnum.SPACE_SPRINT_ONLINE_REPORT, String.valueOf(sprint.getSpaceId()), param.getNotifySetting(), param.getOnlineReport().getDepartment()); bizNotifyRuleService.saveOrUpdateNotifyRule(notifyId, param.getNotifySetting().getSendTime()); } @Override public PageResult<OnlineReportCardDTO> selectSprintCardPage(CardQuery query) { Page<BizSpaceCard> cardPage = spaceCardService.queryCardByCondition(query); List<OnlineReportCardDTO> cardDTOList = OnlineReportCardDTO.buildFromCardEntity(cardPage.getRecords()); if (CollectionUtil.isEmpty(cardDTOList)) { return PageResult.newEmptyPage(Long.valueOf(query.getPageNum()), Long.valueOf(query.getPageSize())); } //获取需求提报人列表 List<Integer> demandIds = cardDTOList.stream().map(OnlineReportCardDTO::getDemandId).distinct().collect(Collectors.toList()); List<BizDemandUser> bizDemandUsers = demandUserService.selectFeishuUserByDemandIds(demandIds, DemandUserTypeEnum.CONTACT_TYPE_BIZ.getId()); Map<Integer, List<BizDemandUser>> submitterMap = bizDemandUsers.stream().collect(Collectors.groupingBy(BizDemandUser::getDemandId)); List<String> cardErps = cardDTOList.stream() .flatMap(cardDTO -> Stream.of( cardDTO.getProductManagerErp(), cardDTO.getProcessorErp(), cardDTO.getTestLeaderErp() )).distinct() .collect(Collectors.toList()); Map<String, BizReport> reportMap = bizReportService.selectReportMap(Arrays.asList(AcceptanceReportEnum.reportType.UAT, AcceptanceReportEnum.reportType.GRADATION), AcceptanceReportEnum.relationBizType.SPRINT, query.getSprintId()); List<Integer> reportIds = reportMap.values().stream().map(BizReport::getId).collect(Collectors.toList()); //获取验收报告信息 List<String> acceptorErps = Collections.emptyList(); Map<Integer, Map<String, BizReportItem>> reportItemMap = new HashMap<>(); if(CollectionUtil.isNotEmpty(reportIds)) { List<BizReportItem> reportItems = reportItemService.listReportItemByReportIds(reportIds); acceptorErps = reportItems.stream().map(BizReportItem::getAccepters) .flatMap(erp -> JSONUtil.toList(erp, String.class).stream()) .filter(StrUtil::isNotBlank) .distinct() .collect(Collectors.toList()); //报告id-卡片id-报告项 reportItemMap = reportItems.stream().collect(Collectors.groupingBy(BizReportItem::getReportId, Collectors.toMap(BizReportItem::getRelationItemIdentifier, Function.identity(), (v1, v2) -> v1))); } cardErps.addAll(acceptorErps); Map<String, UserVO> userVOMap = userHelper.getUserVoMapWithFSNameByErps(cardErps.stream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList()), UserVO::getErp, Function.identity()); for (OnlineReportCardDTO cardDTO : cardDTOList) { //填充需求提报人 cardDTO.setSubmitter(BizDemandUser.buildUserVOList(submitterMap.get(cardDTO.getDemandId()))); //填充卡片相关人信息 cardDTO.setProductManager(userVOMap.get(cardDTO.getProductManagerErp())); cardDTO.setProcessor(userVOMap.get(cardDTO.getProcessorErp())); cardDTO.setTestLeader(userVOMap.get(cardDTO.getTestLeaderErp())); //填充UAT验收信息 BizReport uatReport = reportMap.get(AcceptanceReportEnum.reportType.UAT.name()); cardDTO.fillUATAcceptanceInfo(reportItemMap, uatReport, userVOMap); //填充灰度验收信息 BizReport gradationReport = reportMap.get(AcceptanceReportEnum.reportType.GRADATION.name()); cardDTO.fillGradationAcceptanceInfo(reportItemMap, gradationReport, userVOMap); } return PageResult.newInstance(cardDTOList, cardPage.getPages(), cardPage.getTotal(), cardPage.getCurrent(), cardPage.getSize()); } @Override public List<OnlineReportAppDTO> selectSprintAppList(Integer sprintId) { BizSpaceSprint sprint = bizSpaceSprintService.getById(sprintId); if (ObjectUtil.isEmpty(sprint)) { return Collections.emptyList(); } BizSpace space = bizSpaceService.getById(sprint.getSpaceId()); if (ObjectUtil.isEmpty(space)) { return Collections.emptyList(); } return this.selectSprintAppList(sprintId, space.getKey()); } @Override public List<OnlineReportAppDTO> selectSprintAppList(Integer sprintId, String spaceKey) { DetailVO itsmDetail = getSprintItsmOnlineDetail(sprintId, spaceKey); if (ObjectUtil.isEmpty(itsmDetail) || ObjectUtil.isEmpty(itsmDetail.getApplyCode())) { return Collections.emptyList(); } List<CICDAppGrayscaleDeployInfo> cicdAppGrayscaleDeployInfos = Collections.emptyList(); try { cicdAppGrayscaleDeployInfos = cicdClient.getTaskListByParam(GetCiCdTaskQuery.buildQueryProdTaskQuery(itsmDetail.getApplyCode())); } catch (Exception e) { log.warn("调用CICD接口查询迭代部署完成应用列表失败:", e); } return OnlineReportAppDTO.buildList(cicdAppGrayscaleDeployInfos); } @Override public void sendOnlineReportNow(SprintOnlineReportInfoVO param) { BizSpaceSprint sprint = bizSpaceSprintService.getById(param.getSprintId()); Assert.notNull(sprint, "迭代不存在"); boolean sendSwitch = this.selectSpaceSwitch(sprint.getSpaceId()); Assert.isTrue(sendSwitch, "空间未开启上线报告发送配置"); this.sendOnlineReportByNotifySetting(sprint, param.getNotifySetting(), param.getOnlineReport()); } /** * 查询迭代卡片列表 * * @param sprintId */ public List<OnlineReportCardDTO> selectSprintCardList(Integer sprintId) { CardQuery cardQuery = new CardQuery(); cardQuery.setSprintId(sprintId); cardQuery.setPageNum(1); cardQuery.setPageSize(10000); PageResult<OnlineReportCardDTO> sprintCardPage = this.selectSprintCardPage(cardQuery); return sprintCardPage.getRecords(); } /** * 生成默认的上线报告名称 * * @return */ public OnlineReportBaseInfoDTO genDefaultOnlineReportName(BizSpaceSprint sprint, String spaceKey, String department) { if (StrUtil.isBlank(department)) { SubSystemByParamsDTO subSystemByParamsDTO = cmdbClient.queryProjectByCode(spaceKey); department = ObjectUtil.isNotEmpty(subSystemByParamsDTO) ? subSystemByParamsDTO.getDepartment() : "暂无部门"; } //获取迭代上线流程信息 DetailVO detailVO = getSprintItsmOnlineDetail(sprint.getId(), spaceKey); String deployType = Optional.ofNullable(detailVO).map(DetailVO::getDeployType).orElse(""); String sprintEndDate = ObjectUtil.isNotEmpty(sprint.getEndDate()) ? DateUtil.format(sprint.getEndDate(), DatePattern.PURE_DATE_FORMAT) : ""; String reportName = "版本发布" + "-" + department + "-" + deployType + "-" + sprintEndDate; return new OnlineReportBaseInfoDTO().setReportName(reportName).setDepartment(department).setDeployType(deployType).setSprintEndDate(sprintEndDate); } /** * 获取迭代上线流程信息 * * @param sprintId * @param spaceKey * @return */ private DetailVO getSprintItsmOnlineDetail(Integer sprintId, String spaceKey) { QueryReportVO queryReport = itsmClientService.getQueryReport(sprintId, spaceKey); DetailVO detailVO = null; if (ObjectUtil.isNotEmpty(queryReport) && CollectionUtil.isNotEmpty(queryReport.getDetail())) { detailVO = queryReport.getDetail().get(0); } return detailVO; } @Override public String sprintDeployReportWK(SprintOnlineReportInfoDTO sprintOnlineReportInfoDTO, List<String> chats, boolean acceptanceReportEnable) { SprintDeployReportDTO dto = SprintDeployReportDTO.convertFromSprintOnlineReportInfoDTO(sprintOnlineReportInfoDTO, domain, BizNotifySendWayEnum.LARK.getCode(), acceptanceReportEnable); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream( acceptanceReportEnable ? "templates/SprintDeployReport.json" : "templates/SprintDeployReport-NoAcceptance.json")) { String content = IOUtils.toString(inputStream); ScriptEngine engine = scriptEngineManager.getEngineByName("velocity"); JSONObject wkParam = JSON.parseObject(JSON.toJSONString(dto)); ScriptContext scriptContext = engine.getContext(); wkParam.keySet().forEach(key -> scriptContext.setAttribute(key, wkParam.get(key), ScriptContext.ENGINE_SCOPE)); scriptContext.setWriter(new StringWriter()); String contentText = engine.eval(content).toString(); FeishuMessageSendResponse response = feiShuMessageSender.withCustomTemplate(contentText, "global-template-notice", chats, null, null); AssertUtil.notNull(response, "no response"); AssertUtil.isTrue(Objects.equals(response.getCode(), "0"), response.getMsg()); AssertUtil.notEmpty(response.getData(), "wk message send fault"); return response.getData().get(0); } catch (Exception e) { log.error("sprint deploy report wk message send error", e); } return null; } @Override public void sendOnlineReportByNotifySetting(BizSpaceSprint sprint, NotifySettingDTO notifySetting, OnlineReportBaseInfoDTO reportBaseInfoDTO) { BizSpace space = bizSpaceService.getById(sprint.getSpaceId()); if (ObjectUtil.isEmpty(space)) { log.warn("【发送上线报告】迭代所属子系统不存在:{}", sprint.getSpaceId()); return; } if(ObjectUtil.isEmpty(reportBaseInfoDTO)) { reportBaseInfoDTO = this.getOnlineReportBaseInfoDTO(sprint, space.getKey()); } List<OnlineReportCardDTO> cardList = this.selectSprintCardList(sprint.getId()); List<OnlineReportAppDTO> appList = this.selectSprintAppList(sprint.getId(), space.getKey()); SprintOnlineReportInfoDTO reportInfoDTO = new SprintOnlineReportInfoDTO() .setSprintId(sprint.getId()) .setSprintName(sprint.getName()) .setSpaceKey(space.getKey()) .setOnlineReportBaseInfo(reportBaseInfoDTO) .setOnlineReportCardList(cardList) .setOnlineReportAppList(appList); List<String> sendWays = notifySetting.getSendWays(); boolean acceptanceReportEnable = spaceSettingService.acceptanceReportEnable(space.getId()); if (sendWays.contains(BizNotifySendWayEnum.LARK.getCode())) { List<String> chatIds = notifySetting.getLarks().stream().map(ChatInfoDataResp.ChatData::getChatId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); this.sprintDeployReportWK(reportInfoDTO, chatIds, acceptanceReportEnable); } if (sendWays.contains(BizNotifySendWayEnum.EMAIL.getCode())) { this.sprintDeployReportEmail(reportInfoDTO, Arrays.asList(notifySetting.getEmails().split(",")), null, acceptanceReportEnable); } } @Override public NotifySettingDTO selectSpaceNotifySetting(Integer sprintId) { BizSpace space = bizSpaceSprintService.getSpaceBySprintId(sprintId); if (ObjectUtil.isEmpty(space)) { return null; } BizNotifySetting spaceNotifySetting = bizNotifySettingService.getOneBy(String.valueOf(space.getId()), BizNotifyTypeEnum.SPACE_SPRINT_ONLINE_REPORT.getCode()); if (ObjectUtil.isEmpty(spaceNotifySetting)) { return null; } BizNotifyRule notifyRule = bizNotifyRuleService.getOneByNotifyId(spaceNotifySetting.getId()); return NotifySettingDTO.of(spaceNotifySetting, notifyRule); } @Override public void sendOnlineReportForSprints(List<Integer> sprintIds) { List<BizSpaceSprint> sprints = bizSpaceSprintService.listByIds(sprintIds); for (BizSpaceSprint sprint : sprints) { if(!this.selectSpaceSwitch(sprint.getSpaceId())) { continue; } if (YesOrNoEnum.NO.getNumCode().equals(sprint.getArchived())) { continue; } BizNotifySetting notifySetting = getSprintNotifySetting(sprint); if (ObjectUtil.isEmpty(notifySetting)) { return; } this.sendOnlineReportByNotifySetting(sprint, NotifySettingDTO.of(notifySetting), null); } } @Override public void sprintDeployReportEmail(SprintOnlineReportInfoDTO sprintOnlineReportInfoDTO, List<String> receiver, List<String> carbon, boolean acceptanceReportEnable) { SprintDeployReportDTO dto = SprintDeployReportDTO.convertFromSprintOnlineReportInfoDTO(sprintOnlineReportInfoDTO, domain, BizNotifySendWayEnum.EMAIL.getCode(), acceptanceReportEnable); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("templates/SprintDeployReport.html")) { String content = IOUtils.toString(inputStream); ScriptEngine engine = scriptEngineManager.getEngineByName("velocity"); JSONObject htmlParam = JSON.parseObject(JSON.toJSONString(dto)); ScriptContext scriptContext = engine.getContext(); htmlParam.keySet().forEach(key -> { scriptContext.setAttribute(key, htmlParam.get(key), ScriptContext.ENGINE_SCOPE); }); scriptContext.setAttribute("acceptance_report_enable", acceptanceReportEnable, ScriptContext.ENGINE_SCOPE); scriptContext.setWriter(new StringWriter()); String contentText = engine.eval(content).toString(); emailSender.dcpSendEmail(receiver, carbon, String.format("%s一上线报告", dto.getSprintTitle()), contentText); } catch (Exception e) { log.error("sprint deploy report email message send error", e); } } /** * 获取迭代可用的通知配置信息 * * @param sprint * @return */ @Override public BizNotifySetting getSprintNotifySetting(BizSpaceSprint sprint) { if(ObjectUtil.isEmpty(sprint)) { return null; } BizNotifySetting sprintNotifySetting = bizNotifySettingService.getOneBy(String.valueOf(sprint.getId()), BizNotifyTypeEnum.SPRINT_ONLINE_REPORT.getCode()); if (ObjectUtil.isEmpty(sprintNotifySetting)) { sprintNotifySetting = bizNotifySettingService.getOneBy(String.valueOf(sprint.getSpaceId()), BizNotifyTypeEnum.SPACE_SPRINT_ONLINE_REPORT.getCode()); } return sprintNotifySetting; } @Override public boolean selectSpaceSwitch(Integer spaceId) { return spaceSettingService.checkRequireKeyIsYesCode(spaceId, BizSpaceSettingServiceImpl.REQUIRED_SEND_REPORT_AFTER_SPRINT_ARCHIVED); } /** * 根据迭代id查询生产验证情况 * @param sprintId * @return */ @Override public OnlineReportCheckDTO queryCheck(Integer sprintId) { BizReport bizReport = bizReportService.selectReport(AcceptanceReportEnum.reportType.ONLINE, AcceptanceReportEnum.relationBizType.SPRINT, sprintId); OnlineReportBaseInfoDTO onlineReportBaseInfoDTO; OnlineReportCheckDTO onlineReportCheckDTO = new OnlineReportCheckDTO(); if (ObjectUtil.isNotEmpty(bizReport) && StrUtil.isNotBlank(bizReport.getExtendInfo())) { onlineReportBaseInfoDTO = JSONUtil.toBean(bizReport.getExtendInfo(), OnlineReportBaseInfoDTO.class); onlineReportCheckDTO.setCheckPoint(onlineReportBaseInfoDTO.getCheckPoint()); onlineReportCheckDTO.setCheckPointResult(onlineReportBaseInfoDTO.getCheckPointResult()); onlineReportCheckDTO.setOmittedIssuesForRecord(onlineReportBaseInfoDTO.getOmittedIssuesForRecord()); } return onlineReportCheckDTO; } /** * 更新生产验证情况 * @param onlineReportCheckDTO * @param sprintId */ @Override public void updateCheck(OnlineReportCheckDTO onlineReportCheckDTO, Integer sprintId) { BizSpaceSprint sprint = bizSpaceSprintService.getById(sprintId); Assert.notNull(sprint, "迭代不存在"); BizSpace space = bizSpaceService.getById(sprint.getSpaceId()); Assert.notNull(space, "迭代所属子系统不存在"); //获取报告名称和遗漏问题 OnlineReportBaseInfoDTO onlineReportBaseInfoDTO = getOnlineReportBaseInfoDTO(sprint, space.getKey()); //设置数据 onlineReportBaseInfoDTO.setCheckPoint(onlineReportCheckDTO.getCheckPoint()); onlineReportBaseInfoDTO.setCheckPointResult(onlineReportCheckDTO.getCheckPointResult()); onlineReportBaseInfoDTO.setOmittedIssuesForRecord(onlineReportCheckDTO.getOmittedIssuesForRecord()); //如果有数据直接更新,没有生成新数据 bizReportService.saveOrUpdateOnlineReport(sprintId, onlineReportBaseInfoDTO); } } 当运行updateCheckTest时,报错:java.lang.NullPointerException at com.jd.jacp.report.service.impl.OnlineReportServiceImpl.getOnlineReportBaseInfoDTO(OnlineReportServiceImpl.java:163) at com.jd.jacp.unit.report.OnlineReportServiceImplTest.updateCheckTest(OnlineReportServiceImplTest.java:567) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55) Process finished with exit code -1
最新发布
09-04
<think> 我们注意到在测试类中,`onlineReportService` 被注入了 `@Spy` 和 `@InjectMocks`。在 `updateCheckTest` 测试方法中,我们mock了 `onlineReportService.getOnlineReportBaseInfoDTO` 方法。但是,在运行测试时,却抛出了空指针异常,指向了 `OnlineReportServiceImpl.getOnlineReportBaseInfoDTO` 方法中的第163行。 查看 `OnlineReportServiceImpl.getOnlineReportBaseInfoDTO` 方法: ```java public OnlineReportBaseInfoDTO getOnlineReportBaseInfoDTO(BizSpaceSprint sprint, String spaceKey) { BizReport bizReport = bizReportService.selectReport(AcceptanceReportEnum.reportType.ONLINE, AcceptanceReportEnum.relationBizType.SPRINT, sprint.getId()); OnlineReportBaseInfoDTO onlineReportBaseInfoDTO; if (ObjectUtil.isNotEmpty(bizReport) && StrUtil.isNotBlank(bizReport.getExtendInfo())) { onlineReportBaseInfoDTO = JSONUtil.toBean(bizReport.getExtendInfo(), OnlineReportBaseInfoDTO.class); } else { BizNotifySetting spaceNotifySetting = bizNotifySettingService.getOneBy(String.valueOf(sprint.getSpaceId()), BizNotifyTypeEnum.SPACE_SPRINT_ONLINE_REPORT.getCode()); onlineReportBaseInfoDTO = genDefaultOnlineReportName(sprint, spaceKey, ObjectUtil.isEmpty(spaceNotifySetting) ? null : spaceNotifySetting.getRelatedObjName()); } return onlineReportBaseInfoDTO; } ``` 空指针异常发生在第163行,也就是这个方法内部。但是,我们在测试中已经mock了 `onlineReportService.getOnlineReportBaseInfoDTO` 方法,为什么还会调用真实的实现呢? 原因在于,我们使用的是 `@Spy` 注解。`@Spy` 会部分模拟对象,即对于没有stub的方法,会调用真实的方法。而在我们的测试中,虽然mock了 `getOnlineReportBaseInfoDTO` 方法,但是可能由于某些原因,这个mock并没有生效。 观察测试方法 `updateCheckTest` 中的mock设置: ```java when(onlineReportService.getOnlineReportBaseInfoDTO(eq(sprint), eq("key"))).thenReturn(new OnlineReportBaseInfoDTO()); ``` 这里我们试图让 `getOnlineReportBaseInfoDTO` 方法返回一个空的 `OnlineReportBaseInfoDTO` 对象。但是,由于 `onlineReportService` 是一个spy对象,如果调用 `getOnlineReportBaseInfoDTO` 方法时传入的参数不是 `sprint` 和 `"key"`,那么就会调用真实的方法。 在测试中,我们传入的 `sprint` 对象是我们自己创建的,而 `spaceKey` 是 `"key"`。看起来是匹配的,但是注意 `sprint` 对象是我们自己new的,而在真实代码中,`OnlineReportServiceImpl.getOnlineReportBaseInfoDTO` 方法内部使用了 `sprint.getId()` 和 `sprint.getSpaceId()`。在测试中,我们创建的 `sprint` 对象是: ```java BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); ``` 所以,在mock方法中,我们使用 `eq(sprint)` 应该能够匹配。 但是,为什么还会调用真实方法呢?可能是因为在 `updateCheck` 方法内部,调用 `getOnlineReportBaseInfoDTO` 时传入的 `sprint` 对象并不是我们mock时使用的那个对象(尽管属性相同,但对象引用不同),或者因为其他原因导致mock没有生效。 为了解决这个问题,我们可以尝试以下几种方法之一: 1. 使用 `doReturn` 代替 `when(...).thenReturn(...)` 来避免调用真实方法。 2. 确保传入的参数与mock设置时的参数完全匹配(使用相同的对象引用,或者使用 `eq` 匹配器进行属性匹配)。 由于我们使用的是 `eq(sprint)`,它会调用 `sprint.equals` 方法。而 `BizSpaceSprint` 类如果没有重写 `equals` 方法,那么默认比较的是引用,所以两个属性相同但引用不同的对象不会匹配。 因此,我们可以改为使用 `any()` 匹配器,忽略参数匹配,或者修改mock方式。 建议改为使用 `doReturn` 的方式,这样即使方法内部使用真实对象调用,也可以被拦截。 修改测试方法 `updateCheckTest` 中的mock为: ```java doReturn(new OnlineReportBaseInfoDTO()) .when(onlineReportService) .getOnlineReportBaseInfoDTO(any(), anyString()); ``` 这样,无论传入什么参数,都会返回我们指定的对象。 注意:这样可能会过于宽泛,如果测试中需要根据不同的参数返回不同的结果,则需要更精细的控制。但在这个测试中,我们只需要确保在调用 `getOnlineReportBaseInfoDTO` 方法时返回一个非空对象即可。 修改后的测试方法如下: ```java @Test public void updateCheckTest() { // Mock data BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); BizSpace space = new BizSpace().setId(1).setKey("key"); OnlineReportCheckDTO onlineReportCheckDTO = new OnlineReportCheckDTO() .setCheckPoint("Test CheckPoint") .setCheckPointResult("Test CheckPointResult") .setOmittedIssuesForRecord("Test OmittedIssuesForRecord"); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(space); // 使用doReturn来mock spy对象的方法 doReturn(new OnlineReportBaseInfoDTO()) .when(onlineReportService) .getOnlineReportBaseInfoDTO(any(BizSpaceSprint.class), anyString()); // Act onlineReportService.updateCheck(onlineReportCheckDTO, 1); // Verify verify(bizReportService, times(1)).saveOrUpdateOnlineReport(1, any(OnlineReportBaseInfoDTO.class)); } ``` 这样修改后,无论传入什么参数,`getOnlineReportBaseInfoDTO` 方法都会返回一个空的 `OnlineReportBaseInfoDTO` 对象,而不会执行真实的方法,从而避免空指针异常。 另外,注意在测试中,我们mock了 `bizSpaceSprintService.getById(1)` 返回 `sprint`,`bizSpaceService.getById(1)` 返回 `space`,这些都是正确的。所以空指针异常可能是因为之前mock的 `getOnlineReportBaseInfoDTO` 没有被正确拦截,导致调用了真实方法,而真实方法中可能调用了其他未mock的方法(如 `bizReportService.selectReport` 返回null,然后进入else分支,而else分支中又调用了 `bizNotifySettingService.getOneBy`,这个服务在测试中没有被mock,所以导致空指针)。 使用 `doReturn` 可以避免调用真实方法,从而避免这些问题。 §§相关问题§§ 1. 在Mockito中,`@Spy`注解和`@Mock`注解的主要区别是什么? 2. 使用`when(...).thenReturn(...)`和`doReturn(...).when(...)`在mock方法时有何不同? 3. 如何解决在测试spy对象时,部分方法需要调用真实方法而部分方法需要mock的情况? 4. 当单元测试中出现NullPointerException时,有哪些常见的排查步骤? 5. 在Mockito中,为什么有时候需要使用参数匹配器(如`any()`)?它可能会带来哪些风险? </think> ### 问题分析与解决方案 #### 问题原因分析 1. **NullPointerException来源**:错误发生在`OnlineReportServiceImpl.getOnlineReportBaseInfoDTO`方法的第163行(实际代码行号可能与IDE显示不同): ```java BizReport bizReport = bizReportService.selectReport(...); ``` 2. **根本原因**:在测试中未正确Mock `bizReportService.selectReport()`方法的行为,导致实际调用时返回`null`: - 当`bizReport`为`null`时,代码尝试访问`bizReport.getExtendInfo()`触发NPE #### 修复方案 修改`updateCheckTest`测试方法,增加对`bizReportService.selectReport()`的Mock: ```java @Test public void updateCheckTest() { // Mock数据 BizSpaceSprint sprint = new BizSpaceSprint().setId(1).setSpaceId(1); BizSpace space = new BizSpace().setId(1).setKey("key"); OnlineReportCheckDTO onlineReportCheckDTO = new OnlineReportCheckDTO() .setCheckPoint("Test CheckPoint") .setCheckPointResult("Test CheckPointResult") .setOmittedIssuesForRecord("Test OmittedIssuesForRecord"); // 新增:Mock bizReportService.selectReport() 返回非空对象 BizReport mockReport = new BizReport(); mockReport.setExtendInfo("{}"); // 设置有效的JSON字符串 when(bizReportService.selectReport( eq(AcceptanceReportEnum.reportType.ONLINE), eq(AcceptanceReportEnum.relationBizType.SPRINT), eq(1) )).thenReturn(mockReport); when(bizSpaceSprintService.getById(1)).thenReturn(sprint); when(bizSpaceService.getById(1)).thenReturn(space); when(onlineReportService.getOnlineReportBaseInfoDTO(eq(sprint), eq("key"))) .thenReturn(new OnlineReportBaseInfoDTO()); // 执行测试 onlineReportService.updateCheck(onlineReportCheckDTO, 1); // 验证 verify(bizReportService, times(1)).saveOrUpdateOnlineReport(1, any(OnlineReportBaseInfoDTO.class)); } ``` #### 关键修复点: 1. **新增Mock对象**:创建有效的`BizReport`实例并设置`extendInfo`属性 2. **精确参数匹配**:使用`eq()`匹配器确保参数类型正确 3. **防止NPE**:确保`bizReportService.selectReport()`始终返回有效对象 4. **兼容性**:`extendInfo`设置为空JSON对象`"{}"`,避免后续JSON解析错误 ### 深入分析 #### 问题代码位置 在`OnlineReportServiceImpl.getOnlineReportBaseInfoDTO`中: ```java if (ObjectUtil.isNotEmpty(bizReport) && StrUtil.isNotBlank(bizReport.getExtendInfo())) { // 使用bizReport.getExtendInfo() } else { // 其他逻辑 } ``` 当`bizReport`为`null`时: 1. `ObjectUtil.isNotEmpty(bizReport)`返回`false` 2. 进入`else`分支 3. 但在`else`分支中:`bizNotifySettingService.getOneBy(...)`可能返回`null` 4. 最终调用`genDefaultOnlineReportName(...)`方法,其中可能包含未mock的依赖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值