project(7)

显示用户信息面板

index.html以及很多页面上都有这个用户信息面板
在这里插入图片描述

开发用户信息面板

为了方便显示这个面板中的信息,我们新建一个UserVo的值对象类

我们先完成提问数量和收藏数量的查询和编写即可

所以新建一个UserVo类代码如下

@Data
//支持连缀书写
@Accessors(chain = true)
public class UserVo {
    private Integer id;
    private String username;
    private String nickname;

    //两个面板中显示的数据
    //问题数量
    private int questions;
    //收藏数量
    private int collections;
}

我们先从数据访问层查询这个用户开始

UserMapper接口中添加一个查询User基本信息的方法

@Select("select id,username,nickname from user " +
            " where username=#{username}")
    UserVo findUserVoByUsername(String username);

测试代码如下

 @Autowired
    UserMapper userMapper;
    @Test
    public void testUser(){
        UserVo user=userMapper.findUserVoByUsername("st2");
        System.out.println(user);
    }

为了学习使用QueryWrapper来查询数量,我们在IQuestionService中编写一个根据用户id获得问题数量的方法

代码如下

Integer countQuestionsByUserId(Integer userId);

在QuestionServiceImpl类中实现这个方法

 @Override
    public Integer countQuestionsByUserId(Integer userId) {
        //使用QueryWrapper查询数量的方法
        QueryWrapper<Question> query=new QueryWrapper<>();
        query.eq("user_id",userId);
        query.eq("delete_status",0);
        Integer count=questionMapper.selectCount(query);
        //别忘了返回
        return count;
    }

上面查询的问题数量,实际上是为了让UserVo获得信息的准备工作

而UserVo的创建赋值需要在UserService中

所以完成IUserService接口中方法的编写代码如下

  //查询当前登录用户信息面板的方法
  UserVo currentUserVo();

这个接口方法的实现

UserServiceImpl类代码如下

@Autowired
IQuestionService questionService;
@Override
public UserVo currentUserVo() {
    //获得登录用户名
    String username=currentUsername();
    //获得当前对象基本信息
    UserVo user=userMapper.findUserVoByUsername(username);
    Integer questions=questionService
            .countQuestionsByUserId(user.getId());
    user.setQuestions(questions);
    //用户收藏数信息未做!!!
    return user;
}

也可以进行测试

完成了业务逻辑层的编写下面开始编写控制层

UserController类中调用代码如下

//显示用户信息面板的控制层方法
    @GetMapping("/me")
    public R<UserVo> me(){
        UserVo user=userService.currentUserVo();
        return R.ok(user);
    }

已经完成控制层的编码,下面就剩html和js了

首先编写index.html页面中vue信息的绑定

代码如下

<!--个人信息-->
      <div id="userApp" class="container-fluid font-weight-light">
        <div class="card">
          <h5 class="card-header" v-text="user.nickname">陈某</h5>
          <div class="card-body">
            <div class="list-inline mb-1 ">
                <a class="list-inline-item mx-3 my-1 text-center">
                  <div><strong>10</strong></div>
                  <div>回答</div>
                </a>
                <a class="list-inline-item mx-3 my-1 text-center" href="personal/myQuestion.html">
                  <div>
                    <strong v-text="user.questions">10</strong>
                  </div>
                  <div>提问</div>
                </a>
                <a class="list-inline-item mx-3 my-1 text-center" href="personal/collect.html">
                  <div>
                    <strong v-text="user.collections">10</strong>
                  </div>
                  <div>收藏</div>
                </a>
                <a class="list-inline-item mx-3 my-1 text-center" href="personal/task.html">
                  <div><strong>10</strong></div>
                  <div>任务</div>
                </a>
            </div>
          </div>
        </div>
      </div>

下面开始写js文件

但是为了避免写完js文件在回到index.html文件中添加引用,我们先在index.html中写好引用即可

</body>
<script src="js/utils.js"></script>
<script src="js/index.js"></script>
<script src="js/tags_nav.js"></script>
<script src="js/user_info.js"></script>
</html>

在static文件夹中的js文件夹中创建user_info.js文件来绑定html文件中的内容

