项目总结

Chenyi_li / 2023-05-07 / 原文

在实习中参与的是,内部立项与团队管理项目的开发,主要是服务于BPS部门和B中心员工的。BPS Project Information Publishing Platform项目信息发布平台,主要面向BPS部门的中国用户和美国用户。主要包括项目立项,项目流程关联,还包括会议管理,项目报告,项目任务,测试用例等。关联邮件系统,周报日报。讨论评论区以及个人中心等等。包括Project Leader, Project Member,Project Chariman, Project Reviewer和Project Suspervisor等多个角色。

Descriptions:Project information publish platform used in the BPS department will be developed, in which the information of all BPS internal projects can be centrally managed. The platform can track the overall progress, process and personnel input of all BPS projects, realize open discussions on project issues, and present all information in a project-based manner (for example daily report, weekly report, meeting minutes and problem discussions).
开发用于BPS部门的项目信息发布平台,可以集中管理BPS内部所有项目的信息。平台可以跟踪所有BPS项目的整体进度、过程和人员投入,实现项目问题的公开讨论,并以项目化的方式呈现所有信息(例如日报、周报、会议纪要、问题讨论)

Benefits:At present, the distribution of BPS-related information is scattered, and the existing system can generally only achieve project node tracking, but cannot save and present process information, resulting in inconvenient information transmission and search, and even repeated discussions on the same issues, which all lead to unnecessary waste of time and labor costs. At the same time, due to the dispersion of personnel information, the management cost increases. For the all above reasons, a centralized information release platform is required. In addition, it is convenient for BPS team members to understand the overall operation status of the department, providing an open and transparent environment for everyone, and improving the technical level and operation efficiency of the team more quickly.
目前BPS相关信息分布分散,现有系统一般只能实现项目节点跟踪,无法保存和呈现过程信息,导致信息传递和查找不便,甚至会出现重复讨论同一个问题,这些都会导致不必要的时间和人力成本的浪费。同时,由于人员信息分散,增加了管理成本。基于以上原因,需要一个集中的信息发布平台。此外,方便BPS团队成员了解部门整体运作状况,为大家提供一个公开透明的环境,更快提升团队的技术水平和运作效率。

完成工作:

  1. 做service层非空判断。
  2. 编写feedback模块。
  3. 编写issueTopic讨论区模块。
  4. 设计权限验证方案。
  5. 参与集成测试。
  6. 对ruoyi的User表进行优化。

1. 做service层非空判断。

2. 编写feedback模块。

3. 编写issueTopic讨论区模块。

这是项目中一个类似论坛的功能,是对当前立项和项目的一些讨论,跟普通的论坛不一样的是,需要保持历史版本,和改动过程。在设计的过程中参考了github的issue区来进行设计。
1.项目分为topic部分和一级评论和二级评论。(一级评论是一层,二级评论已经对二级评论的评论是一层)(回头补上这里的设置)。
2.内容改动后,生成一个历史版本。状态,标签属性,是否匿名(可能有关联项目(没做))等改变不生成历史版本,但也要记录。此处设计了TimeLine的方式进行实现。

IssueTopicTimeLineNodeDto.java

点击查看代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Builder(toBuilder = true)
public class IssueTopicTimeLineNodeDto extends BaseResponseDto {

    private String type;

    private IssueTopicCommentL1ResponseDto comment;

    private IssueTopicLabelResponseDto label;

    private String attState;

    private Integer attIsAnony;

    private ProjectInfoResponseDto project;

    private UserInfoDo attAssignee;

    private Map<String, String> dataMap;

    private String versionTime;
}

IssueTopicTimeLineDtoFactory.java

点击查看代码
@Service
public class IssueTopicTimeLineDtoFactory {

    @Autowired
    IssueTopicChangeService issueTopicChangeService;

    @Autowired
    IssueTopicLabelService issueTopicLabelService;

    @Autowired
    IssueTopicCommentL1ChangeService issueTopicCommentL1ChangeService;

