浪花 - 主页开发

一、简易版主页

1. 主页展示用户列表

<template>
  <!--推荐用户列表-->
  <van-card
      v-for="user in userList"
      :desc="user.profile"
      :title="`${user.username}(${user.planetCode})`"
      :thumb="user.avatarUrl"
  >
    <template #tags>
      <van-tag plain type="danger" v-for="tag in user.tags" style="margin-right: 5px; margin-top: 3px">
        {{ tag }}
      </van-tag>
    </template>
    <template #footer>
      <van-button size="small">联系我</van-button>
    </template>
  </van-card>
  <van-empty v-if="!userList || userList.length < 1" description="结果为空" />

</template>

<script setup lang="ts">
import { useRoute } from'vue-router'
import {onMounted, ref} from "vue";
import myAxios from "/src/plugins/myAxios.js";
import qs from 'qs';
import type {UserType} from "@/models/user";


const route = useRoute();
const { tags } = route.query;
const userList = ref([]);

onMounted( async () => {
  const userListData: UserType[] = await myAxios.get('/user/recommend', {
    params: {
      tagNameList: tags,
    },
    paramsSerializer: params => {
      return qs.stringify(params, {indices: false})
    }
  })
      .then(function (response) {
        // handle success
        console.log("/user/recommend succeed", response);
        return response.data;
      })
      .catch(function (error) {
        console.log("/user/recommend error", error);
      })
  if(userListData) {
    userListData.forEach(user => {
      if(user.tags) {
        user.tags = JSON.parse(user.tags);
      }
    });
    userList.value = userListData;
  }
})


</script>

<style scoped>

</style>

2. 抽取卡片组件

  • 搜索结果页和主页的组件重复,提取出 Card 卡片组件,方便后续统一修改样式等操作
  • 注意抽取出来的组件用到了哪些变量:将变量转化为属性,再通过父组件传递属性
  • 使用 withDefaults 添加默认值:增强健壮性(userList 为空时也能正常运行)
<template>
  <!--推荐用户列表-->
  <user-card-list :user-list="userList"/>
  <van-empty v-if="!userList || userList.length < 1" description="结果为空" />
</template>
<template>
  <!--推荐用户列表-->
  <van-card
      v-for="user in userList"
      :desc="user.profile"
      :title="`${user.username}(${user.planetCode})`"
      :thumb="user.avatarUrl"
  >
    <template #tags>
      <van-tag plain type="danger" v-for="tag in user.tags" style="margin-right: 5px; margin-top: 3px">
        {{ tag }}
      </van-tag>
    </template>
    <template #footer>
      <van-button size="small">联系我</van-button>
    </template>
  </van-card>
</template>

<script setup lang="ts">
import type {UserType} from "@/models/user";

interface UserCardListProps {
  userList: UserType[];
}

const props = withDefaults(defineProps<UserCardListProps>(), {
  userList: [],
})

</script>

<style scoped>

</style>

3. 后端返回所有用户

    /**
     * 用户推荐
     * @param request
     * @return 用户列表
     */
    @GetMapping("/recommend")
    public BaseResponse<List<User>> recommendUsers(HttpServletRequest request) {
        log.info("推荐用户列表");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        List<User> userList = userService.list(queryWrapper);// 查询所有用户

        // 返回脱敏后的用户数据
        List<User> list = userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
        return ResultUtils.success(list);
    }

4. 查看页面效果

二、批量导入数据的几种方式

1. 用可视化界面

  • 适合一次性导入、数据量可控

2. 执行 SQL 语句

  • 适用于数据量较小的

3. 编写程序控制导入

  • for 循环,建议分批,要保证可控、幂等性,注意线上数据库和测试数据库有区别
  • 一次性任务:在程序入口使用 @EnableScheduling 开启 SpringBoot 框架对定时任务的支持
  • 执行任务
    • 不能使用 main 方法来执行:SpringBoot 还没有加载这个 Bean,会报空指针异常
    • 使用定时任务框架:在定时任务上使用 @Scheduled 注解设置定时
    • initialDelay:第一次延时执行
    • fixedRate:下一次执行任务的延时时间(设置为 Long.MAX_VALUE 可视为只执行一次)
package com.example.usercenter.once;

import com.example.usercenter.mapper.UserMapper;
import com.example.usercenter.model.domain.User;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;

/**
 * 导入用户任务
 * @author Ghost
 * @version 1.0
 */
@Component
public class InsertUsers {

