调整后端接口,添加帖子详细信息接口,配置详细信息路由
This commit is contained in:
parent
40c652cfa1
commit
5a28eaf90a
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,4 @@ public class Topic {
|
|||||||
Integer type;
|
Integer type;
|
||||||
Date time;
|
Date time;
|
||||||
Integer uid;
|
Integer uid;
|
||||||
String username;
|
|
||||||
String avatar;
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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',
|
||||||
|
36
my-project-frontend/src/views/forum/TopicDetail.vue
Normal file
36
my-project-frontend/src/views/forum/TopicDetail.vue
Normal 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>
|
@ -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}`"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user