当前位置: 首页 > news >正文

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、项目说明
  1. 项目采用前后端分离的架构模式,这种架构模式具有诸多显著的优势。
    • 开发效率提升 :前端开发人员和后端开发人员可以相对独立地进行开发工作,无需相互等待。前端人员可以专注于使用 Vue 等前端技术构建用户界面和交互逻辑,后端人员则专注于利用 Springboot 等后端技术实现业务逻辑和数据处理功能,从而提高了整体开发效率。
    • 技术选型灵活 :前后端分离使得前端和后端可以各自选择最适合的技术栈。前端可以根据项目的 UI 设计和用户体验需求选择 Vue 框架配合 Element Plus UI 等组件库,后端则可以根据业务复杂度、性能要求等因素选择 Springboot 框架结合 Mybatis 等技术,这种灵活性有利于充分发挥各技术的优势,满足项目不同的需求。
    • 可维护性和可扩展性增强 :前后端代码分离,各自形成了独立的模块体系,便于代码的管理和维护。当需要对前端界面进行优化或者对后端业务逻辑进行调整时,可以只针对相应的部分进行修改,而不会对另一部分产生过多的影响。同时,这种分离架构也为项目的后续扩展提供了便利,例如,未来可以方便地添加新的前端功能模块或者后端服务模块,以满足考研信息平台不断发展的业务需求。
  2. 在项目运行时,前端和后端通过定义好的 API 接口进行通信。前端通过 Axios 向端后发送 HTTP 请求,包括获取考研信息、用户注册登录等操作的请求;后端接收到请求后,利用 Springboot 进行业务逻辑处理,通过 Mybatis 操作 MySQL 数据库获取或存储数据,再将处理结果以 JSON 等格式返回给前端,前端再根据返回的数据更新页面显示,实现了前后端的紧密协作,为用户提供高质量的考研信息服务。
3、项目截图

登录注册

​ 平台提供了简洁、便捷的登录注册界面。登录界面包含用户名和密码输入框,以及登录按钮。用户可以输入自己的账号信息进行登录,新用户则可以通过注册界面进行账号注册。注册界面要求填写用户名、密码、邮箱等基本信息,并设置有验证码功能,以确保注册账号的安全性。同时,登录注册界面还设计有忘记密码的找回功能,方便用户在忘记密码时能够快速找回账号登录。

image-20250515202629060

image-20250515202650116

管理员端

​ 管理员登录后进入的管理端界面功能丰富。主要包含考研信息管理模块,可以添加、编辑、删除考研院校和专业的信息,如更新院校的招生简章、专业目录等;还有用户管理模块,能够查看和管理用户的基本信息,处理用户反馈和投诉;此外,还包括数据分析模块,通过图表展示考研相关的数据统计,如用户浏览量、注册用户数量变化趋势等,帮助管理员了解平台的运营情况,以便更好地进行决策和管理。

image-20250515202854735

image-20250515202908378

image-20250515202919816

image-20250515202929621

image-20250515202938507

image-20250515202948464

image-20250515203001176

学生端

​ 学生端界面以提供考研信息查询和学习服务为主。界面包含考研院校和专业查询功能,学生可以通过输入关键词或者筛选条件(如地区、学科类别等)快速查询到自己感兴趣的院校和专业信息。同时,学生端还提供考研资讯推送功能,展示最新的考研政策动态、考试安排、备考技巧等文章,方便学生及时获取考研信息。

image-20250515202739917

image-20250515202754628

image-20250515202803376

image-20250515202812817

学校负责人端

​ 学校负责人登录后可查看本校的考研信息统计情况,如报考本校的人数、各专业的报考热度等。同时,可以对本校的考研信息进行审核和发布,确保信息的准确性和及时性。还可以与其他学校进行考研合作交流的信息对接和管理,促进学校间考研资源的共享和协同。

image-20250515203034286

image-20250515203052343

image-20250515203058974

image-20250515203105139

image-20250515203111818

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);
}

相关文章:

  • LLM Text2SQL NL2SQL 实战总结
  • MongoDB数据库深度解析:架构、特性与应用场景
  • 呼叫中心高可用方案:全方位保障客服业务持续稳定
  • 7、MinIO服务器简介与安装
  • Python3 简易DNS服务器实现
  • Python机器学习笔记(二十三 模型评估与改进-网格搜索)
  • 20、工业协议转换与数据采集中间件 (模拟) - /数据与物联网组件/protocol-converter-middleware
  • 全球宠物经济新周期下的亚马逊跨境采购策略革新——宠物用品赛道成本优化三维路径
  • IP防护等级举例解析
  • 专项智能练习(加强题型)-DA-02
  • websocket入门详解
  • 【Ubuntu】安装BitComet种子下载器
  • 远程实时控制安卓模拟器技术scrcpy
  • 基于EtherCAT与ABP vNext 构建高可用、高性能的工业自动化平台
  • 软考 系统架构设计师系列知识点之杂项集萃(60)
  • Metagloves Pro+Manus Core:一套组合拳打通虚拟制作与现实工业的任督二脉
  • 【笔记】CosyVoice 模型下载小记:简单易懂的两种方法对比
  • Trae 插件 Builder 模式:从 0 到 1 开发天气查询小程序,解锁 AI 编程新体验
  • 康复训练:VR 老年虚拟仿真,趣味助力恢复​
  • IP地址查询可以了解到哪些宿主信息
  • 会谈时间迟迟未定、核心议题存在分歧,俄乌“土耳其谈判”一波三折
  • 圆桌丨新能源车超充技术元年,专家呼吁重视电网承载能力可能面临的结构性挑战
  • 华东政法与复旦上医签署合作框架协议,医学与法学如何交叉融合?
  • 【社论】打破“隐形高墙”,让老年人更好融入社会
  • 赖清德为“临阵脱逃”作准备,国台办:绝不会任“台独”祸首逍遥法外
  • 上海北外滩,未来五年将如何“长个子”“壮筋骨”?