user_info.js代码如下

let userApp = new Vue({
    el: "#userApp",
    data: {
        user: {}
    },
    methods: {
        loadCurrentUser: function () {
            $.ajax({
                url: "/v1/users/me",
                method: "get",
                success: function (r) {
                    console.log(r)
                    if (r.code==OK) {
                        userApp.user=r.data;
                    }else{
                        console.log(r.message);
                    }
                }
            });
        }
    },
    created: function () {
        //页面加载完毕后立即调用loadCurrentUser方法
        this.loadCurrentUser();
    }
});

完成用户信息面板的复用

步骤1:

将我们刚刚编写的index.html页面的用户信息面板的div设置为TH模板

<div id="userApp"  th:fragment="user_info"
           class="container-fluid font-weight-light">

步骤2:

在create.html文件中找到对应的位置,套用模板

<div class="col-4 pb-2 ">
  <div th:replace="index::user_info">
  </div>
  <!--热点数据代码略-->
</div>

步骤3:

create.html文件末尾引入js的支持

</body>
<script src="../js/utils.js"></script>
<script src="../js/tags_nav.js"></script>
<script src="../js/createQuestion.js"></script>
<script src="../js/user_info.js"></script>
</html>

稻草问答讲师回复首页

在这里插入图片描述

学生发布问题已经基本完成

下面是讲师回复

但是讲师回复的前提是讲师也要登录

讲师的登录显示的是一个和index.html页面很相似的内容的另一个页面

是index_teacher.html

显示讲师首页

复制static文件夹下的index_teacher.html到templates下

如果也开发一个控制器来显示讲师的主页

在HomeController中编写代码如下

//临时显示讲师主页的控制器方法
    @GetMapping("/index_teacher.html")
    public ModelAndView indexTeacher(){
        return new ModelAndView("index_teacher");
    }

编写成功后,我们虽然可以顺利访问老师的主页

但是从需求上讲,访问老师还是学生的首页是由登录用户的身份决定的

不能随意指定

所以我们要编写一个功能:按登录用户的身份显示不同的主页

步骤1:

因为我们现在将登录操作和权限管理交由了Spring-Security来处理

所以我们在完成本功能时仍然需要操作很多Spring-Security的API

在UserMapper中添加一个方法,按用户id查询这个用户的所有身份

以便保存掉Spring-Security中

代码如下

//按用户id查询用户的所有角色
    @Select("select r.id,r.name " +
            "from user u " +
            "left join user_role ur on u.id=ur.user_id " +
            "left join role r on r.id=ur.role_id " +
            "where u.id=#{userId}")
    List<Role> findUserRolesById(Integer id);

测试一下

@Autowired
    UserMapper userMapper;
    @Test
    public void roles(){
        List<Role> list=userMapper.findUserRolesById(1);
        for(Role role:list){
            System.out.println(role);
        }
    }

上面的信息查询出来是为了保存到Spring-Security的权限管理中的

我们在UserServiceImpl类中设置过登录用户的权限,现在要去这个方法中重构一下

添加角色信息,UserServiceImpl的getUserDetails方法

代码如下

@Override
    public UserDetails getUserDetails(String username) {
        //根据用户名获得用户对象
        User user=userMapper.findUserByUsername(username);
        //判断用户对象是否为空
        if(user==null) {
            //如果为空直接返回null
            return null;
        }
        //如果不为空根据用户的id查询这个用户的所有权限
        List<Permission> permissions=
                userMapper.findUserPermissionsById(user.getId());
        //将权限List中的权限转成数组方便赋值
        String[] auths=new String[permissions.size()];
        for(int i=0;i<auths.length;i++){
            auths[i]=permissions.get(i).getName();
        }
        //读取用户的所有角色
        List<Role> roles=userMapper.findUserRolesById(user.getId());
        int j=auths.length;
        //扩容上面的数组
        auths= Arrays.copyOf(auths,
                auths.length+roles.size());
        //向数组内容中赋值
        for(Role r:roles){
            auths[j]=r.getName();
            j++;
        }

        //创建UserDetails对象,并为他赋值
        UserDetails ud= org.springframework.security.core.userdetails
                .User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .accountLocked(user.getLocked()==1)//写==1是判断锁定
                .disabled(user.getEnabled()==0)//写==0是判断不可用
                .authorities(auths).build();
        //最后返回UserDetails对象
        return ud;
    }

