From 5b8d440ff3b1956ee2d7000f4528e2f08128ae59 Mon Sep 17 00:00:00 2001 From: guoqibing Date: Mon, 12 Jan 2026 10:10:51 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A9=E6=95=88=E7=9C=8B=E6=9D=BF=E7=AD=89?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sa/zentao/constants/ExportConstants.java | 37 ++++ .../controller/ZtProductController.java | 5 + .../com/sa/zentao/dao/ZtStoryUserDTO.java | 29 ++- .../com/sa/zentao/entity/ZtStoryUser.java | 1 + .../com/sa/zentao/entity/ZtStoryUserspec.java | 1 + .../com/sa/zentao/enums/KanbanColumnType.java | 49 +++++ .../sa/zentao/service/IZtProductService.java | 2 + .../impl/FestivalConfigServiceImpl.java | 4 +- .../zentao/service/impl/IZtCountService.java | 11 +- .../service/impl/ZtKanbanlaneServiceImpl.java | 7 +- .../service/impl/ZtMeetingServiceImpl.java | 22 +- .../service/impl/ZtProductServiceImpl.java | 60 ++++++ .../service/impl/ZtStoryServiceImpl.java | 4 +- .../service/impl/ZtStoryUserServiceImpl.java | 199 ++++++++++++------ .../service/impl/ZtTaskServiceImpl.java | 68 +++--- .../sa/zentao/utils/KanbanStageMapping.java | 87 ++++++++ src/main/resources/mapper/ZtStoryMapper.xml | 163 +++++++------- 17 files changed, 549 insertions(+), 200 deletions(-) create mode 100644 src/main/java/com/sa/zentao/constants/ExportConstants.java create mode 100644 src/main/java/com/sa/zentao/enums/KanbanColumnType.java create mode 100644 src/main/java/com/sa/zentao/utils/KanbanStageMapping.java diff --git a/src/main/java/com/sa/zentao/constants/ExportConstants.java b/src/main/java/com/sa/zentao/constants/ExportConstants.java new file mode 100644 index 0000000..70e20dd --- /dev/null +++ b/src/main/java/com/sa/zentao/constants/ExportConstants.java @@ -0,0 +1,37 @@ +package com.sa.zentao.constants; + +/** + * 导出相关常量 + */ +public class ExportConstants { + + /** + * 导出最大记录数 + */ + public static final int MAX_EXPORT_SIZE = 1000000; + + /** + * Excel sheet 名称 + */ + public static final String SHEET_NAME = "sheet1"; + + /** + * URL 编码空格 + */ + public static final String URL_SPACE_ENCODE = "%20"; + + /** + * 字符串分隔符 + */ + public static final String STRING_SEPARATOR = ":"; + + /** + * ID 分隔符 + */ + public static final String ID_SEPARATOR = ","; + + /** + * 逗号替换为空字符串 + */ + public static final String COMMA_REPLACE = ""; +} \ No newline at end of file diff --git a/src/main/java/com/sa/zentao/controller/ZtProductController.java b/src/main/java/com/sa/zentao/controller/ZtProductController.java index a359f87..76d3293 100644 --- a/src/main/java/com/sa/zentao/controller/ZtProductController.java +++ b/src/main/java/com/sa/zentao/controller/ZtProductController.java @@ -160,4 +160,9 @@ public class ZtProductController { public Result projectTeamById(@RequestBody ZtProjectQo qo){ return Result.success(this.ztProductService.productTeamByPid(qo)); } + + @RequestMapping(value = "/getExecutionByProductId", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result getExecutionByProductId(@RequestBody ZtProjectQo qo){ + return Result.success(ztProductService.getExecutionByProductId(qo)); + } } diff --git a/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java b/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java index 0ce81ab..715f30c 100644 --- a/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java +++ b/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java @@ -2,21 +2,25 @@ package com.sa.zentao.dao; import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.metadata.data.HyperlinkData; import com.alibaba.excel.metadata.data.WriteCellData; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; +import com.sa.zentao.entity.ZtProject; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.List; +import java.util.Set; /** *

@@ -28,6 +32,7 @@ import java.util.List; */ @Data @EqualsAndHashCode(callSuper = false) +@Slf4j public class ZtStoryUserDTO implements Serializable { private static final long serialVersionUID = 1L; @@ -50,6 +55,8 @@ public class ZtStoryUserDTO implements Serializable { */ @ExcelProperty(value = "关联研发需求",index =4) private String storyList; + @ExcelIgnore + private Set execList; @ExcelProperty(value = "状态",index =5) private String status; @@ -281,10 +288,30 @@ public class ZtStoryUserDTO implements Serializable { //1.需要 2.不需要 @ExcelIgnore private Integer needImprove; - + /** + * 需求背景 + */ + @ExcelIgnore + private String storyBackground; /** * 产品用户 */ @ExcelIgnore private String productUser; + + public void setLinkUrl(String baseUrl) { + String reName = this.getTitle(); + String reUrl = baseUrl + "/#/product-user-story-info/" + this.getId(); + WriteCellData hyperlink = new WriteCellData<>(reName); + HyperlinkData hyperlinkData = new HyperlinkData(); + try { + hyperlinkData.setAddress(reUrl.replace(" ", "%20")); + hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL); + hyperlink.setHyperlinkData(hyperlinkData); + this.setUrl(hyperlink); + } catch (Exception e) { + log.error(e.getMessage(), e); + this.setUrl(null); + } + } } diff --git a/src/main/java/com/sa/zentao/entity/ZtStoryUser.java b/src/main/java/com/sa/zentao/entity/ZtStoryUser.java index a5c83bf..25c633d 100644 --- a/src/main/java/com/sa/zentao/entity/ZtStoryUser.java +++ b/src/main/java/com/sa/zentao/entity/ZtStoryUser.java @@ -229,4 +229,5 @@ public class ZtStoryUser implements Serializable { * 产品用户 */ private String productUser; + } diff --git a/src/main/java/com/sa/zentao/entity/ZtStoryUserspec.java b/src/main/java/com/sa/zentao/entity/ZtStoryUserspec.java index 005bdb6..11b99cb 100644 --- a/src/main/java/com/sa/zentao/entity/ZtStoryUserspec.java +++ b/src/main/java/com/sa/zentao/entity/ZtStoryUserspec.java @@ -31,5 +31,6 @@ public class ZtStoryUserspec implements Serializable { private String files; + private String storyBackground; } diff --git a/src/main/java/com/sa/zentao/enums/KanbanColumnType.java b/src/main/java/com/sa/zentao/enums/KanbanColumnType.java new file mode 100644 index 0000000..4a69c44 --- /dev/null +++ b/src/main/java/com/sa/zentao/enums/KanbanColumnType.java @@ -0,0 +1,49 @@ +package com.sa.zentao.enums; + +public enum KanbanColumnType { + + // 需求看板列类型 + STORY_BACKLOG("backlog", "待办"), + STORY_DEVELOPING("developing", "研发中"), + STORY_DEVELOPED("developed", "研发完毕"), + STORY_TESTING("testing", "测试中"), + STORY_TESTED("tested", "测试完毕"), + STORY_RELEASED("released", "已发布"), + STORY_VERIFIED("verified", "已验收"), + STORY_CLOSED("closed", "已关闭"), + + // Bug看板列类型 + BUG_UNCONFIRMED("unconfirmed", "未确认"), + BUG_CONFIRMED("confirmed", "已确认"), + BUG_RESOLVED("resolved", "已解决"), + BUG_CLOSED("closed", "已关闭"), + + // 任务看板列类型 + TASK_WAIT("wait", "等待"), + TASK_DEVELOPING("developing", "进行中"), + TASK_DEVELOPED("developed", "已完成"), + TASK_CANCEL("cancel", "已取消"), + TASK_CLOSED("closed", "已关闭"), + + // 通用列类型 + WAIT("wait", "等待"), + DOING("doing", "进行中"), + DONE("done", "已完成"), + CLOSED("closed", "已关闭"); + + private String value; + private String desc; + + private KanbanColumnType(String value, String desc) { + this.value = value; + this.desc = desc; + } + + public String getValue() { + return this.value; + } + + public String getDesc() { + return this.desc; + } +} \ No newline at end of file diff --git a/src/main/java/com/sa/zentao/service/IZtProductService.java b/src/main/java/com/sa/zentao/service/IZtProductService.java index aad25c0..7a5794b 100644 --- a/src/main/java/com/sa/zentao/service/IZtProductService.java +++ b/src/main/java/com/sa/zentao/service/IZtProductService.java @@ -57,4 +57,6 @@ public interface IZtProductService extends IService { List productListByProgramName(String programName); + PageInfo getExecutionByProductId(ZtProjectQo qo); + } diff --git a/src/main/java/com/sa/zentao/service/impl/FestivalConfigServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/FestivalConfigServiceImpl.java index c4c67e9..2fa66b5 100644 --- a/src/main/java/com/sa/zentao/service/impl/FestivalConfigServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/FestivalConfigServiceImpl.java @@ -46,7 +46,9 @@ public class FestivalConfigServiceImpl extends ServiceImpl festivalConfigs = this.baseMapper.selectList(new QueryWrapper().lambda().eq(FestivalConfig::getName, qo.getName()). + List festivalConfigs = this.baseMapper.selectList(new QueryWrapper().lambda() + .eq(FestivalConfig::getYear,qo.getYear()) + .eq(FestivalConfig::getName, qo.getName()). eq(FestivalConfig::getDeleteFlag, DeleteFlagEnum.USED)); if(!CollectionUtils.isEmpty(festivalConfigs)){ throw new BusinessException("已存在"); diff --git a/src/main/java/com/sa/zentao/service/impl/IZtCountService.java b/src/main/java/com/sa/zentao/service/impl/IZtCountService.java index b1dad68..f7fe31f 100644 --- a/src/main/java/com/sa/zentao/service/impl/IZtCountService.java +++ b/src/main/java/com/sa/zentao/service/impl/IZtCountService.java @@ -741,7 +741,11 @@ public class IZtCountService { for (ZtUser u : ztUsers) { List approvalList = this.taskService.itApprovalByUserName(u.getNickname(), firstDayOfMonth, lastDayOfMonth); - if (u.getAccount().equals("liyuyan")) { + if (u.getAccount().equals("weidongxia")||u.getUserType() == UserType.CPJL) { + ZtProjectQo ztProjectQo =new ZtProjectQo(); + ztProjectQo.setDate(qo.getDate()); + result.add(buildCPJLScore(u, userMap , ztProjectQo,approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d,pids)); + }else if (u.getAccount().equals("liyuyan")) { result.add(buildXMZLScore(u, approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d)); } else if ("liushengqing".equals(u.getAccount())) { result.add(buildUiScore(u, approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d)); @@ -1209,7 +1213,6 @@ public class IZtCountService { dto.setProductProjectOnTimeRateScore(BigDecimal.valueOf(productProjectOnTimeRateScore)); - List allBugList = this.bugService.list(new QueryWrapper().lambda().select(SFunctionColums.bugColumes()) .eq(ZtBug::getOpenedby, u.getAccount()) .in(ZtBug::getBugType,"releaseBug","prod") @@ -1239,6 +1242,7 @@ public class IZtCountService { } dto.setBugScore(bugScore>20?BigDecimal.ZERO:BigDecimal.valueOf((20 - bugScore))); } + dto.setProductBugRate(dto.getBugScore()); //线上重大 dto.setSeriousBug(BigDecimal.valueOf(allBugList.stream().filter(o -> Arrays.asList(1).contains(o.getSeverity())).count())); //线上轻微 @@ -1504,6 +1508,7 @@ public class IZtCountService { Date thisDay = new Date(); thisDay.setMonth(date.getMonth()); thisDay.setDate(date.getDate()); + thisDay.setYear(date.getYear()); thisDay.setHours(17); thisDay.setMinutes(30); thisDay.setSeconds(0); @@ -2287,7 +2292,7 @@ public class IZtCountService { //需求准确性 dataMap.put("accurateDemand", performanceDTO.getAccurateDemand()==null?"0": performanceDTO.getAccurateDemand().toString()); //项目准时率 - dataMap.put("productProjectOnTimeRateScore",performanceDTO.getProductProjectOnTimeRateScore()==null?"0": performanceDTO.getProductProjectOnTimeRateScore().multiply(BigDecimal.valueOf(100)).toString()); + dataMap.put("productProjectOnTimeRateScore",performanceDTO.getProductProjectOnTimeRateScore()==null?"0": performanceDTO.getProductProjectOnTimeRateScore().toString()); //线上Bug得分 缺陷 dataMap.put("bugScope", performanceDTO.getBugScore()==null?"0": performanceDTO.getBugScore().toString()); //开发分配工时 diff --git a/src/main/java/com/sa/zentao/service/impl/ZtKanbanlaneServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtKanbanlaneServiceImpl.java index 00b8440..6047c09 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtKanbanlaneServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtKanbanlaneServiceImpl.java @@ -317,12 +317,15 @@ public class ZtKanbanlaneServiceImpl extends ServiceImpl carIds = new ArrayList<>(Arrays.asList(cell.getCards().split(","))); if(carIds.contains(bussId.toString())){ thisZtKanbancell=cell; - continue; + break; } } List carIds = new ArrayList<>(Arrays.asList(thisZtKanbancell.getCards().split(","))); - carIds.remove(bussId.toString()); + while (carIds.contains(bussId.toString())) { + carIds.remove(bussId.toString()); + } + if(CollectionUtils.isEmpty(carIds)){ thisZtKanbancell.setCards(""); diff --git a/src/main/java/com/sa/zentao/service/impl/ZtMeetingServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtMeetingServiceImpl.java index db22a79..a9c562b 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtMeetingServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtMeetingServiceImpl.java @@ -88,17 +88,17 @@ public class ZtMeetingServiceImpl extends ServiceImpl sMap=getUserStoryMap(list); for (ZtMeetingDTO d:list) { - StringBuilder b=new StringBuilder(); - if(!StringUtils.isEmpty(d.getUsers())){ - String[] split = d.getUsers().split(","); - for (String s:split) { - ZtUser ztUser = userMap.get(s); - if(ztUser!=null){ - b.append(ztUser.getNickname()).append(","); - } - } - d.setUsersName(b.toString()); - } +// StringBuilder b=new StringBuilder(); +// if(!StringUtils.isEmpty(d.getUsers())){ +// String[] split = d.getUsers().split(","); +// for (String s:split) { +// ZtUser ztUser = userMap.get(s); +// if(ztUser!=null){ +// b.append(ztUser.getNickname()).append(","); +// } +// } +// d.setUsersName(b.toString()); +// } StringBuilder storyStr=new StringBuilder(); if(!StringUtils.isEmpty( d.getStoryIds())){ String[] split = d.getStoryIds().split(","); diff --git a/src/main/java/com/sa/zentao/service/impl/ZtProductServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtProductServiceImpl.java index 7697576..00345f8 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtProductServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtProductServiceImpl.java @@ -1,6 +1,7 @@ package com.sa.zentao.service.impl; import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; @@ -687,4 +688,63 @@ public class ZtProductServiceImpl extends ServiceImpl getExecutionByProductId(ZtProjectQo qo) { + Integer productId = qo.getProductId(); + if (productId == null || productId == 0) { + throw new BusinessException("产品ID不能为空"); + } + + // 查询产品关联的项目 + List projectproductlist = this.projectproductService.list( + new QueryWrapper().lambda() + .eq(ZtProjectproduct::getProduct, productId) + ); + + if (CollectionUtils.isEmpty(projectproductlist)) { + return new PageInfo<>(); + } + + // 获取项目ID列表 + List projectIds = projectproductlist.stream() + .map(ZtProjectproduct::getProject) + .collect(Collectors.toList()); + + // 查询项目关联的迭代 + List executionprojectList = this.executionprojectService.list( + new QueryWrapper().lambda() + .in(ZtExecutionproject::getProject, projectIds) + ); + + if (CollectionUtils.isEmpty(executionprojectList)) { + return new PageInfo<>(); + } + + // 获取迭代ID列表 + List executionIds = executionprojectList.stream() + .map(ZtExecutionproject::getExecution) + .collect(Collectors.toList()); + + // 分页查询迭代,按ID倒序排列 + com.github.pagehelper.Page page = PageHelper.startPage(qo.getCurrentPage(), qo.getPageSize()); + LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda() + .in(ZtProject::getId, executionIds); + + // 添加名称搜索 + if (!StringUtils.isEmpty(qo.getName())) { + queryWrapper.like(ZtProject::getName, qo.getName()); + } + + queryWrapper.orderByDesc(ZtProject::getId); + + List executionList = this.projectService.list(queryWrapper); + + // 转换为DTO + List dtoList = BeanCopyUtil.copyListProperties(executionList, ZtProjectDTO::new); + + PageInfo ztProjectDTOPageInfo = new PageInfo<>(dtoList); + ztProjectDTOPageInfo.setTotal(page.getTotal()); + return ztProjectDTOPageInfo; + } } diff --git a/src/main/java/com/sa/zentao/service/impl/ZtStoryServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtStoryServiceImpl.java index eabe37d..bd5955b 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtStoryServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtStoryServiceImpl.java @@ -1057,7 +1057,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); this.baseMapper.updateById(ztStory); actionService.addAction(ActionType.XQ, ActionStatus.ASSIGNTOPRODUCTUSER, ztStory.getId(), ztStory.getProduct() + "", null, null, - RiskUserThreadLocal.get().getName(), "", ""); + RiskUserThreadLocal.get().getName(), "", dto.getAssignedTo()); } @Override @@ -1071,7 +1071,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); this.baseMapper.updateById(ztStory); actionService.addAction(ActionType.XQ, ActionStatus.ASSIGNTOTESTUSER, ztStory.getId(), ztStory.getProduct() + "", null, null, - RiskUserThreadLocal.get().getName(), "", ""); + RiskUserThreadLocal.get().getName(), "", dto.getAssignedTo()); } private List searchBug(SearchQo qo) { diff --git a/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java index f91bf89..a5b83f0 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java @@ -19,10 +19,7 @@ import com.sa.zentao.qo.StoryQo; import com.sa.zentao.qo.ZtProjectQo; import com.sa.zentao.service.*; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.sa.zentao.utils.BeanCopyUtil; -import com.sa.zentao.utils.BeanCopyUtilCallBack; -import com.sa.zentao.utils.SendEmail; -import com.sa.zentao.utils.VxMessageUtils; +import com.sa.zentao.utils.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; @@ -81,6 +78,9 @@ public class ZtStoryUserServiceImpl extends ServiceImpl().lambda().eq(ZtStoryUserspec::getStory, id)); + ZtStoryUserspec spec = this.storyUserspecService.getOne(new QueryWrapper().lambda().eq(ZtStoryUserspec::getStory, id).last(" limit 1")); if (spec == null) { spec = new ZtStoryUserspec(); spec.setStory(id); @@ -229,6 +230,7 @@ public class ZtStoryUserServiceImpl extends ServiceImpl().lambda().eq(ZtStoryUserspec::getStory, id) .set(ZtStoryUserspec::getSpec, dto.getSpec()) .set(ZtStoryUserspec::getVerify, dto.getVerify()) + .set(ZtStoryUserspec::getStoryBackground, dto.getStoryBackground()) .set(ZtStoryUserspec::getFiles, dto.getFileUrl()) ); } @@ -246,13 +250,12 @@ public class ZtStoryUserServiceImpl extends ServiceImpl> getKanban(Integer productId) { List ztStoryUsers = this.baseMapper.selectList(new QueryWrapper() - .lambda().select(ZtStoryUser::getId,ZtStoryUser::getPri,ZtStoryUser::getTitle,ZtStoryUser::getStage,ZtStoryUser::getStatus,ZtStoryUser::getOpenedby,ZtStoryUser::getOpeneddate,ZtStoryUser::getLastediteddate,ZtStoryUser::getProductUser) + .lambda().select(ZtStoryUser::getId, ZtStoryUser::getPri, ZtStoryUser::getTitle, ZtStoryUser::getStage, ZtStoryUser::getStatus, ZtStoryUser::getOpenedby, ZtStoryUser::getOpeneddate, ZtStoryUser::getLastediteddate, ZtStoryUser::getProductUser) .eq(ZtStoryUser::getProduct, productId).orderByDesc(ZtStoryUser::getId)); Map> map = new HashMap<>(); Map stringZtUserMap = this.userService.userMapByIds(null); @@ -261,12 +264,12 @@ public class ZtStoryUserServiceImpl extends ServiceImpl"active".equals(o.getStatus())).collect(Collectors.toList())); - map.put("unconfirmed",ztStoryUserDTOS.stream().filter(o->"unconfirmed".equals(o.getStatus())).collect(Collectors.toList())); - map.put("waitcommunicate",ztStoryUserDTOS.stream().filter(o->"waitcommunicate".equals(o.getStatus())).collect(Collectors.toList())); - map.put("waitdesign",ztStoryUserDTOS.stream().filter(o->"waitdesign".equals(o.getStatus())).collect(Collectors.toList())); - map.put("designdoing",ztStoryUserDTOS.stream().filter(o->"designdoing".equals(o.getStatus())).collect(Collectors.toList())); + map.put("active", ztStoryUserDTOS.stream().filter(o -> "active".equals(o.getStatus())).collect(Collectors.toList())); + map.put("unconfirmed", ztStoryUserDTOS.stream().filter(o -> "unconfirmed".equals(o.getStatus())).collect(Collectors.toList())); + map.put("waitcommunicate", ztStoryUserDTOS.stream().filter(o -> "waitcommunicate".equals(o.getStatus())).collect(Collectors.toList())); + map.put("waitdesign", ztStoryUserDTOS.stream().filter(o -> "waitdesign".equals(o.getStatus())).collect(Collectors.toList())); + map.put("designdoing", ztStoryUserDTOS.stream().filter(o -> "designdoing".equals(o.getStatus())).collect(Collectors.toList())); - map.put("designdone",ztStoryUserDTOS.stream().filter(o->"designdone".equals(o.getStatus())).collect(Collectors.toList())); - map.put("storyunconfirmed",ztStoryUserDTOS.stream().filter(o->"storyunconfirmed".equals(o.getStatus())).collect(Collectors.toList())); - map.put("confirmed",ztStoryUserDTOS.stream().filter(o->"confirmed".equals(o.getStatus())).collect(Collectors.toList())); + map.put("designdone", ztStoryUserDTOS.stream().filter(o -> "designdone".equals(o.getStatus())).collect(Collectors.toList())); + map.put("storyunconfirmed", ztStoryUserDTOS.stream().filter(o -> "storyunconfirmed".equals(o.getStatus())).collect(Collectors.toList())); + map.put("confirmed", ztStoryUserDTOS.stream().filter(o -> "confirmed".equals(o.getStatus())).collect(Collectors.toList())); return map; } @@ -298,18 +301,18 @@ public class ZtStoryUserServiceImpl extends ServiceImpl pIds = this.projectService.authProductList(); - if(CollectionUtils.isEmpty(pIds)&&CollectionUtils.isEmpty(qo.getStoryIds())){ + if (CollectionUtils.isEmpty(pIds) && CollectionUtils.isEmpty(qo.getStoryIds())) { return new PageInfo(); } - if(qo.getProductId()!=null&&qo.getProductId()!=0){ + if (qo.getProductId() != null && qo.getProductId() != 0) { qo.setProductIds(Arrays.asList(qo.getProductId())); - }else{ + } else { qo.setProductIds(pIds); } - if(!CollectionUtils.isEmpty(qo.getStatusList())&&qo.getStatusList().contains("unconfirmed")) { + if (!CollectionUtils.isEmpty(qo.getStatusList()) && qo.getStatusList().contains("unconfirmed")) { qo.getStatusList().addAll(Arrays.asList("unconfirmed", - "waitcommunicate","waitdesign","designdoing","designdone","storyunconfirmed")); + "waitcommunicate", "waitdesign", "designdoing", "designdone", "storyunconfirmed")); } Page page = PageHelper.startPage(qo.getCurrentPage(), qo.getPageSize()); @@ -320,7 +323,9 @@ public class ZtStoryUserServiceImpl extends ServiceImpl userMap = this.userService.userMapByIds(null); - Map> storyUserMap = getStoryUserMap(list); + Map> storyMap = getStoryMap(list); + //story - exec + Map> execMap= getExecMap(storyMap); Map> rMap = getReviewMap(list); for (ZtStoryUserDTO d : list) { @@ -369,31 +374,77 @@ public class ZtStoryUserServiceImpl extends ServiceImpl ztStories = storyUserMap.get(d.getId()); + List ztStories = storyMap.get(d.getId()); if (!CollectionUtils.isEmpty(ztStories)) { - d.setStoryList(ztStories.stream().map(o ->o.getId()+":"+o.getTitle()).collect(Collectors.joining(","))); - d.setSList(CollectionUtils.isEmpty(ztStories)?null:BeanCopyUtil.copyListProperties(ztStories,ZtStoryDTO::new)); - } - - - String reName = d.getTitle(); - String reUrl = url+"/#/product-user-story-info/"+d.getId(); - WriteCellData hyperlink = new WriteCellData<>(reName); - HyperlinkData hyperlinkData = new HyperlinkData(); - try { - hyperlinkData.setAddress(reUrl.replace(" ", "%20")); - hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL); - hyperlink.setHyperlinkData(hyperlinkData); - d.setUrl(hyperlink ); - } catch (Exception e) { - log.error(e.getMessage(), e); - d.setUrl(null ); + for (ZtStory story:ztStories){ + List execList = execMap.get(story.getId()); + if(!CollectionUtils.isEmpty(execList)){ + if(CollectionUtils.isEmpty(d.getExecList())){ + d.setExecList(new HashSet<>()); + } + d.getExecList().addAll(execList); + } } + d.setStoryList(ztStories.stream().map(o -> o.getId() + ":" + o.getTitle()).collect(Collectors.joining(","))); + d.setSList(CollectionUtils.isEmpty(ztStories) ? null : BeanCopyUtil.copyListProperties(ztStories, ZtStoryDTO::new)); + } + d.setLinkUrl(url); } } return new PageInfo(list); } + private Map> getExecMap(Map> storyMap) { + if(storyMap==null||storyMap.isEmpty()){ + return new HashMap<>(); + } + // 收集到指定类型的列表(比如ArrayList),还可扩展去重、排序等 + List storyList = storyMap.values() + .stream() + .filter(list -> list != null) + .flatMap(List::stream) + // 自定义收集器:指定ArrayList,还可加.distinct()去重 + .collect(Collectors.toCollection(ArrayList::new)); + if(CollectionUtils.isEmpty(storyList)){ + return new HashMap<>(); + } + List list = this.projectstoryService.list(new QueryWrapper().lambda() + .eq(ZtProjectstory::getType,ProjectTypeEnums.execution.getValue()) + .in(ZtProjectstory::getStory, storyList.stream().map(o -> o.getId()).collect(Collectors.toUnmodifiableList()))); + if(CollectionUtils.isEmpty(list)){ + return new HashMap<>(); + } + List ztExecList = this.projectService.listByIds(list.stream().map(o -> o.getProject()).collect(Collectors.toUnmodifiableList())); + if(CollectionUtils.isEmpty(list)){ + return new HashMap<>(); + } + // 第一步:先构建 projectId -> ZtProject 的映射(方便快速查找) + Map projectIdToProjectMap = ztExecList.stream() + .collect(Collectors.toMap( + ZtProject::getId, // key:项目ID + project -> project, // value:项目对象 + (existing, replacement) -> existing // 处理重复projectId的情况,保留原有值 + )); + + // 第二步:按story分组,收集对应的ZtProject列表 + Map> storyToProjectsMap = list.stream() + .collect(Collectors.groupingBy( + ZtProjectstory::getStory, // 分组key:story字段 + // 分组value:将每个ZtProjectstory对应的project转换成ZtProject,并收集为列表 + Collectors.mapping( + projectStory -> projectIdToProjectMap.get(projectStory.getProject()), + // 过滤掉null(防止projectId不存在的情况),并收集为List + Collectors.filtering( + project -> project != null, + Collectors.toList() + ) + ) + )); + + // 最终返回这个Map(你可以根据需要调整返回逻辑) + return storyToProjectsMap; + } + @Override @Transactional public void changeStatus(ZtStoryUserDTO dto) { @@ -420,8 +471,8 @@ public class ZtStoryUserServiceImpl extends ServiceImpl ztStories = this.baseMapper.selectList(eq); if (CollectionUtils.isEmpty(ztStories)) { @@ -461,7 +512,7 @@ public class ZtStoryUserServiceImpl extends ServiceImpl> storyUserMap = getStoryUserMap(Arrays.asList(dto)); + Map> storyUserMap = getStoryMap(Arrays.asList(dto)); List ztStories = storyUserMap.get(d.getId()); if (!CollectionUtils.isEmpty(ztStories)) { dto.setStoryList(ztStories.stream().map(o -> o.getTitle()).collect(Collectors.joining(","))); @@ -615,8 +661,9 @@ public class ZtStoryUserServiceImpl extends ServiceImpl list = this.storyService.list(new QueryWrapper().lambda().eq(ZtStory::getUserStory, dto.getId())); if (!CollectionUtils.isEmpty(list)) { List storyDTOList = BeanCopyUtil.copyListProperties(list, ZtStoryDTO::new); - if (!CollectionUtils.isEmpty(storyDTOList)) { + Map> execMap= getExecMap(list.stream().collect(Collectors.groupingBy(ZtStory::getId))); + if (!CollectionUtils.isEmpty(storyDTOList)) { for (ZtStoryDTO storyDTO : storyDTOList) { ztUser = userMap.get(storyDTO.getAssignedTo()); if (ztUser != null) { @@ -626,10 +673,19 @@ public class ZtStoryUserServiceImpl extends ServiceImpl execList = execMap.get(storyDTO.getId()); + if(!CollectionUtils.isEmpty(execList)){ + if(CollectionUtils.isEmpty(dto.getExecList())){ + dto.setExecList(new HashSet<>()); + } + dto.getExecList().addAll(execList); + } } dto.setSList(storyDTOList); } + //story - exec + } List tasks = this.storyUserTaskService.list(new QueryWrapper().lambda().eq(ZtStoryUserTask::getUserStoryId, dto.getId())); if (!CollectionUtils.isEmpty(tasks)) { @@ -651,10 +707,11 @@ public class ZtStoryUserServiceImpl extends ServiceImpl specList = this.storyUserspecService.list(new QueryWrapper().lambda().eq(ZtStoryUserspec::getStory, dto.getId())); - if(!CollectionUtils.isEmpty(specList)){ + if (!CollectionUtils.isEmpty(specList)) { ZtStoryUserspec ztStoryUserspec = specList.get(0); dto.setSpec(ztStoryUserspec.getSpec()); dto.setVerify(ztStoryUserspec.getVerify()); + dto.setStoryBackground(ztStoryUserspec.getStoryBackground()); } return dto; @@ -707,7 +764,6 @@ public class ZtStoryUserServiceImpl extends ServiceImpl storyList = this.storyService.list(new QueryWrapper().lambda() .ne(ZtStory::getStatus, "closed").in(ZtStory::getStage, StoryStageEnums.beforeStatus(7)).eq(ZtStory::getUserStory, id)); - if(!CollectionUtils.isEmpty(storyList)){ - return ; + if (!CollectionUtils.isEmpty(storyList)) { + return; } } storyUser.setStatus(status.getCode()); @@ -823,7 +879,7 @@ public class ZtStoryUserServiceImpl extends ServiceImpl> getStoryUserMap(List list) { + private Map> getStoryMap(List list) { List ids = list.stream().map(o -> o.getId()).collect(Collectors.toList()); List list1 = storyService.list(new QueryWrapper().lambda().in(ZtStory::getUserStory, ids)); @@ -930,16 +986,19 @@ public class ZtStoryUserServiceImpl extends ServiceImpl impleme ZtStory ztStory = this.storyService.getById(story); if (ztStory != null) { dto.setStoryName(ztStory.getTitle()); + dto.setInnerYsFlag(ztStory.getInnerYsFlag()); } } @@ -578,8 +579,8 @@ public class ZtTaskServiceImpl extends ServiceImpl impleme if (story != null && story != 0) { ZtStory ztStory = this.storyService.getById(story); // wait 初始化 projected 已立项 developing 研发中 developed 研发完毕 testing 测试中 tested - if (!Arrays.asList("wait", "projected", "developing", "developed", "testing", "tested",StoryStageEnums.productWaitVerified.getValue() - ,StoryStageEnums.productVerified.getValue()).contains(ztStory.getStage())) { + if (!Arrays.asList("wait", "projected", "developing", "developed", "testing", "tested", StoryStageEnums.productWaitVerified.getValue() + , StoryStageEnums.productVerified.getValue()).contains(ztStory.getStage())) { throw new BusinessException("当前状态无法添加任务"); } if (ztStory.getStatus().equals("closed")) { @@ -1037,7 +1038,7 @@ public class ZtTaskServiceImpl extends ServiceImpl impleme StringUtils.isEmpty(finishBy) ? RiskUserThreadLocal.get().getName() : finishBy, dto.getDeliverContent(), null); if (ztTask.getStory() != null && ztTask.getStory() != 0) { ZtStory ztStory = this.storyService.getById(ztTask.getStory()); - if(ztStory!=null){ + if (ztStory != null) { ztStory.setDeliverContent(dto.getDeliverContent()); ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); ztStory.setLastediteddate(new Date()); @@ -1054,40 +1055,41 @@ public class ZtTaskServiceImpl extends ServiceImpl impleme actionService.addAction(ActionType.RW, ActionStatus.WC, dto.getId(), projectproduct == null ? null : projectproduct.getProduct().toString(), projectproduct == null ? null : projectproduct.getProject(), ztTask.getExecution(), StringUtils.isEmpty(finishBy) ? RiskUserThreadLocal.get().getName() : finishBy, "", null); - if (ztTask.getExecution() != null && ztTask.getExecution() != 0) { - if (StringUtils.isEmpty(dto.getTabType())) { - kanbanlaneService.changeStatus(ztTask.getExecution(), ztTask.getId(), "task", "developed"); - } else { - KanbanQo qo = new KanbanQo(); - qo.setStatusType(dto.getStatusType()); - qo.setTabType(dto.getTabType()); - qo.setId(dto.getId()); - qo.setFromId(dto.getFromId()); - qo.setToId(dto.getToId()); - kanbanlaneService.changeStatus(qo); - } + if (ztTask.getExecution() != null && ztTask.getExecution() != 0) { + if (StringUtils.isEmpty(dto.getTabType())) { + kanbanlaneService.changeStatus(ztTask.getExecution(), ztTask.getId(), "task", "developed"); + } else { + KanbanQo qo = new KanbanQo(); + qo.setStatusType(dto.getStatusType()); + qo.setTabType(dto.getTabType()); + qo.setId(dto.getId()); + qo.setFromId(dto.getFromId()); + qo.setToId(dto.getToId()); + kanbanlaneService.changeStatus(qo); } - // devel 开发 request 需求 test - //如果有一个开发任务进行中,并且所有的测试任务还没有开始,需求的研发阶段为“研发中” - //如果所有的开发任务已经完成,并且所有的测试任务还没有开始,则为“研发完毕" - //如果有一个测试任务进行中,则视为“测试中” - //如果所有的测试任务已经结束,但还有一些开发任务没有结束,则视为"测试中" - //如果所有的测试任务已经结束,并且所有的开发任务已经结束,则视为"测试完毕" + } + // devel 开发 request 需求 test + //如果有一个开发任务进行中,并且所有的测试任务还没有开始,需求的研发阶段为“研发中” + //如果所有的开发任务已经完成,并且所有的测试任务还没有开始,则为“研发完毕" + //如果有一个测试任务进行中,则视为“测试中” + //如果所有的测试任务已经结束,但还有一些开发任务没有结束,则视为"测试中" + //如果所有的测试任务已经结束,并且所有的开发任务已经结束,则视为"测试完毕" // pause 暂停 cancel取消 closed 关闭 done 完成 wait reviewing待评审 doing // devel 开发 request 需求 test - String type = ztTask.getType(); - if (ztTask.getStory() != null && ztTask.getStory() != 0) { - this.storyService.taskFinishChangeStatus(ztTask.getStory(), finishBy, TaskType.transferType(type), false); - } - if (ztTask.getFeedback() != null && ztTask.getFeedback() != 0) { - this.storyFeedbackService.feedbackFinished(ztTask.getFeedback(), ztTask.getFeedbackRemark()); - } - if (ztTask.getStory() != null && ztTask.getStory() != 0 && ObjectUtil.equal(dto.getInnerYsFlag(), 1)) { - ZtStoryDTO qo = new ZtStoryDTO(); - qo.setId(ztTask.getStory()); - this.storyService.testSubmitVerified(qo); - } + String type = ztTask.getType(); + if (ztTask.getStory() != null && ztTask.getStory() != 0) { + this.storyService.taskFinishChangeStatus(ztTask.getStory(), finishBy, TaskType.transferType(type), false); + } + if (ztTask.getFeedback() != null && ztTask.getFeedback() != 0) { + this.storyFeedbackService.feedbackFinished(ztTask.getFeedback(), ztTask.getFeedbackRemark()); + } +// if (ztTask.getStory() != null && ztTask.getStory() != 0 && ObjectUtil.equal(dto.getInnerYsFlag(), 1)) { +// ZtStoryDTO qo = new ZtStoryDTO(); +// qo.setId(ztTask.getStory()); +// this.storyService.testSubmitVerified(qo); +// } } + @Override @Transactional public void closeTask(ZtTaskDTO dto) { diff --git a/src/main/java/com/sa/zentao/utils/KanbanStageMapping.java b/src/main/java/com/sa/zentao/utils/KanbanStageMapping.java new file mode 100644 index 0000000..4deb181 --- /dev/null +++ b/src/main/java/com/sa/zentao/utils/KanbanStageMapping.java @@ -0,0 +1,87 @@ +package com.sa.zentao.utils; + +import com.sa.zentao.enums.KanbanColumnType; + +import java.util.HashMap; +import java.util.Map; + +/** + * 看板阶段映射工具类 + * 用于将业务阶段映射到看板列类型 + */ +public class KanbanStageMapping { + + /** + * 需求阶段到看板列的映射 + */ + private static final Map STORY_STAGE_MAP = new HashMap<>(); + + /** + * 任务状态到看板列的映射 + */ + private static final Map TASK_STATUS_MAP = new HashMap<>(); + + static { + // 初始化需求阶段映射 + STORY_STAGE_MAP.put("closed", KanbanColumnType.STORY_CLOSED); + STORY_STAGE_MAP.put("wait", KanbanColumnType.STORY_BACKLOG); + STORY_STAGE_MAP.put("projected", KanbanColumnType.STORY_BACKLOG); + STORY_STAGE_MAP.put("developing", KanbanColumnType.STORY_DEVELOPING); + STORY_STAGE_MAP.put("developed", KanbanColumnType.STORY_DEVELOPED); + STORY_STAGE_MAP.put("testing", KanbanColumnType.STORY_TESTING); + STORY_STAGE_MAP.put("tested", KanbanColumnType.STORY_TESTED); + STORY_STAGE_MAP.put("released", KanbanColumnType.STORY_RELEASED); + STORY_STAGE_MAP.put("verified", KanbanColumnType.STORY_VERIFIED); + + // 初始化任务状态映射 + TASK_STATUS_MAP.put("wait", KanbanColumnType.TASK_WAIT); + TASK_STATUS_MAP.put("reviewing", KanbanColumnType.TASK_WAIT); + TASK_STATUS_MAP.put("draft", KanbanColumnType.TASK_WAIT); + TASK_STATUS_MAP.put("doing", KanbanColumnType.TASK_DEVELOPING); + TASK_STATUS_MAP.put("done", KanbanColumnType.TASK_DEVELOPED); + TASK_STATUS_MAP.put("cancel", KanbanColumnType.TASK_CANCEL); + TASK_STATUS_MAP.put("closed", KanbanColumnType.TASK_CLOSED); + } + + /** + * 根据需求阶段获取看板列类型 + * + * @param stage 需求阶段 + * @return 看板列类型 + */ + public static KanbanColumnType getStoryColumnByStage(String stage) { + return STORY_STAGE_MAP.getOrDefault(stage, KanbanColumnType.STORY_BACKLOG); + } + + /** + * 根据任务状态获取看板列类型 + * + * @param status 任务状态 + * @return 看板列类型 + */ + public static KanbanColumnType getTaskColumnByStatus(String status) { + return TASK_STATUS_MAP.getOrDefault(status, KanbanColumnType.TASK_WAIT); + } + + /** + * 获取需求阶段对应的看板列值 + * + * @param stage 需求阶段 + * @return 看板列值 + */ + public static String getStoryColumnValue(String stage) { + KanbanColumnType columnType = getStoryColumnByStage(stage); + return columnType.getValue(); + } + + /** + * 获取任务状态对应的看板列值 + * + * @param status 任务状态 + * @return 看板列值 + */ + public static String getTaskColumnValue(String status) { + KanbanColumnType columnType = getTaskColumnByStatus(status); + return columnType.getValue(); + } +} \ No newline at end of file diff --git a/src/main/resources/mapper/ZtStoryMapper.xml b/src/main/resources/mapper/ZtStoryMapper.xml index 4c17f7f..17a62db 100644 --- a/src/main/resources/mapper/ZtStoryMapper.xml +++ b/src/main/resources/mapper/ZtStoryMapper.xml @@ -64,7 +64,7 @@