Spring Boot 8 实现对战记录(次要)&&用户分数排行榜(次要)&&实现分页(重点)

1.上次收尾工作

游戏结束更新积分
userMapper修改为public,因为要在对局结束,修改玩家积分
在这里插入图片描述
在这里插入图片描述

2.主要内容

2.1加上mybatis分页配置

package com.kob.backend.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2.2对局列表和回放

在类内定义属性时,使用spring自动注入的话,若是在service或controller定义,则不用定义static。翻译过来,意思就是,若当前类本身是单例,则属性本身就只会有一份,无论加不加静态变量,效果都一样;若是第三方类,也就是自己写的类,一般都是会定义多个对象,那么就要思考属性到底是属于类的,还是属于对象的。

A: 后端(实现返回对战记录列表)【*】

接口

package com.kob.backend.service.record;

import com.alibaba.fastjson.JSONObject;

public interface GetRecordListService {
    public JSONObject getList(Integer page);//传入第几页,返回JSONObject
}

实现接口

package com.kob.backend.service.impl.record;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kob.backend.mapper.RecordMapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.Record;
import com.kob.backend.pojo.User;
import com.kob.backend.service.record.GetRecordListService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class GetRecordListServiceImpl implements GetRecordListService {
    @Autowired
    private RecordMapper recordMapper ;
    @Autowired
    private UserMapper userMapper;

    @Override
    public JSONObject getList(Integer page) {
        IPage<Record> recordIPage = new Page<>(page,10);//API
        QueryWrapper<Record> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");//按照降序排列
        List<Record> records = recordMapper.selectPage(recordIPage,queryWrapper).getRecords();//一页
        JSONObject resp = new JSONObject();
        List<JSONObject> items = new ArrayList<>();
        for(Record record:records){
            User userA = userMapper.selectById(record.getAId());
            User userB = userMapper.selectById(record.getBId());
            JSONObject item = new JSONObject();
            item.put("a_photo",userA.getPhoto());
            item.put("a_username",userA.getUsername());
            item.put("b_photo",userB.getPhoto());
            item.put("b_username",userB.getUsername());
            item.put("record",record);
            items.add(item);
        }
        resp.put("records",items);
        resp.put("records_count",recordMapper.selectCount(null));
        return resp;
    }
}

控制器

package com.kob.backend.controller.record;

import com.alibaba.fastjson.JSONObject;
import com.kob.backend.service.record.GetRecordListService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class GetRecordListController {
    @Autowired
    private GetRecordListService getRecordListService;

    @GetMapping("/record/getlist/")
    public JSONObject getList(@RequestParam Map<String,String> data){
        Integer page = Integer.parseInt(data.get("page"));
        return getRecordListService.getList(page);
    }
}

B: 前端(实现接收展示对战记录列表)

查询一下后端数据
将列表显示出来
展示table,把之前的table复制过来即可

<template>
    <ContentField>
    <div class="container">
        <div class="row">   
            <div class="card-body">
        <table class="table table-striped table-hover">
            <!-- 表头 -->
            <thead>
                <tr>
                    <th>A</th>
                    <th>B</th>
                    <th>对战结果</th>
                    <th>对战时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <!-- 表身 -->
            <tbody>
                <tr v-for="record in records" :key="record.record.id">
                    <th>
                        <img :src="record.a_photo" alt="" class="record-user-photo">&nbsp;
                        {{record.a_username}}
                    </th>
                    <th>
                        <img :src="record.b_photo" alt="" class="record-user-photo">&nbsp;
                        {{record.b_username}}
                    </th>
                    <th>
                        {{record.result}}
                    </th>
                    <th>
                        {{record.record.createtime}}
                    </th>
                    <th>
                        <button type="button" class="btn btn-secondary">查看录像</button>
                    </th>
                </tr>
            </tbody>
        </table>
            </div> 
        </div>
    </div>
    </ContentField>
</template>

<script>
import ContentField from "@/components/ContentField"
import $ from "jquery"
import { useStore } from "vuex"
import { ref } from "vue"