运行完上面的代码

Spring-Security的权限管理的字符串包含了权限相关的内容和角色相关的内容

以一个学生登录为例

这个数组的内容是:auths={"/index.html","/question/create",

“/question/uploadMultipleFile”,"/question/detail",“ROLE_STUDENT”}

下面我们就来实现根据不同的角色使HomeController中显示主页的控制器代码,返回不同的主页

HomeController类index方法代码如下

//声明两个常亮以便判断用户的角色
    static final GrantedAuthority STUDENT =
            new SimpleGrantedAuthority("ROLE_STUDENT");
    static final GrantedAuthority TEACHER =
            new SimpleGrantedAuthority("ROLE_TEACHER");

    //显示首页
    @GetMapping("/index.html")
    //@AuthenticationPrincipal 注解后面跟Spring-Security的User类型参数
    //表示需要Spring-Security将当前登录用户的权限信息赋值给User对象
    //以便我们在方法中验证他的权限或身份
    public ModelAndView index(
            @AuthenticationPrincipal User user){
        //  根据Spring-Security提供的用户判断权限,绝对返回哪个页面
        if(user.getAuthorities().contains(STUDENT)){
            return  new ModelAndView("index");
        }else if(user.getAuthorities().contains(TEACHER)){
            return new ModelAndView("index_teacher");
        }
        return null;

    }

复用讲师首页的内容

在index_teacher.html页面中做一些修改

首先Th的命名空间

<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">

使用模板替换导航栏

<!--引入标签的导航栏-->
<div class="container-fluid" th:replace="index::tags_nav">
	<!--中间代码略-->
</div>

使用模板替换用户信息面板

<!--个人信息-->
<div th:replace="index::user_info"
	class="container-fluid font-weight-light">
	<!--中间代码略-->
</div>

引入依赖的js文件

</body>
<script src="js/utils.js"></script>
<script src="js/tags_nav.js"></script>
<script src="js/user_info.js"></script>
</html>

显示讲师问题列表

讲师首页显示的问题是学生向当前登录讲师提问的问题

所以和学生首页查询当前登录学生的提问不同,需要编写新的Sql语句查询讲师首页问题列表

QuestionMapper中添加新的方法代码如下

@Repository
public interface QuestionMapper extends BaseMapper<Question> {
    @Select("SELECT q.* " +
            " FROM question q" +
            " LEFT JOIN user_question uq " +
            "      ON q.id=uq.question_id" +
            " WHERE uq.user_id=#{userId} OR q.user_id=#{userId}" +
            " ORDER BY q.createtime DESC")
    List<Question> findTeachersQuestions(Integer userId);

}

可以测试一下

@Autowired
    QuestionMapper questionMapper;
    @Test
    public void teacherQuestions(){
        List<Question> list=
                questionMapper.findTeachersQuestions(3);
        for(Question question:list){
            System.out.println(question);
        }
    }

然后开始编写业务逻辑层的接口

IQuestionService

//分页查询当前登录的老师问题的方法
    PageInfo<Question> getQuestionsByTeacherName(
            String username,Integer pageNum,Integer pageSize
    );

编写接口的实现

QuestionServiceImpl

 @Override
    public PageInfo<Question> getQuestionsByTeacherName(
            String username, Integer pageNum, Integer pageSize) {
        if(pageNum == null)
            pageNum=1;
        if(pageSize == null)
            pageSize=8;

        //根据用户名查询用户对象
        User user=userMapper.findUserByUsername(username);
        //设置分页查询
        PageHelper.startPage(pageNum,pageSize);
        List<Question> questions=
                questionMapper.findTeachersQuestions(user.getId());
        //别忘了,要将问题列中的标签字符串转成标签的List
        for(Question q: questions){
            List<Tag> tags=tagNamesToTags(q.getTagNames());
            q.setTags(tags);
        }
        return new PageInfo<Question>(questions);
    }