    @Resource
    private UserMapper userMapper;

    /**
     * 批量插入用户
     */
//    @Scheduled(initialDelay = 5000, fixedRate = Long.MAX_VALUE)
    public void doInsertUsers() {
        StopWatch stopWatch = new StopWatch();
        System.out.println("goodgoodgood");
        stopWatch.start();
        final int INSERT_NUM = 1000;
        for (int i = 0; i < INSERT_NUM; i++) {
            User user = new User();
            user.setUsername("假用户");
            user.setUserAccount("fakeghost");
            user.setAvatarUrl("https://636f-codenav-8grj8px727565176-1256524210.tcb.qcloud.la/img/logo.png");
            user.setGender(0);
            user.setUserPassword("12345678");
            user.setPhone("123");
            user.setEmail("123@qq.com");
            user.setTags("[]");
            user.setUserStatus(0);
            user.setUserRole(0);
            user.setPlanetCode("11111111");
            userMapper.insert(user);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }
}
  • for 循环的缺点
    • 建立和释放数据库连接==>>批量插入解决
    • for 循环是绝对线性的==>>并发执行
  • 批量插入用户:使用 Mybatis-plus 提供的批量插入 saveBatch 方法,现将要插入的用户存储到一个集合里,再将集合批量插入到数据库
package com.example.usercenter.once;

import com.example.usercenter.model.domain.User;
import com.example.usercenter.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 导入用户任务
 * @author Ghost
 * @version 1.0
 */
@Component
public class InsertUsers {

    @Resource
    private UserService userService;

    /**
     * 批量插入用户
     */
    public void doInsertUsers() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        final int INSERT_NUM = 1000;
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < INSERT_NUM; i++) {
            User user = new User();
            user.setUsername("假用户");
            user.setUserAccount("fakeghost");
            user.setAvatarUrl("https://636f-codenav-8grj8px727565176-1256524210.tcb.qcloud.la/img/logo.png");
            user.setGender(0);
            user.setUserPassword("12345678");
            user.setPhone("123");
            user.setEmail("123@qq.com");
            user.setTags("[]");
            user.setUserStatus(0);
            user.setUserRole(0);
            user.setPlanetCode("11111111");
            userList.add(user);
        }
        // 20 秒 10 万条
        userService.saveBatch(userList, 10000);
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }
}
  • 并发执行:将数据分成多组,开启多线程并发执行 
    • 注意:插入数据的顺序对应用没有影响才使用并发;不要用不支持并发的集合
    /**
     * 并发批量插入用户
     */
    @Test
    public void doConcurrencyInsertUsersTest() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 分十组
        int batchSize = 5000;
        int j = 0;
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            List<User> userList = new ArrayList<>();
            while (true) {
                j++;
                User user = new User();
                user.setUsername("假用户");
                user.setUserAccount("fakeghost");
                user.setAvatarUrl("https://636f-codenav-8grj8px727565176-1256524210.tcb.qcloud.la/img/logo.png");
                user.setGender(0);
                user.setUserPassword("12345678");
                user.setPhone("123");
                user.setEmail("123@qq.com");
                user.setTags("[]");
                user.setUserStatus(0);
                user.setUserRole(0);
                user.setPlanetCode("11111111");
                userList.add(user);
                if (j % batchSize == 0) {
                    break;
                }
            }
            // 异步执行
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                System.out.println("threadName: " + Thread.currentThread().getName());
                userService.saveBatch(userList, batchSize);
            }, executorService);
            futureList.add(future);
        }
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[]{})).join();
        // 20 秒 10 万条
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }

三、分页查询

1. 开启分页

  • 接收前端传来的分页数量和每页展示数目
    /**
     * 用户推荐
     * @param request
     * @return 用户列表
     */
    @GetMapping("/recommend")
    public BaseResponse<Page<User>> recommendUsers(long pageSize, long pageNum, HttpServletRequest request) {
        log.info("推荐用户列表");
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Page<User> userList = userService.page(new Page<>((pageNum - 1) * pageSize, pageSize), queryWrapper);// 查询所有用户
        return ResultUtils.success(userList);
    }

2. 开启 Mybatis-plus 分页配置

/**
 * MyBatisPlus 配置
 * @author 乐小鑫
 * @version 1.0
 */
@Configuration
@MapperScan("com.example.usercenter.mapper")
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循 mybatis 的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

四、查看页面效果

  • 开启分页后后端响应封装到了 Page 中,前端接收时需要取出 response 中的 records

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值