export default {
    components:{
        ContentField
    },
    setup(){
        const store = useStore();
        let total_records = 0 ;
        let current_page = 1 ;
        let records = ref([]);
        console.log("total_records:" + total_records, "     current_page:" + current_page)
        const pull_page = page =>{
            current_page = page ;
            $.ajax({
                url:"http://localhost:3000/record/getlist/",
                type:"get",
                data:{
                    page,
                },
                headers:{
                  Authorization:"Bearer " + store.state.user.token,
                },
                success(resp){
                    records.value = resp.records ;
                    total_records = resp.records_count ;
                },
                error(resp){
                    console.log(resp)
                }
            })
        }

        pull_page(current_page);

        return {
            records,            
        }
    }
}
</script>

<style scoped>
img.record-user-photo {
    width: 5vh;
    border-radius: 50% ;
}
</style>

在这里插入图片描述

2.3.实现查看录像功能

点击后跳转页面,需要写一个新的View
直接将pk界面复制过来,需要判断是录像还是对战
只需要一个PlayGround组件就可以了
把页面加到路由里面点开Router加一个路由

store/record.js

// import $ from "jquery"

export default {
  state: {
    is_record : false ,
    a_steps:"",
    b_steps:"",
    record_loser:"",
  },
  getters: {
  },
  mutations: {
    updateIsRecord(state,is_record){
        state.is_record = is_record;
    },
    updateSteps(state,data){
      state.a_steps = data.a_steps;
      state.b_steps = data.b_steps;
    },
    updateRecordLoser(state,loser){
      state.record_loser = loser ;
    }
  },
  actions: {
  },
  modules: {
  }
}

RecordIndexView.vue

<template>
    <ContentField>
        <table class="table table-striped table-hover">
            <!-- 表头 -->
            <thead>
                <tr>
                    <th>A</th>
                    <th>B</th>
                    <th>对战结果</th>
                    <th>对战时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <!-- 表身 -->
            <tbody>
                <tr v-for="record in records" :key="record.record.id">
                     <td>
                        <img :src="record.a_photo" alt="" class="record-user-photo">
                        &nbsp;
                        <span class="record-user-username">{{ record.a_username }}</span>
                    </td>
                    <td>
                        <img :src="record.b_photo" alt="" class="record-user-photo">
                        &nbsp;
                        <span class="record-user-username">{{ record.b_username }}</span>
                    </td>
                    <td>{{ record.result }}</td>
                    <td>{{ record.record.createtime }}</td>
                    <td>
                        <button @click="open_record_content(record.record.id)" type="button" class="btn btn-secondary">查看录像</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </ContentField>
</template>

<script>
import ContentField from "@/components/ContentField"
import $ from "jquery"
import { useStore } from "vuex"
import { ref } from "vue"
import router from "@/router/index"
export default {
    components:{
        ContentField
    },
    setup(){
        const store = useStore();
        let total_records = 0 ;
        let current_page = 1 ;
        let records = ref([]);
        console.log("total_records:" + total_records, "     current_page:" + current_page)
        const pull_page = page =>{
            current_page = page ;
            $.ajax({
                url:"http://127.0.0.1:3000/record/getlist/",
                type:"get",
                data:{
                    page,
                },
                headers:{
                  Authorization:"Bearer " + store.state.user.token,
                },
                success(resp){
                    console.log("&&&&&&&&&&&&&")
                    records.value = resp.records ;
                    total_records = resp.records_count ;
                },
                error(resp){
                    console.log(resp)
                }
            })
        }

        pull_page(current_page);

        const stringTo2D = map =>{
              let g = [] ;
              for(let i = 0,k = 0;i < 13 ;i ++ ){
                let line = [];
                for(let j = 0 ;j < 14 ;j++){
                    if(map[k] === '0')line.push(0);
                    else line.push(1);
                    k++;
                }
                g.push(line);
              }  
              return g ;
        }

        const open_record_content = recordId => {
            for(const record of records.value){
                
                if(record.record.id === recordId){
                    console.log(record)
                    store.commit("updateIsRecord",true);
                    store.commit("updateGame",{
                        map:stringTo2D(record.record.map),
                        a_id:record.record.aid,
                        a_sx:record.record.asx,
                        a_sy:record.record.asy,
                        b_id:record.record.bid,
                        b_sx:record.record.bsx,
                        b_sy:record.record.bsy,
                    });
                    store.commit("updateSteps",{
                        a_steps : record.record.asteps,
                        b_steps : record.record.bsteps,
                    });
                    console.log(record.record.map,stringTo2D(record.record.map))
                    store.commit("updateRecordLoser",record.record.loser);

                    router.push({
                        name:"record_content",
                        params:{
                            recordId:recordId,//path中的参数,在router中
                        }
                    })
                    break;
                }
            }
        }

        return {
            records,          
            open_record_content,  
        }
    }
}
</script>