    @Autowired
    IssueTopicTaskAssignService issueTopicAssigneeService;

    @Autowired
    ProjectBaseInfoService projectBaseInfoService;

    @Autowired
    ProjectInfoBoFactory projectInfoBoFactory;

    @Autowired
    IssueTopicCommentL1ResponseDtoFactory issueTopicCommentL1ResponseDtoFactory;

    @Autowired
    IssueTopicCommentL1StableService issueTopicCommentL1StableService;

    @Autowired
    IssueTopicLabelBoFactory issueTopicLabelBoFactory;

    @Autowired
    IssueTopicLabelResponseDtoFactory issueTopicLabelResponseDtoFactory;

    @Autowired
    IssueTopicTaskAssignBoFactory issueTopicTaskAssignBoFactory;

    @Autowired
    UserInfoService userInfoService;

    public List<IssueTopicTimeLineNodeDto> create(IssueTopicBo bo) {

        // timeLine 是最后的结果
        List<IssueTopicTimeLineNodeDto> timeLine = new LinkedList<IssueTopicTimeLineNodeDto>();

        // change
        List<IssueTopicChangeDo> issueTopicChangeDos = bo.getChangeDos();

        issueTopicChangeDos.sort(new Comparator<IssueTopicChangeDo>() {
            @Override
            public int compare(IssueTopicChangeDo arg0, IssueTopicChangeDo arg1) {
                return arg0.getVersionTime().compareTo(arg1.getVersionTime());
            }

        });

        // 排序完成后从小到大,第一个元素应该全部装入。后续的比较不同进行装入
        IssueTopicChangeDo currentDo = null, previousDo = null;
        for (int i = 0; i < issueTopicChangeDos.size(); i++) {

            previousDo = currentDo;
            currentDo = issueTopicChangeDos.get(i);

            IssueTopicTimeLineNodeDto.IssueTopicTimeLineNodeDtoBuilder nodeBuilder = IssueTopicTimeLineNodeDto
                    .builder()
                    .type(IssueTimeLineNodeTypeEnums.STATE_CHANGE.getValue())
                    .versionTime(TimeUtils.dateToStringTime(currentDo.getVersionTime()));

            if (StringUtils.isNull(previousDo) || !currentDo.getIsAnonymous().equals(previousDo.getIsAnonymous())) {
                nodeBuilder.attIsAnony(currentDo.getIsAnonymous());
            }

            if (StringUtils.isNull(previousDo) || !currentDo.getState().equals(previousDo.getState())) {
                nodeBuilder.attState(currentDo.getState());
            }

            if (StringUtils.isNull(previousDo) ||
                    (StringUtils.isNotNull(currentDo.getPorjId())
                            && !currentDo.getPorjId().equals(previousDo.getPorjId()))) {

                ProjectBaseInfoDo projectBaseInfoDo = projectBaseInfoService
                        .selectprojectBaseInfoByProjId(currentDo.getPorjId());

                nodeBuilder.project(ProjectInfoResponseDto.builder()
                        .projId(projectBaseInfoDo.getProjId())
                        .projName(projectBaseInfoDo.getProjName())
                        .build());
            }

            if (StringUtils.isNotNull(previousDo) && StringUtils.isNull(currentDo.getPorjId())) {

                if (StringUtils.isNotNull(previousDo.getPorjId())) {

                    nodeBuilder.project(ProjectInfoResponseDto.builder()
                            .projId(0L)
                            .build());

                }

            }

            timeLine.add(nodeBuilder.build());
        }

        // comment
        List<IssueTopicCommentL1Bo> comments = bo.getComments();
        for (IssueTopicCommentL1Bo temp : comments) {

            timeLine.add(IssueTopicTimeLineNodeDto.builder()
                    .type(IssueTimeLineNodeTypeEnums.NEW_COMMENT.getValue())
                    .comment(issueTopicCommentL1ResponseDtoFactory.create(temp))
                    .versionTime(TimeUtils.dateToStringTime(temp.getVersionTime()))
                    .build());
        }

        for (IssueTopicLabelBo issueTopicLabelBoTemp : bo.getLabelBos()) {

            IssueTopicTimeLineNodeDto.IssueTopicTimeLineNodeDtoBuilder nodeBuilder = IssueTopicTimeLineNodeDto
                    .builder();

            if (StringUtils.isNotNull(issueTopicLabelBoTemp.getDeleteTimestamp())) {

                IssueTopicTimeLineNodeDto.IssueTopicTimeLineNodeDtoBuilder anotherBuilder = nodeBuilder.build()
                        .toBuilder();

                anotherBuilder.type(IssueTimeLineNodeTypeEnums.REMOVE_LABEL.getValue())
                        .label(issueTopicLabelResponseDtoFactory.create(issueTopicLabelBoTemp, true))
                        .versionTime(TimeUtils.dateToStringTime(issueTopicLabelBoTemp.getDeleteTimestamp()));

                timeLine.add(anotherBuilder.build());
            }

            nodeBuilder.type(IssueTimeLineNodeTypeEnums.NEW_LABEL.getValue())
                    .label(issueTopicLabelResponseDtoFactory.create(issueTopicLabelBoTemp, false))
                    .versionTime(TimeUtils.dateToStringTime(issueTopicLabelBoTemp.getVersionTime()));

            timeLine.add(nodeBuilder.build());
        }

        // topic re-edit
        for (int i = 1; i < bo.getContentDos().size(); i++) {

            Map<String, String> dataMap = new HashMap<>();

            dataMap.put("oldTopic", bo.getContentDos().get(i - 1).getTopic());
            dataMap.put("oldContentId", bo.getContentDos().get(i - 1).getId().toString());
            dataMap.put("oldTimestamp", TimeUtils.dateToStringTime(bo.getContentDos().get(i - 1).getVersionTime()));
            dataMap.put("newTopic", bo.getContentDos().get(i).getTopic());
            dataMap.put("newContentId", bo.getContentDos().get(i).getId().toString());
            dataMap.put("newTimestamp", TimeUtils.dateToStringTime(bo.getContentDos().get(i).getVersionTime()));

            timeLine.add(IssueTopicTimeLineNodeDto.builder()
                    .type(IssueTimeLineNodeTypeEnums.ISSUE_REEDIT.getValue())
                    .dataMap(dataMap)
                    .versionTime(TimeUtils.dateToStringTime(bo.getContentDos().get(i).getVersionTime()))
                    .build());
        }

        // assignee
        // todo 需求未设定,暂时全显示
        // IssueTopicTaskAssignDoBuilder builder = IssueTopicTaskAssignDo.builder();
        // IssueTopicTaskAssignDo issueTopicTaskAssignDo = builder.topicId(id).build();

        // List<IssueTopicTaskAssignDo> selectIssueTopicTaskAssignList =
        // issueTopicAssigneeService
        // .selectIssueTopicTaskAssignList(issueTopicTaskAssignDo);
        // for (IssueTopicTaskAssignDo tempAssignDo : selectIssueTopicTaskAssignList) {
        // IssueTopicTimeLineNodeDto issueTopicTimeLineNodeDto = new
        // IssueTopicTimeLineNodeDto();
        // issueTopicTimeLineNodeDto.setAttAssignee(userInfoService.selectUserInfoByName(tempAssignDo.getUsername()));
        // issueTopicTimeLineNodeDto.setVersionTime(TimeUtils.dateToStringTime(tempAssignDo.getVersionTime()));
        // }

        timeLine.sort(new Comparator<IssueTopicTimeLineNodeDto>() {
            @Override
            public int compare(IssueTopicTimeLineNodeDto arg0, IssueTopicTimeLineNodeDto arg1) {
                return arg0.getVersionTime().compareTo(arg1.getVersionTime());
            }
        });

        return timeLine;
    }

}

