调整后端接口,添加帖子详细信息接口,配置详细信息路由

This commit is contained in:
柏码の讲师 2023-10-21 23:02:33 +08:00
parent 40c652cfa1
commit 5a28eaf90a
11 changed files with 147 additions and 31 deletions

View File

@ -1,11 +1,13 @@
package com.example.config; package com.example.config;
import com.baomidou.mybatisplus.annotation.DbType;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
/** /**
* 一般Web服务相关配置 * 一般Web服务相关配置
@ -22,4 +24,11 @@ public class WebConfiguration implements WebMvcConfigurer {
public PasswordEncoder passwordEncoder(){ public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
@Bean
public PaginationInnerInterceptor paginationInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(100L);
return paginationInnerInterceptor;
}
} }

View File

@ -2,10 +2,7 @@ package com.example.controller;
import com.example.entity.RestBean; import com.example.entity.RestBean;
import com.example.entity.vo.request.TopicCreateVO; import com.example.entity.vo.request.TopicCreateVO;
import com.example.entity.vo.response.TopicPreviewVO; import com.example.entity.vo.response.*;
import com.example.entity.vo.response.TopicTopVO;
import com.example.entity.vo.response.TopicTypeVO;
import com.example.entity.vo.response.WeatherVO;
import com.example.service.TopicService; import com.example.service.TopicService;
import com.example.service.WeatherService; import com.example.service.WeatherService;
import com.example.utils.Const; import com.example.utils.Const;
@ -13,11 +10,13 @@ import com.example.utils.ControllerUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
//论坛相关接口都在这里 //论坛相关接口都在这里
@Validated
@RestController @RestController
@RequestMapping("/api/forum") @RequestMapping("/api/forum")
public class ForumController { public class ForumController {
@ -56,11 +55,16 @@ public class ForumController {
@GetMapping("/list-topic") @GetMapping("/list-topic")
public RestBean<List<TopicPreviewVO>> listTopic(@RequestParam @Min(0) int page, public RestBean<List<TopicPreviewVO>> listTopic(@RequestParam @Min(0) int page,
@RequestParam @Min(0) int type) { @RequestParam @Min(0) int type) {
return RestBean.success(topicService.listTopicByPage(page, type)); return RestBean.success(topicService.listTopicByPage(page + 1, type));
} }
@GetMapping("/top-topic") @GetMapping("/top-topic")
public RestBean<List<TopicTopVO>> topTopic() { public RestBean<List<TopicTopVO>> topTopic() {
return RestBean.success(topicService.topTopics()); return RestBean.success(topicService.topTopics());
} }
@GetMapping("topic")
public RestBean<TopicDetailVO> topic(@RequestParam @Min(0) int tid){
return RestBean.success(topicService.getTopic(tid));
}
} }

View File

@ -6,6 +6,10 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.example.entity.BaseData; import com.example.entity.BaseData;
import lombok.Data; import lombok.Data;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@Data @Data
@TableName("db_account_privacy") @TableName("db_account_privacy")
public class AccountPrivacy implements BaseData { public class AccountPrivacy implements BaseData {
@ -16,4 +20,16 @@ public class AccountPrivacy implements BaseData {
boolean wx = true; boolean wx = true;
boolean qq = true; boolean qq = true;
boolean gender = true; boolean gender = true;
public String[] hiddenFields(){
List<String> strings = new ArrayList<>();
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
try {
if(field.getType().equals(boolean.class) && !field.getBoolean(this))
strings.add(field.getName());
} catch (Exception ignored) {}
}
return strings.toArray(String[]::new);
}
} }

View File

@ -17,6 +17,4 @@ public class Topic {
Integer type; Integer type;
Date time; Date time;
Integer uid; Integer uid;
String username;
String avatar;
} }

View File

@ -0,0 +1,28 @@
package com.example.entity.vo.response;
import lombok.Data;
import java.util.Date;
@Data
public class TopicDetailVO {
Integer id;
String title;
String content;
Integer type;
Date time;
User user;
@Data
public static class User {
Integer uid;
String username;
String avatar;
String desc;
boolean gender;
String qq;
String wx;
String phone;
String email;
}
}

View File

@ -3,22 +3,7 @@ package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.dto.Topic; import com.example.entity.dto.Topic;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper @Mapper
public interface TopicMapper extends BaseMapper<Topic> { public interface TopicMapper extends BaseMapper<Topic> {
@Select("""
select * from db_topic left join db_account on uid = db_account.id
order by `time` desc limit ${page}, 10
""")
List<Topic> topicList(int page);
@Select("""
select * from db_topic left join db_account on uid = db_account.id
where type = #{type}
order by `time` desc limit ${page}, 10
""")
List<Topic> topicListByType(int page, int type);
} }

View File

@ -6,6 +6,7 @@ import com.example.entity.dto.TopicType;
import com.example.entity.vo.request.TopicCreateVO; import com.example.entity.vo.request.TopicCreateVO;
import com.example.entity.vo.response.TopicPreviewVO; import com.example.entity.vo.response.TopicPreviewVO;
import com.example.entity.vo.response.TopicTopVO; import com.example.entity.vo.response.TopicTopVO;
import com.example.entity.vo.response.TopicDetailVO;
import java.util.List; import java.util.List;
@ -14,4 +15,5 @@ public interface TopicService extends IService<Topic> {
String createTopic(int uid, TopicCreateVO vo); String createTopic(int uid, TopicCreateVO vo);
List<TopicPreviewVO> listTopicByPage(int pageNumber, int type); List<TopicPreviewVO> listTopicByPage(int pageNumber, int type);
List<TopicTopVO> topTopics(); List<TopicTopVO> topTopics();
TopicDetailVO getTopic(int tid);
} }

View File

@ -3,14 +3,14 @@ package com.example.service.impl;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.dto.Topic; import com.example.entity.dto.*;
import com.example.entity.dto.TopicType;
import com.example.entity.vo.request.TopicCreateVO; import com.example.entity.vo.request.TopicCreateVO;
import com.example.entity.vo.response.TopicDetailVO;
import com.example.entity.vo.response.TopicPreviewVO; import com.example.entity.vo.response.TopicPreviewVO;
import com.example.entity.vo.response.TopicTopVO; import com.example.entity.vo.response.TopicTopVO;
import com.example.mapper.TopicMapper; import com.example.mapper.*;
import com.example.mapper.TopicTypeMapper;
import com.example.service.TopicService; import com.example.service.TopicService;
import com.example.utils.CacheUtils; import com.example.utils.CacheUtils;
import com.example.utils.Const; import com.example.utils.Const;
@ -27,6 +27,15 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
@Resource @Resource
TopicTypeMapper typeMapper; TopicTypeMapper typeMapper;
@Resource
AccountMapper accountMapper;
@Resource
AccountDetailsMapper accountDetailsMapper;
@Resource
AccountPrivacyMapper accountPrivacyMapper;
@Resource @Resource
FlowUtils flowUtils; FlowUtils flowUtils;
@ -63,11 +72,12 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
List<TopicPreviewVO> list = cacheUtils.takeListFromCache(key, TopicPreviewVO.class); List<TopicPreviewVO> list = cacheUtils.takeListFromCache(key, TopicPreviewVO.class);
if(list != null) return list; if(list != null) return list;
//无缓存从数据库读取 //无缓存从数据库读取
List<Topic> topics; Page<Topic> page = Page.of(pageNumber, 10);
if(type == 0) if(type == 0)
topics = baseMapper.topicList(pageNumber * 10); baseMapper.selectPage(page, Wrappers.<Topic>query().orderByDesc("time"));
else else
topics = baseMapper.topicListByType(pageNumber * 10, type); baseMapper.selectPage(page, Wrappers.<Topic>query().eq("type", type).orderByDesc("time"));
List<Topic> topics = page.getRecords();
if(topics.isEmpty()) return null; if(topics.isEmpty()) return null;
list = topics.stream().map(this::resolveToPreview).toList(); list = topics.stream().map(this::resolveToPreview).toList();
cacheUtils.saveListToCache(key, list, 20); //进缓存 cacheUtils.saveListToCache(key, list, 20); //进缓存
@ -87,8 +97,30 @@ public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements
}).toList(); }).toList();
} }
@Override
public TopicDetailVO getTopic(int tid) {
TopicDetailVO vo = new TopicDetailVO();
Topic topic = baseMapper.selectById(tid);
BeanUtils.copyProperties(topic, vo);
TopicDetailVO.User user = new TopicDetailVO.User();
BeanUtils.copyProperties(topic, user);
vo.setUser(this.fillUserDetailsByPrivacy(user, topic.getUid()));
return vo;
}
private <T> T fillUserDetailsByPrivacy(T target, int uid) {
AccountDetails details = accountDetailsMapper.selectById(uid);
Account account = accountMapper.selectById(uid);
AccountPrivacy privacy = accountPrivacyMapper.selectById(uid);
String[] ignores = privacy.hiddenFields();
BeanUtils.copyProperties(account, target, ignores);
BeanUtils.copyProperties(details, target, ignores);
return target;
}
private TopicPreviewVO resolveToPreview(Topic topic) { private TopicPreviewVO resolveToPreview(Topic topic) {
TopicPreviewVO vo = new TopicPreviewVO(); TopicPreviewVO vo = new TopicPreviewVO();
BeanUtils.copyProperties(accountMapper.selectById(topic.getUid()), vo);
BeanUtils.copyProperties(topic, vo); BeanUtils.copyProperties(topic, vo);
List<String> images = new ArrayList<>(); List<String> images = new ArrayList<>();
StringBuilder previewText = new StringBuilder(); StringBuilder previewText = new StringBuilder();

View File

@ -30,8 +30,12 @@ const router = createRouter({
children: [ children: [
{ {
path: '', path: '',
name: 'post-list', name: 'topics',
component: () => import('@/views/forum/TopicList.vue') component: () => import('@/views/forum/TopicList.vue')
}, {
path: 'post-detail/:tid',
name: 'post-detail',
component: () => import('@/views/forum/TopicDetail.vue')
}, { }, {
path: 'user-setting', path: 'user-setting',
name: 'user-setting', name: 'user-setting',

View File

@ -0,0 +1,36 @@
<script setup>
import {useRoute} from "vue-router";
import {get} from "@/net";
import {reactive} from "vue";
const route = useRoute()
const tid = route.params.tid
const topic = reactive({
data: null,
comments: []
})
get(`/api/forum/topic?tid=${tid}`, data => topic.value = data)
</script>
<template>
<div class="topic-page">
<div class="topic-main">
</div>
</div>
</template>
<style scoped>
.topic-page {
display: flex;
flex-direction: column;
gap: 10px;
}
.topic-main {
display: flex;
border-radius: 7px;
}
</style>

View File

@ -19,6 +19,7 @@ import {ElMessage} from "element-plus";
import TopicEditor from "@/components/TopicEditor.vue"; import TopicEditor from "@/components/TopicEditor.vue";
import {useStore} from "@/store"; import {useStore} from "@/store";
import axios from "axios"; import axios from "axios";
import router from "@/router";
const store = useStore() const store = useStore()
@ -134,7 +135,8 @@ navigator.geolocation.getCurrentPosition(position => {
<transition name="el-fade-in-linear" mode="out-in"> <transition name="el-fade-in-linear" mode="out-in">
<div v-if="topics.list.length"> <div v-if="topics.list.length">
<div v-infinite-scroll="updateList"> <div v-infinite-scroll="updateList">
<light-card v-for="item in topics.list" class="topic-card"> <light-card v-for="item in topics.list" class="topic-card"
@click="router.push('/index/post-detail/' + item.id)">
<div style="display: flex"> <div style="display: flex">
<div> <div>
<el-avatar :size="30" :src="`${axios.defaults.baseURL}/images${item.avatar}`"/> <el-avatar :size="30" :src="`${axios.defaults.baseURL}/images${item.avatar}`"/>