<style scoped>
img.record-user-photo {
    width: 5vh;
    border-radius: 50% ;
}
</style>

Gamemap.js

    //获取键盘输入
    add_listening_events(){
       
        if(this.store.state.record.is_record){//record
            let k = 0 ;//第k步
            const [snake0, snake1] = this.snakes ;
            const a_steps = this.store.state.record.a_steps ;
            const b_steps = this.store.state.record.b_steps ;
            console.log(this.store.state.record) ;
            const loser = this.store.state.record.record_loser ;

            const interval_id = setInterval(()=>{
                if(k >= a_steps.length - 1){
                    if(loser === "all" || loser === "A"){
                        snake0.status = "die";
                    }
                    if(loser === "all" || loser === "B"){
                        snake1.status = "die";
                    }  
                    clearInterval(interval_id);
                } else {
                    snake0.set_direction(parseInt(a_steps[k]));
                    snake1.set_direction(parseInt(b_steps[k]));
                }
                k ++ ;
            },300)

        }
        else//PK
        {
            this.ctx.canvas.focus();//cts[DOM] canvas[画板]
            //为画板绑定keydown事件
            this.ctx.canvas.addEventListener("keydown",e => {
                let d = -1;
                if(e.key === "w") d = 0;
                else if(e.key === "d") d = 1;
                else if(e.key === 's') d = 2;
                else if(e.key === 'a') d = 3;
    
                if(d >= 0){
                    this.store.state.pk.socket.send(JSON.stringify({
                        event:"move",
                        direction:d,
                    }))
                }       
            })
        }   
   }

2.4.(重点)实现对战记录的分页功能_前端(后端已经在【*】处实现)

把前端功能加上一个跳转的样式
Bootstrap组件Pagination
放到table外面就可以了
框架

<nav aria-label="..." >
    <ul class="pagination" style="float: right;">
        
        <li class="page-item" @click="click_page(-2)">
            <a class="page-link" href="#" >前一页</a>
        </li>
        
        <li :class="'page-item ' + page.is_active " v-for="page of pages" :key="page.number" @click="click_page(page.number)">
            <a class="page-link" href="#">{{page.number}}</a>
        </li>
 
        <li class="page-item" @click="click_page(-1)">
            <a class="page-link" href="#">后一页</a>
        </li>

    </ul>
</nav>

逻辑

<script>
import ContentField from "@/components/ContentField"
import $ from "jquery"
import { useStore } from "vuex"
import { ref } from "vue"
import router from "@/router/index"
export default {
    components:{
        ContentField
    },
    setup(){
        const store = useStore();
        let total_records = 0 ;
        let current_page = 1 ;
        let records = ref([]);
        let pages = ref([]);
        

        //实现:点击分页,传入page-->加载新页面数据-->更新前端分页数
        //选择第几分页,传page
        const click_page = (page) => {
            
            //只有前一页&&后一页才会用到<<当前页>>
            if(page === -2) page = current_page - 1 ;
            else if(page === -1) page = current_page + 1 ;

            let max_pages = parseInt(Math.ceil(total_records / 10));
            if(page >= 1 && page <= max_pages){
                pull_page(page);//加载一个新分页
            }
        }

        //更新前端的分页显示数量,同时选择的页数激活
        const update_pages = () => {
            let max_pages = parseInt(Math.ceil(total_records / 10));
            let new_pages = [] ;
            for(let i = current_page -2 ; i <= current_page +2 ; i ++ ){
                if(i >=1 && i <= max_pages){
                    new_pages.push({
                        number : i,
                        is_active : i === current_page ? "active" : "",
                    });
                }
            }
            pages.value = new_pages ;
        }

        //接受后端第page分页的数据,同时更新前端的分页显示数量
        const pull_page = page =>{
            console.log(total_records,current_page);
            current_page = page ;
            $.ajax({
                url:"http://127.0.0.1:3000/record/getlist/",
                type:"get",
                data:{
                    page,
                },
                headers:{
                  Authorization:"Bearer " + store.state.user.token,
                },
                success(resp){
                    records.value = resp.records ;
                    total_records = resp.records_count ;
                    update_pages();//更新前端的小方块页数
                },
                error(resp){
                    console.log(resp)
                }
            })
        }

        pull_page(current_page);
        
	    return {
            pages,
            click_page,
        }
    }
}
</script>

