Springboot考研信息平台
Springboot考研信息平台
文章目录
- Springboot考研信息平台
- 1、技术栈
- 2、项目说明
- 3、项目截图
- 4、核心代码
- 4.1、前端核心代码
- 4.2、后端核心代码
1、技术栈
前端
- Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。 Vue 作为前端核心框架,提供了响应式的数据绑定和高效的组件化开发模式。通过 Vue,开发人员可以轻松地创建动态的、交互式的用户界面,使平台能够实时响应用户的操作,如信息查询、表单提交等,为用户提供健康、流畅的操作体验。
- Axios 是一个基于 Promise 的 HTTP 库,可以用于浏览器和 node.js。在该平台中,Axios 主要负责与后端进行数据交互。它能够以简洁的代码实现对后端接口的请求,无论是获取考研资讯、用户信息,还是用户的提交注册、登录等操作,都能高效地完成数据的传输。同时,Axios 还支持请求和响应的拦截,方便进行请求的全局配置和响应的统一处理,如添加请求头信息、处理请求错误等,提高了前端与后端通信的可靠性和灵活性。
- Element Plus 是一套基于 Vue 3 的桌面端组件库。在考研信息平台中,Element Plus UI 为平台提供了丰富多样的 UI 组件,如布局组件(Grid 布局)、表格组件、表单组件(输入框、下拉菜单等)、按钮组件、弹窗组件等。这些组件具有统一的视觉风格和交互规范,能够快速搭建出美观、专业的用户界面。例如,利用 Element Plus 的表格组件可以方便地展示考研院校信息、专业信息等列表数据;借助弹窗组件可以实现用户注册、登录等信息输入的交互功能,大大提高了前端开发的效率和界面的一致性。
- Apache ECharts 是一个商业级的数据可视化工具,以简洁直观的图表展示数据。在考研信息平台里,Apache ECharts 用于将复杂的考研数据以图表形式呈现给用户。比如,可以展示历年考研报名人数的趋势图、不同专业考研竞争比例的饼图、各院校录取分数线的柱状图等。通过这些可视化图表,用户能够更快速、清晰地了解考研相关的数据情况,辅助他们做出更合理的考研决策。
后端
- Springboot 是一个用于快速开发基于 Spring 的 Java 应用程序的框架,它简化了 Spring 应用的初始搭建以及开发过程。在考研信息平台的后端,Springboot 作为核心框架,提供了强大的依赖管理和自动配置功能。通过 Springboot,可以快速地搭建起后端的项目架构,整合各种技术组件,如 Mybatis、MySQL 等。它使得后端开发更加高效、简洁,开发人员可以专注于业务逻辑的实现,如用户管理、考研信息管理、数据统计等功能的开发,而无需过多关注复杂的配置细节。
- MySQL 是一个关系型数据库管理系统,在考研信息平台中用于存储各类数据。这些数据包括用户信息(如用户名、密码、浏览历史等)、考研院校和专业信息(院校名称、专业代码、招生简章等)、考研资讯(政策动态、考试安排等)、以及用户与平台的交互记录(如查询记录、收藏信息等)。MySQL 提供了可靠的数据存储和查询功能,通过 SQL 语言可以高效地对数据进行增删改查操作,确保平台数据的完整性和安全性,为平台的稳定运行提供数据支撑。
- Mybatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。在该平台后端,Mybatis 用于实现 Java 应用程序与 MySQL 数据库之间的交互。它通过映射器(mapper)将 Java 接口和 XML 中的 SQL 映射,使得开发人员可以使用面向对象的方式进行数据库操作。例如,通过定义相应的 mapper 接口和 XML 映射文件,可以方便地实现对用户表、考研信息表等的 CRUD(增删改查)操作。Mybatis 提供了足够的灵活性,能够处理复杂的业务场景,同时避免了繁琐的 JDBC 编程,提高了后端开发的效率。
- 阿里云 OSS(Object Storage Service)是一种海量、安全、低成本、高可靠的云存储服务。在考研信息平台中,阿里 OSS云 用于存储一些文件类型的资源,如考研院校的招生简章文件、考研辅导资料、用户上传的个人资料等。通过使用阿里云 OSS,平台可以方便地管理和分发这些文件资源,用户可以快速地访问和下载所需的文件。同时,阿里云 提 OSS供了强大的存储容量和高可用性,确保文件的安全存储和稳定访问,为平台的文件存储需求提供了可靠的解决方案。
2、项目说明
- 项目采用前后端分离的架构模式,这种架构模式具有诸多显著的优势。
- 开发效率提升 :前端开发人员和后端开发人员可以相对独立地进行开发工作,无需相互等待。前端人员可以专注于使用 Vue 等前端技术构建用户界面和交互逻辑,后端人员则专注于利用 Springboot 等后端技术实现业务逻辑和数据处理功能,从而提高了整体开发效率。
- 技术选型灵活 :前后端分离使得前端和后端可以各自选择最适合的技术栈。前端可以根据项目的 UI 设计和用户体验需求选择 Vue 框架配合 Element Plus UI 等组件库,后端则可以根据业务复杂度、性能要求等因素选择 Springboot 框架结合 Mybatis 等技术,这种灵活性有利于充分发挥各技术的优势,满足项目不同的需求。
- 可维护性和可扩展性增强 :前后端代码分离,各自形成了独立的模块体系,便于代码的管理和维护。当需要对前端界面进行优化或者对后端业务逻辑进行调整时,可以只针对相应的部分进行修改,而不会对另一部分产生过多的影响。同时,这种分离架构也为项目的后续扩展提供了便利,例如,未来可以方便地添加新的前端功能模块或者后端服务模块,以满足考研信息平台不断发展的业务需求。
- 在项目运行时,前端和后端通过定义好的 API 接口进行通信。前端通过 Axios 向端后发送 HTTP 请求,包括获取考研信息、用户注册登录等操作的请求;后端接收到请求后,利用 Springboot 进行业务逻辑处理,通过 Mybatis 操作 MySQL 数据库获取或存储数据,再将处理结果以 JSON 等格式返回给前端,前端再根据返回的数据更新页面显示,实现了前后端的紧密协作,为用户提供高质量的考研信息服务。
3、项目截图
登录注册
平台提供了简洁、便捷的登录注册界面。登录界面包含用户名和密码输入框,以及登录按钮。用户可以输入自己的账号信息进行登录,新用户则可以通过注册界面进行账号注册。注册界面要求填写用户名、密码、邮箱等基本信息,并设置有验证码功能,以确保注册账号的安全性。同时,登录注册界面还设计有忘记密码的找回功能,方便用户在忘记密码时能够快速找回账号登录。
管理员端
管理员登录后进入的管理端界面功能丰富。主要包含考研信息管理模块,可以添加、编辑、删除考研院校和专业的信息,如更新院校的招生简章、专业目录等;还有用户管理模块,能够查看和管理用户的基本信息,处理用户反馈和投诉;此外,还包括数据分析模块,通过图表展示考研相关的数据统计,如用户浏览量、注册用户数量变化趋势等,帮助管理员了解平台的运营情况,以便更好地进行决策和管理。
学生端
学生端界面以提供考研信息查询和学习服务为主。界面包含考研院校和专业查询功能,学生可以通过输入关键词或者筛选条件(如地区、学科类别等)快速查询到自己感兴趣的院校和专业信息。同时,学生端还提供考研资讯推送功能,展示最新的考研政策动态、考试安排、备考技巧等文章,方便学生及时获取考研信息。
学校负责人端
学校负责人登录后可查看本校的考研信息统计情况,如报考本校的人数、各专业的报考热度等。同时,可以对本校的考研信息进行审核和发布,确保信息的准确性和及时性。还可以与其他学校进行考研合作交流的信息对接和管理,促进学校间考研资源的共享和协同。
4、核心代码
4.1、前端核心代码
首页
<template><div class="home-view"><!-- 顶部图片轮播 --><el-carousel height="400px" class="top-carousel"><el-carousel-item v-for="index in 3" :key="index"><img:src="getImageUrl(index)"alt="Banner"class="carousel-image"/></el-carousel-item></el-carousel><!-- 三列内容区 --><div class="triple-layout"><!-- 修改后的政策部分 --><div class="policy-section"><h2 class="section-title">最新政策</h2><div class="policy-list"><divv-for="policy in featuredPolicies":key="policy.id"class="policy-item"><div class="policy-header"><el-tageffect="plain"size="small":type="policyTypeMap[policy.policyType]">{{ policy.policyType }}</el-tag><span class="publish-date"><i class="el-icon-date"></i>{{ formatDate(policy.publishDate) }}</span></div><h3 class="policy-title">{{ policy.title }}</h3><div class="policy-content"><p class="content-excerpt">{{ truncateText(policy.content, 80) }}</p><div class="publish-info"><el-icon><office-building /></el-icon><span class="department">{{ policy.publishDepartment }}</span></div></div><el-buttontype="primary"size="small"link@click="showPolicyDetail(policy)">查看详情</el-button></div></div><!-- 政策详情对话框 --><el-dialogv-model="dialogVisible":title="currentPolicy.title"width="60%"><el-descriptions border :column="1" size="medium"><el-descriptions-item label="发布部门"><el-tag size="small">{{ currentPolicy.publishDepartment }}</el-tag></el-descriptions-item><el-descriptions-item label="政策类型"><el-tagsize="small":type="policyTypeMap[currentPolicy.policyType]">{{ currentPolicy.policyType }}</el-tag></el-descriptions-item><el-descriptions-item label="发布日期">{{ formatDate(currentPolicy.publishDate) }}</el-descriptions-item><el-descriptions-item label="生效日期">{{ formatDate(currentPolicy.effectiveDate) }}</el-descriptions-item><el-descriptions-item label="政策内容"><div class="policy-full-content">{{ currentPolicy.content }}</div><el-buttonv-if="currentPolicy.attachmentUrl"type="primary"size="small"class="mt-2"@click="downloadAttachment(currentPolicy.attachmentUrl)">下载附件</el-button></el-descriptions-item></el-descriptions></el-dialog></div><!-- 招生简章 --><div class="admission-section"><h2 class="section-title">招生简章</h2><div class="admission-list"><divv-for="admission in latestAdmissions":key="admission.id"class="admission-item"><div class="admission-content"><h4 class="admission-title">{{ admission.title }}</h4><div class="admission-meta"><span class="university">{{getUniversityName(admission.university_id)}}</span><span class="date">{{ formatDate(admission.publish_date) }}</span></div></div><el-buttontype="primary"size="small"link@click="showAdmissionDetail(admission)">查看详情</el-button></div></div></div><!-- 招生简章详情对话框 --><el-dialogv-model="admissionDialogVisible":title="currentAdmission.title"width="60%"><el-descriptions border :column="1" size="medium"><el-descriptions-item label="发布院校"><el-tag size="small">{{getUniversityName(currentAdmission.university_id)}}</el-tag></el-descriptions-item><el-descriptions-item label="招生年份">{{ currentAdmission.admission_year }}</el-descriptions-item><el-descriptions-item label="发布日期">{{ formatAdmissionDate(currentAdmission.publish_date) }}</el-descriptions-item><el-descriptions-item label="内容详情"><div class="admission-content">{{ currentAdmission.content }}<el-buttonv-if="currentAdmission.attachment_url"type="primary"size="small"class="mt-2"@click="downloadAttachment(currentAdmission.attachment_url)">下载招生简章全文</el-button></div></el-descriptions-item></el-descriptions></el-dialog><!-- 优化后的论坛讨论部分 --><div class="forum-section"><h2 class="section-title">热门讨论</h2><div class="forum-list"><divv-for="post in filteredHotPosts":key="post.id"class="forum-item":class="{ 'deleted-post': post.status === 'deleted' }"@click="goToForumView()"style="cursor: pointer;"><div class="post-header"><el-avatar:src="post.author?.image || defaultAvatar"size="small"/><div class="post-meta"><div class="meta-line"><span class="author">用户{{ post.author_id }}</span><el-tagsize="mini":type="postStatusMap[post.status].type"effect="plain">{{ postStatusMap[post.status].label }}</el-tag></div><span class="post-time"><i class="el-icon-time"></i>{{ formatPostTime(post.created_at) }}</span></div></div><div class="post-content"><h5 class="post-title"><spanclass="topic-tag":style="topicStyle(post.target_type)">{{ targetTypeMap[post.target_type] }}</span>{{ post.title }}</h5><div class="post-stats"><div class="stat-item"><i class="el-icon-chat-line-round"></i><span class="count">{{ post.reply_count }}</span></div><div class="stat-item"><i class="el-icon-view"></i><span class="count">{{ post.view_count }}</span></div><el-tagv-if="post.is_top"size="mini"type="warning"effect="dark"class="top-tag">置顶</el-tag></div></div></div></div></div></div></div>
</template><script setup>
import { ref, computed } from 'vue' // 添加 computed 导入
import { getPolicyList } from '@/api/policy'
import { getAdmissionList } from '@/api/admission'
import { getPostList } from '@/api/forum'
import { getUniversityList } from '@/api/university'
import { OfficeBuilding } from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import { useRouter } from 'vue-router' // 引入 useRouter
const router = useRouter()// import { OfficeBuilding } from '@element-plus/icons-vue'// 数据获取
const featuredPolicies = ref([])
const latestAdmissions = ref([])
const hotPosts = ref([])
const universities = ref([])// 新增论坛相关数据
const defaultAvatar = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
)
const postStatusMap = {published: { label: '已发布', type: 'success' },pending: { label: '审核中', type: 'warning' },deleted: { label: '已删除', type: 'danger' },
}
const targetTypeMap = {policy: '政策相关',admission: '招生讨论',university: '院校交流',major: '专业探讨',general: '综合讨论',
}
const topicColors = {policy: '#f56c6c',admission: '#409eff',university: '#67c23a',major: '#e6a23c',general: '#909399',
}const dialogVisible = ref(false)
const currentPolicy = ref({})
const currentSlideIndex = ref(0)const policyTypeMap = {'国家线': 'danger','地方政策': 'warning','院校政策': 'success',
}// 计算属性
const filteredHotPosts = computed(() => {return hotPosts.value.filter((post) => post.status !== 'deleted').sort((a, b) => b.reply_count - a.reply_count).slice(0, 5)
})// 新增方法
const topicStyle = (type) => ({backgroundColor: topicColors[type] + '15',color: topicColors[type],borderColor: topicColors[type],
})const formatPostTime = (timeStr) => {return dayjs(timeStr).format('MM-DD HH:mm')
}const showPolicyDetail = (policy) => {currentPolicy.value = policydialogVisible.value = true
}const handleCarouselChange = (index) => {currentSlideIndex.value = index
}const admissionDialogVisible = ref(false)
const currentAdmission = ref({university_id: null,admission_year: '',publish_date: '',content: '',attachment_url: null,
})const showAdmissionDetail = (admission) => {// 使用 router.push 进行页面跳转router.push({name: 'AdmissionsDetail',params: { id: admission.id } // 传递招生简章的 ID 作为参数})
}// 修改日期格式化方法
const formatAdmissionDate = (dateStr) => {return dayjs(dateStr).format('YYYY-MM-DD')
}const downloadAttachment = (url) => {// 实现附件下载逻辑window.open(url, '_blank')
}
// 新增图片路径处理方法
const getImageUrl = (index) => {return new URL(`../assets/lun/${index}.png`, import.meta.url).href
}
// 原有方法保持不变...
const loadData = async () => {const [policyRes, admissionRes, postRes, uniRes] = await Promise.all([getPolicyList(null, 'published'),getAdmissionList({ status: 'published' }),getPostList({ order: 'hot' }),getUniversityList(),])featuredPolicies.value = policyRes.data.datalatestAdmissions.value = admissionRes.data.datahotPosts.value = postRes.data.datauniversities.value = uniRes.data.data
}// 工具函数
const getUniversityName = (id) => {return universities.value.find((u) => u.id === id)?.name || '未知院校'
}const formatDate = (dateArray) => {if (!dateArray || dateArray.length !== 3) return ''return dayjs(new Date(dateArray[0], dateArray[1] - 1, dateArray[2])).format('YYYY-MM-DD')
}const truncateText = (text, length) => {return text.length > length ? text.slice(0, length) + '...' : text
}// 新增跳转方法
const goToForumView = () => {router.push({ name: 'forumView' });
}loadData()
</script><style scoped>
.home-view {max-width: 1400px;margin: 0 auto;padding: 0 20px;
}/* 顶部轮播 */
.top-carousel {border-radius: 12px;overflow: hidden;margin-bottom: 30px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}.carousel-image {width: 100%;height: 400px;object-fit: cover;
}/* 三列布局 */
.triple-layout {display: grid;grid-template-columns: repeat(3, 1fr);gap: 24px;margin-bottom: 40px;
}.section-title {font-size: 18px;color: #303133;margin-bottom: 16px;padding-bottom: 8px;border-bottom: 2px solid #409eff;
}/* 政策部分样式 */
.policy-section {background: #fff;border-radius: 8px;padding: 16px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}.policy-list {max-height: 320px; /* 根据需要调整 */overflow-y: auto;
}.policy-item {display: flex;flex-direction: column;justify-content: space-between;padding: 12px 0;border-bottom: 1px solid #eee;
}.policy-item:last-child {border-bottom: none;
}.policy-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 12px;
}.policy-title {font-size: 16px;color: #1a1a1a;margin-bottom: 12px;line-height: 1.4;
}.policy-content {font-size: 14px;color: #444;line-height: 1.6;margin-bottom: 12px;
}.publish-info {font-size: 12px;color: #666;display: flex;align-items: center;gap: 8px;margin-top: 12px;
}/* 招生简章 */
.admission-section {background: #fff;border-radius: 8px;padding: 16px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}.admission-list {max-height: 320px;overflow-y: auto;
}.admission-item {display: flex;justify-content: space-between;align-items: center;padding: 12px 0;border-bottom: 1px solid #eee;
}.admission-item:last-child {border-bottom: none;
}.admission-title {font-size: 14px;margin-bottom: 6px;
}.admission-meta {font-size: 12px;color: #666;display: flex;gap: 8px;
}/* 新增论坛样式 */
.forum-section {background: #fff;border-radius: 12px;padding: 20px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}.forum-list {max-height: 400px;overflow-y: auto;padding-right: 8px;
}.forum-item {padding: 16px;margin-bottom: 12px;background: #f8fafc;border-radius: 8px;transition: all 0.3s ease;border: 1px solid transparent;&:hover {transform: translateX(4px);box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);border-color: #409eff30;}
}.post-header {display: flex;align-items: center;margin-bottom: 12px;
}.post-meta {margin-left: 12px;.meta-line {display: flex;align-items: center;gap: 8px;}.author {font-size: 13px;color: #606266;font-weight: 500;}.post-time {font-size: 12px;color: #909399;display: flex;align-items: center;gap: 4px;}
}.post-title {font-size: 14px;color: #303133;margin: 0 0 8px 0;display: flex;align-items: center;gap: 8px;.topic-tag {font-size: 12px;padding: 2px 8px;border-radius: 4px;border: 1px solid;flex-shrink: 0;}
}.post-stats {display: flex;align-items: center;gap: 16px;.stat-item {display: flex;align-items: center;gap: 4px;font-size: 12px;color: #909399;i {font-size: 14px;}.count {font-weight: 500;}}.top-tag {margin-left: auto;}
}.deleted-post {opacity: 0.6;background: #fef0f0;.post-title {color: #f56c6c;text-decoration: line-through;}
}/* 滚动条样式 */
.forum-list::-webkit-scrollbar {width: 6px;
}
.forum-list::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 4px;
}
.forum-list::-webkit-scrollbar-thumb {background: #c0c4cc;border-radius: 4px;
}
.forum-list::-webkit-scrollbar-thumb:hover {background: #909399;
}/* 响应式 */
@media (max-width: 992px) {.triple-layout {grid-template-columns: 1fr;gap: 20px;}.policy-section {height: auto; /* 移除高度限制 */}.top-carousel {height: 300px;}.carousel-image {height: 300px;}
}.policy-full-content {white-space: pre-wrap;line-height: 1.8;max-height: 400px;overflow-y: auto;padding: 8px;background: #f8f9fa;border-radius: 4px;
}:deep(.el-descriptions__body) {background: #f8f9fa;
}.admission-content {white-space: pre-wrap;line-height: 1.8;padding: 12px;background: #f8f9fa;border-radius: 4px;max-height: 400px;overflow-y: auto;
}.admission-content p {margin-bottom: 1em;
}
</style>
专业
<template><div style="margin: 0 10px;"><!-- 搜索栏 --><div style="margin-bottom: 20px;"><el-inputv-model="searchName"placeholder="请输入专业名称"prefix-icon="el-icon-search"clearablestyle="width: 250px; margin-right: 10px;"/><el-selectv-model="selectedUniversity"filterableclearableplaceholder="选择所属院校"style="width: 200px; margin-right: 10px;"><el-option v-for="uni in universityList":key="uni.id":label="uni.name":value="uni.id"/></el-select><el-button type="primary" @click="loadMajors">搜索</el-button><el-button type="success" @click="openAddDialog">新增专业</el-button></div><!-- 数据表格 --><el-table :data="paginatedData" style="width: 100%"><el-table-column type="index" label="序号" width="60" /><el-table-column prop="name" label="专业名称" width="150" /><el-table-column label="所属院校" width="180"><template #default="{ row }">{{ getUniversityName(row.university_id) }}</template></el-table-column><el-table-column prop="duration" label="学制" width="100" /><el-table-column label="学费" width="120"><template #default="{ row }">{{ row.tuition_fee ? `¥${row.tuition_fee.toFixed(2)}` : '-' }}</template></el-table-column><!-- 新增课程列 --><el-table-column label="课程" width="200"><template #default="{ row }"><div style="display: flex; flex-wrap: wrap; gap: 5px;"><el-tagv-for="(course, index) in row.courses":key="index"type="info"size="small">{{ course }}</el-tag></div></template></el-table-column><el-table-column label="操作" width="150" fixed="right"><template #default="{ row }"><el-button link type="primary" @click="openDetail(row)">详情</el-button><el-button link type="primary" @click="openEditDialog(row)">编辑</el-button><el-button link type="danger" @click="handleDelete(row.id)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div style="margin-top: 20px; text-align: center;"><el-paginationbackgroundlayout="prev, pager, next":total="tableData.length":page-size="pageSize":current-page="currentPage"@current-change="handlePageChange"/></div><!-- 专业详情对话框 --><el-dialog v-model="detailVisible" title="专业详情" width="800px"><el-descriptions :column="2" border><el-descriptions-item label="专业名称">{{ currentMajor.name }}</el-descriptions-item><el-descriptions-item label="所属院校">{{ getUniversityName(currentMajor.university_id) }}</el-descriptions-item><el-descriptions-item label="学制">{{ currentMajor.duration }}</el-descriptions-item><el-descriptions-item label="学费">¥{{ currentMajor.tuition_fee?.toFixed(2) || '-' }}</el-descriptions-item><el-descriptions-item label="课程设置" :span="2"><el-tag v-for="(course, index) in currentMajor.courses" :key="index"style="margin-right: 5px; margin-bottom: 5px;">{{ course }}</el-tag></el-descriptions-item><el-descriptions-item label="就业方向" :span="2">{{ currentMajor.employment_direction || '暂无信息' }}</el-descriptions-item><el-descriptions-item label="专业简介" :span="2">{{ currentMajor.description || '暂无简介' }}</el-descriptions-item></el-descriptions></el-dialog><!-- 新增/编辑对话框 --><el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"><el-form :model="formData" label-width="100px"><el-form-item label="专业名称" required><el-input v-model="formData.name" /></el-form-item><el-form-item label="所属院校" required><el-select v-model="formData.university_id"filterableplaceholder="请选择院校"style="width: 100%;"><el-option v-for="uni in universityList":key="uni.id":label="uni.name":value="uni.id"/></el-select></el-form-item><el-form-item label="学制"><el-input v-model="formData.duration" placeholder="如:4年" /></el-form-item><el-form-item label="学费"><el-input v-model="formData.tuition_fee" type="number"><template #append>元/年</template></el-input></el-form-item><!-- 修改对话框中的课程输入绑定 --><el-form-item label="课程设置"><el-inputv-model="coursesInput"type="textarea":rows="3"placeholder="请输入课程名称,用逗号分隔(如:高等数学, 大学英语)"/></el-form-item><el-form-item label="就业方向"><el-input v-model="formData.employment_direction"type="textarea":rows="2"placeholder="请输入主要就业方向"/></el-form-item><el-form-item label="专业简介"><el-input v-model="formData.description"type="textarea":rows="4"placeholder="请输入专业详细介绍"/></el-form-item></el-form><template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="submitForm">确定</el-button></template></el-dialog></div></template><script setup>import { ref, computed, onMounted } from 'vue'import { ElMessage, ElMessageBox } from 'element-plus'import { getMajorList, deleteMajor, createMajor, updateMajor } from '@/api/major'import { getUniversityList } from '@/api/university'// 移除原来的coursesText计算属性
const coursesInput = ref('')// 数据相关const tableData = ref([])const universityList = ref([])const searchName = ref('')const selectedUniversity = ref('')const currentPage = ref(1)const pageSize = 8// 分页计算const paginatedData = computed(() => {const start = (currentPage.value - 1) * pageSizereturn tableData.value.slice(start, start + pageSize)})// 对话框相关const dialogVisible = ref(false)const detailVisible = ref(false)const dialogTitle = ref('新增专业')const currentMajor = ref({})const formData = ref({name: '',university_id: null,duration: '',tuition_fee: null,courses: [],employment_direction: '',description: ''})// // 课程设置文本处理
// const coursesText = computed({
// get: () => formData.value.courses?.join(', ') || '',
// set: (val) => {
// formData.value.courses = val.split(/[,,]/).map(s => s.trim()).filter(Boolean)
// }
// })// 加载院校列表const loadUniversities = async () => {try {const res = await getUniversityList('', '')universityList.value = res.data.data} catch (error) {ElMessage.error('加载院校列表失败')}}// 加载专业列表const loadMajors = async () => {try {const res = await getMajorList({name: searchName.value,university_id: selectedUniversity.value})tableData.value = res.data.data} catch (error) {ElMessage.error('加载失败')}}// 获取院校名称const getUniversityName = (id) => {const uni = universityList.value.find(u => u.id === id)return uni?.name || '未知院校'}// 打开详情对话框const openDetail = (row) => {currentMajor.value = rowdetailVisible.value = true}// 打开新增对话框时重置输入
const openAddDialog = () => {formData.value = {name: '',university_id: null,duration: '',tuition_fee: null,courses: [],employment_direction: '',description: ''}coursesInput.value = '' // 新增时清空课程输入dialogTitle.value = '新增专业'dialogVisible.value = true
}// 打开编辑对话框时填充数据
const openEditDialog = (row) => {formData.value = { ...row }coursesInput.value = row.courses.join(', ') // 将数组转为字符串dialogTitle.value = '编辑专业'dialogVisible.value = true
}
// 修改后的提交方法
const submitForm = async () => {try {// 在提交前处理课程数据const payload = {...formData.value,courses: coursesInput.value.split(/[,,]/) // 处理中英文逗号.map(s => s.trim()).filter(Boolean),tuition_fee: Number(formData.value.tuition_fee) || null}if (formData.value.id) {await updateMajor(formData.value.id, payload)} else {await createMajor(payload)}ElMessage.success('操作成功')dialogVisible.value = falseloadMajors()} catch (error) {ElMessage.error('操作失败')}
}// 删除处理const handleDelete = async (id) => {try {await ElMessageBox.confirm('确定删除该专业吗?', '警告', { type: 'warning' })await deleteMajor(id)ElMessage.success('删除成功')loadMajors()} catch (error) {console.log('取消删除')}}// 分页切换const handlePageChange = (page) => {currentPage.value = page}// 初始化加载onMounted(() => {loadUniversities()loadMajors()})</script>
考研政策
<template><div style="margin: 0 10px;"><!-- 搜索栏 --><div style="margin-bottom: 20px;"><el-inputv-model="searchTitle"placeholder="请输入政策标题"prefix-icon="el-icon-search"clearablestyle="width: 250px; margin-right: 10px;"/><el-selectv-model="searchStatus"clearableplaceholder="选择状态"style="width: 120px; margin-right: 10px;"><el-option label="已发布" value="published" /><el-option label="草稿" value="draft" /></el-select><el-button type="primary" @click="loadPolicies">搜索</el-button><el-button type="success" @click="openAddDialog">新增政策</el-button></div><!-- 数据表格 --><el-table :data="paginatedData" style="width: 100%"><el-table-column type="index" label="序号" width="60" /><el-table-column prop="title" label="政策标题" width="200" /><el-table-column prop="publishDepartment" label="发布部门" width="150" /><el-table-column prop="policyType" label="政策类型" width="120"><template #default="{ row }"><el-tag>{{ row.policyType || '-' }}</el-tag></template></el-table-column><el-table-column prop="publishDate" label="发布时间" width="120" /><el-table-column prop="effectiveDate" label="生效时间" width="120" /><el-table-column prop="status" label="状态" width="100"><template #default="{ row }"><el-tag :type="row.status === 'published' ? 'success' : 'info'">{{ row.status === 'published' ? '已发布' : '草稿' }}</el-tag></template></el-table-column><el-table-column prop="viewCount" label="浏览次数" width="100" /><el-table-column fixed="right" label="操作" width="150"><template #default="{ row }"><el-button link type="primary" @click="openEditDialog(row)">编辑</el-button><el-button link type="danger" @click="handleDelete(row.id)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div style="margin-top: 20px; text-align: center;"><el-paginationbackgroundlayout="prev, pager, next":total="tableData.length":page-size="pageSize":current-page="currentPage"@current-change="handlePageChange"/></div><!-- 新增/编辑对话框 --><el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"><el-form :model="formData" label-width="100px"><el-form-item label="政策标题" required><el-input v-model="formData.title" /></el-form-item><el-form-item label="政策类型"><el-select v-model="formData.policyType" placeholder="请选择"><el-option label="国家线" value="国家线" /><el-option label="院校政策" value="院校政策" /></el-select></el-form-item><el-form-item label="发布部门"><el-input v-model="formData.publishDepartment" /></el-form-item><el-form-item label="发布时间"><el-date-pickerv-model="formData.publishDate"type="date"value-format="YYYY-MM-DD"/></el-form-item><el-form-item label="生效时间"><el-date-pickerv-model="formData.effectiveDate"type="date"value-format="YYYY-MM-DD"/></el-form-item><el-form-item label="政策内容"><el-input v-model="formData.content" type="textarea" :rows="4" /></el-form-item><el-form-item label="附件上传"><el-uploadaction="http://localhost:8080/upload":headers="{ 'Authorization': 'Bearer ' + token }":data="{ type: 'policy' }"name="file":on-success="handleUploadSuccess":on-error="handleUploadError":show-file-list="false"><el-button type="primary">点击上传PDF</el-button><template #tip><div class="el-upload__tip">只能上传PDF文件,且不超过10MB</div></template></el-upload><div v-if="formData.attachmentUrl" style="margin-top: 10px;">当前附件: <a :href="formData.attachmentUrl" target="_blank" class="link">{{ formData.attachmentUrl }}</a><el-button type="danger" size="small" @click="formData.attachmentUrl = ''"style="margin-left: 10px;">移除</el-button></div></el-form-item><el-form-item label="状态"><el-radio-group v-model="formData.status"><el-radio label="published">发布</el-radio><el-radio label="draft">草稿</el-radio></el-radio-group></el-form-item></el-form><template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="submitForm">确定</el-button></template></el-dialog></div></template><script setup>import { ref, computed, onMounted } from 'vue'import { ElMessage, ElMessageBox } from 'element-plus'import { getPolicyList, deletePolicy, createPolicy, updatePolicy } from '@/api/policy'// 数据相关const tableData = ref([])const searchTitle = ref('')const searchStatus = ref('')const currentPage = ref(1)const pageSize = 10const token = ref(localStorage.getItem('token')) // 根据实际存储方式调整// 分页计算const paginatedData = computed(() => {const start = (currentPage.value - 1) * pageSizereturn tableData.value.slice(start, start + pageSize)})// 对话框相关const dialogVisible = ref(false)const dialogTitle = ref('新增政策')const formData = ref({title: '',content: '',publishDepartment: '',policyType: '',publishDate: '',effectiveDate: '',attachmentUrl: '',status: 'draft'})// 文件上传处理const handleUploadSuccess = (response) => {if (response.code === 1) {formData.value.attachmentUrl = response.dataElMessage.success('上传成功')} else {ElMessage.error(response.message || '上传失败')}}const handleUploadError = (error) => {ElMessage.error('文件上传失败: ' + (error.message || '未知错误'))}// 加载数据(含日期转换)const loadPolicies = async () => {try {const res = await getPolicyList(searchTitle.value, searchStatus.value)tableData.value = res.data.data.map(item => ({...item,publishDate: formatDateArray(item.publishDate),effectiveDate: formatDateArray(item.effectiveDate),createdAt: formatDateArray(item.createdAt)}))} catch (error) {ElMessage.error('加载失败')}}// 日期数组转字符串([2024,2,28] → "2024-02-28")const formatDateArray = (dateArray) => {if (!dateArray || dateArray.length !== 3) return ''const [year, month, day] = dateArrayreturn `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`}// 打开新增对话框const openAddDialog = () => {formData.value = {title: '',content: '',publishDepartment: '',policyType: '',publishDate: '',effectiveDate: '',attachmentUrl: '',status: 'draft'}dialogTitle.value = '新增政策'dialogVisible.value = true}// 打开编辑对话框const openEditDialog = (row) => {formData.value = { ...row }dialogTitle.value = '编辑政策'dialogVisible.value = true}// 提交表单(字段名转换)const submitForm = async () => {try {const payload = {title: formData.value.title,content: formData.value.content,publishDepartment: formData.value.publishDepartment,policyType: formData.value.policyType,publishDate: formData.value.publishDate,effectiveDate: formData.value.effectiveDate,attachmentUrl: formData.value.attachmentUrl,status: formData.value.status}if (formData.value.id) {await updatePolicy(formData.value.id, payload)} else {await createPolicy(payload)}ElMessage.success('操作成功')dialogVisible.value = falseloadPolicies()} catch (error) {ElMessage.error('操作失败')}}// 删除处理const handleDelete = async (id) => {try {await ElMessageBox.confirm('确定删除该政策吗?', '警告', { type: 'warning' })await deletePolicy(id)ElMessage.success('删除成功')loadPolicies()} catch (error) {console.log('取消删除')}}// 分页切换const handlePageChange = (page) => {currentPage.value = page}// 初始化加载onMounted(() => {loadPolicies()})</script><style scoped>.link {color: #409eff;text-decoration: underline;word-break: break-all;}</style>
数据
<template><div class="dashboard"><el-row :gutter="20"><!-- 用户统计 --><el-col :span="12"><el-card><template #header><span>用户统计</span></template><BaseChart :options="userChartOptions" /></el-card></el-col><!-- 发帖统计 --><el-col :span="12"><el-card><template #header><span>论坛发帖统计</span></template><BaseChart :options="postChartOptions" /></el-card></el-col></el-row><el-row :gutter="20" class="mt-4"><!-- 招生简章统计 --><el-col :span="12"><el-card><template #header><span>招生简章状态分布</span></template><BaseChart :options="admissionChartOptions" /></el-card></el-col><!-- 政策统计 --><el-col :span="12"><el-card><template #header><span>政策类型分布</span></template><BaseChart :options="policyChartOptions" /></el-card></el-col></el-row></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import BaseChart from '@/components/BaseChart.vue';
import { getUserList } from '@/api/user';
import { getPostList } from '@/api/forum';
import { getAdmissionList } from '@/api/admission';
import { getPolicyList } from '@/api/policy';
import { ElMessage } from 'element-plus';// 用户统计图表数据
const userChartOptions = ref({});
// 发帖统计图表数据
const postChartOptions = ref({});
// 招生简章统计图表数据
const admissionChartOptions = ref({});
// 政策统计图表数据
const policyChartOptions = ref({});// 处理用户数据
const processUserData = (users) => {const identityCount = users.reduce((acc, user) => {acc[user.identity] = (acc[user.identity] || 0) + 1;return acc;}, {});// 添加 identity 映射const identityMapping = {'ADMIN': '管理员','USER': '用户'};return {tooltip: { trigger: 'item' },series: [{type: 'pie',data: Object.entries(identityCount).map(([name, value]) => ({name: identityMapping[name] || name, // 使用映射后的名称value}))}]};
};// 处理发帖数据
const processPostData = (posts) => {const postCountByMonth = posts.reduce((acc, post) => {const month = new Date(post.created_at).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit' });acc[month] = (acc[month] || 0) + 1;return acc;}, {});return {xAxis: {type: 'category',data: Object.keys(postCountByMonth)},yAxis: { type: 'value' },series: [{data: Object.values(postCountByMonth),type: 'bar',barWidth: '60%'}]};
};// 处理招生简章数据
const processAdmissionData = (admissions) => {const statusCount = admissions.reduce((acc, item) => {acc[item.status] = (acc[item.status] || 0) + 1;return acc;}, {});// 添加状态映射const statusMapping = {'published': '已发布','draft': '草稿'};return {tooltip: { trigger: 'item' },series: [{type: 'pie',radius: ['40%', '70%'],data: Object.entries(statusCount).map(([name, value]) => ({name: statusMapping[name] || name, // 使用映射后的名称value}))}]};
};// 处理政策数据
const processPolicyData = (policies) => {// 使用中文类型映射(根据实际业务需求补充)const typeMapping = {'national': '国家线','local': '地方政策','institution': '院校政策'};const typeCount = policies.reduce((acc, policy) => {const rawType = policy.policyType;// 使用映射后的类型,如果没有映射则显示原值const type = typeMapping[rawType] || rawType || '未分类';acc[type] = (acc[type] || 0) + 1;return acc;}, {});return {tooltip: {trigger: 'item',formatter: '{a} <br/>{b}: {c} ({d}%)'},legend: {orient: 'vertical',right: 10,top: 'middle'},series: [{name: '政策类型',type: 'pie',radius: [50, 100],center: ['40%', '50%'],roseType: 'radius',itemStyle: {borderRadius: 5},label: {show: true,formatter: '{b}: {c}'},data: Object.entries(typeCount).sort((a, b) => b[1] - a[1]).map(([name, value]) => ({name,value,itemStyle: {color: getColorByType(name) // 自定义颜色方法}}))}]};
};// 示例颜色分配(可根据需要扩展)
function getColorByType(type) {const colors = {'国家线': '#5470c6','地方政策': '#91cc75','院校政策': '#fac858','未分类': '#ee6666'};return colors[type] || '#73c0de';
}onMounted(async () => {try {// 用户数据const userRes = await getUserList('', '');const rawUserData = userRes.data?.data || userRes.data;if (!Array.isArray(rawUserData)) throw new Error('用户数据格式异常');userChartOptions.value = processUserData(rawUserData);// 发帖数据const postRes = await getPostList(null);const rawPostData = postRes.data.dataif (!Array.isArray(rawPostData)) throw new Error('发帖数据格式异常');postChartOptions.value = processPostData(rawPostData);// 招生简章数据const admissionRes = await getAdmissionList({title: '',status: '',university_id: '',})const rawAdmissionData = admissionRes.data.data;if (!Array.isArray(rawAdmissionData)) throw new Error('招生简章数据格式异常');admissionChartOptions.value = processAdmissionData(rawAdmissionData);// 政策数据const policyRes = await getPolicyList({ status: undefined });const rawPolicyData = policyRes.data.data;if (!Array.isArray(rawPolicyData)) throw new Error('政策数据格式异常');policyChartOptions.value = processPolicyData(rawPolicyData);} catch (error) {console.error('完整错误信息:', error);ElMessage.error(`数据加载失败: ${error.response?.data?.message || error.message}`);}
});
</script><style scoped>
.dashboard {padding: 20px;
}.mt-4 {margin-top: 20px;
}
</style>
4.2、后端核心代码
controller
@Slf4j
@RestController
@RequestMapping("/admission")
@RequiredArgsConstructor
public class AdmissionController {private final AdmissionService admissionService;@GetMapping("/list")public Result getAdmissionList(@RequestParam(required = false) String title,@RequestParam(required = false) String status,@RequestParam(required = false) Integer universityId) {List<Admission> admissionList = admissionService.getAdmissionList(title, status, universityId);return Result.success(admissionList);}@DeleteMapping("/delete")public Result deleteAdmission(@RequestParam Integer id) {admissionService.deleteAdmission(id);return Result.success();}@PostMapping("/add")public Result createAdmission(@RequestBody Admission admission) {log.info("新增:{},", admission);admissionService.createAdmission(admission);return Result.success();}@PutMapping("/update/{id}")public Result updateAdmission(@PathVariable Integer id, @RequestBody Admission admission) {admission.setId(id);log.info("更新:{}",admission);admissionService.updateAdmission(admission);return Result.success();}@GetMapping("/detail/{id}")public Result<UniversityVO> getDetail(@PathVariable Integer id){log.info("详情:{}",id);UniversityVO vo = admissionService.getDetail(id);return Result.success(vo);}
}
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j
public class ApiController {private final UserService userService;private final JwtProperties jwtProperties;/*** 登录* @param userDTO* @return*/@PostMapping("/login")public Result Login(@RequestBody UserDTO userDTO){log.info("用户登录信息:{}",userDTO);User user = userService.login(userDTO);if (user == null){return Result.error("用户名或密码错误");}//创建载荷,将用户id写进载荷Map<String, Object> map = new HashMap<>();map.put(JwtClaimsConstant.USER_ID, user.getId());//生成令牌String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),map);return Result.success(token);}/*** 注册* @param user* @return*/@PostMapping("/register")public Result register(@RequestBody User user){log.info("注册用户:{}",user);userService.register(user);return Result.success();}
}
@RestController
@RequestMapping("/major")
@Slf4j
@RequiredArgsConstructor
public class MajorController {private final MajorService majorService;/*** 查询专业列表* @param name* @param universityId* @return*/@GetMapping("list")public Result getMajorList(@RequestParam(required = false) String name,@RequestParam(required = false,name = "university_id") String universityId){log.info("查询专业列表:{},{}", name, universityId);List<MajorVO> list = majorService.getList(name, universityId);return Result.success(list);}@DeleteMapping("delete")public Result deleteMajor(Integer id){log.info("删除专业:{}", id);majorService.deleteMajor(id);return Result.success();}@PostMapping("add")public Result addMajor(@RequestBody MajorVO majorVO){log.info("添加专业:{}", majorVO);majorService.addMajor(majorVO);return Result.success();}@PutMapping("update/{id}")public Result updateMajor(@PathVariable Integer id, @RequestBody MajorVO majorVO){log.info("修改专业:{},{}", id, majorVO);majorService.updateMajor(id, majorVO);return Result.success();}
}
@RestController
@RequestMapping("/policy")
@Slf4j
public class PolicyController {@Autowiredprivate PolicyService policyService;// 分页查询政策列表@GetMapping("/list")public Result<List<Policy>> list(@RequestParam(required = false) String title,@RequestParam(required = false) String status) {log.info("分页查询政策列表:{},{}", title, status);List<Policy> policies = policyService.getPolicyList(title, status);return Result.success(policies);}// 新增政策@PostMapping("/add")public Result<String> add(@RequestBody Policy policy) {log.info("新增政策:{}", policy);policyService.addPolicy(policy);return Result.success("添加成功");}// 修改政策@PutMapping("/update/{id}")public Result<String> update(@PathVariable Integer id,@RequestBody Policy policy) {policy.setId(id);policyService.updatePolicy(policy);return Result.success("更新成功");}// 删除政策@DeleteMapping("/delete")public Result<String> delete(@RequestParam Integer id) {policyService.deletePolicy(id);return Result.success("删除成功");}
}
service
@Service
@RequiredArgsConstructor
public class AdmissionServiceImpl implements AdmissionService {private final AdmissionMapper admissionMapper;private final UniversityMapper universityMapper;/*** 获取列表** @param title* @param status* @param universityId* @return*/@Overridepublic List<Admission> getAdmissionList(String title, String status, Integer universityId) {List<Admission> list = admissionMapper.selectAll(title,status,universityId);return list;}/*** 根据id删除** @param id* @return*/@Overridepublic void deleteAdmission(Integer id) {admissionMapper.deleteById(id);}/*** 新增** @param admission*/@Overridepublic void createAdmission(Admission admission) {admission.setCreatedBy(1L);admission.setCreatedAt(LocalDateTime.now());admissionMapper.insert(admission);}/*** 修改** @param admission*/@Overridepublic void updateAdmission(Admission admission) {admissionMapper.update(admission);}/*** 详情** @param id* @return*/@Overridepublic UniversityVO getDetail(Integer id) {Admission admission = admissionMapper.getById(id);if (admission == null) {return null;}Integer universityId = admission.getUniversityId();University university = universityMapper.getById(universityId);if (university == null) {return null;}UniversityVO vo = new UniversityVO();BeanUtils.copyProperties(university, vo);List<Admission> list = admissionMapper.selectByUniversityId(universityId);vo.setAdmissions(list);return vo;}
}
@Service
@RequiredArgsConstructor
public class MajorServiceImpl implements MajorService {private final MajorMapper majorMapper;/*** 查询专业列表** @param name* @param universityId* @return*/@Overridepublic List<MajorVO> getList(String name, String universityId) {List<Major> list = majorMapper.selectByCondition(name, universityId);List<MajorVO> vos = new ArrayList<>();for (Major major : list) {String coursesStr = major.getCourses();MajorVO majorVO = new MajorVO();BeanUtils.copyProperties(major, majorVO);if (coursesStr != null && !coursesStr.isEmpty()) {List<String> coursesList = Arrays.asList(coursesStr.split(",\\s*"));majorVO.setCourses(coursesList); // 设置到 MajorVO} else {majorVO.setCourses(new ArrayList<>()); // 确保 VO 中的课程列表不为空}vos.add(majorVO);}return vos;}/*** 删除专业** @param id*/@Overridepublic void deleteMajor(Integer id) {majorMapper.delete(id);}/*** 新增专业** @param majorVO*/@Overridepublic void addMajor(MajorVO majorVO) {Major major = new Major();BeanUtils.copyProperties(majorVO, major);major.setCourses(String.join(",", majorVO.getCourses()));majorMapper.insert(major);}/*** 修改** @param id* @param majorVO*/@Overridepublic void updateMajor(Integer id, MajorVO majorVO) {Major major = new Major();BeanUtils.copyProperties(majorVO, major);major.setCourses(String.join(",", majorVO.getCourses()));major.setId(id);System.out.println(major);majorMapper.update(major);}
}
@Service
public class PolicyServiceImpl implements PolicyService {@Autowiredprivate PolicyMapper policyMapper;@Overridepublic List<Policy> getPolicyList(String title, String status) {return policyMapper.selectByCondition(title, status);}@Overridepublic void addPolicy(Policy policy) {// 设置默认值if (policy.getStatus() == null) policy.setStatus("draft");if (policy.getViewCount() == null) policy.setViewCount(0);policyMapper.insert(policy);}@Overridepublic void updatePolicy(Policy policy) {policyMapper.update(policy);}@Overridepublic void deletePolicy(Integer id) {policyMapper.deleteById(id);}
}
mapper
@Mapper
public interface AdmissionMapper {List<Admission> selectAll(@Param("title") String title, @Param("status") String status, @Param("universityId") Integer universityId);@Delete("delete from admissions where id = #{id}")void deleteById(Integer id);@Insert({"INSERT INTO admissions (title, university_id, admission_year, content, publish_date, attachment_url, status, created_by, created_at)","VALUES (#{title}, #{universityId}, #{admissionYear}, #{content}, #{publishDate}, #{attachmentUrl}, #{status}, #{createdBy}, #{createdAt})"})@Options(useGeneratedKeys = true, keyProperty = "id") // 如果 `id` 是自增主键void insert(Admission admission);void update(Admission admission);@Select("select * from admissions where id = #{id}")Admission getById(Integer id);@Select("select * from admissions where university_id = #{universityId}")List<Admission> selectByUniversityId(Integer universityId);
}
@Mapper
public interface PostMapper {List<ForumPost> selectList(@Param("title")String title,@Param("status")String status,@Param("targetType") String targetType);@Update("update forum_posts set status = #{status} where id = #{id}")void updateStatus(@Param("id") Integer id,@Param("status") String status);@Insert("INSERT INTO forum_posts (title, content, author_id, target_type, target_id, status, is_top, view_count, reply_count, created_at, updated_at) " +"VALUES (#{title}, #{content}, #{authorId}, #{targetType}, #{targetId}, #{status}, #{isTop}, #{viewCount}, #{replyCount}, #{createdAt}, #{updatedAt})")void insert(ForumPost post);@Update({"<script>","UPDATE forum_posts","<set>","<if test='title != null'>title = #{title},</if>","<if test='content != null'>content = #{content},</if>","<if test='authorId != null'>author_id = #{authorId},</if>","<if test='targetType != null'>target_type = #{targetType},</if>","<if test='targetId != null'>target_id = #{targetId},</if>","<if test='status != null'>status = #{status},</if>","<if test='isTop != null'>is_top = #{isTop},</if>","<if test='viewCount != null'>view_count = #{viewCount},</if>","<if test='replyCount != null'>reply_count = #{replyCount},</if>","<if test='createdAt != null'>created_at = #{createdAt},</if>","<if test='updatedAt != null'>updated_at = #{updatedAt},</if>","</set>","WHERE id = #{id}","</script>"})void update(ForumPost post);
}
@Mapper
public interface UniversityMapper {// 分页查询院校列表(动态SQL)List<University> selectByCondition(@Param("name") String name, @Param("region") String region);// 新增院校@Insert("INSERT INTO universities (name, region, ranking, official_website, description, logo_url, created_by, created_at) " +"VALUES (#{name}, #{region}, #{ranking}, #{officialWebsite}, #{description}, #{logoUrl}, #{createdBy}, NOW())")@Options(useGeneratedKeys = true, keyProperty = "id")void insert(University university);// 更新院校@Update("UPDATE universities SET name=#{name}, region=#{region}, ranking=#{ranking}, " +"official_website=#{officialWebsite}, description=#{description}, logo_url=#{logoUrl} " +"WHERE id=#{id}")void update(University university);// 删除院校@Delete("DELETE FROM universities WHERE id=#{id}")void deleteById(Integer id);@Select("select * from universities where id = #{id}")University getById(Integer universityId);
}
相关文章:
Springboot考研信息平台
Springboot考研信息平台 文章目录 Springboot考研信息平台1、技术栈2、项目说明3、项目截图4、核心代码4.1、前端核心代码4.2、后端核心代码 1、技术栈 前端 Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。 Vue 作为前端核心框架,提供了响应式的数据绑定和高…...
Spring 框架 JDBC 模板技术详解
一、JDBC 模板技术概述 在传统 JDBC 开发中,开发人员需要手动处理数据库连接(Connection)、事务管理、语句执行(Statement)和结果集(ResultSet)等繁琐操作,不仅代码冗余度高&#x…...
Console Importer浏览器插件的编译 及 制作成.crx浏览器插件的步骤
近日由于下载Console Importer浏览器插件(一个前端调试窗口方便引下第三方库便于学习测试的插件)找不到资源,于是找到该插件的源码,地址:https://github.com/pd4d10/console-importer),发现该插件基于一款名为“Plasmo…...
ArcGIS切片方案记录bundle文件
文章目录 前言一、导入底图二、生成切片方案三、导出切片方案总结 前言 切片的作用是让前端可以访问地图的Mapsever来加载底图。arcgis切片是测绘人员或者WebGIs人员需要认识到的操作。 一、导入底图 首先10.8的ArcGis,这里没有Pro,Pro其实也是一样的操…...
山东大学计算机图形学期末复习6——CG10下
##CG10下 将世界坐标中的任意点 P P P 变换到以相机为中心的“观察坐标系”下(右手坐标系) n \mathbf{n} n:从相机眼睛朝向观察点的反方向,代表“前方”; u \mathbf{u} u:观察坐标系的 x 轴,向…...
【Spring Cloud Gateway】Nacos整合遇坑记:503 Service Unavailable
一、场景重现 最近在公司进行微服务架构升级,将原有的 Spring Cloud Hoxton 版本升级到最新的 2021.x 版本,同时使用 Nacos 作为服务注册中心和配置中心。在完成基础框架搭建后,我使用 Spring Cloud Gateway 作为API 网关,通过 N…...
[Linux]从零开始的STM32MP157 Busybox根文件系统测试及打包
一、前言 在上一篇教程中,我们成功编译了Busybox根文件系统并且能够正常使用,但是大家应该也发现了我们构建的根文件系统存在许多问题,比如一些找不到文件的报错。并且在实际的产品中一般都是将根文件系统烧录到EMMC中,并不是像我…...
【Pandas】pandas DataFrame eval
Pandas2.2 DataFrame Computations descriptive stats 方法描述DataFrame.abs()用于返回 DataFrame 中每个元素的绝对值DataFrame.all([axis, bool_only, skipna])用于判断 DataFrame 中是否所有元素在指定轴上都为 TrueDataFrame.any(*[, axis, bool_only, skipna])用于判断…...
考研408《计算机组成原理》复习笔记,第二章(2)数值数据的表示和运算(浮点数篇)
一、回顾定点数知识点 ——定点小数机器码表示 ——定点整数机器码表示 ——【原码】和【移码】的作用 二、浮点数表示 1、概念引入 我们生活中有很多 “带小数”,也就是浮点数,也就是【整数部分】和【纯小数部分】都不为0,那么这样的小数…...
【虚幻引擎】UE5独立游戏开发全流程(商业级架构)
本套课程我将会讲解一下知识 1.虚幻引擎的常用功能节点、模块包含但不限于动画模块、UI模块、AI模块、碰撞模块、伤害模块、背包模块、准心模块、武器模块、可拾取物品模块、死亡等模块。 2.整个游戏的设计思路(游戏架构),本套教程讲解了如…...
大语言模型 08 - 从0开始训练GPT 0.25B参数量 - MiniMind 单机多卡 torchrun deepspeed
写在前面 GPT(Generative Pre-trained Transformer)是目前最广泛应用的大语言模型架构之一,其强大的自然语言理解与生成能力背后,是一个庞大而精细的训练流程。本文将从宏观到微观,系统讲解GPT的训练过程,…...
使用gitbook 工具编写接口文档或博客
步骤一:在项目目录中初始化一个 GitBook 项目 mkdir mybook && cd mybook git init npm init -y步骤二:添加书籍结构(如 book.json, README.md) echo "# 我的书" > README.md echo "{}" > bo…...
Mysql视图详解
文章目录 1、视图简介 && 前置准备2、基本crud语法3、检查选项(with check option)CASCADEDLOCAL总结 4、视图更新限定条件 1、视图简介 && 前置准备 视图 (View) 是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,…...
leetcode 56. 合并区间
题目描述 代码: class Solution {struct Interval{int left;int right;Interval(int l0,int r0):left(l),right(r){}bool operator<(const Interval& rhs) const{return left<rhs.left;}};public:vector<vector<int>> merge(vector<vecto…...
Mac 环境下 JDK 版本切换全指南
概要 在 macOS 上安装了多个 JDK 后,可以通过系统自带的 /usr/libexec/java_home 工具来查询并切换不同版本的 Java。只需在终端中执行 /usr/libexec/java_home -V 列出所有已安装的 JDK,然后将你想使用的版本路径赋值给环境变量 JAVA_HOME,…...
【生活相关-日语-日本-东京-搬家后-引越(ひっこし)(3)-踩坑点:国民健康保险】
【生活相关-日语-日本-东京-搬家后-引越(ひっこし)(3)-注意点:国民健康保险】 1、前言2、情况说明(1)问题说明(2)情况说明(1)收到情况(…...
C++ asio网络编程(6)利用C11模拟伪闭包实现连接的安全回收
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、智能指针管理Session二、用智能指针来实现Server的函数1.start_accept()1.引用计数注意点2.std::bind 与异步回调函数的执行顺序分析 2.handle_accept1.异步…...
【视频】解决FFmpeg将RTSP转RTMP流时,出现的卡死、出错等问题
1、简述 如果不修改图像内容,可以使用FFmpeg命令来将RTSP转RTMP流。 SRS视频服务器就是这么干的,它没有使用FFmpeg接口,而是直接使用FFmpeg命令来转流。 但是在使用中,约到了一些问题,比如转流时卡死、转流出错等等,下面描述怎么解决这些问题 2、出错重启 在shell脚本…...
飞牛NAS本地部署开源TTS文本转语音工具EasyVoice与远程使用流程
文章目录 前言1. 环境准备2. Docker部署与运行3. 简单使用测试4. 安装内网穿透4.1 开启ssh连接安装cpolar4.2 创建公网地址 5. 配置固定公网地址总结 前言 本文主要介绍如何在fnOS飞牛云NAS使用Docker本地部署一款非常好用的开源TTS文本转语音工具EasyVoice,并结合…...
STC51系列单片机引脚分类与功能速查表(以STC89C52为例)
1. 基本I/O端口 端口引脚范围类型主要功能特殊说明P0P0.0~P0.7开漏双向I/O1. 通用I/O(需外接上拉电阻) 2. 数据总线(D0-D7) 3. 低8位地址总线(A0-A…...
recvfrom和sendto函数中地址参数的作用
在 UDP 通信中,recvfrom 和 sendto 函数中的地址参数起着至关重要的作用。 以下是对这两个函数中地址参数的作用、所属方以及缺失地址时的后果的详细解释。 recvfrom 函数 int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_add…...
运维职业发展思维导图
主要内容如下: 一、 初级入行阶段 这是职业生涯的起点,主要涉及基础技能的学习和实践。 Linux初学: 重点是学习Linux系统的基础命令和操作。IDC机房运维: 负责数据中心机房内设备的管理和日常维护工作。Helpdesk桌面运维: 提供桌面技术支持,帮助用户解决遇到的计算机软硬…...
【数据处理】Python对CMIP6数据进行插值——详细解析实现(附源码)
目录 Python对CMIP6数据进行插值一、引言代码概览思维导图 二、数据预处理三、数据区域裁剪四、插值(一) 垂直插值(二) 水平插值 五、保存插值好的文件六、文件合并与气候态计算七、代码优化技巧八、多线程处理九、全部代码 Pytho…...
worldquant rank函数
https://support.worldquantbrain.com/hc/en-us/community/posts/13869304934935-%E6%80%8E%E6%A0%B7%E7%90%86%E8%A7%A3rank%E5%87%BD%E6%95%B0 链接。进的话可以填我的邀请码JS34795我可以带你 现在学习rank函数 我们所说的做多和做空 首先,当我们讨论Long和S…...
工业4.0神经嫁接术:ethernet ip转profinet协议通信步骤图解
在现代工业自动化领域,不同品牌的设备和协议之间的兼容性问题一直是个挑战。我们的包装线项目就遇到了这样的难题:需要将Rockwell Allen-Bradley的EtherNet/IP伺服系统与西门子PLC的PROFINET主站进行无缝对接。为了解决这一问题,我们采用了et…...
数据库——数据操作语言DML
(2)数据操作语言DML 简称DML——Data Manipulation Language用来对数据库中表的记录进行更新关键字:insert,delete,update A、 插入表记录 向表中插入数据 格式:insert into 表名(字段1,字段2,字段3……) values(值1,值2,值3);…...
文件防泄密的措施有哪些?
文件防泄密措施需要从技术、管理和物理三个层面综合施策,以下为常见措施分类整理: 一、技术防护措施 华途加密技术 文件加密:使用AES、RSA等算法对敏感文件加密。 传输加密:通过SSL/TLS、VPN保障传输安全,禁止明文传…...
C++ Mac 打包运行方案(cmake)
文章目录 背景动态库梳理打包方案静态库处理动态库处理(PCL库)编译链接动态库后处理逻辑 批量信任 背景 使用C编写的一个小项目,需要打包成mac下的可执行文件(免安装版本),方便分发给其他mac执行,需要把项目的动态库都…...
数学复习笔记 10
前言 我觉得数学的高分乃至满分属于那些,聪明,坚韧,勇敢,细致的人。我非常惭愧自己不是这样的人,我在生活中发现了这样的同学,和他们交流的时候我常常感到汗流浃背,因为他们非常扎实的基础知识…...
Oracle-相关笔记
Oracle Database Online Documentation 11g 连接 WinR sqlplus username/passwordhostname:port/service_namesqlplus user02/123456192.xxx:1521/orclsqlplus / as sysdba #SQL*Plus 終端编码使用UTF-8 chcp 65001#打开SQL*Plus程序 sqlplus /nolog#使用dba角色登录(用 1.…...
mac安装cast
背景 pycharm本地运行脚本时提示cast没有安装 问题原因 脚本尝试调用cast命令(以太坊开发工具foundry中的子命令),但您的系统未安装该工具。 从日志可见,错误发生在通过sysutil.py执行shell命令时。 解决方案 方法1…...
CodeEdit:macOS上一款可以让Xcode退休的IDE
CodeEdit 是一款轻量级、原生构建的代码编辑器,完全免费且开源。它使用纯 swift 实现,而且专为 macOS 设计,旨在为开发者提供更高效、更可靠的编程环境,同时释放 Mac 的全部潜力。 Stars 数21,719Forks 数1,081 主要特点 macOS 原…...
opencv4.11编译Debug提示缺少python312_d.lib或python3*_d.lib的解决办法
前言 当我们编译OpenCV 4.11的时候可能会遇到提示缺少库文件,这个时候我们可以下载Python源码编译这个lib。 也可以下载我上传的版本(python312_d.lib),但是如果是其他版本需要自己编译。编译步骤如下,大概几分钟搞定…...
html的鼠标点击事件有哪些写法
在HTML中,鼠标点击事件的实现方式多样,以下从基础语法到现代实践为您详细梳理: 一、基础写法:直接内联事件属性 在HTML标签内通过on前缀事件属性绑定处理函数,适合简单交互场景: <!-- 单击事件 -->…...
深度解析物理机服务器故障修复时间:影响因素与优化策略
一、物理机故障修复的核心影响因素 物理机作为企业 IT 基础设施的核心载体,其故障修复效率直接关系到业务连续性。故障修复时间(MTTR)受多重因素交叉影响: 1. 故障类型的复杂性 硬件级故障: 简单故障:内存…...
蓝桥杯 2024 C++国 B最小字符串
P10910 [蓝桥杯 2024 国 B] 最小字符串 题目描述 给定一个长度为 N N N 且只包含小写字母的字符串 S S S,和 M M M 个小写字母 c 1 , c 2 , ⋯ , c M c_1, c_2, \cdots, c_M c1,c2,⋯,cM。现在你要把 M M M 个小写字母全部插入到字符串 S S S 中&…...
解密企业级大模型智能体Agentic AI 关键技术:MCP、A2A、Reasoning LLMs-docker MCP解析
解密企业级大模型智能体Agentic AI 关键技术:MCP、A2A、Reasoning LLMs-docker MCP解析 这里面有很重要的原因其中一个很其中一个原因是因为如果你使用docker的方式,你可以在虚拟环境下就类似于这个沙箱的这个机制可以进行隔离。这对于安全,…...
访问 Docker 官方镜像源(包括代理)全部被“重置连接”或超时
华为云轻量应用服务器(Ubuntu 系统) 遇到的问题是: 🔒 访问 Docker 官方镜像源(包括代理)全部被“重置连接”或超时了,说明你这台服务器的出境网络对这些国外域名限制很严格,常见于华…...
前馈神经网络回归(ANN Regression)从原理到实战
前馈神经网络回归(ANN Regression)从原理到实战 一、回归问题与前馈神经网络的适配性分析 在机器学习领域,回归任务旨在建立输入特征与连续型输出变量之间的映射关系。前馈神经网络(Feedforward Neural Network)作为最基础的神经网络架构&a…...
RNN/LSTM原理与 PyTorch 时间序列预测实战
🕰️ RNN / LSTM 原理与 PyTorch 时间序列预测实战 在处理时间序列数据、语音信号、文本序列等连续性强的问题时,循环神经网络(RNN)及其改进版本 LSTM(长短期记忆网络)是最常见也最有效的模型之一。本文将深入讲解 RNN 和 LSTM 的核心原理,并通过 PyTorch 实现一个时间…...
Docker容器镜像与容器常用操作指南
一、镜像基础操作 搜索镜像 docker search <镜像名>在Docker Hub中查找公开镜像,例如: docker search nginx拉取镜像 docker pull <镜像名>:<标签>从仓库拉取镜像到本地,标签默认为latest: docker pull nginx:a…...
1:OpenCV—图像基础
OpenCV教程 头文件 您只需要在程序中包含 opencv2/opencv.hpp 头文件。该头文件将包含应用程序的所有其他必需头文件。因此,您不再需要费心考虑程序应包含哪些头文件。 例如 - #include <opencv2/opencv.hpp>命名空间 所有 OpenCV 类和函数都在 cv 命名空…...
测试--BUG(软件测试⽣命周期 bug的⽣命周期 与开发产⽣争执怎么办)
1. 软件测试的⽣命周期 软件测试贯穿于软件的整个⽣命周期,针对这句话我们⼀起来看⼀下软件测试是如何贯穿软件的整个⽣命周期。 软件测试的⽣命周期是指测试流程,这个流程是按照⼀定顺序执⾏的⼀系列特定的步骤,去保证产品质量符合需求。在软…...
基于大模型预测围术期麻醉苏醒时间的技术方案
目录 一、数据收集与处理(一)数据来源(二)数据预处理二、大模型构建与训练(一)模型选择(二)模型训练三、围术期麻醉苏醒时间预测(一)术前预测(二)术中动态预测四、并发症风险预测(一)风险因素分析(二)风险预测模型五、基于预测制定手术方案(一)个性化手术规划…...
QT6 源(101)阅读与注释 QPlainTextEdit,其继承于QAbstractScrollArea,属性学习与测试
(1) (2) (3)属性学习与测试 : (4) (5) 谢谢...
电池组PACK自动化生产线:多领域电池生产的“智能引擎”
在电池产业蓬勃发展的当下,电池组PACK自动化生产线凭借其高效、精准、智能的优势,成为众多电池生产领域的核心装备。它广泛适用于数码电池、工具电池、储能电池、电动车电池以及动力电池的生产,有力推动了相关产业的升级与发展。 数码电池领…...
生成式AI在编程中的应用场景:从代码生成到安全检测
引言 生成式AI正在深刻改变软件开发的方式,从代码编写到测试、文档和维护,AI技术正在为每个环节带来革命性的变革。本文将深入探讨生成式AI在编程中的主要应用场景,分析其优势与局限性,并展望未来发展趋势。 主要应用场景 1. 代…...
安全牛报告解读《低空经济发展白皮书(3.0)安全体系》
一、概述 《低空经济发展白皮书(3.0)安全体系》由粤港澳大湾区数字经济研究院(IDEA研究院)发布,旨在构建低空经济安全发展的系统性框架,解决规模化低空飞行中的安全挑战。核心目标是明确安全体系需覆盖的飞…...
“2W2H”分析方法
“2W2H”是一种常用的分析方法,它通过回答**What(是什么)、Why(为什么)、How(怎么做)、How much(多少)**这四个问题来全面了解和分析一个事物或问题。这种方法可以帮助你…...
【数据挖掘笔记】兴趣度度量Interest of an association rule
在数据挖掘中,关联规则挖掘是一个重要的任务。兴趣度度量是评估关联规则的重要指标,以下是三个常用的兴趣度度量:支持度、置信度和提升度。 支持度(Support) 计算方法 支持度表示包含项集的事务占总事务的比例&…...