逻辑

前端操作:

管理员点击”新增”按钮
弹出空白表单,填写教师信息(用户名、姓名、性别等)
点击”保存”按钮提交

后端处理:

// 1. 检查用户名是否已存在(防重复)
Teacher dbTeacher = teacherMapper.selectByUsername(teacher.getUsername());
if(已存在){
直接报错:”用户名已存在”
}

// 2. 设置默认值:
if(没填密码){
自动设置密码为”123456”
}
强制设置角色为”TEACHER”

// 3. 保存到数据库
teacherMapper.insert(teacher);

结果反馈:

成功:提示”新增成功”,列表自动刷新显示新教师
失败:提示具体原因

关键处理点

唯一性校验:通过用户名检查教师是否已存在
默认值设置:
密码为空时自动设为”123456”
角色固定设为”TEACHER”
异常处理:用户名存在时抛出业务异常
数据流向:Controller → Service → Mapper → DB(数据库)

创建教师信息表

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `teacher` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名',
`sex` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '性别',
`title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '职称',
`speciality_id` int DEFAULT NULL COMMENT '专业id',
`role` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',
`avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教师信息';

idea中Mac快捷键

替换

get,set方法:

找到插件GenerateAllSetter

一键调用一个对象的所有的set方法,get方法等
在方法上生成两个对象的转换

image-20250414213103135

快捷键:control+enter

导包:

自动导包/修复导包问题:⌘ + Enter
优化导包(移除未使用的):⌃ + ⌥ + O
手动导包提示:⌘ + N

entity—teacher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class Teacher extends Account {
private Integer id;
private String username;
private String password;
private String name;
private String sex;
private String title;
private Integer specialityId;
private String specialityName;
private String role;
private String avatar;

//get和set方法
@Override
public Integer getId() {
return id;
}

@Override
public void setId(Integer id) {
this.id = id;
}

@Override
public String getUsername() {
return username;
}

@Override
public void setUsername(String username) {
this.username = username;
}

@Override
public String getPassword() {
return password;
}

@Override
public void setPassword(String password) {
this.password = password;
}

@Override
public String getName() {
return name;
}

@Override
public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public Integer getSpecialityId() {
return specialityId;
}

public void setSpecialityId(Integer specialityId) {
this.specialityId = specialityId;
}

@Override
public String getRole() {
return role;
}

@Override
public void setRole(String role) {
this.role = role;
}

@Override
public String getAvatar() {
return avatar;
}
@Override
public void setAvatar(String avatar) {
this.avatar = avatar;
}

public String getSpecialityName() {
return specialityName;
}
public void setSpecialityName(String specialityName) {
this.specialityName = specialityName;
}
}

解决前端vue里面没有错警告

比如

image-20250414222503009

设置(setting)里面——修改html里面

前端添加教师信息

1
2
3
4
5
<el-menu-item index="/teacher">
<el-icon><Avatar /></el-icon>
/*图标在 https://element-plus.org/ 里面找*/
<span>教师信息</span>
</el-menu-item>

image-20250414223204643

现在前端也有教师信息菜单了,图标也可以更改

配置路由,加一个teacher信息

1
{ path: 'teacher', component: () => import('@/views/manager/teacher.vue')},

image-20250414224445279

在manager下面新建一个Teacher.vue

vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<template>
<div>
<!-- 新增按钮 可以参考Admin.vue里面的内容 -->
<div class="card" style="margin-bottom: 5px">
<div style="margin-bottom: 10px">
<!-- 新增教师按钮,点击触发handleAdd方法 -->
<el-button type="primary" @click="handleAdd">新增</el-button>
</div>
</div>
<!-- 教师信息弹窗对话框 -->
<el-dialog title="教师信息" width="40%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form :model="data.form" label-width="100px" style="padding-right: 50px">
<!--prop v-model 里面要与后端保持一致不然接收不到 -->
<el-form-item label="账号" prop="username">
<el-input v-model="data.form.username" autocomplete="off" placeholder="请输入账号"/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="data.form.name" autocomplete="off" placeholder="请输入姓名"/>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="data.form.sex" placeholder="请选择性别" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
<el-form-item label="职称" prop="title">
<el-select v-model="data.form.title" placeholder="请选择职称" style="width: 100%">
<el-option label="讲师" value="讲师"></el-option>
<el-option label="副教授" value="副教授"></el-option>
<el-option label="教授" value="教授"></el-option>
</el-select>
</el-form-item>
</el-form>
<!-- 弹窗取消,保存 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>

<script setup>
//导包
import {reactive} from "vue";
import request from "@/utils/request";
import {ElMessage} from "element-plus";

const data = reactive({
// 教师信息弹窗对话框
formVisible: false,
// 教师信息表单数据
form: {
},
})
// 新增教师按钮点击事件
const handleAdd = () => {
// 清空教师信息表单数据
data.form = {

}
// 打开教师信息弹窗对话框
data.formVisible = true
}
const save = () => {
// 调用后端接口保存教师信息
// 这里可以使用axios或者其他请求库发送请求
// 例如:axios.post('/api/teacher/add', data.form)
request.post('/teacher/add', data.form).then(res => {
if (res.code === '200') {
// 保存成功,提示用户
ElMessage.success('操作成功')
// 关闭教师信息弹窗对话框
data.formVisible = false
}else {
// 保存失败,提示用户
ElMessage.error(res.msg)
}
})
}
</script>