IssueTopicBo.java

点击查看代码
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class IssueTopicBo {

    // stable
    private Long id;

    private String submitter;

    private UserInfoBo submitterInfo;

    private Date submissionTime;

    // current attributes
    private String currState;

    private Date closeTime;

    private Boolean isAnonymous;

    private List<IssueTopicSubscribeDo> subscribeUserDos;

    // project
    private ProjectBaseInfoDo currProjectBaseInfoDo;

    // current content
    private String currTopic;

    private String currContent;

    private Integer commentsNumber;

    // FeedbackVoDo 是返回的所有给他投票的人名列表
    // {"aggree":["zhangsan,lisi"],"disaggree":["zhaoliu","kk"]}
    private Map<String, List<IssueTopicVoteDo>> voteMap;

    // ShowLabel
    private List<IssueTopicLabelDo> currLabels;

    // 未来任务交给谁
    private List<IssueTopicTaskAssignBo> currAssignees;

    // 用来生成时间线的素材
    private List<IssueTopicCommentL1Bo> comments; // comment藏在时间线里面

    private List<IssueTopicChangeDo> changeDos;

    private List<IssueTopicContentDo> contentDos;

    private List<IssueTopicLabelBo> labelBos;

}

IssueTopicBoFactory.java

点击查看代码
public class IssueTopicBoFactory extends abstractFactory<IssueTopicBo, IssueTopicStableDo> {

