【Web应用】若依框架:基础篇14 源码阅读-后端代码分析-课程管理模块前后端代码分析
文章目录
- 一、课程管理模块前端代码截图
- 二、前端代码及分析
- index.vue
- course.js
- 三、前端执行流程
- 1. 组件初始化
- 2. 查询操作
- 3. 列表操作
- 4. 对话框操作
- 5. API 请求
- 6. 执行流程总结
- 关键点
- 四、课程管理模块后端代码截图
- 五、后端代码块
- CourseController
- ICourseService
- CourseMapper
- Course
- 六、后端执行逻辑
- 1. `Course` 类
- 2. `CourseMapper` 接口
- 3. `ICourseService` 接口
- 4. `CourseController` 类
- 调用关系总结
- 示例流程
- 七、前后端交互执行流程
- **1. 初始化阶段**
- **前端操作**
- **后端处理**
- **前端响应**
- **2. 查询操作**
- **前端操作**
- **后端处理**
- **前端响应**
- **3. 新增课程**
- **前端操作**
- **后端处理**
- **前端响应**
- **4. 修改课程**
- **前端操作**
- **后端处理**
- **前端响应**
- **5. 删除课程**
- **前端操作**
- **后端处理**
- **前端响应**
- **6. 导出操作**
- **前端操作**
- **后端处理**
- **前端响应**
- **7. 总结:前后端交互流程**
- **关键点**
一、课程管理模块前端代码截图
二、前端代码及分析
index.vue
<template><div class="app-container"><el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="课程编码" prop="code"><el-inputv-model="queryParams.code"placeholder="请输入课程编码"clearable@keyup.enter="handleQuery"/></el-form-item><el-form-item label="课程学科" prop="subject"><el-select v-model="queryParams.subject" placeholder="请选择课程学科" clearable><el-optionv-for="dict in course_subject":key="dict.value":label="dict.label":value="dict.value"/></el-select></el-form-item><el-form-item label="课程名称" prop="name"><el-inputv-model="queryParams.name"placeholder="请输入课程名称"clearable@keyup.enter="handleQuery"/></el-form-item><el-form-item label="适用人群" prop="applicablePerson"><el-inputv-model="queryParams.applicablePerson"placeholder="请输入适用人群"clearable@keyup.enter="handleQuery"/></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"plainicon="Plus"@click="handleAdd"v-hasPermi="['course:course:add']">新增</el-button></el-col><el-col :span="1.5"><el-buttontype="success"plainicon="Edit":disabled="single"@click="handleUpdate"v-hasPermi="['course:course:edit']">修改</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="Delete":disabled="multiple"@click="handleDelete"v-hasPermi="['course:course:remove']">删除</el-button></el-col><el-col :span="1.5"><el-buttontype="warning"plainicon="Download"@click="handleExport"v-hasPermi="['course:course:export']">导出</el-button></el-col><right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar></el-row><el-table v-loading="loading" :data="courseList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="课程id" align="center" prop="id" /><el-table-column label="课程编码" align="center" prop="code" /><el-table-column label="课程学科" align="center" prop="subject"><template #default="scope"><dict-tag :options="course_subject" :value="scope.row.subject"/></template></el-table-column><el-table-column label="课程名称" align="center" prop="name" /><el-table-column label="价格" align="center" prop="price" /><el-table-column label="适用人群" align="center" prop="applicablePerson" /><el-table-column label="课程介绍" align="center" prop="info" /><el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['course:course:edit']">修改</el-button><el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['course:course:remove']">删除</el-button></template></el-table-column></el-table><paginationv-show="total>0":total="total"v-model:page="queryParams.pageNum"v-model:limit="queryParams.pageSize"@pagination="getList"/><!-- 添加或修改课程管理对话框 --><el-dialog :title="title" v-model="open" width="500px" append-to-body><el-form ref="courseRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="课程编码" prop="code"><el-input v-model="form.code" placeholder="请输入课程编码" /></el-form-item><el-form-item label="课程学科" prop="subject"><el-select v-model="form.subject" placeholder="请选择课程学科"><el-optionv-for="dict in course_subject":key="dict.value":label="dict.label":value="dict.value"></el-option></el-select></el-form-item><el-form-item label="课程名称" prop="name"><el-input v-model="form.name" placeholder="请输入课程名称" /></el-form-item><el-form-item label="价格" prop="price"><el-input v-model="form.price" placeholder="请输入价格" /></el-form-item><el-form-item label="适用人群" prop="applicablePerson"><el-input v-model="form.applicablePerson" placeholder="请输入适用人群" /></el-form-item><el-form-item label="课程介绍" prop="info"><el-input v-model="form.info" placeholder="请输入课程介绍" /></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></template></el-dialog></div>
</template><script setup name="Course">
import { listCourse, getCourse, delCourse, addCourse, updateCourse } from "@/api/course/course"const { proxy } = getCurrentInstance()
const { course_subject } = proxy.useDict('course_subject')const courseList = ref([])
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")const data = reactive({form: {},queryParams: {pageNum: 1,pageSize: 10,code: null,subject: null,name: null,applicablePerson: null,},rules: {code: [{ required: true, message: "课程编码不能为空", trigger: "blur" }],subject: [{ required: true, message: "课程学科不能为空", trigger: "change" }],name: [{ required: true, message: "课程名称不能为空", trigger: "blur" }],price: [{ required: true, message: "价格不能为空", trigger: "blur" }],applicablePerson: [{ required: true, message: "适用人群不能为空", trigger: "blur" }],info: [{ required: true, message: "课程介绍不能为空", trigger: "blur" }],}
})const { queryParams, form, rules } = toRefs(data)/** 查询课程管理列表 */
function getList() {loading.value = truelistCourse(queryParams.value).then(response => {courseList.value = response.rowstotal.value = response.totalloading.value = false})
}// 取消按钮
function cancel() {open.value = falsereset()
}// 表单重置
function reset() {form.value = {id: null,code: null,subject: null,name: null,price: null,applicablePerson: null,info: null,createTime: null,updateTime: null}proxy.resetForm("courseRef")
}/** 搜索按钮操作 */
function handleQuery() {queryParams.value.pageNum = 1getList()
}/** 重置按钮操作 */
function resetQuery() {proxy.resetForm("queryRef")handleQuery()
}// 多选框选中数据
function handleSelectionChange(selection) {ids.value = selection.map(item => item.id)single.value = selection.length != 1multiple.value = !selection.length
}/** 新增按钮操作 */
function handleAdd() {reset()open.value = truetitle.value = "添加课程管理"
}/** 修改按钮操作 */
function handleUpdate(row) {reset()const _id = row.id || ids.valuegetCourse(_id).then(response => {form.value = response.dataopen.value = truetitle.value = "修改课程管理"})
}/** 提交按钮 */
function submitForm() {proxy.$refs["courseRef"].validate(valid => {if (valid) {if (form.value.id != null) {updateCourse(form.value).then(response => {proxy.$modal.msgSuccess("修改成功")open.value = falsegetList()})} else {addCourse(form.value).then(response => {proxy.$modal.msgSuccess("新增成功")open.value = falsegetList()})}}})
}/** 删除按钮操作 */
function handleDelete(row) {const _ids = row.id || ids.valueproxy.$modal.confirm('是否确认删除课程管理编号为"' + _ids + '"的数据项?').then(function() {return delCourse(_ids)}).then(() => {getList()proxy.$modal.msgSuccess("删除成功")}).catch(() => {})
}/** 导出按钮操作 */
function handleExport() {proxy.download('course/course/export', {...queryParams.value}, `course_${new Date().getTime()}.xlsx`)
}getList()
</script>
course.js
import request from '@/utils/request'// 查询课程管理列表
export function listCourse(query) {return request({url: '/course/course/list',method: 'get',params: query})
}// 查询课程管理详细
export function getCourse(id) {return request({url: '/course/course/' + id,method: 'get'})
}// 新增课程管理
export function addCourse(data) {return request({url: '/course/course',method: 'post',data: data})
}// 修改课程管理
export function updateCourse(data) {return request({url: '/course/course',method: 'put',data: data})
}// 删除课程管理
export function delCourse(id) {return request({url: '/course/course/' + id,method: 'delete'})
}
三、前端执行流程
根据提供的 Vue 组件代码和 API 请求代码,我们可以分析出前端在执行过程中的主要步骤和逻辑。以下是详细的执行过程分析:
1. 组件初始化
-
模板渲染:
- 组件模板中定义了一个课程管理的界面,包括搜索表单、操作按钮、课程列表表格、分页组件以及添加/修改课程的对话框。
- 使用了 Element Plus 组件库(如
el-form
、el-table
、el-dialog
等)。
-
脚本初始化:
- 引入了课程管理的 API 请求函数(
listCourse
、getCourse
、delCourse
、addCourse
、updateCourse
)。 - 定义了组件的响应式数据,包括课程列表(
courseList
)、查询参数(queryParams
)、表单数据(form
)、对话框状态(open
)等。 - 从字典中获取课程学科数据(
course_subject
)。
- 引入了课程管理的 API 请求函数(
-
初始化请求:
- 调用
getList()
方法获取课程列表数据,渲染到表格中。
- 调用
2. 查询操作
-
搜索表单:
- 用户可以在搜索表单中输入课程编码、选择课程学科、输入课程名称和适用人群。
- 点击“搜索”按钮时,触发
handleQuery()
方法。 handleQuery()
方法将pageNum
重置为 1,然后调用getList()
方法。
-
重置表单:
- 点击“重置”按钮时,触发
resetQuery()
方法。 resetQuery()
方法重置查询表单,然后调用handleQuery()
方法重新获取数据。
- 点击“重置”按钮时,触发
3. 列表操作
-
表格数据:
- 课程列表数据通过
el-table
渲染,每行数据包括课程 ID、编码、学科、名称、价格、适用人群和介绍。 - 表格支持多选,选中数据后更新
ids
、single
和multiple
状态。
- 课程列表数据通过
-
操作按钮:
- 新增:点击“新增”按钮时,触发
handleAdd()
方法,重置表单并打开对话框。 - 修改:点击“修改”按钮时,触发
handleUpdate()
方法,获取课程详情并打开对话框。 - 删除:点击“删除”按钮时,触发
handleDelete()
方法,弹出确认对话框,确认后调用delCourse()
删除课程。 - 导出:点击“导出”按钮时,触发
handleExport()
方法,调用proxy.download()
导出课程数据。
- 新增:点击“新增”按钮时,触发
4. 对话框操作
-
添加/修改课程:
- 对话框中包含课程表单,用户可以输入课程编码、选择学科、输入名称、价格、适用人群和介绍。
- 点击“确定”按钮时,触发
submitForm()
方法。 submitForm()
方法验证表单数据,根据是否有id
决定是新增还是修改课程,调用addCourse()
或updateCourse()
方法。- 操作成功后,关闭对话框并重新获取课程列表。
-
取消操作:
- 点击“取消”按钮时,触发
cancel()
方法,关闭对话框并重置表单。
- 点击“取消”按钮时,触发
5. API 请求
- 请求函数:
listCourse(query)
:发送 GET 请求,获取课程列表数据。getCourse(id)
:发送 GET 请求,获取单个课程的详细信息。addCourse(data)
:发送 POST 请求,新增课程。updateCourse(data)
:发送 PUT 请求,修改课程。delCourse(id)
:发送 DELETE 请求,删除课程。
6. 执行流程总结
- 组件初始化时,调用
getList()
获取课程列表。 - 用户通过搜索表单或分页组件触发查询操作,调用
getList()
更新数据。 - 用户点击操作按钮(新增、修改、删除、导出)时,触发相应的方法。
- 对话框中的表单提交时,调用
submitForm()
方法,根据操作类型调用对应的 API 请求。 - API 请求成功后,更新界面数据(如关闭对话框、重新获取列表等)。
关键点
- 响应式数据:使用
ref
和reactive
定义响应式数据,确保界面实时更新。 - API 请求:通过
request
工具函数封装 HTTP 请求,简化代码。 - 表单验证:使用
el-form
的验证规则确保用户输入的有效性。 - 组件通信:通过
props
和emit
实现父子组件通信(如right-toolbar
组件)。
通过以上分析,可以清晰地了解该前端组件的执行过程和逻辑。
四、课程管理模块后端代码截图
五、后端代码块
CourseController
package com.ruoyi.course.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.course.domain.Course;
import com.ruoyi.course.service.ICourseService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;/*** 课程管理Controller* * @author ruoyi* @date 2025-05-27*/
@RestController
@RequestMapping("/course/course")
public class CourseController extends BaseController
{@Autowiredprivate ICourseService courseService;/*** 查询课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:list')")@GetMapping("/list")public TableDataInfo list(Course course){startPage();List<Course> list = courseService.selectCourseList(course);return getDataTable(list);}/*** 导出课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:export')")@Log(title = "课程管理", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, Course course){List<Course> list = courseService.selectCourseList(course);ExcelUtil<Course> util = new ExcelUtil<Course>(Course.class);util.exportExcel(response, list, "课程管理数据");}/*** 获取课程管理详细信息*/@PreAuthorize("@ss.hasPermi('course:course:query')")@GetMapping(value = "/{id}")public AjaxResult getInfo(@PathVariable("id") Long id){return success(courseService.selectCourseById(id));}/*** 新增课程管理*/@PreAuthorize("@ss.hasPermi('course:course:add')")@Log(title = "课程管理", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody Course course){return toAjax(courseService.insertCourse(course));}/*** 修改课程管理*/@PreAuthorize("@ss.hasPermi('course:course:edit')")@Log(title = "课程管理", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody Course course){return toAjax(courseService.updateCourse(course));}/*** 删除课程管理*/@PreAuthorize("@ss.hasPermi('course:course:remove')")@Log(title = "课程管理", businessType = BusinessType.DELETE)@DeleteMapping("/{ids}")public AjaxResult remove(@PathVariable Long[] ids){return toAjax(courseService.deleteCourseByIds(ids));}
}
ICourseService
package com.ruoyi.course.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.course.domain.Course;
import com.ruoyi.course.service.ICourseService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;/*** 课程管理Controller* * @author ruoyi* @date 2025-05-27*/
@RestController
@RequestMapping("/course/course")
public class CourseController extends BaseController
{@Autowiredprivate ICourseService courseService;/*** 查询课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:list')")@GetMapping("/list")public TableDataInfo list(Course course){startPage();List<Course> list = courseService.selectCourseList(course);return getDataTable(list);}/*** 导出课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:export')")@Log(title = "课程管理", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, Course course){List<Course> list = courseService.selectCourseList(course);ExcelUtil<Course> util = new ExcelUtil<Course>(Course.class);util.exportExcel(response, list, "课程管理数据");}/*** 获取课程管理详细信息*/@PreAuthorize("@ss.hasPermi('course:course:query')")@GetMapping(value = "/{id}")public AjaxResult getInfo(@PathVariable("id") Long id){return success(courseService.selectCourseById(id));}/*** 新增课程管理*/@PreAuthorize("@ss.hasPermi('course:course:add')")@Log(title = "课程管理", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody Course course){return toAjax(courseService.insertCourse(course));}/*** 修改课程管理*/@PreAuthorize("@ss.hasPermi('course:course:edit')")@Log(title = "课程管理", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody Course course){return toAjax(courseService.updateCourse(course));}/*** 删除课程管理*/@PreAuthorize("@ss.hasPermi('course:course:remove')")@Log(title = "课程管理", businessType = BusinessType.DELETE)@DeleteMapping("/{ids}")public AjaxResult remove(@PathVariable Long[] ids){return toAjax(courseService.deleteCourseByIds(ids));}
}
CourseMapper
package com.ruoyi.course.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.course.domain.Course;
import com.ruoyi.course.service.ICourseService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;/*** 课程管理Controller* * @author ruoyi* @date 2025-05-27*/
@RestController
@RequestMapping("/course/course")
public class CourseController extends BaseController
{@Autowiredprivate ICourseService courseService;/*** 查询课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:list')")@GetMapping("/list")public TableDataInfo list(Course course){startPage();List<Course> list = courseService.selectCourseList(course);return getDataTable(list);}/*** 导出课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:export')")@Log(title = "课程管理", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, Course course){List<Course> list = courseService.selectCourseList(course);ExcelUtil<Course> util = new ExcelUtil<Course>(Course.class);util.exportExcel(response, list, "课程管理数据");}/*** 获取课程管理详细信息*/@PreAuthorize("@ss.hasPermi('course:course:query')")@GetMapping(value = "/{id}")public AjaxResult getInfo(@PathVariable("id") Long id){return success(courseService.selectCourseById(id));}/*** 新增课程管理*/@PreAuthorize("@ss.hasPermi('course:course:add')")@Log(title = "课程管理", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody Course course){return toAjax(courseService.insertCourse(course));}/*** 修改课程管理*/@PreAuthorize("@ss.hasPermi('course:course:edit')")@Log(title = "课程管理", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody Course course){return toAjax(courseService.updateCourse(course));}/*** 删除课程管理*/@PreAuthorize("@ss.hasPermi('course:course:remove')")@Log(title = "课程管理", businessType = BusinessType.DELETE)@DeleteMapping("/{ids}")public AjaxResult remove(@PathVariable Long[] ids){return toAjax(courseService.deleteCourseByIds(ids));}
}
Course
package com.ruoyi.course.controller;import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.course.domain.Course;
import com.ruoyi.course.service.ICourseService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;/*** 课程管理Controller* * @author ruoyi* @date 2025-05-27*/
@RestController
@RequestMapping("/course/course")
public class CourseController extends BaseController
{@Autowiredprivate ICourseService courseService;/*** 查询课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:list')")@GetMapping("/list")public TableDataInfo list(Course course){startPage();List<Course> list = courseService.selectCourseList(course);return getDataTable(list);}/*** 导出课程管理列表*/@PreAuthorize("@ss.hasPermi('course:course:export')")@Log(title = "课程管理", businessType = BusinessType.EXPORT)@PostMapping("/export")public void export(HttpServletResponse response, Course course){List<Course> list = courseService.selectCourseList(course);ExcelUtil<Course> util = new ExcelUtil<Course>(Course.class);util.exportExcel(response, list, "课程管理数据");}/*** 获取课程管理详细信息*/@PreAuthorize("@ss.hasPermi('course:course:query')")@GetMapping(value = "/{id}")public AjaxResult getInfo(@PathVariable("id") Long id){return success(courseService.selectCourseById(id));}/*** 新增课程管理*/@PreAuthorize("@ss.hasPermi('course:course:add')")@Log(title = "课程管理", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody Course course){return toAjax(courseService.insertCourse(course));}/*** 修改课程管理*/@PreAuthorize("@ss.hasPermi('course:course:edit')")@Log(title = "课程管理", businessType = BusinessType.UPDATE)@PutMappingpublic AjaxResult edit(@RequestBody Course course){return toAjax(courseService.updateCourse(course));}/*** 删除课程管理*/@PreAuthorize("@ss.hasPermi('course:course:remove')")@Log(title = "课程管理", businessType = BusinessType.DELETE)@DeleteMapping("/{ids}")public AjaxResult remove(@PathVariable Long[] ids){return toAjax(courseService.deleteCourseByIds(ids));}
}
六、后端执行逻辑
在这个项目结构中,course
包下的四个绿色类文件(CourseController
、Course
、CourseMapper
、ICourseService
)通常在若依(RuoYi)框架中扮演不同的角色,它们之间的调用关系如下:
1. Course
类
- 角色:领域模型(Domain Model)或实体类。
- 作用:表示课程(Course)的基本信息,通常与数据库中的
course
表对应。它包含了课程的属性,如课程名称、描述、创建时间等。 - 调用关系:不直接调用其他类,而是被其他类(如
CourseMapper
、CourseService
)使用。
2. CourseMapper
接口
- 角色:数据访问层(DAO)。
- 作用:定义了与数据库交互的方法,如插入、查询、更新和删除课程信息。通常使用 MyBatis 框架,通过 XML 映射文件或注解实现 SQL 语句。
- 调用关系:
- 被
CourseService
调用,用于执行数据库操作。 - 依赖于
Course
类,因为它的方法通常以Course
对象作为参数或返回值。
- 被
3. ICourseService
接口
- 角色:服务层接口。
- 作用:定义了与课程相关的业务逻辑方法,如添加课程、查询课程列表等。它是对外暴露的业务接口。
- 调用关系:
- 被
CourseController
调用,用于处理业务逻辑。 - 依赖于
CourseMapper
,因为业务逻辑可能需要访问数据库。
- 被
4. CourseController
类
- 角色:控制层(Controller)。
- 作用:处理 HTTP 请求,接收前端传来的参数,调用
CourseService
的方法执行业务逻辑,并返回响应结果。 - 调用关系:
- 调用
ICourseService
的方法,执行业务逻辑。 - 依赖于
Course
类,因为请求参数和响应数据通常以Course
对象的形式传递。
- 调用
调用关系总结
- 前端请求 →
CourseController
。 CourseController
→ 调用ICourseService
的方法。ICourseService
的实现类 → 调用CourseMapper
的方法。CourseMapper
→ 执行数据库操作,操作Course
对象。- 最终结果通过
CourseController
返回给前端。
示例流程
假设有一个添加课程的请求:
- 前端发送添加课程的请求到
CourseController
。 CourseController
调用ICourseService.addCourse()
方法。CourseServiceImpl
(ICourseService
的实现类)调用CourseMapper.insertCourse()
方法。CourseMapper
执行 SQL 插入语句,将课程信息存入数据库。- 返回操作结果,最终由
CourseController
返回给前端。
这种分层架构(Controller-Service-Mapper-Domain)是典型的 Web 应用设计模式,有助于代码的模块化和可维护性。
七、前后端交互执行流程
我们可以详细讲解整个前后端交互的过程与步骤。以下是完整的交互流程:
1. 初始化阶段
前端操作
- 组件加载:
- Vue 组件初始化时,调用
getList()
方法。 getList()
方法调用listCourse(queryParams)
API 请求,向后端发送 GET 请求,携带查询参数(如pageNum
、pageSize
、code
等)。
- Vue 组件初始化时,调用
后端处理
- 接收请求:
- 后端
CourseController
接收/course/course/list
的 GET 请求。
- 后端
- 业务逻辑:
- 调用
ICourseService.listCourse(queryParams)
方法,查询数据库获取课程列表。
- 调用
- 返回数据:
- 返回查询结果,格式通常为
{ rows: [...], total: number }
。
- 返回查询结果,格式通常为
前端响应
- 数据渲染:
- 前端接收后端返回的数据,更新
courseList
和total
,渲染到表格和分页组件。
- 前端接收后端返回的数据,更新
2. 查询操作
前端操作
- 用户输入查询条件:
- 用户在搜索表单中输入课程编码、选择学科等。
- 触发查询:
- 点击“搜索”按钮,调用
handleQuery()
方法。 handleQuery()
重置pageNum
为 1,并调用getList()
。
- 点击“搜索”按钮,调用
后端处理
- 接收请求:
- 后端接收新的查询参数,重复初始化阶段的查询逻辑。
- 返回数据:
- 返回符合查询条件的课程列表。
前端响应
- 更新界面:
- 前端更新
courseList
和total
,表格重新渲染。
- 前端更新
3. 新增课程
前端操作
- 打开对话框:
- 点击“新增”按钮,调用
handleAdd()
方法,打开对话框并重置表单。
- 点击“新增”按钮,调用
- 提交表单:
- 用户填写课程信息,点击“确定”按钮,调用
submitForm()
方法。 submitForm()
验证表单数据,调用addCourse(form)
API 请求,向后端发送 POST 请求,携带课程数据。
- 用户填写课程信息,点击“确定”按钮,调用
后端处理
- 接收请求:
- 后端
CourseController
接收/course/course
的 POST 请求。
- 后端
- 业务逻辑:
- 调用
ICourseService.addCourse(course)
方法,将课程数据插入数据库。
- 调用
- 返回结果:
- 返回操作结果(如成功或失败信息)。
前端响应
- 成功处理:
- 前端接收成功响应,关闭对话框,调用
getList()
重新获取课程列表。
- 前端接收成功响应,关闭对话框,调用
- 失败处理:
- 显示错误提示(如
proxy.$modal.msgError("新增失败")
)。
- 显示错误提示(如
4. 修改课程
前端操作
- 打开对话框:
- 点击“修改”按钮,调用
handleUpdate(row)
方法。 - 调用
getCourse(id)
API 请求,获取课程详情。
- 点击“修改”按钮,调用
- 填充表单:
- 后端返回课程详情,填充到表单中。
- 提交表单:
- 用户修改信息后点击“确定”,调用
submitForm()
方法。 submitForm()
验证数据,调用updateCourse(form)
API 请求,向后端发送 PUT 请求,携带更新后的课程数据。
- 用户修改信息后点击“确定”,调用
后端处理
- 接收请求:
- 后端
CourseController
接收/course/course
的 PUT 请求。
- 后端
- 业务逻辑:
- 调用
ICourseService.updateCourse(course)
方法,更新数据库中的课程数据。
- 调用
- 返回结果:
- 返回操作结果。
前端响应
- 成功处理:
- 前端接收成功响应,关闭对话框,调用
getList()
重新获取课程列表。
- 前端接收成功响应,关闭对话框,调用
- 失败处理:
- 显示错误提示。
5. 删除课程
前端操作
- 触发删除:
- 点击“删除”按钮,调用
handleDelete(row)
方法。 - 弹出确认对话框,用户确认后调用
delCourse(ids)
API 请求,向后端发送 DELETE 请求,携带课程 ID。
- 点击“删除”按钮,调用
后端处理
- 接收请求:
- 后端
CourseController
接收/course/course/{id}
的 DELETE 请求。
- 后端
- 业务逻辑:
- 调用
ICourseService.deleteCourseById(id)
方法,从数据库中删除课程数据。
- 调用
- 返回结果:
- 返回操作结果。
前端响应
- 成功处理:
- 前端接收成功响应,调用
getList()
重新获取课程列表。
- 前端接收成功响应,调用
- 失败处理:
- 显示错误提示。
6. 导出操作
前端操作
- 触发导出:
- 点击“导出”按钮,调用
handleExport()
方法。 - 调用
proxy.download()
方法,向后端发送导出请求,携带查询参数。
- 点击“导出”按钮,调用
后端处理
- 接收请求:
- 后端
CourseController
接收/course/course/export
的请求。
- 后端
- 业务逻辑:
- 调用
ICourseService.exportCourse(queryParams)
方法,生成 Excel 文件。
- 调用
- 返回文件:
- 返回文件流,前端下载。
前端响应
- 下载文件:
- 前端接收文件流,触发浏览器下载。
7. 总结:前后端交互流程
- 前端发起请求:
- 用户操作(如查询、新增、修改、删除、导出)触发前端 API 请求。
- 后端处理请求:
- 后端
Controller
接收请求,调用Service
层处理业务逻辑,操作数据库。
- 后端
- 后端返回响应:
- 返回 JSON 数据或文件流。
- 前端处理响应:
- 成功时更新界面数据或触发下载。
- 失败时显示错误提示。
关键点
- API 设计:前后端通过 RESTful API 交互,明确请求路径、方法和参数。
- 数据格式:通常使用 JSON 格式传输数据,导出时使用文件流。
- 错误处理:前后端均需处理异常情况(如网络错误、数据验证失败)。
- 用户体验:前端通过加载状态(如
v-loading
)和提示信息(如msgSuccess
)优化用户体验。
通过以上流程,前后端协同工作,实现了课程管理的完整功能。
相关文章:

【Web应用】若依框架:基础篇14 源码阅读-后端代码分析-课程管理模块前后端代码分析
文章目录 一、课程管理模块前端代码截图二、前端代码及分析index.vuecourse.js 三、前端执行流程1. 组件初始化2. 查询操作3. 列表操作4. 对话框操作5. API 请求6. 执行流程总结关键点 四、课程管理模块后端代码截图五、后端代码块CourseControllerICourseServiceCourseMapperC…...
在 Linux 上安装 `pgvector`(这是一个 PostgreSQL 的向量类型扩展,常用于处理嵌入向量,便于进行向量相似度搜索)
1. 安装 PostgreSQL 确保你已经安装好 PostgreSQL 数据库。 例如在 Ubuntu 上: sudo apt update sudo apt install postgresql postgresql-contrib2. 安装依赖 pgvector 扩展用的是 make、gcc 等开发工具,因此你需要先安装 PostgreSQL 的开发包和编译…...
09.MySQL内外连接
09.MySQL内外连接 文章目录 MySQL内外连接 内连接 外连接 左外连接 右外连接 简单案例 MySQL内外连接 在数据库操作中,表的连接是一个非常重要的概念。简单来说,连接就是将两个或多个表中的数据按照某种规则结合起来,从而获取我们所需要的…...
Python爬虫实战:研究Scrapy-Splash库相关技术
1 引言 1.1 研究背景与意义 网络爬虫作为一种自动获取互联网信息的技术,在数据挖掘、信息检索、舆情分析等领域有着广泛的应用。然而,随着 Web 技术的不断发展,越来越多的网站采用 JavaScript 动态渲染技术,如 React、Vue 等框架构建的单页应用 (SPA)。这些网站的内容通常…...

智能升级:中国新能源汽车充电桩规模化建设与充电桩智慧管理方案
近年来,中国新能源汽车产业快速发展,市场规模持续扩大,但充电基础设施的建设与管理仍面临布局不均、利用率低、智能化水平不足等问题。为推动新能源汽车普及,国家正加速充电桩的规模化建设,并通过智慧化管理提升运营效…...
AlphaFold3服务器安装与使用(非docker)(1)
1. 服务器显卡驱动准备 这部分我会详细记录一下我踩过的坑及怎样拯救的,原谅啰嗦啦 ^_^ 1.1 服务器旧配置 1.1.1 nvidia-smi [xxxxxxlocalhost ~]# nvidia-smi Thu May 29 20:54:00 2025 -------------------------------------------------------------…...

接口自动化测试之pytest接口关联框架封装
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一般情况下,我们是通过一个yaml文件进行关联实现 在根目录下新建一个文件yaml,通过上述conftest.py文件实现全局变量的更新: 1.首先需要建…...

M1安装并使用Matlab2024a进行java相机标定
安装 Matlab下载地址:https://www.macxin.com/archives/23771.html注意⚠️:如若需要java调用Matlab函数,则需要java版本为21 使用 安装完成之后运行此节目可以看到: 构建jar 命令行输入deploytool,会有一个弹窗&a…...

02-Redis常见命令
02-Redis常见命令 Redis数据结构介绍 Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样: 贴心小建议:命令不要死记,学会查询就好啦 Redis为了方便学习,将操作不同数据类型的命…...
【论文阅读笔记】Text-to-SQL Empowered by Large Language Models: A Benchmark Evaluation
文章目录 Text-to-SQL Empowered by Large Language Models: A Benchmark Evaluation一、论文基本信息1. 文章标题2. 所属刊物/会议3. 发表年份4. 作者列表5. 发表单位 二、摘要三、解决问题四、创新点五、自己的见解和感想六、研究背景七、研究方法(模型、实验数据…...
使用ArcPy进行栅格数据分析
设置工作环境 在开始编写脚本之前,需要设置好工作环境。这包括指定工作空间(workspace)和输出路径。工作空间是包含所有输入数据的文件夹或地理数据库,而输出路径则是处理结果将要保存的位置。 import arcpy from arcpy import …...

华为OD机试真题——告警抑制(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…...

Java转Go日记(五十七):gin 中间件
1. 全局中间件 所有请求都经过此中间件 package mainimport ("fmt""time""github.com/gin-gonic/gin" )// 定义中间 func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()fmt.Println("中间件开始执行了&quo…...
《树数据结构解析:核心概念、类型特性、应用场景及选择策略》
在数据结构中,树是一种分层的非线性数据结构,由节点和边组成,具有唯一根节点、子树分层结构和无环特性。其核心价值在于高效处理层次化数据或动态集合,广泛应用于算法、数据库、文件系统等领域。 一、树的核心概念 根节点&#…...
在本地查看服务器上的TensorBoard
建立本地服务器与远程服务器的通信,将TensorBoard的映射端口与本地端口连接起来,本地终端运行: ssh -L 本地端口:127.0.0.1:TensorBoard端口 用户名服务器的IP地址 -p 服务器登录端口 e.g. ssh -L 10010:127.0.0.1:39353 sx110.92.137.56 -…...
硬件开发全解:从入门教程到实战案例与丰富项目资源
硬件开发全解:从入门教程到实战案例与丰富项目资源 一、硬件开发基础 1.1 硬件开发概述 硬件开发,简单来说,就是从构思到实现一个电子设备的全过程。这一过程涉及到电子电路设计、嵌入式系统编程、传感器和执行器的集成等多个关键领域。在电子…...

嵌入式学习笔记 - freeRTOS的两种临界禁止
一 禁止中断 通过函数taskENTER_CRITICAL() ,taskEXIT_CRITICAL()实现 更改就绪列表时,通常是通过禁止中断的方式,进入临界段,因为systick中断中有可以更改就绪列表的权利, 就绪列表(如 pxReadyTasksLis…...

202403-02-相似度计算 csp认证
其实这个问题就是求两篇文章的词汇的交集和并集,首先一说到并集,我就想到了set集合数据结构,set中的元素必须唯一。 STL之set的基本使用–博客参考 所以将两个文章的词汇全部加入set中,并求出set的大小,即为并集的大小…...

【Oracle】游标
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 游标基础概述1.1 游标的概念与作用1.2 游标的生命周期1.3 游标的分类 2. 显式游标2.1 显式游标的基本语法2.1.1 声明游标2.1.2 带参数的游标 2.2 游标的基本操作2.2.1 完整的游标操作示例 2.3 游标属性2.3.1…...
MySQL 中 char 与 varchar 的区别
在 MySQL 的字段类型中,char和varchar是用来处理字符串。本文来学习二者区别 一、本质区别:空间分配的 “固执” 与 “灵活” 1. char:空间占满 固定长度特性: 定义时指定长度(如char(10)),无…...
DeepSeek 赋能智能零售,解锁动态定价新范式
目录 一、引言二、智能零售动态定价策略概述2.1 动态定价的概念与原理2.2 动态定价在智能零售中的重要性2.3 传统动态定价策略的局限性 三、DeepSeek 技术解析3.1 DeepSeek 的技术原理与架构3.2 DeepSeek 的优势与特点 四、DeepSeek 在智能零售动态定价中的应用机制4.1 数据收集…...
在Flutter中定义全局对象(如$http)而不需要import
在Flutter中定义全局对象(如$http)而不需要import 在Flutter中,有几种方法可以定义全局可访问的对象(如$http)而不需要在每个文件中import: 方法1:使用GetX的依赖注入(推荐&#x…...

<4>, Qt窗口
目录 一,菜单栏 二,工具栏 三,状态栏 四,浮动窗口 五,对话框 一,菜单栏 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);// 创建菜单栏…...

6.04打卡
浙大疏锦行 DAY 43 复习日 作业: kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:并拆分成多个文件 损失: 0.502 | 准确率: 75.53% 训练完成 import torch import torch.nn as nn import torch.optim as optim from…...

【基于SpringBoot的图书购买系统】操作Jedis对图书图书的增-删-改:从设计到实战的全栈开发指南
引言 在当今互联网应用开发中,缓存技术已成为提升系统性能和用户体验的关键组件。Redis作为一款高性能的键值存储数据库,以其丰富的数据结构、快速的读写能力和灵活的扩展性,被广泛应用于各类系统的缓存层设计。本文将围绕一个基于Redis的图…...
Ubuntu中TFTP服务器安装使用
TFTP服务器 在 Ubuntu 下使用 TFTP(Trivial File Transfer Protocol) 服务,通常用于简单的文件传输(如网络设备固件更新、嵌入式开发等)。 1 TFTP服务器安装 sudo apt-get install tftp-hpa sudo apt-get install…...

Spring Boot微服务架构(十):Docker与K8S部署的区别
Spring Boot微服务在Docker与Kubernetes(K8S)中的部署存在显著差异,主要体现在技术定位、管理能力、扩展性及适用场景等方面。以下是两者的核心区别及实践对比: 一、技术定位与核心功能 Docker 功能:专注于单节点容器化…...
接口重试的7种常用方案!
前言 记得五年前的一个深夜,某个电商平台的订单退款接口突发异常,因为银行系统网络抖动,退款请求连续失败。 原本技术团队只是想“好心重试几次”,结果开发小哥写的重试代码竟疯狂调用了银行的退款接口 82次! 最终导致…...

vue3:Table组件动态的字段(列)权限、显示隐藏和左侧固定
效果展示 根据后端接口返回,当前登录用户详情中的页面中el-table组件的显示隐藏等功能。根据菜单id查询该菜单下能后显示的列。 后端返回的数据类型: 接收到后端返回的数据后处理数据结构. Table组件文件 <!-- 自己封装的Table组件文件 --> onMounted(()>…...

pikachu靶场通关笔记13 XSS关卡09-XSS之href输出
目录 一、href 1、常见取值类型 2、使用示例 3、安全风险 二、源码分析 1、进入靶场 2、代码审计 3、渗透思路 三、渗透实战 1、注入payload1 2、注入payload2 3、注入payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关)渗透集合ÿ…...