Controller—TeacherController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.example.controller;

import com.example.common.Result;
import com.example.entity.Admin;
import com.example.entity.Teacher;
import com.example.service.AdminService;
import com.example.service.TeacherService;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* 教师模块前端操作接口入口
* 拿到数据后,调用service层的方法,返回结果
* 前端调用接口时,需要传入参数,调用service层的方法,返回结果
**/
@RestController

@RequestMapping("/teacher")
public class TeacherController {
@Resource
// 注入TeacherService
private TeacherService teacherService;

/**
* 新增教师
* */
@PostMapping("/add")
public Result add(@RequestBody Teacher teacher){
teacherService.add(teacher);
return Result.success();
}
}

Service—TeacherService 核心逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.service;

import cn.hutool.core.util.ObjectUtil;
import com.example.entity.Teacher;
import com.example.exception.CustomException;
import com.example.mapper.TeacherMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
/**
* 教师模块业务逻辑接口
* 要把数据往数据库里存,调用mapper层的方法,返回结果
* 业务逻辑接口调用mapper层的方法,返回结果
*/
@Service
public class TeacherService {
// 注入TeacherMapper
@Resource
private TeacherMapper teacherMapper;
/**
* 新增教师
*/
public void add(Teacher teacher) {
// ToDo 新增教师逻辑处理
// 检查教师是否已经存在,检查用户唯一性
Teacher dbTeacher = teacherMapper.selectByUsername(teacher.getUsername());
if (ObjectUtil.isNotEmpty(dbTeacher)) {
throw new CustomException("用户名已存在");
}
// 如果没有密码,就设置密码为123456
if(ObjectUtil.isEmpty(teacher.getPassword())){
teacher.setPassword("123456");
}
// 固定角色
teacher.setRole("TEACHER");
// 持久化操作
teacherMapper.insert(teacher);
}
}

mapper—TeacherMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.mapper;

//接口

import com.example.entity.Teacher;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface TeacherMapper {
// 新增教师
void insert(Teacher teacher);
@Select("select * from teacher where username = #{username}")
Teacher selectByUsername(String username);
}

TeacherMapper.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.TeacherMapper">
<insert id="insert" parameterType="com.example.entity.Teacher" useGeneratedKeys="true">
insert into teacher (username, password, name,sex,title,speciality_id,role,avatar)
values (#{username}, #{password}, #{name}, #{sex}, #{title}, #{specialityId}, #{role}, #{avatar})
</insert>
</mapper>

简单逻辑在mapper可以用注解,复杂逻辑用xml
namespace 对应mapper接口的全类名

1. 映射文件的位置必须和mapper接口的包结构相同
    2. 映射文件的名称必须和mapper接口的名称相同
    3. 映射文件的后缀名必须是.xml
    1. 映射文件中的sql语句的id必须和mapper接口中的方法名相同
    2. 映射文件中的sql语句的参数类型必须和mapper接口中的方法参数类型相同
    3. 映射文件中的sql语句的返回值类型必须和mapper接口中的方法返回值类型相同

1. Controller(控制层)

  • 职责
    • 接收客户端请求(HTTP/RPC等),解析参数(如URL、Body、Header)。
    • 调用 Service 层处理业务逻辑。
    • 返回响应(JSON/XML等)或视图(如HTML)。
  • 特点
    • 轻量级,仅做请求转发和参数校验,不包含业务逻辑
    • 通常使用 @Controller@RestController 注解(Spring中)。

2. Service(业务逻辑层)

  • 职责
    • 处理核心业务逻辑(如订单创建、用户权限校验)。
    • 协调多个 Mapper 操作(事务管理)。
    • 调用外部服务(如支付接口、消息队列)。
  • 特点
    • 使用 @Service 注解,是业务逻辑的集中点
    • 可能涉及事务(@Transactional)。

3. Mapper(数据访问层,或称DAO)

  • 职责
    • 直接操作数据库(CRUD),执行SQL语句。
    • 与数据库表一一对应(如MyBatis的 @Mapper 或JPA的Repository)。
  • 特点
    • 无业务逻辑,仅做数据持久化。
    • 使用 @Mapper(MyBatis)或继承 JpaRepository(Spring Data JPA)。

三者的协作流程

  1. 请求入口
    • 客户端 → Controller(接收请求) → 调用 Service
  2. 业务处理
    • Service 处理逻辑 → 调用 Mapper 读写数据库。
  3. 数据持久化
    • Mapper 执行SQL → 返回数据给 ServiceService 返回结果给 Controller
  4. 响应输出
    • Controller 将结果封装为响应 → 返回客户端。

关键原则

  • 单向依赖
    ControllerServiceMapper(禁止反向依赖或跨层调用)。
  • 职责隔离
    • Controller 不管业务,Service 不管SQL,Mapper 只管数据库。
  • 复用性
    一个 Service 可能被多个 Controller 调用,一个 Mapper 可能被多个 Service 调用。

通过这种分层设计,系统更易于维护、扩展和测试,符合高内聚低耦合的软件工程原则

先写前端然后写后端——ControllerServiceMapper