        @Autowired
        private IssueTopicChangeService issueTopicChangeService;

        @Autowired
        private ProjectBaseInfoService projectBaseInfoService;

        @Autowired
        private IssueTopicContentService issueTopicContentService;

        @Autowired
        private IssueTopicVoteService issueTopicVoteService;

        @Autowired
        private UserInfoService userInfoService;

        @Autowired
        private UserInfoBoFactory userInfoBoFactory;

        @Autowired
        private IssueTopicCommentL1StableService issueTopicCommentL1StableService;

        @Autowired
        private IssueTopicCommentL1BoFactory issueTopicCommentL1BoFactory;

        @Autowired
        private IssueTopicLabelBoFactory issueTopicLabelBoFactory;

        @Autowired
        private IssueTopicLabelService issueTopicLabelService;

        @Autowired
        private IssueTopicTaskAssignService issueTopicAssigneeService;

        @Autowired
        private IssueTopicTaskAssignBoFactory issueTopicTaskAssignBoFactory;

        @Autowired
        private IssueTopicSubscribeService issueTopicSubscribeService;

        @Override
        public IssueTopicBo create(IssueTopicStableDo issueTopicStableDo) {

                return create(issueTopicStableDo, false);

        }

        /**
         * @arg isSimple[Boolean]: indecate to create a simple BO, e.g. used to issue
         *      topic list.
         */
        public IssueTopicBo create(IssueTopicStableDo issueTopicStableDo, Boolean isSimple) {

                IssueTopicBoBuilder builder = IssueTopicBo.builder();

                if (StringUtils.isNotNull(issueTopicStableDo) && StringUtils.isNotNull(issueTopicStableDo.getId())) {

                        // stable
                        builder.id(issueTopicStableDo.getId()).submitter(issueTopicStableDo.getSubmitter())
                                        .submissionTime(issueTopicStableDo.getVersionTime());

                        // userInfo
                        builder.submitterInfo(userInfoBoFactory.create(
                                        userInfoService.selectUserInfoByName(issueTopicStableDo.getSubmitter())));

                        // change
                        IssueTopicChangeDo issueTopicChangeDo = issueTopicChangeService
                                        .selectIssueTopicChangeByTopicid(issueTopicStableDo.getId());

                        builder.currProjectBaseInfoDo(projectBaseInfoService
                                        .selectprojectBaseInfoByProjId(issueTopicChangeDo.getPorjId()));

                        builder.currState(issueTopicChangeDo.getState())
                                        .isAnonymous(StringUtils.isNotNull(issueTopicChangeDo.getIsAnonymous())
                                                        && issueTopicChangeDo.getIsAnonymous() > 0 ? true : false);

                        if (issueTopicChangeDo.getState().equals(IssueTopicStateEnums.CLOSED.getValue())) {
                                builder.closeTime(issueTopicChangeDo.getVersionTime());
                        }

                        // content
                        IssueTopicContentDo issueTopicContentDo = issueTopicContentService
                                        .selectIssueTopicContentByTopicId(issueTopicStableDo.getId());

                        builder.currTopic(issueTopicContentDo.getTopic());

                        // currLabels
                        List<IssueTopicLabelDo> issueTopicLabelDos = issueTopicLabelService.selectIssueTopicLabelDoList(
                                        IssueTopicLabelDo.builder().topicId(issueTopicStableDo.getId()).build());
                        builder.currLabels(issueTopicLabelDos);

                        // subscribeUserDos
                        builder.subscribeUserDos(issueTopicSubscribeService.selectIssueTopicSubscribeList(
                                        IssueTopicSubscribeDo.builder().topicId(issueTopicStableDo.getId())
                                                        .deleteFlag(0).build()));

                        // comments number
                        List<IssueTopicCommentL1StableDo> issueTopicCommentL1StableDos = issueTopicCommentL1StableService
                                        .selectIssueTopicCommentL1StableList(IssueTopicCommentL1StableDo
                                                        .builder()
                                                        .topicId(issueTopicStableDo.getId()).build());
                        builder.commentsNumber(issueTopicCommentL1StableDos.size());

                        if (!isSimple) {

                                // current content
                                builder.currContent(issueTopicContentDo.getContent());

                                // changDos
                                List<IssueTopicChangeDo> changeDos = issueTopicChangeService
                                                .selectIssueTopicHistoryChangeById(issueTopicStableDo.getId());

                                builder.changeDos(changeDos);

                                // contents
                                List<IssueTopicContentDo> issueTopicContentDos = issueTopicContentService
                                                .selectIssueTopicContentHistoryByTopicId(issueTopicStableDo.getId());
                                builder.contentDos(issueTopicContentDos);

                                // assignees
                                List<IssueTopicTaskAssignDo> issueTopicTaskAssignDos = issueTopicAssigneeService
                                                .selectIssueTopicTaskAssignList(
                                                                IssueTopicTaskAssignDo.builder().topicId(
                                                                                issueTopicStableDo.getId()).build());
                                builder.currAssignees(issueTopicTaskAssignBoFactory.create(issueTopicTaskAssignDos));

                                // comments
                                builder.comments(issueTopicCommentL1BoFactory.create(issueTopicCommentL1StableDos));

                                // labels
                                builder.labelBos(issueTopicLabelBoFactory
                                                .create(issueTopicLabelService.selectIssueTopicLabelDoHistoryById(
                                                                issueTopicStableDo.getId())));

                                // vote
                                IssueTopicVoteDo issueTopicVoteDo = IssueTopicVoteDo.builder()
                                                .topicId(issueTopicStableDo.getId()).build();

                                List<IssueTopicVoteDo> issueTopicVoteDos = issueTopicVoteService
                                                .selectIssueTopicVoteList(issueTopicVoteDo);
                                HashMap<String, List<IssueTopicVoteDo>> voteMap = new HashMap<>();
                                for (IssueTopicVoteDo issueTopicVoteTempDo : issueTopicVoteDos) {
                                        String voteKey = issueTopicVoteTempDo.getVote();
                                        if (!voteMap.containsKey(voteKey)) {
                                                voteMap.put(voteKey, new LinkedList<IssueTopicVoteDo>());
                                        }
                                        voteMap.get(voteKey).add(issueTopicVoteTempDo);
                                }
                                builder.voteMap(voteMap);

                        } else {
                                builder.currContent(null)
                                                .changeDos(new ArrayList<IssueTopicChangeDo>())
                                                .contentDos(new ArrayList<IssueTopicContentDo>())
                                                .currAssignees(new ArrayList<IssueTopicTaskAssignBo>())
                                                .comments(new ArrayList<IssueTopicCommentL1Bo>())
                                                .labelBos(new ArrayList<IssueTopicLabelBo>())
                                                .voteMap(null);
                        }

                }
                return builder.build();

        }

