目录
[2. 用户管理](#2. 用户管理)
[编辑 3.任务管理](#编辑 3.任务管理)
[编辑 审核(退回需要写明原因)](#编辑 审核(退回需要写明原因))
[编辑 编辑](#编辑 编辑)
[编辑 编辑](#编辑 编辑)
[编辑 2.首页](#编辑 2.首页)
[编辑 3.任务发布](#编辑 3.任务发布)
[编辑 7.消息中心](#编辑 7.消息中心)
[三、 项目实现简述](#三、 项目实现简述)
[编辑 部分实现源码:](#编辑 部分实现源码:)
一、项目介绍
前后端分离实现
- 项目分为三个部分:springboot后端、管理后台和用户平台pc端;
- 后端采用springboot+redis+mybatisplus+mysql+JWT token;
- 管理后台采用vue+elementui,用户平台端同样,框架类似,很好的框架、适合新手、老手,都可以,没有复杂错乱的结构。
二、项目截图
管理后台
1.登录(默认管理员账号密码均为:admin)
2. 用户管理
分为学生管理、教师管理,同表设计,界面差不多
3.任务管理
互助单(学生发布)
行政单(教师发布)
审核(退回需要写明原因)
4.管理员管理
5.个人信息、修改密码
用户端
1.登录
2.首页
可以对任务进行接取,搜索任务等
可评论
3.任务发布
互助单发布
行政单差发布
4.个人中心
5.我的发布
6.我的接取
7.消息中心
各种实时消息通知
三、 项目实现简述
1.项目需求、要求文档
2.后端项目
idea开发
部分实现源码:
java
package com.product.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
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.product.entity.*;
import com.product.entity.base.PageQuery;
import com.product.entity.base.Result;
import com.product.entity.base.ResultPage;
import com.product.entity.vo.CommentVO;
import com.product.entity.vo.HelpTaskVO;
import com.product.enumerate.*;
import com.product.mapper.CommentMapper;
import com.product.mapper.HelpTaskMapper;
import com.product.mapper.RecruitRecordMapper;
import com.product.param.HelpTaskParam;
import com.product.service.*;
import com.product.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
public class HelpTaskServiceImpl extends ServiceImpl<HelpTaskMapper, HelpTask> implements HelpTaskService {
@Resource
private FileUploadService fileUploadService;
@Resource
private UserService userService;
@Resource
private RecruitRecordMapper recruitRecordMapper;
@Resource
private CommentMapper commentMapper;
@Resource
private MsgRecordService msgRecordService;
@Resource
private RecruitRecordService recruitRecordService;
/**
* 添加或更新
*/
@Override
public Result<?> add(Integer userId, HelpTaskParam param) {
if (param.getRecruitStartTime().isAfter(param.getRecruitEndTime())) {
return Result.failMsg("招募结束时间不能小于开始时间");
}
User user = userService.getUserById(userId);
if (Objects.equals(UserType.TEACHER.getValue(), user.getUserType()) && Objects.equals(param.getPublishType(), PublishType.MUTUAL_AID.getValue())) {
return Result.failMsg("教师不可发布任务单");
}
if (Objects.equals(UserType.STUDENT.getValue(), user.getUserType()) && Objects.equals(param.getPublishType(), PublishType.ADMINISTRATION.getValue())) {
return Result.failMsg("学生不可发布行政单");
}
if (param.getFile() != null) {
String coverPath = fileUploadService.uploadImage(param.getFile());
param.setCoverPath(coverPath);
}
LocalDateTime now = LocalDateTime.now();
param.setUpdateTime(now);
if (param.getId() == null) {//添加
param.setUserId(userId);
param.setCreateTime(now);
param.setAuditStatus(AuditStatus.UNDER_REVIEW.getValue());
param.setTaskStatus(HelpTaskStatus.TO_BE_RECRUITED.getValue());
save(param);
} else {//
param.setAuditStatus(AuditStatus.UNDER_REVIEW.getValue());
param.setRejectRemark("");
HelpTask helpTask = getById(param.getId());
if (Objects.equals(helpTask.getTaskStatus(), HelpTaskStatus.TASK_FAIL.getValue())
|| Objects.equals(helpTask.getTaskStatus(), HelpTaskStatus.TASK_FINISHED.getValue())) {
return Result.failMsg("任务状态不允许修改");
}
updateById(param);
}
return Result.OKMsg("保存成功,审核中~");
}
/**
* 分页
*/
@Override
public ResultPage<HelpTaskVO> getHomePage(PageQuery pageQuery) {
Page<HelpTask> page;
LambdaQueryWrapper<HelpTask> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(pageQuery.getKeyWord())) {
queryWrapper.and(i->i.like(HelpTask::getName, pageQuery.getKeyWord()).or().like(HelpTask::getContent, pageQuery.getKeyWord()));
}
if (pageQuery.getPublishType() != null) {
queryWrapper.eq(HelpTask::getPublishType, pageQuery.getPublishType());
}
if (pageQuery.getTaskType() != null) {
queryWrapper.eq(HelpTask::getType, pageQuery.getTaskType());
}
queryWrapper.eq(HelpTask::getAuditStatus, AuditStatus.PUBLISHED.getValue());
queryWrapper.in(HelpTask::getTaskStatus, Arrays.asList(
HelpTaskStatus.TO_BE_RECRUITED.getValue(),
HelpTaskStatus.RECRUITMENT_IN_PROGRESS.getValue(),
HelpTaskStatus.TASK_IN_PROGRESS.getValue()
)
);
queryWrapper.orderByDesc(HelpTask::getCreateTime);
page = page(pageQuery.build(), queryWrapper);
List<HelpTaskVO> helpTaskVOList = new ArrayList<>();
if (CollectionUtil.isNotEmpty(page.getRecords())) {
page.getRecords().forEach(item -> {
HelpTaskVO helpTaskVO = new HelpTaskVO();
BeanUtil.copyProperties(item, helpTaskVO);
this.fixOtherInfo(helpTaskVO, false);
helpTaskVOList.add(helpTaskVO);
});
}
return ResultPage.OK(page.getTotal(), page.getCurrent(), page.getSize(), helpTaskVOList);
}
/**
* 分页
*/
@Override
public ResultPage<HelpTaskVO> getAdminPage(PageQuery pageQuery) {
Page<HelpTask> page;
LambdaQueryWrapper<HelpTask> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(pageQuery.getKeyWord())) {
queryWrapper.like(HelpTask::getName, pageQuery.getKeyWord());
}
if (pageQuery.getPublishType() != null) {
queryWrapper.eq(HelpTask::getPublishType, pageQuery.getPublishType());
}
if (pageQuery.getTaskType() != null) {
queryWrapper.eq(HelpTask::getType, pageQuery.getTaskType());
}
if (pageQuery.getAuditStatus() != null) {
queryWrapper.eq(HelpTask::getAuditStatus, pageQuery.getAuditStatus());
}
if (pageQuery.getTaskStatus() != null) {
queryWrapper.eq(HelpTask::getTaskStatus, pageQuery.getTaskStatus());
}
queryWrapper.orderByDesc(HelpTask::getCreateTime);
page = page(pageQuery.build(), queryWrapper);
List<HelpTaskVO> helpTaskVOList = new ArrayList<>();
if (CollectionUtil.isNotEmpty(page.getRecords())) {
page.getRecords().forEach(item -> {
HelpTaskVO helpTaskVO = new HelpTaskVO();
BeanUtil.copyProperties(item, helpTaskVO);
this.fixOtherInfo(helpTaskVO, false);
helpTaskVOList.add(helpTaskVO);
});
}
return ResultPage.OK(page.getTotal(), page.getCurrent(), page.getSize(), helpTaskVOList);
}
/**
* 分页
*/
@Override
public ResultPage<HelpTaskVO> getMyPublishPage(PageQuery pageQuery) {
Page<HelpTask> page;
LambdaQueryWrapper<HelpTask> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(HelpTask::getUserId, JwtUtil.getUserId());
if (StringUtils.isNotBlank(pageQuery.getKeyWord())) {
queryWrapper.like(HelpTask::getName, pageQuery.getKeyWord());
}
if (pageQuery.getPublishType() != null) {
queryWrapper.eq(HelpTask::getPublishType, pageQuery.getPublishType());
}
if (pageQuery.getTaskType() != null) {
queryWrapper.eq(HelpTask::getType, pageQuery.getTaskType());
}
if (pageQuery.getAuditStatus() != null) {
queryWrapper.eq(HelpTask::getAuditStatus, pageQuery.getAuditStatus());
}
if (pageQuery.getTaskStatus() != null) {
queryWrapper.eq(HelpTask::getTaskStatus, pageQuery.getTaskStatus());
}
queryWrapper.orderByDesc(HelpTask::getCreateTime);
page = page(pageQuery.build(), queryWrapper);
List<HelpTaskVO> helpTaskVOList = new ArrayList<>();
if (CollectionUtil.isNotEmpty(page.getRecords())) {
page.getRecords().forEach(item -> {
HelpTaskVO helpTaskVO = new HelpTaskVO();
BeanUtil.copyProperties(item, helpTaskVO);
this.fixOtherInfo(helpTaskVO, false);
helpTaskVOList.add(helpTaskVO);
});
}
return ResultPage.OK(page.getTotal(), page.getCurrent(), page.getSize(), helpTaskVOList);
}
@Override
public Result<List<CommentVO>> getCommentList(int helpTaskId) {
List<CommentVO> commentVOList = new ArrayList<>();
List<Comment> commentList = commentMapper.selectList(Wrappers.<Comment>lambdaQuery().eq(Comment::getHelpTaskId, helpTaskId));
if (CollUtil.isNotEmpty(commentList)) {
List<Integer> userIds = commentList.stream().map(Comment::getUserId).collect(Collectors.toList());
userIds.addAll(commentList.stream().map(Comment::getBeReplyUserId).collect(Collectors.toList()));
List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, userIds));
userList.forEach(item -> {
item.setAvatar(fileUploadService.getRealPath(item.getAvatar()));
});
Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, obj -> obj));
commentList.forEach(item -> {
CommentVO commentVO = new CommentVO();
BeanUtil.copyProperties(item, commentVO);
commentVO.setPublishUser(userMap.get(item.getUserId()));
commentVO.setBeReplyUser(userMap.get(item.getBeReplyUserId()));
commentVOList.add(commentVO);
});
}
return Result.OK(commentVOList);
}
@Override
public void fixOtherInfo(HelpTaskVO helpTaskVO, boolean comment) {
helpTaskVO.setPublishTypeText(PublishType.valueOf(helpTaskVO.getPublishType()).getText());
helpTaskVO.setTypeText(HelpTaskType.valueOf(helpTaskVO.getType()).getText());
helpTaskVO.setTaskStatusText(HelpTaskStatus.valueOf(helpTaskVO.getTaskStatus()).getText());
helpTaskVO.setAuditStatusText(AuditStatus.valueOf(helpTaskVO.getAuditStatus()).getText());
helpTaskVO.setCoverPath(fileUploadService.getRealPath(helpTaskVO.getCoverPath()));
User user = userService.getUserById(helpTaskVO.getUserId());
user.setAvatar(fileUploadService.getRealPath(user.getAvatar()));
helpTaskVO.setPublishUser(user);
//招募成员
List<RecruitRecord> recruitRecords = recruitRecordMapper.selectList(Wrappers.<RecruitRecord>lambdaQuery().eq(RecruitRecord::getHelpTaskId, helpTaskVO.getId()));
if (CollUtil.isNotEmpty(recruitRecords)) {
List<Integer> userIds = recruitRecords.stream().map(RecruitRecord::getUserId).collect(Collectors.toList());
List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, userIds));
userList.forEach(item -> {
item.setAvatar(fileUploadService.getRealPath(item.getAvatar()));
});
helpTaskVO.setUserList(userList);
}
int commentNum = Math.toIntExact(commentMapper.selectCount(Wrappers.<Comment>lambdaQuery().eq(Comment::getHelpTaskId, helpTaskVO.getId())));
helpTaskVO.setCommentNum(commentNum);
//评论列表
if (comment) {
List<Comment> commentList = commentMapper.selectList(Wrappers.<Comment>lambdaQuery().eq(Comment::getHelpTaskId, helpTaskVO.getId()));
if (CollUtil.isNotEmpty(commentList)) {
List<CommentVO> commentVOList = new ArrayList<>();
List<Integer> userIds = commentList.stream().map(Comment::getUserId).collect(Collectors.toList());
userIds.addAll(commentList.stream().map(Comment::getBeReplyUserId).collect(Collectors.toList()));
List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, userIds));
userList.forEach(item -> {
item.setAvatar(fileUploadService.getRealPath(item.getAvatar()));
});
Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, obj -> obj));
commentList.forEach(item -> {
CommentVO commentVO = new CommentVO();
BeanUtil.copyProperties(item, commentVO);
commentVO.setPublishUser(userMap.get(item.getUserId()));
commentVO.setBeReplyUser(userMap.get(item.getBeReplyUserId()));
commentVOList.add(commentVO);
});
helpTaskVO.setCommentList(commentVOList);
}
}
}
/**
* 删除
*
* @param userId 用户ID
* @param ids 所操作记录
*/
@Override
public Result<?> del(Integer userId, String ids) {
String[] idsArr = ids.split(",");
List<Long> idsList = new ArrayList<>();
for (String str : idsArr) {
idsList.add(Long.parseLong(str));
}
boolean change = remove(Wrappers.<HelpTask>lambdaQuery().in(HelpTask::getId, idsList));
if (change) {
return Result.OKMsg("删除成功");
} else {
return Result.failMsg("删除失败,请重试");
}
}
/**
* 更新状态
*
* @param userId 用户ID
* @param ids 所操作记录
* @return
*/
@Override
public Result<?> updateAuditStatus(Integer userId, Integer auditStatus, String ids, String rejectRemark) {
String[] idsArr = ids.split(",");
String content = "任务审核状态发生了变化【" + AuditStatus.valueOf(auditStatus).getText() + "】,请及时查看";
for (String str : idsArr) {
lambdaUpdate()
.eq(HelpTask::getId, Integer.parseInt(str))
.set(HelpTask::getAuditStatus, AuditStatus.valueOf(auditStatus).getValue())
.set(HelpTask::getRejectRemark, rejectRemark)
.set(HelpTask::getUpdateSystemAdminId, userId)
.set(HelpTask::getUpdateSystemTime, LocalDateTime.now())
.update();
HelpTask helpTask = getById(Integer.parseInt(str));
// if (!Objects.equals(helpTask.getUserId(), userId)) {
MsgRecord msgRecord = new MsgRecord();
msgRecord.setReceiveUserId(helpTask.getUserId());
msgRecord.setRelId(helpTask.getId());
msgRecord.setStatus(YesOrNo.NO.getValue());
msgRecord.setMsgType(MsgType.HELP_TASK.getValue());
msgRecord.setContent(content);
msgRecordService.add(msgRecord);
// }
}
return Result.OKMsg("操作成功");
}
/**
* 更新状态
*
* @param userId 用户ID
* @param ids 所操作记录
* @return
*/
@Transactional(rollbackFor = Exception.class)
@Override
public Result<?> updateStatus(Integer userId, Integer status, String ids) {
String[] idsArr = ids.split(",");
String content = "任务状态发生了变化【" + HelpTaskStatus.valueOf(status).getText() + "】,请及时查看";
for (String str : idsArr) {
lambdaUpdate()
.eq(HelpTask::getId, Integer.parseInt(str))
.set(HelpTask::getTaskStatus, HelpTaskStatus.valueOf(status).getValue())
.set(HelpTask::getUpdateSystemAdminId, userId)
.set(HelpTask::getUpdateSystemTime, LocalDateTime.now())
.update();
HelpTask helpTask = getById(Integer.parseInt(str));
// if (!Objects.equals(helpTask.getUserId(), userId)) {
MsgRecord msgRecord = new MsgRecord();
msgRecord.setReceiveUserId(helpTask.getUserId());
msgRecord.setRelId(helpTask.getId());
msgRecord.setStatus(YesOrNo.NO.getValue());
msgRecord.setMsgType(MsgType.HELP_TASK.getValue());
msgRecord.setContent(content);
msgRecordService.add(msgRecord);
// }
//任务完成,奖励积分 行政单才有积分
if (Objects.equals(helpTask.getPublishType(), PublishType.ADMINISTRATION.getValue()) && Objects.equals(HelpTaskStatus.TASK_FINISHED, HelpTaskStatus.valueOf(status))) {
//招募成员
List<RecruitRecord> recruitRecords = recruitRecordMapper.selectList(
Wrappers.<RecruitRecord>lambdaQuery()
.eq(RecruitRecord::getHelpTaskId, helpTask.getId())
.eq(RecruitRecord::getAddIntegral, YesOrNo.NO.getValue())
);
if (CollUtil.isNotEmpty(recruitRecords)) {
List<Integer> userIds = recruitRecords.stream().map(RecruitRecord::getUserId).collect(Collectors.toList());
List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, userIds));
userList.forEach(item -> {
Integer integral = item.getIntegral();
if (integral == null) {
integral = 0;
}
integral += helpTask.getIntegral();
userService.lambdaUpdate()
.eq(User::getId, item.getId())
.set(User::getIntegral, integral)
.update();
});
//更新已经清算积分 避免重复状态修改重复增加积分
List<Integer> idList = recruitRecords.stream().map(RecruitRecord::getId).collect(Collectors.toList());
recruitRecordService.lambdaUpdate()
.in(RecruitRecord::getId, idList)
.set(RecruitRecord::getAddIntegral, YesOrNo.YES.getValue())
.update();
}
}
}
return Result.OKMsg("操作成功");
}
/**
* 详情
*
* @param id 主键
*/
@Override
public Result<HelpTaskVO> getDetailById(int id) {
HelpTask helpTask = getById(id);
HelpTaskVO vo = BeanUtil.copyProperties(helpTask, HelpTaskVO.class);
this.fixOtherInfo(vo, true);
return Result.OK(vo);
}
/**
* 评论
*/
@Transactional(rollbackFor = Exception.class)
@Override
public Result<Comment> addComment(Integer userId, Comment comment) {
comment.setUserId(userId);
commentMapper.insert(comment);
MsgRecord msgRecord = new MsgRecord();
msgRecord.setUserId(comment.getUserId());
msgRecord.setBeReplyUserId(comment.getBeReplyUserId());
msgRecord.setReceiveUserId(comment.getBeReplyUserId());
msgRecord.setRelId(comment.getHelpTaskId());
msgRecord.setStatus(YesOrNo.NO.getValue());
msgRecord.setMsgType(MsgType.COMMENT.getValue());
msgRecord.setContent("评论了你,请及时回复~");
msgRecordService.add(msgRecord);
return Result.OK("评论成功", comment);
}
/**
* 评论
*/
@Override
public Result<?> delComment(int id) {
int change = commentMapper.deleteById(id);
return Result.OKMsg("删除成功");
}
}
3.前端vue项目
管理后台前端vue项目:
用户端前端vue项目:
登录页面代码案例:
html
<template>
<div id="login-body">
<div style="width: 100%;height: 100%;overflow: hidden;">
<div class="name">大学校园互助平台</div>
<div class="login-modal">
<div class="title">登录</div>
<el-form class="login-form"
:rules="loginRules"
ref="loginForm"
:model="loginForm"
label-width="0">
<el-form-item prop="username">
<el-input
placeholder="请输入用户名"
prefix-icon="el-icon-user"
v-model="loginForm.username">
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
:type="passwordType"
placeholder="请输入密码"
prefix-icon="el-icon-lock"
v-model="loginForm.password">
</el-input>
</el-form-item>
<el-form-item>
<el-row :span="24">
<!-- <el-col :span="12">-->
<!-- <el-checkbox v-model="loginForm.rememberPwd">记住密码</el-checkbox>-->
<!-- </el-col>-->
<el-col :span="24">
<el-popover
placement="top-start"
title=""
width="200"
trigger="hover"
content="忘记密码请联系系统管理员">
<span style="color: #1890ff;float: right;" slot="reference">忘记密码</span>
</el-popover>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary"
style="width: 100%;"
@click.native.prevent="handleLogin"
class="login-submit">
登录
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import {getStore,setStore} from "@/utils/store.js";
export default {
data() {
return {
loading: false,
passwordType: "password",
loginForm: {
//用户名
username: "",
//密码
password: "",
adminType: "1",
rememberPwd: false,
},
roles:[
{val: '1',name:'管理员'},
{val: '2',name:'教师'},
],
loginRules: {
username: [
{required: true, message: "请输入用户名", trigger: "change"}
],
password: [
{required: true, message: "请输入密码", trigger: "change"}
],
adminType: [
{required: true, message: "请选择角色", trigger: "change"}
]
},
};
},
watch: {
},
computed: {
},
mounted() {
window.addEventListener('keydown', this.keyDown)
},
methods: {
keyDown (e) {
// 回车则执行登录方法 enter键的ASCII是13
if (e.keyCode === 13) {
this.handleLogin() // 需要执行的方法方法
}
},
destroyed () {
window.removeEventListener('keydown', this.keyDown, false)
},
showPassword() {
this.passwordType === ""
? (this.passwordType = "password")
: (this.passwordType = "");
},
handleLogin() {//登录
this.$refs.loginForm.validate(valid => {
if (valid) {
const loading = this.$loading({
lock: true,
text: '登录中,请稍后。。。',
spinner: "el-icon-loading"
});
this.$store.dispatch('login',this.loginForm).then((res)=>{
if(res.code === 200){
this.destroyed();
this.$notify({
title: '登录成功',
message: res.data.username+',欢迎您!',
type: 'success'
});
this.$router.push({path: '/'});
}
}).finally(() =>
loading.close()
);
}
});
},
}
};
</script>
<style>
#login-body{
width: 100%;
height: 100%;
background-size: 100% 100%;
background-image: linear-gradient(to top,rgba(0,0,0,0.5),rgba(0,0,0,0.5)), url("../../../public/img/bg2.jpg");
background-repeat: no-repeat;
}
.name{
line-height: 50px;
font-size: 30px;
font-weight: 700;
color: #FFFFFF;
margin-left: 10px;
}
.login-modal{
position: relative;
width: 420px;
height: 400px;
margin: 0 auto;
top: 50%;
margin-top: -200px;
background-color: #FFFFFF;
border-radius: 5px;
}
.title{
height: 100px;
line-height: 100px;
font-weight: 600;
text-align: center;
font-size: 28px;
}
.login-form{
margin: 20px 40px;
}
</style>
四、总结
项目页面完整,后续可能将不断升级。
关注作者,及时了解更多好项目!
更多优质项目请看作者主页!
获取源码或如需帮助,可通过博客后面名片+作者即可!
其他作品集合(主页更多): 低价多销-CSDN博客