美文网首页
标签缓存展示-相关问题-回复通知-回复展示-(9)

标签缓存展示-相关问题-回复通知-回复展示-(9)

作者: 弹钢琴的崽崽 | 来源:发表于2020-03-11 07:43 被阅读0次

    1. 标签展示

    1.1 question.html展示标签

    通过","切割成数组,并循环

    <hr class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
        <span class="label label-info question-tag" th:each="tag : ${question.tag.split(',')}">
            <span class="glyphicon glyphicon-tags" ></span>
            <span class="label label-info" th:text="${tag}"></span>
        </span>
    
    </div>
    

    1.2 标签样式question-tag

    2. 相关问题展示

    2.1 编写sql

    QuestionMapper.xml文件

    这样可以模糊查询通过标签,查出有相同标签的文章

    <select id="selectRelated" parameterType="life.guohui.community.model.Question" resultMap="BaseResultMap">
        SELECT * from question WHERE id != #{id} and tag regexp #{tag}
    </select>
    

    2.2 Service层进行封装

    ,替换为|用于查询

    public List<QuestionDTO> selectRelated(QuestionDTO queryDTO) {
        if(StringUtils.isBlank(queryDTO.getTag())){
            return new ArrayList<>();
        }
        String[] tags = StringUtils.split(queryDTO.getTag(), ",");
        String regexpTag = Arrays.stream(tags).collect(Collectors.joining("|"));
        Question question = new Question();
        question.setId(queryDTO.getId());
        question.setTag(regexpTag);
        List<Question> questions = questionMapper.selectRelated(question);
        List<QuestionDTO> questionDTOS = questions.stream().map(q -> {
            QuestionDTO questionDTO = new QuestionDTO();
            BeanUtils.copyProperties(q, questionDTO);
            return questionDTO;
        }).collect(Collectors.toList());
        return questionDTOS;
    }
    

    2.3 Controller使用

    2.4 页面展示

    3. 指定标签展示

    3.1 创建TagDTO

    • categoryName:上层标签
    • tags:下层标签

    3.2 缓存标签类

    • 用来存放标签的内容,所有的标签都通过它的get()方法获取
    • filterInvalid(String tags):用来校验传来的标签中标签库里是否存在
    package life.guohui.community.cache;
    public class TagCache {
        public static List<TagDTO> get(){
            ArrayList<TagDTO> tagDTOS = new ArrayList<>();
            TagDTO program = new TagDTO();
            program.setCategoryName("开发语言");
            program.setTags(Arrays.asList("javascript", "php", "css", "html", "html5", "java", "node.js", "python", "c++", "c", "golang", "objective-c", "typescript", "shell", "swift", "c#", "sass", "ruby", "bash", "less", "asp.net", "lua", "scala", "coffeescript", "actionscript", "rust", "erlang", "perl"));
            tagDTOS.add(program);
            TagDTO framework = new TagDTO();
            framework.setCategoryName("平台框架");
            framework.setTags(Arrays.asList("laravel", "spring", "express", "django", "flask", "yii", "ruby-on-rails", "tornado", "koa", "struts"));
            tagDTOS.add(framework);
    
    
            TagDTO server = new TagDTO();
            server.setCategoryName("服务器");
            server.setTags(Arrays.asList("linux", "nginx", "docker", "apache", "ubuntu", "centos", "缓存 tomcat", "负载均衡", "unix", "hadoop", "windows-server"));
            tagDTOS.add(server);
    
            TagDTO db = new TagDTO();
            db.setCategoryName("数据库");
            db.setTags(Arrays.asList("mysql", "redis", "mongodb", "sql", "oracle", "nosql memcached", "sqlserver", "postgresql", "sqlite"));
            tagDTOS.add(db);
    
            TagDTO tool = new TagDTO();
            tool.setCategoryName("开发工具");
            tool.setTags(Arrays.asList("git", "github", "visual-studio-code", "vim", "sublime-text", "xcode intellij-idea", "eclipse", "maven", "ide", "svn", "visual-studio", "atom emacs", "textmate", "hg"));
            tagDTOS.add(tool);
            return tagDTOS;
        }
        public static String filterInvalid(String tags){
            String[] split = StringUtils.split(tags, ",");
            List<TagDTO> tagDTOS = get();
            List<String> tagList = tagDTOS.stream().flatMap(tag -> tag.getTags().stream()).collect(Collectors.toList());
            String invalid = Arrays.stream(split).filter(t -> !tagList.contains(t)).collect(Collectors.joining(","));
            return invalid;
        }
    }
    

    3.3 在PublishController中把标签存到Model中

    3.4 在发布问题功能中做校验

    String invalid = TagCache.filterInvalid(tag);
    if(StringUtils.isNotBlank(invalid)){
        model.addAttribute("error", "输入非法标签:"+invalid);
        return "publish";
    }
    

    3.5 publish.html处理

    function selectTag(e){
        var value = e.getAttribute("data-tag");
        var previous = $("#tag").val();
    
        if(previous.indexOf(value) == -1){
            if(previous){
                $("#tag").val(previous+','+value);
            }else {
                $("#tag").val(value);
            }
        }
    }
    function showSelectTag() {
        $("#select-tag").show();
    }
    

    第二个参数为index,利用它的.first判断是不是第一个元素,来控制选中高亮及标签显示

    th:each="selectCategory,selectCategoryStat : ${tags}"
    
    <div id="select-tag" class="publish-tag-tab">
        <ul class="nav nav-tabs" role="tablist">
            <li role="presentation" th:each="selectCategory,selectCategoryStat : ${tags}"
                th:class="${selectCategoryStat.first ? 'active':''}">
                <a th:href="${'#'+selectCategory.categoryName}" aria-controls="home" role="tab"
                   data-toggle="tab" th:text="${selectCategory.categoryName}"></a>
            </li>
        </ul>
        <div class="tab-content">
            <div role="tabpanel"  th:id="${selectCategory.categoryName}"
                 th:class="${selectCategoryStat.first ? 'active tab-pane':'tab-pane'}"
                 th:each="selectCategory: ${tags}">
                <span>
                    <span class="label label-info" th:each="selectTag : ${selectCategory.tags}">
                        <span class="glyphicon glyphicon-tags" onclick="selectTag(this)"
                              th:data-tag="${selectTag}"
                              th:text="${' '+selectTag}">
                        </span>
                    </span>
                </span>
            </div>
        </div>
    </div>
    

    4. 回复通知

    4.1 表的设计

    create table notification
    (
        id bigint auto_increment primary key,
        notifier bigint not null,-- 评论当前问题的人的Id
        receiver bigint not null,-- 接受者Id,当前问题的发起者Id
        outerid bigint not null,-- 当前问题的Id
        type int not null,-- 回复了问题(1),还是回复了评论(2)
        gmt_create bigint not null,
        status int default 0 not null,-- 已读Or未读
        notifier_name varchar(100),-- 评论当前问题的人名
        outer_title varchar(256)-- 当前问题的标题
    );
    

    逆向工程生成对应的实体类和mapper

    4.2 创建NotificationTypeEnum

    • 区分是回复了问题,还是回复了评论。
    • nameOfType(int type)方法返回数字对应的信息
    package life.guohui.community.enums;
    public enum NotificationTypeEnum {
        REPLY_QUESTION(1,"回复了问题"),
        REPLY_COMMENT(2,"回复了评论")
        ;
        private int type;
        private String name;
    
        NotificationTypeEnum(int status, String name) {
            this.type = status;
            this.name = name;
        }
    
        public int getType() {
            return type;
        }
    
        public String getName() {
            return name;
        }
        public static String nameOfType(int type) {
            for (NotificationTypeEnum notificationTypeEnum : NotificationTypeEnum.values()) {
                if (notificationTypeEnum.getType() == type) {
                    return notificationTypeEnum.getName();
                }
            }
            return "";
        }
    }
    

    4.3 创建NotificationStatusEnum

    用来区分回复是已读还是未读

    4.4 创建NotificationDTO

    用来在页面上展示对应的数据

    package life.guohui.community.dto;
    @Data
    public class NotificationDTO {
        private Long id;
        private Long gmtCreate;
        private Integer status;
        private Long notifier;//评论者
        private Long outerid;//问题Id
        private String notifierName;//评论者的名字
        private String outerTitle;//问题提出者的问题标题
        private String typeName;
        private Integer type;
    }
    

    4.5 评论后会去哪?增加回复

    a. 点击评论后发送Ajax请求/commentCommentController执行insert方法

    b. 添加一个评论时,我们就要同时创建一个通知

    增加一个创建通知的方法,该方法的参数:

    • 评论的实体类对象Comment
    • 问题的提出者Id:receiver
    • 当前评论者的名字:notifierName,从Controller层中传过来,从session中获取user
    • 当前问题的标题:outerTitle
    • 回复的类型:notificationType,回复的是问题Or评论
    • 当前问题的Id:outerId
    private void createNotify(Comment comment, Long receiver, String notifierName, String outerTitle, NotificationTypeEnum notificationType,Long outerId) {
        Notification notification = new Notification();
        notification.setGmtCreate(System.currentTimeMillis());
        notification.setType(notificationType.getType());
        notification.setOuterid(outerId);//发布问题的id
        notification.setNotifier(comment.getCommentator());//评论人的人的id
        notification.setStatus(NotificationStatusEnum.UNREAD.getStatus());
        notification.setReceiver(receiver);//当前问题发布人的Id
        notification.setNotifierName(notifierName);//评论的人的名字
        notification.setOuterTitle(outerTitle);//发布问题的人的问题标题
        notificationMapper.insert(notification);
    }
    

    c. 在添加评论的方法中使用创建通知方法

    @Transactional
    public void insert(Comment comment, User commentator) {
        if(comment.getParentId() == null || comment.getParentId() == 0){
            throw new CustomizeException(CustomizeErrorCode.TARGET_PARAM_NOT_FOUND);
        }
        if(comment.getType() == null || !CommentTypeEnum.isExist(comment.getType())){
            throw new CustomizeException(CustomizeErrorCode.TYPE_PARAM_WRONG);
        }
        if(comment.getType() == CommentTypeEnum.COMMENT.getType()){
            //回复评论
            Comment dbComment = commentMapper.selectByPrimaryKey(comment.getParentId());
            if(dbComment == null){
                throw new CustomizeException(CustomizeErrorCode.COMMENT_NOT_FOUND);
            }
            Question question = questionMapper.selectByPrimaryKey(dbComment.getParentId());
            if(question == null){
                throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
            }
            commentMapper.insert(comment);
    
            //增加评论数
            Comment parentComment = new Comment();
            parentComment.setId(comment.getParentId());
            parentComment.setCommentCount(1);
            commentMapper.incCommentCount(parentComment);
            //创建通知
            createNotify(comment, dbComment.getCommentator(), commentator.getName(), question.getTitle(), NotificationTypeEnum.REPLY_COMMENT,question.getId());
        }else {
            //回复问题
            Question question = questionMapper.selectByPrimaryKey(comment.getParentId());
            if(question == null){
                throw new CustomizeException(CustomizeErrorCode.QUESTION_NOT_FOUND);
            }
            comment.setCommentCount(0);
            commentMapper.insert(comment);
            question.setCommentCount(1);
            questionMapper.incCommentCount(question);
            //创建通知
            createNotify(comment,question.getCreator(),commentator.getName(),question.getTitle(),NotificationTypeEnum.REPLY_QUESTION,question.getId());
        }
    }
    

    4.6 展示回复

    a. 给分页DTO添加泛型

    可以存入不同类型的集合数据

    package life.guohui.community.dto;
    @Data
    public class PaginationDTO<T> {
        private List<T> data;
        private boolean showPrevious;
        private boolean showFirstPage;
        private boolean showNext;
        private boolean showEndPage;
        private Integer page;
        private Integer totalPage;
        private List<Integer> pages = new ArrayList<>();
    
    
        public void setPagination(Integer totalPage, Integer page) {
            this.totalPage = totalPage;
            this.page = page;
            pages.add(page);
            for(int i = 1;i <=3; i++){
                if(page-i>0){
                    pages.add(0,page-i);
                }
                if(page+i <= totalPage){
                    pages.add(page+i);
                }
            }
            //是否展示上一页
            if(page == 1){
                showPrevious = false;
            }else {
                showPrevious = true;
            }
            //是否展示下一页
            if(page == totalPage){
                showNext = false;
            }else{
                showNext = true;
            }
            //是否展示第一页
            if(pages.contains(1)){
                showFirstPage = false;
            }else{
                showFirstPage = true;
            }
            //是否展示最后一页
            if(pages.contains(totalPage)){
                showEndPage = false;
            }else{
                showEndPage = true;
            }
        }
    }
    

    b. 创建NotificationService

    • 查出当前用户的所有的回复
    • 将查出的所有Notification对象,赋值给NotificationDTO对象
    public PaginationDTO list(Long id, Integer page, Integer size) {
        PaginationDTO<NotificationDTO> paginationDTO = new PaginationDTO();
        Integer totalPage;
        //拿到总数
        NotificationExample notificationExample = new NotificationExample();
        notificationExample.createCriteria().andReceiverEqualTo(id);
        Integer totalCount = (int)notificationMapper.countByExample(notificationExample);
    
        if(totalCount % size == 0){totalPage = totalCount/size;}else{totalPage = totalCount/size + 1;}
        if(page<1){            page = 1;        }
        if(page>totalPage){            page = totalPage;        }
        paginationDTO.setPagination(totalPage,page);
        Integer offset = size * (page - 1);
        NotificationExample example1 = new NotificationExample();
        example1.createCriteria().andReceiverEqualTo(id);
        List<Notification> notifications = notificationMapper.selectByExampleWithRowbounds(example1, new RowBounds(offset, size));
    
        if(notifications.size() == 0){
            return paginationDTO;
        }
    
        List<NotificationDTO> notificationDTOS = new ArrayList<>();
        for (Notification notification : notifications) {
            NotificationDTO notificationDTO = new NotificationDTO();
            BeanUtils.copyProperties(notification,notificationDTO);
            notificationDTO.setTypeName(NotificationTypeEnum.nameOfType(notification.getType()));
            notificationDTOS.add(notificationDTO);
        }
        paginationDTO.setData(notificationDTOS);
        return paginationDTO;
    }
    

    c. 要查出当前用户未读评论的数量

    public Long unreadCount(Long userId) {
        NotificationExample notificationExample = new NotificationExample();
        notificationExample.createCriteria().andReceiverEqualTo(userId).andStatusEqualTo(NotificationStatusEnum.UNREAD.getStatus());
        return notificationMapper.countByExample(notificationExample);
    }
    

    d. 在ProfileController中存入Model

    e. 页面的渲染

    <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" th:if="${section == 'replies'}">
        <div class="media" th:each="notification : ${pagination.data}">
    
            <div class="media-body">
                <p class="media-heading" >
                    <span th:text="${notification.notifierName+' '+ notification.typeName+' '}" ></span>
                    <a th:href="@{'/notification/'+${notification.id}}" th:text="${notification.outerTitle}"></a>
                    <span class="label label-danger" th:if="${notification.status == 0}">未读</span>
                </p>
            </div>
        </div>
    </div>
    

    未读数量

    <a href="/profile/questions"
       th:class="${section == 'questions'}? 'active list-group-item' : 'list-group-item'">我的问题</a>
    <a href="/profile/replies"
       th:class="${section == 'replies'}? 'active list-group-item' : 'list-group-item'">
        最新回复
        <span class="badge" th:text="${unreadCount}"></span>
    </a>
    

    4.7 问题的标题是链接

    点击后跳转到该问题的question.html页面

    a. Controller中增加read(id,user)方法

    id为回复对象的Id,该方法返回这个notificationDTO对象

    b. Service中

    • 通过Id查出这个回复的信息,修改它的status为已读。
    • Notification的值赋给NotificationDTO并返回。
    public NotificationDTO read(Long id, User user) {
        Notification notification = notificationMapper.selectByPrimaryKey(id);
        if(notification == null){
            throw new CustomizeException(CustomizeErrorCode.NOTIFICATION_NOT_FOUND);
        }
        //接收者和当前用户是不是同一个人,防止从网址中输入其他接收者的id
        if(!Objects.equals(notification.getReceiver(),user.getId())){
            throw new CustomizeException(CustomizeErrorCode.READ_NOTIFICATION_FAIL);
        }
        //更新已读
        notification.setStatus(NotificationStatusEnum.READ.getStatus());
        notificationMapper.updateByPrimaryKey(notification);
        NotificationDTO notificationDTO = new NotificationDTO();
        BeanUtils.copyProperties(notification,notificationDTO);
        notificationDTO.setTypeName(NotificationTypeEnum.nameOfType(notification.getType()));
        return notificationDTO;
    }
    

    4.8 修复小问题

    a. 把未读的回复存到session中

    b. 将回复列表按时间倒序

    相关文章

      网友评论

          本文标题:标签缓存展示-相关问题-回复通知-回复展示-(9)

          本文链接:https://www.haomeiwen.com/subject/nnypdhtx.html