        public List<IssueTopicBo> create(List<IssueTopicStableDo> bos, Boolean isSimple) {

                List<IssueTopicBo> responseDtos = new ArrayList<>();

                if (StringUtils.isNotNull(bos)) {

                        for (IssueTopicStableDo bo : bos) {

                                responseDtos.add(create(bo, isSimple));

                        }

                }

                return responseDtos;

        }

}

IssueTopicLabelBo.java

点击查看代码
public class IssueTopicLabelBo {
    private Long id;

    private Long topicId;

    private String labelName;

    private String description;

    private Date versionTime;

    private String insertOperator;

    private UserInfoBo insertOperatorInfo;

    private String deleteOperator;

    private UserInfoBo deleteOperatorInfo;

    private Date deleteTimestamp;

}

IssueTopicLabelResponseDtoFactory.java

点击查看代码
public class IssueTopicLabelResponseDtoFactory {

    @Autowired
    private UserInfoResponseDtoFactory userInfoResponseDtoFactory;

    public IssueTopicLabelResponseDto create(IssueTopicLabelBo bo, Boolean isDelete) {

        IssueTopicLabelResponseDto.IssueTopicLabelResponseDtoBuilder dtoBuilder = IssueTopicLabelResponseDto.builder();

        if (StringUtils.isNotNull(bo)) {

            dtoBuilder.id(bo.getId())
                    .topicId(bo.getTopicId())
                    .labelName(bo.getLabelName())
                    .description(bo.getDescription());

            if (!isDelete) {
                dtoBuilder.operatorInfo(userInfoResponseDtoFactory.create(bo.getInsertOperatorInfo()))
                        .versionTime(TimeUtils.dateToStringTime(bo.getVersionTime()));
            } else {
                dtoBuilder.operatorInfo(userInfoResponseDtoFactory.create(bo.getDeleteOperatorInfo()))
                        .versionTime(TimeUtils.dateToStringTime(bo.getDeleteTimestamp()));
            }

        }

        return dtoBuilder.build();
    }

}

