From e948b53e897c7edd176e5245b26c82d26f7c2a1e Mon Sep 17 00:00:00 2001 From: guoqibing Date: Tue, 23 Dec 2025 10:17:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=A7=E5=93=81=E8=A7=92=E8=89=B2=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 11 ++ .../zentao/controller/ZtStoryController.java | 44 +++++ .../controller/ZtStoryUserController.java | 16 ++ .../zentao/controller/ZtUserController.java | 8 +- .../com/sa/zentao/dao/PerformanceDTO.java | 22 +++ .../java/com/sa/zentao/dao/ZtProjectDTO.java | 3 + .../java/com/sa/zentao/dao/ZtStoryDTO.java | 31 +++- .../com/sa/zentao/dao/ZtStoryUserDTO.java | 16 +- .../java/com/sa/zentao/dao/ZtTaskDTO.java | 4 + .../java/com/sa/zentao/entity/ZtStory.java | 20 ++- .../com/sa/zentao/entity/ZtStoryUser.java | 4 + .../com/sa/zentao/enums/ActionStatus.java | 6 + .../com/sa/zentao/enums/StoryStageEnums.java | 2 + .../java/com/sa/zentao/enums/UserType.java | 1 + .../com/sa/zentao/mapper/ZtStoryMapper.java | 4 + src/main/java/com/sa/zentao/qo/StoryQo.java | 4 + .../java/com/sa/zentao/qo/ZtProjectQo.java | 6 +- .../sa/zentao/service/IZtStoryService.java | 13 +- .../zentao/service/IZtStoryUserService.java | 3 + .../zentao/service/impl/IZtCountService.java | 149 ++++++++++++++-- .../service/impl/ZtProjectServiceImpl.java | 7 +- .../service/impl/ZtReleaseServiceImpl.java | 6 +- .../service/impl/ZtStoryServiceImpl.java | 161 ++++++++++++++++-- .../service/impl/ZtStoryUserServiceImpl.java | 31 +++- .../service/impl/ZtTaskServiceImpl.java | 17 +- .../com/sa/zentao/utils/KanBanConstant.java | 4 + src/main/resources/mapper/ZtStoryMapper.xml | 47 ++++- .../resources/mapper/ZtStoryUserMapper.xml | 12 +- .../templates/scope/产品经理考核.xlsx | Bin 0 -> 12632 bytes 29 files changed, 598 insertions(+), 54 deletions(-) create mode 100644 src/main/resources/templates/scope/产品经理考核.xlsx diff --git a/pom.xml b/pom.xml index f997e28..647e22c 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,17 @@ 3.3.0 + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.hutool + hutool-all + 4.6.3 + com.itextpdf diff --git a/src/main/java/com/sa/zentao/controller/ZtStoryController.java b/src/main/java/com/sa/zentao/controller/ZtStoryController.java index b5b24b3..3d7588d 100644 --- a/src/main/java/com/sa/zentao/controller/ZtStoryController.java +++ b/src/main/java/com/sa/zentao/controller/ZtStoryController.java @@ -8,11 +8,13 @@ import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.github.pagehelper.PageInfo; import com.sa.zentao.dao.*; import com.sa.zentao.entity.ZtProject; import com.sa.zentao.entity.ZtProjectproduct; import com.sa.zentao.entity.ZtStoryFeedback; +import com.sa.zentao.entity.ZtStoryUser; import com.sa.zentao.enums.StoryStageEnums; import com.sa.zentao.enums.StoryStatusEnums; import com.sa.zentao.enums.UserStoryEnums; @@ -26,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -182,6 +185,27 @@ public class ZtStoryController { } + /** + * 测试提交验收 + * @param qo + * @return + */ + @RequestMapping(value = "/testSubmitVerified", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result testSubmitVerified(@RequestBody ZtStoryDTO qo){ + ztStoryService.testSubmitVerified(qo); + return Result.success(); + } + /** + * 产品验收通过 + * @param qo + * @return + */ + @RequestMapping(value = "/storyProductUserYs", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result storyProductUserYs(@RequestBody ZtStoryDTO qo){ + ztStoryService.storyProductUserYs(qo); + return Result.success(); + } + @RequestMapping(value = "/storyListByProject", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") public Result storyListByProject(@RequestBody ZtProjectQo qo){ @@ -336,6 +360,26 @@ public class ZtStoryController { List list = ztStoryService.execListByProject(dto); return Result.success(list); } + /** + * 指派产品人 + * @param dto + * @return + */ + @RequestMapping(value = "/assignedToProductUser", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result assignedToProductUser(@RequestBody @Validated ZtStoryDTO dto){ + this.ztStoryService.assignedToProductUser(dto); + return Result.success(); + } + /** + * 指派测试人 + * @param dto + * @return + */ + @RequestMapping(value = "/assignedToTestUser", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result assignedToTestUser(@RequestBody @Validated ZtStoryDTO dto){ + this.ztStoryService.assignedToTestUser(dto); + return Result.success(); + } diff --git a/src/main/java/com/sa/zentao/controller/ZtStoryUserController.java b/src/main/java/com/sa/zentao/controller/ZtStoryUserController.java index 3b798b9..686b97a 100644 --- a/src/main/java/com/sa/zentao/controller/ZtStoryUserController.java +++ b/src/main/java/com/sa/zentao/controller/ZtStoryUserController.java @@ -7,10 +7,14 @@ import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.github.pagehelper.PageInfo; +import com.sa.zentao.conf.RiskUserThreadLocal; import com.sa.zentao.dao.*; import com.sa.zentao.entity.ZtStory; import com.sa.zentao.entity.ZtStoryUser; +import com.sa.zentao.enums.ActionStatus; +import com.sa.zentao.enums.ActionType; import com.sa.zentao.enums.UserStoryEnums; import com.sa.zentao.qo.StoryQo; import com.sa.zentao.qo.ZtProjectQo; @@ -19,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; @@ -56,6 +61,17 @@ public class ZtStoryUserController { return Result.success(); } + /** + * 指派产品人 + * @param dto + * @return + */ + @RequestMapping(value = "/assignedToProductUser", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") + public Result assignedToProductUser(@RequestBody @Validated ZtStoryUserDTO dto){ + this.storyUserService.assignedToProductUser(dto); + return Result.success(); + } + @RequestMapping(value = "/addStory", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") public Result addStory(@RequestBody ZtStoryUserDTO dto){ diff --git a/src/main/java/com/sa/zentao/controller/ZtUserController.java b/src/main/java/com/sa/zentao/controller/ZtUserController.java index bdeaf2c..dfcc160 100644 --- a/src/main/java/com/sa/zentao/controller/ZtUserController.java +++ b/src/main/java/com/sa/zentao/controller/ZtUserController.java @@ -135,11 +135,11 @@ public class ZtUserController { } eq.in(ZtUser::getAccount,execution.stream().map(o->o.getAccount()).collect(Collectors.toList())); }else{ - this.userMapper.selectOne(new QueryWrapper().lambda().eq(ZtUser::getAccount,RiskUserThreadLocal.get().getName())); + this.userMapper.selectOne(new QueryWrapper().lambda() + .eq(ZtUser::getAccount,RiskUserThreadLocal.get().getName())); } - - return Result.success(userService.list(eq - )); + eq.eq(dto.getUserType()!=null,ZtUser::getUserType,dto.getUserType()); + return Result.success(userService.list(eq)); } @RequestMapping(value = "/storyReviewList", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") diff --git a/src/main/java/com/sa/zentao/dao/PerformanceDTO.java b/src/main/java/com/sa/zentao/dao/PerformanceDTO.java index 55434c6..13411e5 100644 --- a/src/main/java/com/sa/zentao/dao/PerformanceDTO.java +++ b/src/main/java/com/sa/zentao/dao/PerformanceDTO.java @@ -160,4 +160,26 @@ public class PerformanceDTO implements Serializable { private BigDecimal professionalSkillEnhancementScore=BigDecimal.ZERO; //问题管理得分 private BigDecimal developFeedbackStory=BigDecimal.valueOf(10); + + /** + * 产品经理 + */ + // 产品缺陷率 + private BigDecimal productBugRate=BigDecimal.ZERO; + //项目准时率 需求发布准时率 研发需求/用户需求 + private BigDecimal productProjectOnTimeRateScore=BigDecimal.ZERO; + //主动性责任感 + private BigDecimal productResponsibilityScore=BigDecimal.valueOf(5); + //需求会议 + private BigDecimal productMeetScore=BigDecimal.valueOf(10); + //问题响应 + private BigDecimal productProblemResponse=BigDecimal.valueOf(15); + //需求准确性 + private BigDecimal accurateDemand=BigDecimal.valueOf(20);; + //产品规划 + private BigDecimal productPlanning=BigDecimal.valueOf(10);; + //本阶段计划发布数量 + private BigDecimal productPlanStoryNum; + //本阶段准时发布研发数量 + private BigDecimal productPlanStoryOnTimeNum; } diff --git a/src/main/java/com/sa/zentao/dao/ZtProjectDTO.java b/src/main/java/com/sa/zentao/dao/ZtProjectDTO.java index 0dc7b63..9b165a0 100644 --- a/src/main/java/com/sa/zentao/dao/ZtProjectDTO.java +++ b/src/main/java/com/sa/zentao/dao/ZtProjectDTO.java @@ -3,6 +3,7 @@ package com.sa.zentao.dao; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; +import com.sa.zentao.enums.UserType; import lombok.Data; import lombok.EqualsAndHashCode; @@ -205,6 +206,8 @@ public class ZtProjectDTO implements Serializable { private String deleted; + private UserType userType; + private List children; private List storyIds; diff --git a/src/main/java/com/sa/zentao/dao/ZtStoryDTO.java b/src/main/java/com/sa/zentao/dao/ZtStoryDTO.java index 1c7f84d..973ad71 100644 --- a/src/main/java/com/sa/zentao/dao/ZtStoryDTO.java +++ b/src/main/java/com/sa/zentao/dao/ZtStoryDTO.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.sa.zentao.entity.ZtStoryCase; import com.sa.zentao.entity.ZtStoryreview; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; @@ -31,6 +32,7 @@ public class ZtStoryDTO implements Serializable { private static final long serialVersionUID = 1L; + @NotNull(message = "请录入ID") @ExcelProperty(value = "ID",index =0) private Integer id; @ExcelProperty(value = "研发需求名称",index =1) @@ -48,6 +50,8 @@ public class ZtStoryDTO implements Serializable { private Date planEndDate; @ExcelProperty(value = "阶段",index =6) private String stage; + @ExcelIgnore + private String stageName; @ExcelProperty(value = "指派给",index =7) private String assignedToName; @@ -152,8 +156,32 @@ public class ZtStoryDTO implements Serializable { private Integer frombug; @ExcelIgnore private Integer feedback; + /** + * 产品人 + */ + @ExcelIgnore + private String productUser; + /** + * 产品人 + */ + @ExcelIgnore + private String productUserName; + /** + * 产品人 + */ + @ExcelIgnore + private String testUser; + /** + * 产品人 + */ + @ExcelIgnore + private String testUserName; - + /** + * 1是否内部验收 0不需要 + */ + @ExcelIgnore + private Integer innerYsFlag; @ExcelIgnore private String keywords; @@ -217,7 +245,6 @@ public class ZtStoryDTO implements Serializable { private Date planYsDate; - @ExcelIgnore private Date activateddate; @ExcelIgnore diff --git a/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java b/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java index 2f0b99b..0ce81ab 100644 --- a/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java +++ b/src/main/java/com/sa/zentao/dao/ZtStoryUserDTO.java @@ -6,8 +6,11 @@ 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 jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import java.io.Serializable; import java.time.LocalDate; @@ -31,6 +34,7 @@ public class ZtStoryUserDTO implements Serializable { // @TableId(value = "id", type = IdType.AUTO) // @ExcelProperty + @NotNull(message = "ID不能为空") @ExcelProperty(value = "ID",index =0) private Integer id; @@ -130,6 +134,10 @@ public class ZtStoryUserDTO implements Serializable { @ExcelIgnore private String color; @ExcelIgnore + private String productUserName; + @ExcelIgnore + private String productUserColor; + @ExcelIgnore private String stage; @ExcelIgnore @@ -147,7 +155,7 @@ public class ZtStoryUserDTO implements Serializable { private String openedby; - + @NotBlank(message = "指派??") @ExcelIgnore private String assignedto; @ExcelIgnore @@ -273,4 +281,10 @@ public class ZtStoryUserDTO implements Serializable { //1.需要 2.不需要 @ExcelIgnore private Integer needImprove; + + /** + * 产品用户 + */ + @ExcelIgnore + private String productUser; } diff --git a/src/main/java/com/sa/zentao/dao/ZtTaskDTO.java b/src/main/java/com/sa/zentao/dao/ZtTaskDTO.java index e2cb23d..8a29081 100644 --- a/src/main/java/com/sa/zentao/dao/ZtTaskDTO.java +++ b/src/main/java/com/sa/zentao/dao/ZtTaskDTO.java @@ -212,4 +212,8 @@ public class ZtTaskDTO implements Serializable { * 交付物 */ private String deliverContent; + /** + * 1提交 0不提交 + */ + private Integer innerYsFlag; } diff --git a/src/main/java/com/sa/zentao/entity/ZtStory.java b/src/main/java/com/sa/zentao/entity/ZtStory.java index 0455a44..effffcf 100644 --- a/src/main/java/com/sa/zentao/entity/ZtStory.java +++ b/src/main/java/com/sa/zentao/entity/ZtStory.java @@ -190,7 +190,12 @@ public class ZtStory implements Serializable { private Date developedDate; //结束日期 测试结束 private Date endDate; - + //内部验收时间 + @TableField(exist = false) + private Date innerYsTime; + //测试提交验收时间 + @TableField(exist = false) + private Date testSubmitYsTime; private Integer project; //1通过 2不通过 private Integer ysFlag; @@ -223,5 +228,16 @@ public class ZtStory implements Serializable { // private Date develDate; // //测试完成时间 // private Date testedDate; - + /** + * 内部验收标识 1需要内部验收 0不需要 + */ + private Integer innerYsFlag; + /** + * 产品人员 + */ + private String productUser; + /** + * 测试人员 + */ + private String testUser; } diff --git a/src/main/java/com/sa/zentao/entity/ZtStoryUser.java b/src/main/java/com/sa/zentao/entity/ZtStoryUser.java index e746de6..a5c83bf 100644 --- a/src/main/java/com/sa/zentao/entity/ZtStoryUser.java +++ b/src/main/java/com/sa/zentao/entity/ZtStoryUser.java @@ -225,4 +225,8 @@ public class ZtStoryUser implements Serializable { private String deliverRemark; private String oldStatus; + /** + * 产品用户 + */ + private String productUser; } diff --git a/src/main/java/com/sa/zentao/enums/ActionStatus.java b/src/main/java/com/sa/zentao/enums/ActionStatus.java index 701aa8a..c285fe9 100644 --- a/src/main/java/com/sa/zentao/enums/ActionStatus.java +++ b/src/main/java/com/sa/zentao/enums/ActionStatus.java @@ -62,6 +62,12 @@ public enum ActionStatus { XGMM(101, "xgmm","修改密码"), CZMM(102, "czmm","重置密码"), CZINFO(103, "czmm","重置"), + + //测试提交验收 + TESTSUBMITYS(200, "testSubmitYs","测试提交验收"), + PRODUCTYS(201, "productYs","产品验收"), + ASSIGNTOPRODUCTUSER(202, "assignProductUser","指派产品人"), + ASSIGNTOTESTUSER(203, "assignTestUser","指派测试人"), ; @EnumValue diff --git a/src/main/java/com/sa/zentao/enums/StoryStageEnums.java b/src/main/java/com/sa/zentao/enums/StoryStageEnums.java index 5268e39..b3b7a38 100644 --- a/src/main/java/com/sa/zentao/enums/StoryStageEnums.java +++ b/src/main/java/com/sa/zentao/enums/StoryStageEnums.java @@ -16,6 +16,8 @@ public enum StoryStageEnums { developed(10, "developed","研发完毕"), testing(5, "testing","测试中"), tested(6, "tested","测试完毕"), + productWaitVerified(9, "productWaitVerified","产品验收中"), + productVerified(10, "productVerified","产品已验收"), released(7, "released","已发布"), verified(8, "verified","已验收"), ; diff --git a/src/main/java/com/sa/zentao/enums/UserType.java b/src/main/java/com/sa/zentao/enums/UserType.java index b6ea646..f0defeb 100644 --- a/src/main/java/com/sa/zentao/enums/UserType.java +++ b/src/main/java/com/sa/zentao/enums/UserType.java @@ -13,6 +13,7 @@ public enum UserType { UI(7, "UI工程师"), XMJL(8, "项目经理"), XMZL(9, "项目助理"), + CPJL(10, "产品经理"), ; @EnumValue diff --git a/src/main/java/com/sa/zentao/mapper/ZtStoryMapper.java b/src/main/java/com/sa/zentao/mapper/ZtStoryMapper.java index 8b0ab39..347c065 100644 --- a/src/main/java/com/sa/zentao/mapper/ZtStoryMapper.java +++ b/src/main/java/com/sa/zentao/mapper/ZtStoryMapper.java @@ -10,6 +10,7 @@ import org.apache.ibatis.annotations.Param; import java.util.Date; import java.util.List; +import java.util.Map; /** *

@@ -59,4 +60,7 @@ public interface ZtStoryMapper extends BaseMapper { * @return */ List searchAll(@Param("keyword") String keyword,@Param("pIds")List pIds); + + Map storyReleaseOnTimeByProducts(@Param("pIds") List pids, + @Param("startTime") Date firstDayOfMonth,@Param("endTime") Date lastDayOfMonth); } diff --git a/src/main/java/com/sa/zentao/qo/StoryQo.java b/src/main/java/com/sa/zentao/qo/StoryQo.java index 497ddeb..22300c6 100644 --- a/src/main/java/com/sa/zentao/qo/StoryQo.java +++ b/src/main/java/com/sa/zentao/qo/StoryQo.java @@ -22,6 +22,10 @@ public class StoryQo extends BaseQo { private Integer productId; private String openedby; private String status; + /** + * 用户产品 + */ + private String productUser; private List productIds; private List statusList; diff --git a/src/main/java/com/sa/zentao/qo/ZtProjectQo.java b/src/main/java/com/sa/zentao/qo/ZtProjectQo.java index daa873f..b616861 100644 --- a/src/main/java/com/sa/zentao/qo/ZtProjectQo.java +++ b/src/main/java/com/sa/zentao/qo/ZtProjectQo.java @@ -37,7 +37,11 @@ public class ZtProjectQo extends BaseQo { private String severity; private String productName; - + /** + * 产品人 + */ + private String productUser; + private String testUser; private Integer productId; private List productIds; diff --git a/src/main/java/com/sa/zentao/service/IZtStoryService.java b/src/main/java/com/sa/zentao/service/IZtStoryService.java index 010a171..ec9b0c9 100644 --- a/src/main/java/com/sa/zentao/service/IZtStoryService.java +++ b/src/main/java/com/sa/zentao/service/IZtStoryService.java @@ -57,7 +57,7 @@ public interface IZtStoryService extends IService { void waitStory(Integer story); void startStory(Integer story); //开发完成 - void finishStory(Integer story,String finishBy); + void develFinishStory(Integer story,String finishBy); //测试完毕 void testedStory(Integer story,String finishBy); //测试中 @@ -105,4 +105,15 @@ public interface IZtStoryService extends IService { List getStoryListByDatePidsProject(List pids, Integer id, Date firstDayOfMonth, Date lastDayOfMonth); PageInfo searchByName(SearchQo qo); + + void testSubmitVerified(ZtStoryDTO qo); + + void storyProductUserYs(ZtStoryDTO qo); + + Map storyReleaseOnTimeByProducts(@Param("pids") List pids, @Param("startTime")Date firstDayOfMonth, + @Param("endTime")Date lastDayOfMonth); + + void assignedToProductUser(ZtStoryDTO dto); + + void assignedToTestUser(ZtStoryDTO dto); } diff --git a/src/main/java/com/sa/zentao/service/IZtStoryUserService.java b/src/main/java/com/sa/zentao/service/IZtStoryUserService.java index 8974572..2c7a6e3 100644 --- a/src/main/java/com/sa/zentao/service/IZtStoryUserService.java +++ b/src/main/java/com/sa/zentao/service/IZtStoryUserService.java @@ -54,4 +54,7 @@ public interface IZtStoryUserService extends IService { void confirmStory(ZtStoryUserDTO dto); void needMeetOrDesign(ZtStoryUserDTO dto); + + void assignedToProductUser(ZtStoryUserDTO dto); + } 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 0609081..b1dad68 100644 --- a/src/main/java/com/sa/zentao/service/impl/IZtCountService.java +++ b/src/main/java/com/sa/zentao/service/impl/IZtCountService.java @@ -1,5 +1,6 @@ package com.sa.zentao.service.impl; +import cn.hutool.core.util.ObjectUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.annotation.ExcelIgnore; @@ -242,7 +243,10 @@ public class IZtCountService { .map(o -> o.getId() + "").collect(Collectors.joining(","))); result.setTestingCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) - .filter(o -> Arrays.asList("testing", "tested").contains(o.getStage())).map(o -> o.getId() + "").collect(Collectors.joining(","))); + .filter(o -> Arrays.asList("testing", "tested" + ,StoryStageEnums.productWaitVerified.getValue() + ,StoryStageEnums.productVerified.getValue() + ).contains(o.getStage())).map(o -> o.getId() + "").collect(Collectors.joining(","))); // developing 研发中 developed 研发完毕 testing 测试中 tested测试完毕 result.setJxzCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) @@ -350,7 +354,10 @@ public class IZtCountService { //0825 计划中和研发中合并 result.setJxzCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) - .filter(o -> Arrays.asList("wait","tested", "testing", "developed", "developing").contains(o.getStage()) + .filter(o -> Arrays.asList("wait","tested", "testing", "developed", "developing" + ,StoryStageEnums.productWaitVerified.getValue() + ,StoryStageEnums.productVerified.getValue()).contains(o.getStage() + ) ).map(o -> o.getId() + "").collect(Collectors.joining(","))); result.setCswbCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) @@ -380,7 +387,7 @@ public class IZtCountService { result = setFeedback(result, feedbacks); - } else if (userType == UserType.XMGLY || userType == UserType.XMZL) { + } else if (userType == UserType.XMGLY || userType == UserType.XMZL||userType == UserType.CPJL) { List pList = this.productService.listByIds(pIds); @@ -412,7 +419,9 @@ public class IZtCountService { .map(o -> o.getId() + "").collect(Collectors.joining(","))); result.setTestingCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) - .filter(o -> Arrays.asList("testing", "tested").contains(o.getStage())).map(o -> o.getId() + "").collect(Collectors.joining(","))); + .filter(o -> Arrays.asList("testing", "tested",StoryStageEnums.productVerified.getValue() + ,StoryStageEnums.productWaitVerified.getValue() + ).contains(o.getStage())).map(o -> o.getId() + "").collect(Collectors.joining(","))); result.setJxzCount(ztStory.stream().filter(o -> "active".equals(o.getStatus())) .filter(o -> Arrays.asList("developing", "developed").contains(o.getStage())).map(o -> o.getId() + "").collect(Collectors.joining(","))); @@ -543,6 +552,10 @@ public class IZtCountService { continue; } performanceDTO=JSON.parseObject(ztMonthScore.getScopeJson(),PerformanceDTO.class); + if (ztUser.getAccount().equals("weidongxia")) { + generatorCPJLExcel(qo, performanceDTO.getUserName(), performanceDTO, qo.getDate(), perList); + list.add(performanceDTO.getUserName() + "产品经理考核.xlsx"); + } else if (ztUser.getAccount().equals("liyuyan")) { generatorXMZLExcel(performanceDTO.getUserName(), performanceDTO, qo.getDate()); list.add(performanceDTO.getUserName() + "项目助理考核.xlsx"); @@ -564,6 +577,9 @@ public class IZtCountService { } else if (ztUser.getUserType() == UserType.XMJL || ztUser.getUserType() == UserType.XMGLY || ztUser.getUserType() == UserType.GSGC) { generatorXMJLExcel(qo, performanceDTO.getUserName(), performanceDTO, qo.getDate(), perList); list.add(performanceDTO.getUserName() + "项目经理考核.xlsx"); + }else if (ztUser.getUserType() == UserType.CPJL ) { + generatorCPJLExcel(qo, performanceDTO.getUserName(), performanceDTO, qo.getDate(), perList); + list.add(performanceDTO.getUserName() + "产品经理考核.xlsx"); } } String dir = System.getProperty("user.dir") + "/"; @@ -1167,6 +1183,69 @@ public class IZtCountService { return dto; } + PerformanceDTO buildCPJLScore(ZtUser u,Map userMap,ZtProjectQo qo, List approvalList, Date firstDayOfMonth, Date lastDayOfMonth, List taskList, Date d,List pids) { + + /** + * 产品经理 + */ + PerformanceDTO dto = new PerformanceDTO(); + dto.setUserName(u.getNickname()); + dto.setAccount(u.getAccount()); + //项目准时率 Σ (当月上线准时上线的需求量) / Σ (当月规划上线的需求总量) * 100≥95% + +// dto.setProjectOnTimeRate(); + //产品缺陷率 +// dto.setProductBugRate(); + Map map=this.storyService.storyReleaseOnTimeByProducts(pids,firstDayOfMonth,lastDayOfMonth); + Integer planReleaseStoryNum = Integer.valueOf(map.getOrDefault("planReleaseStoryNum", 0).toString()); + Integer storyOnTimeNum = Integer.valueOf(map.getOrDefault("onTimeNum", 0).toString()); + dto.setProductPlanStoryNum(BigDecimal.valueOf(planReleaseStoryNum)); + dto.setProductPlanStoryOnTimeNum(BigDecimal.valueOf(storyOnTimeNum)); + int storyOnTimeRate = ObjectUtil.equal(planReleaseStoryNum, 0) ? 0 : + (BigDecimal.valueOf(storyOnTimeNum*100).divide(BigDecimal.valueOf(planReleaseStoryNum), 0, BigDecimal.ROUND_UP)).intValue(); + int productProjectOnTimeRateScore=storyOnTimeRate==0?0: (storyOnTimeRate>=95)? 20: + (storyOnTimeRate>=90&&storyOnTimeRate<95)?(20-(95-storyOnTimeRate)): + ((90-storyOnTimeRate)*2+5>20)? 0:(20-((90-storyOnTimeRate)*2+5)); + 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") + .ge(ZtBug::getOpeneddate,firstDayOfMonth) + .le(ZtBug::getOpeneddate,lastDayOfMonth) + .in(ZtBug::getProduct,pids)); + allBugList = this.bugService.getNormalBugList(allBugList); + //开发分配工时 + ZtCountQo ztCountQo = new ZtCountQo(); + BeanUtils.copyProperties(qo, ztCountQo); + List ztProducts = this.productService.listByIds(pids); + List perList = this.programCount(ztCountQo,ztProducts.stream().map(o->o.getProgram()).collect(Collectors.toList())); + dto = buildTaskManage(dto, perList, userMap,firstDayOfMonth,lastDayOfMonth,pids); + //开发任务分配工时 + BigDecimal allocationTime = dto.getAllocationTime(); + double bugRate=(allocationTime.doubleValue()==0||allBugList.size()==0)?0:BigDecimal.valueOf(allBugList.size()*1000).divide(allocationTime,2,BigDecimal.ROUND_HALF_UP).doubleValue(); + dto.setBugDensity(BigDecimal.valueOf(bugRate)); + if(bugRate<=5){ + dto.setBugScore(BigDecimal.valueOf(20)); + }else{ +// long deductBugScore = Math.min(seriousBug * 10L + slightBug * 3L, 20L); +// dto.setBugScore(BigDecimal.valueOf(20L-deductBugScore)); + //严重10 分 轻微3分一个 + int bugScore = 0; + for (ZtBug b : allBugList) { + bugScore += b.getSeverity() != 1 ? 3 : 10; + } + dto.setBugScore(bugScore>20?BigDecimal.ZERO:BigDecimal.valueOf((20 - bugScore))); + } + //线上重大 + dto.setSeriousBug(BigDecimal.valueOf(allBugList.stream().filter(o -> Arrays.asList(1).contains(o.getSeverity())).count())); + //线上轻微 + dto.setSlightBug( BigDecimal.valueOf(allBugList.stream().filter(o -> Arrays.asList(4, 2, 3).contains(o.getSeverity())).count())); + return dto; + } + //满分30 private PerformanceDTO getMeetScore(ZtUser u, Date start, Date end, PerformanceDTO dto) { List pIds = this.projectService.authProductList(); @@ -2197,7 +2276,52 @@ public class IZtCountService { writeXlsx(name, "templates/scope/项目经理考核.xlsx", name + "项目经理考核.xlsx", dataMap); } + void generatorCPJLExcel(ZtCountQo qo, String name, PerformanceDTO performanceDTO, Date d, List perList) { + Map userMap = userService.userMapByIds(null); + Map dataMap = new HashMap<>(); + dataMap.put("name", name); + dataMap.put("date", DateUtils.formatDate(d, "yyyy-MM")); + //产品规划 + dataMap.put("productPlanning",performanceDTO.getProductPlanning()==null?"0":performanceDTO.getProductPlanning().toString()); + //需求准确性 + dataMap.put("accurateDemand", performanceDTO.getAccurateDemand()==null?"0": performanceDTO.getAccurateDemand().toString()); + //项目准时率 + dataMap.put("productProjectOnTimeRateScore",performanceDTO.getProductProjectOnTimeRateScore()==null?"0": performanceDTO.getProductProjectOnTimeRateScore().multiply(BigDecimal.valueOf(100)).toString()); + //线上Bug得分 缺陷 + dataMap.put("bugScope", performanceDTO.getBugScore()==null?"0": performanceDTO.getBugScore().toString()); + //开发分配工时 + dataMap.put("allocationTime", performanceDTO.getBugScore()==null?"0": performanceDTO.getAllocationTime().toString()); + //轻微 + dataMap.put("slightBug", performanceDTO.getSlightBug()==null?"0": performanceDTO.getSlightBug().toString()); + dataMap.put("seriousBug", performanceDTO.getSeriousBug()==null?"0": performanceDTO.getSeriousBug().toString()); + //问题响应 + dataMap.put("productProblemResponse", performanceDTO.getProductProblemResponse()==null?"0": performanceDTO.getProductProblemResponse().toString()); + //会议管理 + dataMap.put("productMeetScore", performanceDTO.getProductMeetScore()==null?"0": performanceDTO.getProductMeetScore().toString()); + //主动性责任感 + dataMap.put("productResponsibilityScore", performanceDTO.getProductResponsibilityScore()==null?"0": performanceDTO.getProductResponsibilityScore().toString()); + dataMap.put("productPlanStoryNum", performanceDTO.getProductPlanStoryNum()==null?"0": performanceDTO.getProductPlanStoryNum().toString()); + dataMap.put("productPlanStoryOnTimeNum", performanceDTO.getProductPlanStoryOnTimeNum()==null?"0": performanceDTO.getProductPlanStoryOnTimeNum().toString()); + dataMap.put("allocationTime", performanceDTO.getAllocationTime()==null?"0": performanceDTO.getAllocationTime().toString()); + dataMap.put("slightBug", performanceDTO.getSlightBug()==null?"0": performanceDTO.getSlightBug().toString()); + dataMap.put("seriousBug", performanceDTO.getSeriousBug()==null?"0": performanceDTO.getSeriousBug().toString()); + dataMap.put("bugDensity", performanceDTO.getBugDensity()==null?"0": performanceDTO.getBugDensity().toString()); + + + dataMap.put("总分", devlopTotal( + dataMap.get("productPlanning"), + dataMap.get("accurateDemand"), + dataMap.get("productProjectOnTimeRateScore"), + dataMap.get("bugScope"), + dataMap.get("productProblemResponse"), + dataMap.get("productMeetScore"), + dataMap.get("productResponsibilityScore") + )); + + writeXlsx(name, "templates/scope/产品经理考核.xlsx", name + "产品经理考核.xlsx", dataMap); + + } private String versionPlanScore(ZtCountQo qo, List perList, Map userMap) { return "20"; // BigDecimal allocationTime =BigDecimal.ZERO; @@ -2280,7 +2404,7 @@ public class IZtCountService { //线上bug List bList = this.bugService.list(new QueryWrapper().lambda().in(ZtBug::getProject, projectIds) .ge(ZtBug::getOpeneddate, firstDayOfMonth).le(ZtBug::getOpeneddate, lastDayOfMonth) - .eq(ZtBug::getBugType, "prod") + .in(ZtBug::getBugType, "prod","releaseBug") ); if (CollectionUtils.isEmpty(bList)) { result.setBugScore(BigDecimal.valueOf(20)); @@ -2303,9 +2427,9 @@ public class IZtCountService { List slightBugList = bList.stream().filter(o -> slights.contains(o.getSeverity())).collect(Collectors.toList()); result.setSlightBug(BigDecimal.valueOf(slightBugList.size())); - BigDecimal bugRate = BigDecimal.valueOf(bList.size()).divide(BigDecimalUtils.isZero(allocationTime) ? BigDecimal.valueOf(1) : allocationTime, 2, BigDecimal.ROUND_HALF_UP); + BigDecimal bugRate = BigDecimal.valueOf(bList.size()).divide(BigDecimalUtils.isZero(allocationTime) ? BigDecimal.valueOf(1) : allocationTime, 3, BigDecimal.ROUND_HALF_UP); result.setBugDensity(bugRate); - int i = bugRate.multiply(BigDecimal.valueOf(100)).intValue(); + int i = bugRate.multiply(BigDecimal.valueOf(1000)).intValue(); if (i <= 5) { result.setBugScore(BigDecimal.valueOf(20)); return result; @@ -2367,10 +2491,6 @@ public class IZtCountService { } private PerformanceDTO buildTaskManage(PerformanceDTO result, List perList, Map userMap,Date startDate,Date endDate,List pids) { - - - - //多个项目组人员 List multipUser = this.getmultipleDepartProjectTeam(startDate, endDate); //分配总工时 @@ -2588,7 +2708,9 @@ public class IZtCountService { //获取测试需求 List cswcStoryList = this.storyService.list(new QueryWrapper().lambda().in(ZtStory::getProduct, pids) .in(ZtStory::getAssignedTo, Arrays.asList(u.getAccount())).ge(ZtStory::getReleaseddate, firstDayOfMonth).le(ZtStory::getReleaseddate, lastDayOfMonth)); - + if (name.equals("weidongxia")) { + result = buildCPJLScore(u, userMap , qo,approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d,pids); + } else if (name.equals("liyuyan")) { result = buildXMZLScore(u, approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d); } else if ("liushengqing".equals(name)) { @@ -2616,6 +2738,9 @@ public class IZtCountService { result.setProfessionalSkillEnhancementScore(BigDecimal.valueOf(0)); } else if (u.getUserType() == UserType.XMZL) { result = buildXMZLScore(u, approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d); + } else if (u.getUserType() == UserType.CPJL) { + //产品经理 + result = buildCPJLScore(u, userMap , qo,approvalList, firstDayOfMonth, lastDayOfMonth, taskList, d,pids); } return result; diff --git a/src/main/java/com/sa/zentao/service/impl/ZtProjectServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtProjectServiceImpl.java index 2267ddc..aa4d873 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtProjectServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtProjectServiceImpl.java @@ -260,7 +260,6 @@ public class ZtProjectServiceImpl extends ServiceImpl allTaskList =this.taskService.taskListByEIdsAndDate(firstDayOfMonth,lastDayOfMonth,products.stream().map(o->o.getId()).collect(Collectors.toList())); - if (CollectionUtils.isEmpty(allTaskList)) { - return new PageInfo<>(); - } +// if (CollectionUtils.isEmpty(allTaskList)) { +// return new PageInfo<>(); +// } List teams = this.teamService.list(new QueryWrapper().lambda().eq(ZtTeam::getType, "execution") diff --git a/src/main/java/com/sa/zentao/service/impl/ZtReleaseServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtReleaseServiceImpl.java index 1cfd7c7..d071a2d 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtReleaseServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtReleaseServiceImpl.java @@ -112,7 +112,7 @@ public class ZtReleaseServiceImpl extends ServiceImpl sList = this.storyService.list(new QueryWrapper() .lambda().eq(ZtStory::getStatus, "active") .in(ZtStory::getId, list.stream().map(o -> o.getStory()).distinct().collect(Collectors.toList())) - .eq(ZtStory::getStage, StoryStageEnums.tested.getValue())); + .eq(ZtStory::getStage, StoryStageEnums.productVerified.getValue())); if (!CollectionUtils.isEmpty(sList)) { //没有被关联的需求 测试完成的 List releaseStoryList = this.releaseDetailsService.list(new QueryWrapper().lambda() @@ -368,7 +368,7 @@ public class ZtReleaseServiceImpl extends ServiceImpl ztStories = CollectionUtils.isEmpty(storyList)?new ArrayList<>():storyService.listByIds(storyList); List bugs =CollectionUtils.isEmpty(bugList)?new ArrayList<>(): bugService.listByIds(bugList); - long notTested = ztStories.stream().filter(o -> !o.getStage().equals("tested")).count(); + long notTested = ztStories.stream().filter(o -> !o.getStage().equals(StoryStageEnums.productVerified.getValue())).count(); if(notTested>0){ throw new BusinessException("当前需求存在未测试完成的请检查"); } @@ -378,7 +378,7 @@ public class ZtReleaseServiceImpl extends ServiceImpl impl if (ztUser != null) { d.setUserStoryCreateUser(ztUser.getNickname()); } + ztUser = userMap.get(d.getProductUser()); + if (ztUser != null) { + d.setProductUserName(ztUser.getNickname()); + } List ztProjectList = executionMapByStory.get(d.getId()); if (!CollectionUtils.isEmpty(ztProjectList)) { // d.setExecution(ztProject.getId()); @@ -808,6 +813,8 @@ public class ZtStoryServiceImpl extends ServiceImpl impl for (ZtStoryDTO d : list) { d.setRevieweUser(d.getReviewedby().replaceAll(",", "")); d.setViews(rMap.get(d.getId())); + StoryStageEnums stageEnums = StoryStageEnums.transfer(d.getStage()); + d.setStageName(stageEnums==null?null:stageEnums.getDesc()); List ztTasks = taskCountMap.get(d.getId()); ZtStoryCaseDTO ztStoryCaseDTO = caseMap.get(d.getId()); if (ztStoryCaseDTO != null) { @@ -849,6 +856,14 @@ public class ZtStoryServiceImpl extends ServiceImpl impl if (ztUser != null) { d.setYsUserName(ztUser.getNickname()); } + ztUser = userMap.get(d.getProductUser()); + if (ztUser != null) { + d.setProductUserName(ztUser.getNickname()); + } + ztUser = userMap.get(d.getTestUser()); + if (ztUser != null) { + d.setTestUserName(ztUser.getNickname()); + } } } @@ -956,6 +971,105 @@ public class ZtStoryServiceImpl extends ServiceImpl impl return searchDTOPageInfo; } + @Override + @Transactional + public void testSubmitVerified(ZtStoryDTO qo) { + ZtStory ztStory = this.baseMapper.selectById(qo.getId()); + if(ztStory==null){ + throw new BusinessException("未查询到需求"); + } + if(!StoryStatusEnums.active.getValue().equals(ztStory.getStatus())){ + throw new BusinessException("需求状态不对"); + } + if(!StoryStageEnums.tested.getValue().equals(ztStory.getStage())){ + throw new BusinessException("需求状态不对"); + } + if(ObjectUtil.equal(ztStory.getInnerYsFlag(),1)){ + throw new BusinessException("无需内部验收,不需要提交"); + } + //产品验收中 + ztStory.setStage(StoryStageEnums.productWaitVerified.getValue()); + ztStory.setTestSubmitYsTime(new Date()); + ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); + ztStory.setLastediteddate(new Date()); + this.baseMapper.updateById(ztStory); + + List ztProjectstories = this.projectstoryService.projectListByStory(Arrays.asList(ztStory.getId()), ProjectTypeEnums.execution); + //更改看板 + + if(!CollectionUtils.isEmpty(ztProjectstories)){ + for(ZtProjectstory ztProjectstory:ztProjectstories){ + kanbanlaneService.changeStatus(ztProjectstory.getProject(),ztStory.getId(),"story",StoryStageEnums.productWaitVerified.getValue()); + } + } + actionService.addAction(ActionType.XQ, ActionStatus.TESTSUBMITYS, ztStory.getId(), ztStory.getProduct() + "", null, null, + RiskUserThreadLocal.get().getName(), "", ""); + } + + @Override + public void storyProductUserYs(ZtStoryDTO qo) { + ZtStory ztStory = this.baseMapper.selectById(qo.getId()); + if(ztStory==null){ + throw new BusinessException("未查询到需求"); + } + if(!StoryStatusEnums.active.getValue().equals(ztStory.getStatus())){ + throw new BusinessException("需求状态不对"); + } + if(!StoryStageEnums.productWaitVerified.getValue().equals(ztStory.getStage())){ + throw new BusinessException("需求状态不对"); + } + //产品验收中 + ztStory.setStage(StoryStageEnums.productWaitVerified.getValue()); + ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); + ztStory.setLastediteddate(new Date()); + ztStory.setInnerYsTime(new Date()); + this.baseMapper.updateById(ztStory); + + List ztProjectstories = this.projectstoryService.projectListByStory(Arrays.asList(ztStory.getId()), ProjectTypeEnums.execution); + //更改看板 + + if(!CollectionUtils.isEmpty(ztProjectstories)){ + for(ZtProjectstory ztProjectstory:ztProjectstories){ + kanbanlaneService.changeStatus(ztProjectstory.getProject(),ztStory.getId(),"story",StoryStageEnums.productVerified.getValue()); + } + } + actionService.addAction(ActionType.XQ, ActionStatus.PRODUCTYS, ztStory.getId(), ztStory.getProduct() + "", null, null, + RiskUserThreadLocal.get().getName(), "", ""); + } + + @Override + public Map storyReleaseOnTimeByProducts(List pids, Date firstDayOfMonth, Date lastDayOfMonth) { + return this.baseMapper.storyReleaseOnTimeByProducts(pids,firstDayOfMonth,lastDayOfMonth); + } + + @Override + public void assignedToProductUser(ZtStoryDTO dto) { + ZtStory ztStory = this.baseMapper.selectById(dto.getId()); + if(ztStory==null){ + throw new BusinessException("未查询到数据"); + } + ztStory.setProductUser(dto.getAssignedTo()); + ztStory.setLastediteddate(new Date()); + ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); + this.baseMapper.updateById(ztStory); + actionService.addAction(ActionType.XQ, ActionStatus.ASSIGNTOPRODUCTUSER, ztStory.getId(), ztStory.getProduct() + "", null, null, + RiskUserThreadLocal.get().getName(), "", ""); + } + + @Override + public void assignedToTestUser(ZtStoryDTO dto) { + ZtStory ztStory = this.baseMapper.selectById(dto.getId()); + if(ztStory==null){ + throw new BusinessException("未查询到数据"); + } + ztStory.setTestUser(dto.getAssignedTo()); + ztStory.setLastediteddate(new Date()); + ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); + this.baseMapper.updateById(ztStory); + actionService.addAction(ActionType.XQ, ActionStatus.ASSIGNTOTESTUSER, ztStory.getId(), ztStory.getProduct() + "", null, null, + RiskUserThreadLocal.get().getName(), "", ""); + } + private List searchBug(SearchQo qo) { return this.baseMapper.searchBug(qo.getKeyword()); } @@ -1153,9 +1267,14 @@ public class ZtStoryServiceImpl extends ServiceImpl impl } } + /** + * 开发完成 + * @param id + * @param finishBy + */ @Override @Transactional - public void finishStory(Integer id, String finishBy) { + public void develFinishStory(Integer id, String finishBy) { if (id == null || id == 0) { return; @@ -1207,7 +1326,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl throw new BusinessException("未查询到数据-需求"); } - if (!Arrays.asList("wait", "projected", "developing", "developed", "testing").contains(ztStory.getStage())) { + if (!Arrays.asList("wait", "projected", "developing", "developed", "testing",StoryStageEnums.productWaitVerified.getValue(),StoryStageEnums.productVerified.getValue()).contains(ztStory.getStage())) { return; } @@ -1242,6 +1361,13 @@ public class ZtStoryServiceImpl extends ServiceImpl impl actionService.addAction(ActionType.XQ, ActionStatus.CSWC, ztStory.getId(), ztStory.getProduct() + "", ztStory.getProject(), null, RiskUserThreadLocal.get().getName(), null, ""); } + //不需要内部验收 产品已验收 + if(!ObjectUtil.equal(ztStory.getInnerYsFlag(),1)){ + newStatus = StoryStageEnums.productVerified.getValue(); + ztStory.setStage(newStatus); + ztStory.setInnerYsTime(new Date()); + } + if (ztStory.getStartDate() == null) { ztStory.setStartDate(new Date()); } @@ -1253,7 +1379,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl for (Integer execId : execIds) { if (execId != null) { - this.kanbanlaneService.changeStatus(execId, ztStory.getId(), "story", "tested"); + this.kanbanlaneService.changeStatus(execId, ztStory.getId(), "story", newStatus); if (!oldStatus.equalsIgnoreCase(newStatus) && ztStory.getProduct() != 0) { ProductStoryStatus oldPStatus = null; @@ -1270,7 +1396,6 @@ public class ZtStoryServiceImpl extends ServiceImpl impl } - //发布 @Override @Transactional @@ -1280,22 +1405,25 @@ public class ZtStoryServiceImpl extends ServiceImpl impl } ZtStory ztStory = this.baseMapper.selectById(story); + if (ztStory == null) { + throw new BusinessException("未查询到数据-需求"); + } if (ztStory.getFeedback() != null && ztStory.getFeedback() != 0) { this.storyFeedbackService.feedbackFinished(ztStory.getFeedback(), null); } - if (ztStory == null) { - throw new BusinessException("未查询到数据-需求"); - } - if (!Arrays.asList("wait", "projected", "developing", "developed", "testing", "tested").contains(ztStory.getStage())) { + + if (!Arrays.asList("wait", "projected", "developing", "developed", "testing", "tested", + StoryStageEnums.productWaitVerified.getValue() + ,StoryStageEnums.productVerified.getValue()).contains(ztStory.getStage())) { return; } if ("closed".equals(ztStory.getStatus())) { throw new BusinessException("当前已关闭"); } - if (!"tested".equals(ztStory.getStage())) { + if (!StoryStageEnums.productVerified.getValue().equals(ztStory.getStage())) { throw new BusinessException("当前无法更改发布状态"); } //这个需求被多少执行关联 @@ -1397,7 +1525,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl if (!CollectionUtils.isEmpty(testedList)) { testedStory(story, finishBy); } else { - this.finishStory(story, finishBy); + this.develFinishStory(story, finishBy); } } @@ -1438,7 +1566,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl .eq(ZtTask::getType, TaskType.devel.getCode()) .in(ZtTask::getStatus, Arrays.asList("done"))); if (!CollectionUtils.isEmpty(develedList)) { - this.finishStory(story, finishBy); + this.develFinishStory(story, finishBy); return; } return; @@ -1486,7 +1614,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl if (done < 1) { } else { - this.finishStory(story, finishBy); + this.develFinishStory(story, finishBy); } } } else { @@ -2150,6 +2278,14 @@ public class ZtStoryServiceImpl extends ServiceImpl impl if (ztUser != null) { d.setYsUserName(ztUser.getNickname()); } + ztUser = userMap.get(d.getProductUser()); + if (ztUser != null) { + d.setProductUserName(ztUser.getNickname()); + } + ztUser = userMap.get(d.getTestUser()); + if (ztUser != null) { + d.setTestUserName(ztUser.getNickname()); + } ZtStoryspec storyspec = this.storyspecService.getOne(new QueryWrapper().lambda().eq(ZtStoryspec::getStory, d.getId())); if (storyspec != null) { @@ -2449,6 +2585,7 @@ public class ZtStoryServiceImpl extends ServiceImpl impl ProductStoryStatus oldPStatus = null; if (status.equalsIgnoreCase("developing") || status.equalsIgnoreCase("testing") || status.equalsIgnoreCase("tested") || status.equalsIgnoreCase("developed") + || status.equalsIgnoreCase(StoryStageEnums.productWaitVerified.getValue())|| status.equalsIgnoreCase(StoryStageEnums.productVerified.getValue()) ) { oldPStatus = ProductStoryStatus.JH; 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 9530596..f91bf89 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtStoryUserServiceImpl.java @@ -252,10 +252,10 @@ 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) + .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(ztStoryUsers.stream().map(o -> o.getOpenedby()).collect(Collectors.toList())); + Map stringZtUserMap = this.userService.userMapByIds(null); List ztStoryUserDTOS = BeanCopyUtil.copyListProperties(ztStoryUsers, ZtStoryUserDTO::new, new BeanCopyUtilCallBack() { @Override @@ -265,6 +265,11 @@ public class ZtStoryUserServiceImpl extends ServiceImpl ztStories = storyUserMap.get(d.getId()); if (!CollectionUtils.isEmpty(ztStories)) { d.setStoryList(ztStories.stream().map(o ->o.getId()+":"+o.getTitle()).collect(Collectors.joining(","))); @@ -377,7 +387,6 @@ public class ZtStoryUserServiceImpl extends ServiceImpl> storyUserMap = getStoryUserMap(Arrays.asList(dto)); List ztStories = storyUserMap.get(d.getId()); if (!CollectionUtils.isEmpty(ztStories)) { @@ -867,6 +880,18 @@ public class ZtStoryUserServiceImpl extends ServiceImpl> getStoryUserMap(List list) { List ids = list.stream().map(o -> o.getId()).collect(Collectors.toList()); diff --git a/src/main/java/com/sa/zentao/service/impl/ZtTaskServiceImpl.java b/src/main/java/com/sa/zentao/service/impl/ZtTaskServiceImpl.java index a722d3c..8e77e89 100644 --- a/src/main/java/com/sa/zentao/service/impl/ZtTaskServiceImpl.java +++ b/src/main/java/com/sa/zentao/service/impl/ZtTaskServiceImpl.java @@ -1,5 +1,6 @@ package com.sa.zentao.service.impl; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -577,7 +578,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").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")) { @@ -1034,6 +1036,13 @@ public class ZtTaskServiceImpl extends ServiceImpl impleme actionService.addAction(ActionType.RW, ActionStatus.TJJFW, dto.getId(), projectproduct == null ? null : projectproduct.getProduct().toString(), projectproduct == null ? null : projectproduct.getProject(), ztTask.getExecution(), 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){ + ztStory.setDeliverContent(dto.getDeliverContent()); + ztStory.setLasteditedby(RiskUserThreadLocal.get().getName()); + ztStory.setLastediteddate(new Date()); + this.storyService.updateById(ztStory); + } actionService.addAction(ActionType.XQ, ActionStatus.TJJFW, ztTask.getStory(), projectproduct == null ? null : projectproduct.getProduct().toString(), projectproduct == null ? null : projectproduct.getProject(), ztTask.getExecution(), StringUtils.isEmpty(finishBy) ? RiskUserThreadLocal.get().getName() : finishBy, dto.getDeliverContent(), null); } @@ -1073,7 +1082,11 @@ public class ZtTaskServiceImpl extends ServiceImpl impleme 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 diff --git a/src/main/java/com/sa/zentao/utils/KanBanConstant.java b/src/main/java/com/sa/zentao/utils/KanBanConstant.java index 21af141..d6b14fc 100644 --- a/src/main/java/com/sa/zentao/utils/KanBanConstant.java +++ b/src/main/java/com/sa/zentao/utils/KanBanConstant.java @@ -22,6 +22,8 @@ public class KanBanConstant { put("test","测试"); put("testing","进行中"); put("tested","完成"); + put("productWaitVerified","产品验收中"); + put("productVerified","产品已验收"); put("released","已发布"); put("verified","已验收"); put("closed","已关闭"); @@ -70,6 +72,8 @@ public class KanBanConstant { put("story-test","test"); put("story-testing","test"); put("story-tested","test"); + put("story-productWaitVerified","productWaitVerified"); + put("story-productVerified","productVerified"); put("story-verified","verified"); put("story-released","released"); put("story-closed","closed"); diff --git a/src/main/resources/mapper/ZtStoryMapper.xml b/src/main/resources/mapper/ZtStoryMapper.xml index 75480d1..2adafaa 100644 --- a/src/main/resources/mapper/ZtStoryMapper.xml +++ b/src/main/resources/mapper/ZtStoryMapper.xml @@ -136,6 +136,8 @@ s.ys_user, s.task_count, s.ys_date, + s.inner_ys_flag, + s.product_user, su.title userStoryName, su.id userStoryId, ps.title parentName, @@ -160,6 +162,10 @@ and s.assignedTo= #{qo.userName} + + and s.product_user= #{qo.userName} + + and s.openedBy= #{qo.userName} @@ -219,6 +225,9 @@ and s.title like concat('%', #{qo.title}, '%') + + and s.product_user like concat('%', #{qo.productUser}, '%') + and s.module = #{qo.module} @@ -357,6 +366,9 @@ s.ys_date, s.devel_plan_end_time, s.test_plan_end_time, + s.inner_ys_flag, + s.product_user, + s.test_user, pt.name productName from zt_story s @@ -410,7 +422,12 @@ and s.title like concat('%', #{qo.name}, '%') - + + and s.product_user like concat('%', #{qo.productUser}, '%') + + + and s.assignedTo like concat('%', #{qo.testUser}, '%') + and pt.name like concat('%', #{qo.productName}, '%') @@ -455,7 +472,12 @@ and s.status ='reviewing' and v.result = '' - + + and s.product_user = #{qo.userName} + + + and s.assignedTo = #{qo.userName} + and s.status = 'active' @@ -1292,4 +1314,25 @@ and (spc.spec like concat('%', #{keyword}, '%') or spc.verify like concat('%', #{keyword}, '%') or st.title like concat('%', #{keyword}, '%') ) order by id desc ) + + + diff --git a/src/main/resources/mapper/ZtStoryUserMapper.xml b/src/main/resources/mapper/ZtStoryUserMapper.xml index de8f051..4e0e5b4 100644 --- a/src/main/resources/mapper/ZtStoryUserMapper.xml +++ b/src/main/resources/mapper/ZtStoryUserMapper.xml @@ -140,7 +140,7 @@ s.deliver_remark, s.old_status, s.ys_user, - + s.product_user, pt.name productName from zt_story_user s LEFT JOIN zt_product pt on s.product = pt.id WHERE 1=1 @@ -188,9 +188,9 @@ and pt.name like concat('%', #{qo.productName}, '%') - - - + + and s.product_user like concat('%', #{qo.productUser}, '%') + @@ -199,10 +199,12 @@ and s.status != 'closed' + + and s.product_user = #{qo.userName} + and s.openedby= #{qo.userName} - and s.id = #{qo.id} diff --git a/src/main/resources/templates/scope/产品经理考核.xlsx b/src/main/resources/templates/scope/产品经理考核.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..815aeb41eb2710eeb09d848bfbc3264d284a08f8 GIT binary patch literal 12632 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