目录
一、项目介绍
二、项目截图
管理后台
1.登录(默认管理员账号密码均为:admin)
2. 用户管理
编辑 3.任务管理
互助单(学生发布)
行政单(教师发布)
编辑 审核(退回需要写明原因)
4.管理员管理
编辑 编辑
5.个人信息、修改密码
编辑 编辑
用户端
1.登录
编辑 2.首页
可以对任务进行接取,搜索任务等
可评论
编辑 3.任务发布
互助单发布
行政单差发布
4.个人中心
5.我的发布
6.我的接取
编辑 7.消息中心
三、 项目实现简述
1.项目需求、要求文档
2.后端项目
idea开发
编辑 部分实现源码:
3.前端vue项目
管理后台前端vue项目:
登录页面代码案例:
四、总结
一、项目介绍
前后端分离实现
- 项目分为三个部分: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开发
部分实现源码:
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 {@Resourceprivate FileUploadService fileUploadService;@Resourceprivate UserService userService;@Resourceprivate RecruitRecordMapper recruitRecordMapper;@Resourceprivate CommentMapper commentMapper;@Resourceprivate MsgRecordService msgRecordService;@Resourceprivate RecruitRecordService recruitRecordService;/*** 添加或更新*/@Overridepublic 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("保存成功,审核中~");}/*** 分页*/@Overridepublic 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);}/*** 分页*/@Overridepublic 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);}/*** 分页*/@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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 所操作记录*/@Overridepublic 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*/@Overridepublic 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)@Overridepublic 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 主键*/@Overridepublic 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)@Overridepublic 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);}/*** 评论*/@Overridepublic Result<?> delComment(int id) {int change = commentMapper.deleteById(id);return Result.OKMsg("删除成功");}
}
3.前端vue项目
管理后台前端vue项目:
用户端前端vue项目:
登录页面代码案例:
<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-inputplaceholder="请输入用户名"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-popoverplacement="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是13if (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博客