IssueTopicLabelResponseDto.java

点击查看代码
public class IssueTopicLabelResponseDto extends BaseResponseDto {

    private Long id;

    private Long topicId;

    private String labelName;

    private String description;

    private String versionTime;

    private UserInfoResponseDto operatorInfo;

}
3.投票功能实现


虽然前度只需要返回一个数字,但是本着Bo存储尽可能详细的信息的原则。bo这样设计


单独设计了哥vote表通过id进行关联(这里截图的是feedback模块的临时功能)像极了topk那个算法题

FeedbackResponseDto.java

点击查看代码
public class FeedbackResponseDto extends BaseResponseDto {

    private Long id;

    private UserInfoResponseDto submitterInfo;

    private String content;

    private HashMap<String, Long> voteCounter;

    private String isPicked;

    private Boolean isMyTopic;

    // voteId
    private Long voteId;

    private String submissionTime;

    private String group;

    private String state;

    private List<FeedbackCommentResponseDto> comments;

}

FeedbackResponseDtoFactory.java

点击查看代码
public class FeedbackResponseDtoFactory extends abstractFactory<FeedbackResponseDto, FeedbackBo> {

    @Autowired
    SysUserServiceImpl sysUserServiceImpl;

    @Autowired
    UserInfoResponseDtoFactory userInfoResponseDtoFactory;

    @Autowired
    UserInfoBoFactory userInfoBoFactory;

    @Autowired
    FeedbackVoteService feedbackVoteService;

    @Autowired
    FeedbackCommentResponseDtoFactory feedbackCommentResponseDtoFactory;

    @Autowired
    FeedbackCommentBoFactory feedbackCommentBoFactory;

