豆宝社区项目实战教程简介
本项目实战教程配有免费视频教程,配套代码完全开源。手把手从零开始搭建一个目前应用最广泛的Springboot+Vue前后端分离多用户社区项目。本项目难度适中,为便于大家学习,每一集视频教程对应在Github上的每一次提交。
项目首页截图
代码开源地址
视频教程地址
前端技术栈
Vue Vuex Vue Router Axios Bulma Buefy Element Vditor DarkReader
后端技术栈
Spring Boot Mysql Mybatis MyBatis-Plus Spring Security JWT Lombok
作者详情前端
API
src\api创建follow.js
import request from '@/utils/request'
// 关注
export function follow(id) {
return request(({
url: `/relationship/subscribe/${id}`,
method: 'get'
}))
}
// 取消关注
export function unFollow(id) {
return request(({
url: `/relationship/unsubscribe/${id}`,
method: 'get'
}))
}
// 验证是否关注
export function hasFollow(topicUserId) {
return request(({
url: `/relationship/validate/${topicUserId}`,
method: 'get'
}))
}
Author.vue
src\views\post\新增Author.vue
<template>
<section id="author">
<el-card class="" shadow="never">
<div slot="header">
<span class="has-text-weight-bold">👨💻 关于作者</span>
</div>
<div class="has-text-centered">
<p class="is-size-5 mb-5">
<router-link :to="{ path: `/member/${user.username}/home` }">
{{ user.alias }} <span class="is-size-7 has-text-grey">{{ '@' + user.username }}</span>
</router-link>
</p>
<div class="columns is-mobile">
<div class="column is-half">
<code>{{ user.topicCount }}</code>
<p>文章</p>
</div>
<div class="column is-half">
<code>{{ user.followerCount }}</code>
<p>粉丝</p>
</div>
</div>
<div>
<button
v-if="hasFollow"
class="button is-success button-center is-fullwidth"
@click="handleUnFollow(user.id)"
>
已关注
</button>
<button v-else class="button is-link button-center is-fullwidth" @click="handleFollow(user.id)">
关注
</button>
</div>
</div>
</el-card>
</section>
</template>
<script>
import { follow, hasFollow, unFollow } from '@/api/follow'
import { mapGetters } from 'vuex'
export default {
name: 'Author',
props: {
user: {
type: Object,
default: null
}
},
data() {
return {
hasFollow: false
}
},
mounted() {
this.fetchInfo()
},
computed: {
...mapGetters([
'token'
])
},
methods: {
fetchInfo() {
if(this.token != null && this.token !== '')
{
hasFollow(this.user.id).then(value => {
const { data } = value
this.hasFollow = data.hasFollow
})
}
},
handleFollow: function(id) {
if(this.token != null && this.token !== '')
{
follow(id).then(response => {
const { message } = response
this.$message.success(message)
this.hasFollow = !this.hasFollow
this.user.followerCount = parseInt(this.user.followerCount) + 1
})
}
else{
this.$message.success('请先登录')
}
},
handleUnFollow: function(id) {
unFollow(id).then(response => {
const { message } = response
this.$message.success(message)
this.hasFollow = !this.hasFollow
this.user.followerCount = parseInt(this.user.followerCount) - 1
})
}
}
}
</script>
<style scoped>
</style>
修改Detail.vue
src\views\post\Detail.vue
作者详情后端
BmsFollowService
@Service
public class BmsFollowService extends ServiceImpl<BmsFollowMapper, BmsFollow> {
}
BmsFollowController
package com.notepad.blog.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.notepad.blog.common.api.ApiResult;
import com.notepad.blog.common.exception.ApiAsserts;
import com.notepad.blog.domain.BmsFollow;
import com.notepad.blog.domain.UmsUser;
import com.notepad.blog.service.BmsFollowService;
import com.notepad.blog.service.UmsUserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName BmsFollowMapper
* @description:
* @author: 一粒麦子
* @Date 2021/2/13 20:00
**/
@RestController
@RequestMapping("/relationship")
public class BmsFollowController{
@Resource
private BmsFollowService bmsFollowService;
@Resource
private UmsUserService umsUserService;
/**
* 关注
* @param userName
* @param parentId
* @return
*/
@GetMapping("/subscribe/{userId}")
public ApiResult<Object> handleFollow(
@RequestHeader(value = "userName") String userName
, @PathVariable("userId") String parentId) {
UmsUser umsUser = umsUserService.getOne(new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername,userName));
if (parentId.equals(umsUser.getId())) {
ApiAsserts.fail("您脸皮太厚了,怎么可以关注自己呢 😮");
}
BmsFollow one = bmsFollowService.getOne(
new LambdaQueryWrapper<BmsFollow>()
.eq(BmsFollow::getParentId, parentId)
.eq(BmsFollow::getFollowerId, umsUser.getId()));
if (!ObjectUtils.isEmpty(one)) {
ApiAsserts.fail("已关注");
}
BmsFollow follow = new BmsFollow();
follow.setParentId(parentId);
follow.setFollowerId(umsUser.getId());
bmsFollowService.save(follow);
return ApiResult.success(null, "关注成功");
}
/**
* 取消关注
* @param userName
* @param parentId
* @return
*/
@GetMapping("/unsubscribe/{userId}")
public ApiResult<Object> handleUnFollow(@RequestHeader(value = "userName") String userName
, @PathVariable("userId") String parentId) {
UmsUser umsUser = umsUserService.getOne(new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername,userName));
BmsFollow one = bmsFollowService.getOne(
new LambdaQueryWrapper<BmsFollow>()
.eq(BmsFollow::getParentId, parentId)
.eq(BmsFollow::getFollowerId, umsUser.getId()));
if (ObjectUtils.isEmpty(one)) {
ApiAsserts.fail("未关注!");
}
bmsFollowService.remove(new LambdaQueryWrapper<BmsFollow>().eq(BmsFollow::getParentId, parentId)
.eq(BmsFollow::getFollowerId, umsUser.getId()));
return ApiResult.success(null, "成功取消关注");
}
/**
* 验证是否关注
* @param userName
* @param topicUserId
* @return
*/
@GetMapping("/validate/{topicUserId}")
public ApiResult<Map<String, Object>> isFollow(@RequestHeader(value = "userName") String userName
, @PathVariable("topicUserId") String topicUserId) {
UmsUser umsUser = umsUserService.getOne(new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername,userName));
Map<String, Object> map = new HashMap<>(16);
map.put("hasFollow", false);
if (!ObjectUtils.isEmpty(umsUser)) {
BmsFollow one = bmsFollowService.getOne(new LambdaQueryWrapper<BmsFollow>()
.eq(BmsFollow::getParentId, topicUserId)
.eq(BmsFollow::getFollowerId, umsUser.getId()));
if (!ObjectUtils.isEmpty(one)) {
map.put("hasFollow", true);
}
}
return ApiResult.success(map);
}
}