在这里插入图片描述

2.5.后端排行榜的分页功能(后端+前端)

5.1后端

玩家天梯分一共两项
接口
在这里插入图片描述
实现接口
在这里插入图片描述
控制器
在这里插入图片描述
前端

<template>
    <ContentField>
        <table class="table table-striped table-hover" style="text-align:center;">
            <!-- 表头 -->
            <thead>
                <tr>
                    <th>玩家</th>
                    <th>天梯分数排名</th>
                </tr>
            </thead>
            <!-- 表身 -->
            <tbody>
                <tr v-for="user in users" :key="user.id">
                     <td>
                        <img :src="user.photo" alt="" class="record-user-photo">
                        &nbsp;
                        <span class="record-user-username">{{ user.username }}</span>
                     </td>
                     <td>
                        {{user.rating}}
                     </td>
                </tr>
            </tbody>
        </table>
        <nav aria-label="..." >
            <ul class="pagination" style="float: right;">
                
                <li class="page-item" @click="click_page(-2)">
                    <a class="page-link" href="#" >前一页</a>
                </li>
                
                <li :class="'page-item ' + page.is_active " v-for="page of pages" :key="page.number" @click="click_page(page.number)">
                    <a class="page-link" href="#">{{page.number}}</a>
                </li>
         
                <li class="page-item" @click="click_page(-1)">
                    <a class="page-link" href="#">后一页</a>
                </li>

            </ul>
        </nav>
    </ContentField>
</template>

<script>
import ContentField from "@/components/ContentField"
import $ from "jquery"
import { useStore } from "vuex"
import { ref } from "vue"
export default {
    components:{
        ContentField
    },
    setup(){
        const store = useStore();
        let total_users = 0 ;
        let current_page = 1 ;
        let users = ref([]);
        let pages = ref([]);
        

        //实现:点击分页,传入page-->加载新页面数据-->更新前端分页数
        //选择第几分页,传page
        const click_page = (page) => {
            
            //只有前一页&&后一页才会用到<<当前页>>
            if(page === -2) page = current_page - 1 ;
            else if(page === -1) page = current_page + 1 ;

            let max_pages = parseInt(Math.ceil(total_users / 3));
            if(page >= 1 && page <= max_pages){
                pull_page(page);//加载一个新分页
            }
        }

        //更新前端的分页显示数量,同时选择的页数激活
        const update_pages = () => {
            let max_pages = parseInt(Math.ceil(total_users / 3));
            let new_pages = [] ;
            for(let i = current_page -2 ; i <= current_page +2 ; i ++ ){
                if(i >=1 && i <= max_pages){
                    new_pages.push({
                        number : i,
                        is_active : i === current_page ? "active" : "",
                    });
                }
            }
            pages.value = new_pages ;
        }

        //接受后端第page分页的数据,同时更新前端的分页显示数量
        const pull_page = page =>{
            current_page = page ;
            $.ajax({
                url:"http://127.0.0.1:3000/ranklist/getlist/",
                type:"get",
                data:{
                    page:page,
                },
                headers:{
                  Authorization:"Bearer " + store.state.user.token,
                },
                success(resp){
                    users.value = resp.users ;
                    total_users = resp.users_count ;
                    update_pages();//更新前端的小方块页数
                },
                error(resp){
                    console.log(resp)
                }
            })
        }

        pull_page(current_page);


        return {
            users,          
            pages,
            click_page,
        }
    }
}
</script>

<style scoped>
img.record-user-photo {
    width: 5vh;
    border-radius: 50% ;
}
</style>

2.6.给Bot数量加上限制

在这里插入图片描述

 QueryWrapper<Bot> queryWrapper = new QueryWrapper<>();
 queryWrapper.eq("user_id",user.getId());
 if(botMapper.selectCount(queryWrapper)>=10){
     map.put("error_message","每个用户最多创建10个Bot!");
     return map;
 }
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿斯卡码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值