    @Override
    public FeedbackResponseDto create(FeedbackBo feedbackBo) {

        if (StringUtils.isNull(feedbackBo)) {
            return FeedbackResponseDto.builder().build();
        }

        FeedbackResponseDtoBuilder dtoBuilder = FeedbackResponseDto.builder();

        // isAnonymous
        if (feedbackBo.getIsAnonymous()) {
            dtoBuilder.submitterInfo(UserInfoResponseDto.builder().nickName("匿名(Anonymous)").build());
        } else {
            // submitterInfo
            dtoBuilder.submitterInfo(userInfoResponseDtoFactory.create(feedbackBo.getSubmitterInfo()));
        }

        // voteCounter
        HashMap<String, Long> voteCounterMap = new HashMap<String, Long>();
        Map<String, List<FeedbackVoteDo>> voteMap = feedbackBo.getVoteMap();
        for (Map.Entry<String, List<FeedbackVoteDo>> entry : voteMap.entrySet()) {
            String key = entry.getKey();
            Long vaule = (long) entry.getValue().size();
            voteCounterMap.put(key, vaule);
        }
        dtoBuilder.voteCounter(voteCounterMap);

        // isPicked&&voteId
        String isPicked = null;
        Long voteId = null;
        Boolean isMyTopic = false;

        try {

            SysUser user = SecurityUtils.getLoginUser().getUser();

            isMyTopic = user.getUserName().equals(feedbackBo.getSubmiter());

            try {

                FeedbackVoteDo feedbackVoteDoExample = FeedbackVoteDo.builder()
                        .topicId(feedbackBo.getId())
                        .userName(user.getUserName())
                        .build();
                List<FeedbackVoteDo> feedbackVoteDos = feedbackVoteService
                        .selectFeedbackVoteList(feedbackVoteDoExample);

                if (StringUtils.isNotNull(feedbackVoteDos) && feedbackVoteDos.size() > 0) {
                    isPicked = feedbackVoteDos.get(0).getVote();
                    voteId = feedbackVoteDos.get(0).getId();
                }

            } catch (Exception e) {

                for (Map.Entry<String, List<FeedbackVoteDo>> entry : voteMap.entrySet()) {

                    for (FeedbackVoteDo voteDo : entry.getValue()) {
                        if (voteDo.getUserName().equals(user.getUserName())) {
                            isPicked = voteDo.getVote();
                            voteId = voteDo.getId();
                            break;
                        }
                    }

                    if (StringUtils.isNotNull(voteId)) {
                        break;
                    }
                }

            }

        } catch (Exception e) {
            // TODO: handle exception
        }

        dtoBuilder.isPicked(isPicked).voteId(voteId).isMyTopic(isMyTopic);

        // submissionTime
        dtoBuilder.submissionTime(TimeUtils.dateToStringTime(feedbackBo.getSubmissionTime()));

        // group&&statue
        dtoBuilder.group(feedbackBo.getGroup()).state(feedbackBo.getState());

        // id
        dtoBuilder.id(feedbackBo.getId());

        // content
        dtoBuilder.content(feedbackBo.getContent());

        // comments
        dtoBuilder.comments(feedbackCommentResponseDtoFactory.create(feedbackBo.getComments()));

        return dtoBuilder.build();
    }

}

FeedbackResponseDtoFactory.java

点击查看代码
public class FeedbackResponseDtoFactory extends abstractFactory<FeedbackResponseDto, FeedbackBo> {

    @Autowired
    SysUserServiceImpl sysUserServiceImpl;

    @Autowired
    UserInfoResponseDtoFactory userInfoResponseDtoFactory;

    @Autowired
    UserInfoBoFactory userInfoBoFactory;

    @Autowired
    FeedbackVoteService feedbackVoteService;

    @Autowired
    FeedbackCommentResponseDtoFactory feedbackCommentResponseDtoFactory;

    @Autowired
    FeedbackCommentBoFactory feedbackCommentBoFactory;