控制层的调用

QuestionController

@GetMapping("/teacher")
    @PreAuthorize("hasRole('ROLE_TEACHER')")
    public R<PageInfo<Question>> teachers(
            //声明权限是为了获得用户名的
            @AuthenticationPrincipal User user,
            Integer pageNum){
        if(pageNum == null)
            pageNum = 1;
        Integer pageSize=8;
        //调用业务逻辑层的方法
        PageInfo<Question> pageInfo=questionService
                .getQuestionsByTeacherName(
                     user.getUsername(),pageNum,pageSize
                );
        return R.ok(pageInfo);
    }

index_teacher.html页面也可以使用th模板来复用问题列表

定义模板

在index.html页面中找到定义id为QuestionApp的div

修改代码如下

<div class="container-fluid" id="questionsApp"
            th:fragment="questions_app">

套用模板

在index_teacher.html页面中找到对应的div编写复用代码

<div class="container-fluid"
             th:replace="index::questions_app">

最后完成js文件的编写

可以复制index.js修改名称为index_teacher.js

修改ajax的调用路径即可

/*
显示登录讲师的问题列表
 */
let questionsApp = new Vue({
    el:'#questionsApp',
    data: {
        questions:[],
        pageInfo:{},
        style:"fa-tasks",
        navTitle:"我的任务"
    },
    methods: {
        loadQuestions:function (pageNum) {
            if(!pageNum){ //如果pageNum为空,默认页码为1
                pageNum=1;
            }
            $.ajax({
                url: '/v1/questions/teacher',
                method: "GET",
                data:{pageNum:pageNum},
                success: function (r) {
                    console.log("成功加载数据");
                    console.log(r);
                    if(r.code === OK){
                        questionsApp.questions = r.data.list;
                        //调用计算持续时间的方法
                        questionsApp.updateDuration();
                        //调用显示所有按标签呈现的图片
                        questionsApp.updateTagImage();
                        questionsApp.pageInfo=r.data;
                    }
                }
            });
        },
        updateTagImage:function(){
            let questions = this.questions;
            for(let i=0; i<questions.length; i++){
                //获得当前问题对象的所有标签的集合(数组)
               let tags = questions[i].tags;
               //js代码中特有的写法if(tags)
               //相当于判断tags非空
               if(tags){
                   //获取当前问题的第一个标签对应的图片文件路径
                   let tagImage = 'img/tags/'+tags[0].id+'.jpg';
                   console.log(tagImage);
                   //将这个文件路径保存到tagImage属性用,以便页面调用
                   questions[i].tagImage = tagImage;
               }
            }
        },
        updateDuration:function () {
            let questions=this.questions;
            for(let i=0;i<questions.length;i++){
                //获得问题中的创建时间属性(毫秒数)
                let createtime=new Date(questions[i].createtime).getTime();
                //获得当前时间的毫秒数
                let now=new Date().getTime();
                //计算时间差(秒)
                let durtaion=(now-createtime)/1000;
                if(durtaion<60){
                    // 显示刚刚
                    //duration这个名字可以随便起,只要保证和页面上取的一样就行
                    questions[i].duration="刚刚";
                }else if(durtaion<60*60){
                    // 显示XX分钟
                    questions[i].duration=
                        (durtaion/60).toFixed(0)+"分钟前";
                }else if (durtaion<60*60*24){
                    //显示XX小时
                    questions[i].duration=
                        (durtaion/60/60).toFixed(0)+"小时前";
                }else{
                    //显示XX天
                    questions[i].duration=
                        (durtaion/60/60/24).toFixed(0)+"天前";
                }

            }

        }
    },
    created:function () {
        console.log("执行了方法");
        this.loadQuestions(1);
    }
});

最后引用js依赖

</body>
<script src="js/utils.js"></script>
<script src="js/tags_nav.js"></script>
<script src="js/user_info.js"></script>
<script src="js/index_teacher.js"></script>
</html>

还可以尝试修改"我的问答""我的任务"的绑定

这里不强制要求

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值