    @Override
    public FeedbackResponseDto create(FeedbackBo feedbackBo) {

        if (StringUtils.isNull(feedbackBo)) {
            return FeedbackResponseDto.builder().build();
        }

        FeedbackResponseDtoBuilder dtoBuilder = FeedbackResponseDto.builder();

        // isAnonymous
        if (feedbackBo.getIsAnonymous()) {
            dtoBuilder.submitterInfo(UserInfoResponseDto.builder().nickName("匿名(Anonymous)").build());
        } else {
            // submitterInfo
            dtoBuilder.submitterInfo(userInfoResponseDtoFactory.create(feedbackBo.getSubmitterInfo()));
        }

        // voteCounter
        HashMap<String, Long> voteCounterMap = new HashMap<String, Long>();
        Map<String, List<FeedbackVoteDo>> voteMap = feedbackBo.getVoteMap();
        for (Map.Entry<String, List<FeedbackVoteDo>> entry : voteMap.entrySet()) {
            String key = entry.getKey();
            Long vaule = (long) entry.getValue().size();
            voteCounterMap.put(key, vaule);
        }
        dtoBuilder.voteCounter(voteCounterMap);

        // isPicked&&voteId
        String isPicked = null;
        Long voteId = null;
        Boolean isMyTopic = false;

        try {

            SysUser user = SecurityUtils.getLoginUser().getUser();

            isMyTopic = user.getUserName().equals(feedbackBo.getSubmiter());

            try {

                FeedbackVoteDo feedbackVoteDoExample = FeedbackVoteDo.builder()
                        .topicId(feedbackBo.getId())
                        .userName(user.getUserName())
                        .build();
                List<FeedbackVoteDo> feedbackVoteDos = feedbackVoteService
                        .selectFeedbackVoteList(feedbackVoteDoExample);

                if (StringUtils.isNotNull(feedbackVoteDos) && feedbackVoteDos.size() > 0) {
                    isPicked = feedbackVoteDos.get(0).getVote();
                    voteId = feedbackVoteDos.get(0).getId();
                }

            } catch (Exception e) {

                for (Map.Entry<String, List<FeedbackVoteDo>> entry : voteMap.entrySet()) {

                    for (FeedbackVoteDo voteDo : entry.getValue()) {
                        if (voteDo.getUserName().equals(user.getUserName())) {
                            isPicked = voteDo.getVote();
                            voteId = voteDo.getId();
                            break;
                        }
                    }

                    if (StringUtils.isNotNull(voteId)) {
                        break;
                    }
                }

            }

        } catch (Exception e) {
            // TODO: handle exception
        }

        dtoBuilder.isPicked(isPicked).voteId(voteId).isMyTopic(isMyTopic);

        // submissionTime
        dtoBuilder.submissionTime(TimeUtils.dateToStringTime(feedbackBo.getSubmissionTime()));

        // group&&statue
        dtoBuilder.group(feedbackBo.getGroup()).state(feedbackBo.getState());

        // id
        dtoBuilder.id(feedbackBo.getId());

        // content
        dtoBuilder.content(feedbackBo.getContent());

        // comments
        dtoBuilder.comments(feedbackCommentResponseDtoFactory.create(feedbackBo.getComments()));

        return dtoBuilder.build();
    }

}
##4. 设计权限验证方案。

5. 参与集成测试。

6. 对ruoyi的User表进行优化。

遇到的问题:
1.ruoyi代码生成工具里面默认提供了两种select方法一个是根据id,一个是根据对象,返回值是对象list,在comment可变表和issueTopic表中,因为做的是可编辑的,id字段自增(没有实际意义吧),commentId关联的是稳定表,很多时候需要是需要根据commentId进行查询,查出来的应该是长度为1的对象列表。早期项目里面有不少测试的问题,可能是多个,但是为了省劲。对多个数据的情况返回时间戳最新的,并且把之前的数据删除,但是在这里忘记的做。在单元测试的时候传入comment_id非空判断,导致成了做无条件查询,把所有数据查出来,删了除最新的一条外的所有数据。