SpringBoot+VUE前后端分离项目学习笔记 - 【15 SpringBoot和Vue实现注册和异常处理】

news2024/11/15 0:09:58

前端代码

在这里插入图片描述

Header.vue

获取登录用户的昵称在Header进行显示,加入个人信息的路由

<template>
  <div style="line-height: 60px; display: flex">
    <div style="flex: 1;">
      <span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse"></span>

      <el-breadcrumb separator="/" style="display: inline-block; margin-left: 10px">
        <el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
        <el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <el-dropdown style="width: 100px; cursor: pointer;">
      <div style="display: inline-block">
        <img :src="user.avatarUrl" alt=""
             style="width: 30px; border-radius: 50%; position: relative; top: 10px; right: 5px">
        <span>{{user.nickname}}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
      </div>
      <el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center">
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">
          <router-link to="/person">个人信息</router-link>
        </el-dropdown-item>
        <el-dropdown-item style="font-size: 14px; padding: 5px 0">
          <router-link to="/login" style="text-decoration: none">退出</router-link>
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
export default {
  name: "Header",
  props: {
    collapseBtnClass: String,
  },
  computed: {
    currentPathName () {
      return this.$store.state.currentPathName;  //需要监听的数据
    }
  },
  data() {
      return {
        user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
      }
  },
  methods: {
    collapse() {
      // this.$parent.$parent.$parent.$parent.collapse()  // 通过4个 $parent 找到父组件,从而调用其折叠方法
      this.$emit("asideCollapse")
    },
    logout() {
      this.$router.push("/login")
      localStorage.removeItem("user")
      this.$message.success("退出成功")
    }
  }
}
</script>

<style scoped>
</style>


Login.vue

登录的成功后将用户信息储存下来

<template>
  <div class="wrapper">
    <div style="margin: 200px auto; background-color: #fff; width: 350px; height: 300px; padding: 20px; border-radius: 10px">
      <div style="margin: 20px 0; text-align: center; font-size: 24px"><b>登 录</b></div>
      <el-form :model="user" :rules="rules" ref="userForm">
        <el-form-item prop="username">
          <el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
        </el-form-item>
        <el-form-item style="margin: 10px 0; text-align: right">
          <el-button type="primary" size="small"  autocomplete="off" @click="login">登录</el-button>
          <el-button type="warning" size="small"  autocomplete="off" @click="$router.push('/register')">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {
      user: {},
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
        ],
      }
    }
  },
  methods: {
    login() {
      this.$refs['userForm'].validate((valid) => {
        if (valid) {  // 表单校验合法
          this.request.post("/user/login", this.user).then(res => {
            if(res.code === '200') {
              localStorage.setItem("user", JSON.stringify(res.data))  // 存储用户信息到浏览器
              this.$router.push("/")
              this.$message.success("登录成功")
            } else {
              this.$message.error(res.msg)
            }
          })
        }
      });
    }
  }
}
</script>

<style>
.wrapper {
  height: 100vh;
  background-image: linear-gradient(to bottom right, #FC466B , #3F5EFB);
  overflow: hidden;
}
</style>

Register.vue

<template>
  <div class="wrapper">
    <div style="margin: 150px auto; background-color: #fff; width: 350px; height: 350px; padding: 20px; border-radius: 10px">
      <div style="margin: 20px 0; text-align: center; font-size: 24px"><b>注 册</b></div>
      <el-form :model="user" :rules="rules" ref="userForm">
        <el-form-item prop="username">
          <el-input placeholder="请输入账号" size="medium" prefix-icon="el-icon-user" v-model="user.username"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input placeholder="请输入密码" size="medium" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
        </el-form-item>
        <el-form-item prop="confirmPassword">
          <el-input placeholder="请确认密码" size="medium" prefix-icon="el-icon-lock" show-password v-model="user.confirmPassword"></el-input>
        </el-form-item>
        <el-form-item style="margin: 5px 0; text-align: right">
          <el-button type="primary" size="small"  autocomplete="off" @click="login">注册</el-button>
          <el-button type="warning" size="small"  autocomplete="off" @click="$router.push('/login')">返回登录</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  name: "Register",
  data() {
    return {
      user: {},
      rules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 5 个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
        ],
        confirmPassword: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
        ],
      }
    }
  },
  methods: {
    login() {
      this.$refs['userForm'].validate((valid) => {
        if (valid) {  // 表单校验合法
          if (this.user.password !== this.user.confirmPassword) {
            this.$message.error("两次输入的密码不一致")
            return false
          }
          this.request.post("/user/register", this.user).then(res => {
            if(res.code === '200') {
              this.$message.success("注册成功")
            } else {
              this.$message.error(res.msg)
            }
          })
        }
      });
    }
  }
}
</script>

<style>
.wrapper {
  height: 100vh;
  background-image: linear-gradient(to bottom right, #FC466B , #3F5EFB);
  overflow: hidden;
}
</style>

User.vue

接口改造res->res.data

<template>
  <div>
    <div style="margin: 10px 0">
      <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="username"></el-input>
      <el-input style="width: 200px" placeholder="请输入邮箱" suffix-icon="el-icon-message" class="ml-5" v-model="email"></el-input>
      <el-input style="width: 200px" placeholder="请输入地址" suffix-icon="el-icon-position" class="ml-5" v-model="address"></el-input>
      <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
      <el-button type="warning" @click="reset">重置</el-button>
    </div>

    <div style="margin: 10px 0">
      <el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
      <el-popconfirm
          class="ml-5"
          confirm-button-text='确定'
          cancel-button-text='我再想想'
          icon="el-icon-info"
          icon-color="red"
          title="您确定批量删除这些数据吗?"
          @confirm="delBatch"
      >
        <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
      </el-popconfirm>
      <el-upload action="http://localhost:9090/user/import" :show-file-list="false" accept="xlsx" :on-success="handleExcelImportSuccess" style="display: inline-block">
        <el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i></el-button>
      </el-upload>
      <el-button type="primary" @click="exp" class="ml-5">导出 <i class="el-icon-top"></i></el-button>
    </div>

    <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"  @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
      <el-table-column prop="username" label="用户名" width="140"></el-table-column>
      <el-table-column prop="nickname" label="昵称" width="120"></el-table-column>
      <el-table-column prop="email" label="邮箱"></el-table-column>
      <el-table-column prop="phone" label="电话"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
      <el-table-column label="操作"  width="200" align="center">
        <template slot-scope="scope">
          <el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
          <el-popconfirm
              class="ml-5"
              confirm-button-text='确定'
              cancel-button-text='我再想想'
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="del(scope.row.id)"
          >
            <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <div style="padding: 10px 0">
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[2, 5, 10, 20]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total">
      </el-pagination>
    </div>

    <el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%" >
      <el-form label-width="80px" size="small">
        <el-form-item label="用户名">
          <el-input v-model="form.username" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="昵称">
          <el-input v-model="form.nickname" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="邮箱">
          <el-input v-model="form.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="form.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="form.address" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "User",
  data() {
    return {
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: 10,
      username: "",
      email: "",
      address: "",
      form: {},
      dialogFormVisible: false,
      multipleSelection: []
    }
  },
  created() {
    this.load()
  },
  methods: {
    load() {
      this.request.get("/user/page", {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          username: this.username,
          email: this.email,
          address: this.address,
        }
      }).then(res => {
        console.log(res)

        this.tableData = res.data.records
        this.total = res.data.total

      })
    },
    save() {
      this.request.post("/user", this.form).then(res => {
        if (res.data) {
          this.$message.success("保存成功")
          this.dialogFormVisible = false
          this.load()
        } else {
          this.$message.error("保存失败")
        }
      })
    },
    del(id) {
      this.request.delete("/user/" + id).then(res => {
        if (res.data) {
          this.$message.success("删除成功")
          this.load()
        } else {
          this.$message.error("删除失败")
        }
      })
    },
    delBatch() {
      let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
      this.request.post("/user/del/batch", ids).then(res => {
        if (res.data) {
          this.$message.success("批量删除成功")
          this.load()
        } else {
          this.$message.error("批量删除失败")
        }
      })
    },
    handleAdd() {
      this.dialogFormVisible = true
      this.form = {}
    },
    handleEdit(row) {
      this.form = row
      this.dialogFormVisible = true
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },
    reset() {
      this.username = ""
      this.email = ""
      this.address = ""
      this.load()
    },
    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    },
    exp() {
      window.open("http://localhost:9090/user/export")
    },
    handleExcelImportSuccess() {
      this.$message.success("导入成功")
      this.load()
    }
  }
}
</script>


<style>
.headerBg {
  background: #eee!important;
}
</style>


Person.vue

增加个人信息页

<template>
  <div>
  <el-card style="width: 500px;">
    <el-form label-width="80px" size="small">
      <el-form-item label="用户名">
        <el-input v-model="form.username" disabled autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="昵称">
        <el-input v-model="form.nickname" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="邮箱">
        <el-input v-model="form.email" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="电话">
        <el-input v-model="form.phone" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="地址">
        <el-input type="textarea" v-model="form.address" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="save">确 定</el-button>
      </el-form-item>
    </el-form>
  </el-card>
  </div>
</template>

<script>
export default {
  name: "Person",
  data() {
    return {
      form: {},
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
    }
  },
  created() {
    this.request.get("/user/username/" + this.user.username).then(res => {
      if(res.code === '200') {
        this.form = res.data
      }
    })
  },
  methods: {
    save() {
      this.request.post("/user", this.form).then(res => {
        if (res.code === '200') {
          this.$message.success("保存成功")
        } else {
          this.$message.error("保存失败")
        }
      })
    },
  }
}
</script>

<style scoped>
</style>

router/index.js

加入注册页面与个人信息页面的路由

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from "@/store";
//import store from "../store"

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    component: () => import('../views/Manage.vue'),
    redirect: "/home",
    children: [
      { path: 'home', name: '首页', component: () => import('../views/Home.vue')},
      { path: 'user', name: '用户管理', component: () => import('../views/User.vue')},
      { path: 'person', name: '用户管理', component: () => import('../views/Person.vue')},
    ]
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('../views/Login.vue')
  },
  {
    path: '/register',
    name: 'Register',
    component: () => import('../views/Register.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
  localStorage.setItem("currentPathName", to.name)  // 设置当前的路由名称,为了在Header组件中去使用
  store.commit("setPath")  // 触发store的数据更新
  next()  // 放行路由
})

export default router


后端代码

在这里插入图片描述

UserDTO.java (User包装类)

登录用户实体类加入昵称和头像属性

package com.zj.demo.controller.dao;

import lombok.Data;

/**
 * 接受前端登录请求的参数
 */
@Data
public class UserDTO {
    private String username;
    private String password;
    private String nickname;
    private String avatarUrl;
}

Result.java【接口统一改造】

之前的接口返回类型有Boolean也有List不统一,前端需要处理多种返回类型,不太合理,所以需要进行统一接口改造

新建common包,创建Result类,具体代码如下

package com.zj.demo.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private String code;
    private String msg;
    private Object data;

    public static Result success(){
        return new Result(Constants.CODE_200, "", null);
    }
    public static Result success(Object data){
        return new Result(Constants.CODE_200, "", data);
    }
    public static Result error(String code,String msg){
        return new Result(code, msg, null);
    }
    public static Result error() {
        return new Result(Constants.CODE_500, "系统错误", null);
    }

}


Constants.java(常量类,定义状态码Code)

package com.zj.demo.common;

public interface Constants {
    String CODE_200 = "200"; //成功
    String CODE_401 = "401";  // 权限不足
    String CODE_400 = "400";  // 参数错误
    String CODE_500 = "500"; // 系统错误
    String CODE_600 = "600"; // 其他业务异常
}

UserController.java

使用同一接口类型替换之前的返回类型

package com.zj.demo.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zj.demo.common.Constants;
import com.zj.demo.common.Result;
import com.zj.demo.controller.dao.UserDTO;
import com.zj.demo.entity.User;
import com.zj.demo.service.IUserService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author Joyce
 * @since 2023-01-04
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private IUserService userService;


    /**
     * 登录界面
     */
    @PostMapping("/login")
    public Result login(@RequestBody UserDTO userDTO)
    {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        //判断用户名和密码是否为空[HUTOOL工具包]
        if(StrUtil.isBlank(username) || StrUtil.isBlank(password))
        {
            return Result.error(Constants.CODE_400,"参数错误");
        }
        UserDTO dto = userService.login(userDTO);
        return Result.success(dto);
    }
    @PostMapping("/register")
    public Result register(@RequestBody UserDTO userDTO)
    {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        //判断用户名和密码是否为空[HUTOOL工具包]
        if(StrUtil.isBlank(username) || StrUtil.isBlank(password))
        {
            return Result.error(Constants.CODE_400,"参数错误");
        }
        User dto = userService.register(userDTO);
        return Result.success(dto);
    }
    // 新增或者更新
    @PostMapping
    public Result save(@RequestBody User user) {
            return Result.success(userService.saveOrUpdate(user));
          }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
            return Result.success(userService.removeById(id));
            }

    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
            return Result.success(userService.removeByIds(ids));
            }

    @GetMapping
    public Result findAll() {
            return Result.success(userService.list());
            }

    @GetMapping("/{id}")
    public Result findOne(@PathVariable Integer id) {
            return Result.success(userService.getById(id));
            }

    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                               @RequestParam Integer pageSize,
                               @RequestParam(defaultValue ="") String username,
                               @RequestParam(defaultValue ="") String email,
                               @RequestParam(defaultValue ="") String address
                               ) {
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            if (!"".equals(username)) {
                queryWrapper.like("username", username);
            }
            if (!"".equals(email)) {
                queryWrapper.like("nickname", email);
            }
            //和上面的if判断效果一样
            queryWrapper.like(!address.equals(""),"address", address);
            queryWrapper.orderByDesc("id");
            return Result.success(userService.page(new Page<>(pageNum, pageSize), queryWrapper));
            }
    /**
     * 导出接口
     */
    @GetMapping("/export")
    public void export(HttpServletResponse response) throws Exception {
        // 从数据库查询出所有的数据
        List<User> list = userService.list();
        // 通过工具类创建writer 写出到磁盘路径
//        ExcelWriter writer = ExcelUtil.getWriter(filesUploadPath + "/用户信息.xlsx");
        // 在内存操作,写出到浏览器
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义标题别名,不设置则使用实体类字段名
        writer.addHeaderAlias("username", "用户名");
        writer.addHeaderAlias("password", "密码");
        writer.addHeaderAlias("nickname", "昵称");
        writer.addHeaderAlias("email", "邮箱");
        writer.addHeaderAlias("phone", "电话");
        writer.addHeaderAlias("address", "地址");
        writer.addHeaderAlias("createTime", "创建时间");
        writer.addHeaderAlias("avatarUrl", "头像");

        // 一次性写出list内的对象到excel,使用默认样式,强制输出标题
        writer.write(list, true);

        // 设置浏览器响应的格式
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode("用户信息", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();

    }
    /**
     * excel 导入
     * @param file
     * @throws Exception
     */
    @PostMapping("/import")
    public Result imp(MultipartFile file) throws Exception {
        InputStream inputStream = file.getInputStream();
        ExcelReader reader = ExcelUtil.getReader(inputStream);
        // 方式1:(推荐) 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
//        List<User> list = reader.readAll(User.class);

        // 方式2:忽略表头的中文,直接读取表的内容
        List<List<Object>> list = reader.read(1);
        List<User> users = CollUtil.newArrayList();
        for (List<Object> row : list) {
            User user = new User();
            user.setUsername(row.get(0).toString());
            user.setPassword(row.get(1).toString());
            user.setNickname(row.get(2).toString());
            user.setEmail(row.get(3).toString());
            user.setPhone(row.get(4).toString());
            user.setAddress(row.get(5).toString());
            user.setAvatarUrl(row.get(6).toString());
            users.add(user);
        }

        userService.saveBatch(users);
        return Result.success(true);
    }

    @GetMapping("/username/{username}")
    public Result findOne(@PathVariable String username) {
     QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        return Result.success(userService.getOne(queryWrapper));
    }

}


UserService.java

之前登陆返回类型Boolean的需要改为UserDTO【因为需要获取用户信息用以登录后首页Header显示】
还有注册功能的加入

package com.zj.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.zj.demo.controller.dao.UserDTO;
import com.zj.demo.entity.User;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-04
 */
public interface IUserService extends IService<User> {

    UserDTO login(UserDTO userDTO);

    User register(UserDTO userDTO);
}

UserServiceImpl.java

相应的登陆代码修改以及编写注册服务

package com.zj.demo.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zj.demo.common.Constants;
import com.zj.demo.controller.dao.UserDTO;
import com.zj.demo.entity.User;
import com.zj.demo.exception.ServiceException;
import com.zj.demo.mapper.UserMapper;
import com.zj.demo.service.IUserService;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Joyce
 * @since 2023-01-04
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private static final Log LOG = Log.get();

    @Override
    public UserDTO login(UserDTO userDTO) {
        User one = getUserInfo(userDTO);
        if (one != null) {
            BeanUtil.copyProperties(one, userDTO, true);
            return userDTO;
        } else {
            throw new ServiceException(Constants.CODE_600, "用户名或密码错误");
        }
    }

    @Override
    public User register(UserDTO userDTO) {
        User one = getUserInfo(userDTO);
        if (one == null) {
            one = new User();
            BeanUtil.copyProperties(userDTO, one, true);
            save(one);  // 把 copy完之后的用户对象存储到数据库
        } else {
            throw new ServiceException(Constants.CODE_600, "用户已存在");
        }
        return one;
    }

    private User getUserInfo(UserDTO userDTO) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userDTO.getUsername());
        queryWrapper.eq("password", userDTO.getPassword());
        User one;
        try {
            one = getOne(queryWrapper); // 从数据库查询用户信息
        } catch (Exception e) {
            LOG.error(e);
            throw new ServiceException(Constants.CODE_500, "系统错误");
        }
        return one;
    }

}

涉及到自定义异常,下面进行编写

GlobalExceptionHandler.java

全局异常处理器编写

package com.zj.demo.exception;

import com.zj.demo.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 如果抛出的的是ServiceException,则调用该方法
     * @param se 业务异常
     * @return Result
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public Result handle(ServiceException se){
        return Result.error(se.getCode(), se.getMessage());
    }

}

ServiceException.java(自定义业务异常)

自定义异常类【包含我们自定义的状态码,以及错误msg】
全局异常会调用该类用来返回服务异常情况下的的状态码以及错误信息
PS: RuntimeException是java提供的一种运行异常,我们自定义的异常需要继承它

package com.zj.demo.exception;

import lombok.Getter;

/**
 * 自定义异常
 */
@Getter
public class ServiceException extends RuntimeException {
    private String code;

    public ServiceException(String code, String msg) {
        super(msg);
        this.code = code;
    }

}

页面测试

登录页面

在这里插入图片描述
在这里插入图片描述

注册页面

在这里插入图片描述

个人信息页面

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue 父子组件通讯时执行的生命周期的顺序

Vue的生命周期 Vue的生命周期分为三个阶段&#xff1a;初始化阶段&#xff0c;更新数据阶段&#xff0c;销毁实例阶段 1&#xff0c;初始化阶段&#xff1a; beforeCreate()实例创建前&#xff1a;数据和模板均未获取到created()实例创建后&#xff1a;最早可以访问到data数…

sqlmap之绕过安全狗

sql注入不会绕过WAF&#xff1f;关注我&#xff0c;让我带你由简入难实战各个WAF&#xff0c;今天先来看看web安全渗透必会的安全狗WAF&#xff0c;你会绕吗&#xff1f;看我带你将它拿下 目录 一&#xff1a;环境配置 1.sqli-labs的sql注入靶场环境 2.安全狗waf软件 3.检测…

nrm使用详解

目录1. 什么是 nrm2. 安装 nrm3. 使用 nrm3.1 查看所有源列表3.2 切换源3.3 添加源3.4 删除源3.5 查看所有源的响应速率1. 什么是 nrm npm 的源管理器&#xff0c;切换下载安装 项目依赖 时的源地址。默认包含 npm yarn tencent cnpm taobao npmMirror&#xff0c;支持添加、删…

docker安装kafka

1.下载镜像 docker pull wurstmeister/kafka docker pull zookeeper:latest 2.启动镜像 1&#xff09;启动zookeeper docker run -d -p2181:2181 -v /etc/localtime:/etc/localtime --name zookeeper zookeeper:latest2)启动kafka docker run -d -p9092:9092 \-e KAFKA_ZO…

Twitter开发者账号申请

Twitter开发者账号申请 前期准备 Twitter绑定手机号 申请开发者账号要求绑定手机号&#xff0c;建议境外手机号&#xff0c;国内手机号容易出现收不到验证码的问题。一个可以正常使用的Twitter账号 被封的不可以。 开始注册 注册开发者管理平台账号 登录Twitter开发者管理…

STM32——ADC模数转换器

文章目录一、ADC模数转化器ADC简介逐次逼近型ADCADC框图二、ADC基本结构三、触发转换控制四、输入通道五、规则组的四种转换模式单次转换&#xff0c;非扫描模式连续转换&#xff0c;非扫描模式单次转换&#xff0c;扫描模式连续转换&#xff0c;扫描模式六、数据对齐七、转换时…

Linux系统下的rpm管理

文章目录Linux系统下的rpm管理1.介绍2.rpm包的简单查询指令3.rpm包的其它查询指今4.卸载rpm包5.安装rpm包Linux系统下的rpm管理 1.介绍 rpm用于互联网下载包的打包及安装工具&#xff0c;它包含在某些Linux分发版中。它生成具有.RPM扩展名的文件。RPM是RedHat Package Manage…

【设计篇】35 # 如何让可视化设计更加清晰?

说明 【跟月影学可视化】学习笔记。 分清信息主次&#xff0c;建立视觉层次 用醒目的颜色突出显示数据&#xff0c;把被淡化的其他视觉元素当作背景。 比如&#xff1a;平均温度与露点的散点例子 <!DOCTYPE html> <html lang"en"><head><m…

linux上安装python3.7.4

1.wget下载python3安装包 wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz2.wget下载的压缩包默认在~目录下 3.进入~目录&#xff0c;直接解压压缩包 tar -xzvf Python-3.7.2.tgz4.进入python解压后的目录&#xff0c;为python配置环境&#xff0c;指定pytho…

CMMI-培训管理

培训管理&#xff08;Training Management, TM&#xff09;是指根据机构&#xff08;或项目&#xff09;的需求来制定培训计划&#xff0c;并监督该计划的实施&#xff0c;确保培训取得预期效果。培训管理过程域是SPP模型的重要组成部分。本规范阐述了培训管理过程域的两个主要…

头歌:UDP Ping程序实现 接收并转发消息

头歌平台&#xff1a;头歌实践教学平台 (educoder.net)任务描述本关任务&#xff1a;在 Ping 服务端程序框架中&#xff0c;完成接收数据&#xff0c;并回传给相应客户端。如何接收数据包套接字数据在 UDP 通信中使用sendto()函数发送 UDP 数据。将数据发送到套接字&#xff0c…

Parallel Processing in Python - Python 中的并行处理

并行处理可以增加程序完成的任务数量&#xff0c;从而减少整体处理时间。这些有助于处理大规模问题。 参考链接&#xff1a;Parallel Processing in Python - GeeksforGeeks 1 并行处理介绍 对于并行性&#xff0c;重要的是将问题划分为不依赖于其他子单元 (或较少依赖) 的子…

【Vue中使用Echarts】大屏可视化项目整体布局(pink老师vue 版)

文章目录一、效果展示二、基本的布局三、背景四、代码布局中遇到的一些问题一、效果展示 先看一下展示的效果&#xff0c;无论是尺寸多宽的屏幕&#xff0c;都会将内容显示完整&#xff0c;做到了正正的响应式。唯一不足的是图表中的样例&#xff0c;会随着图表的缩放而变换位…

35-剑指 Offer 37. 序列化二叉树

题目 请实现两个函数&#xff0c;分别用来序列化和反序列化二叉树。 你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑&#xff0c;你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 …

第02讲:HTTP操作之ElasticSearch索引操作

3.1.1、索引操作 实验1&#xff1a;创建索引 对比关系型数据库&#xff0c;创建索引就等同于创建数据库 在 Postman 中&#xff0c;向 ES 服务器发 PUT 请求 :http://127.0.0.1:9200/shopping { "acknowledged"【响应结果】: true, # true操作成功 "shards…

npm与包

1、包 1.1、什么是包 Node.js 中的第三方模块又叫做包。就像电脑和计算机指的是相同的东西&#xff0c;第三方模块和包指的是同一个概念&#xff0c;只不过叫法不同。 1.2、包的来源 不同于 Node.js 中的内置模块与自定义模块&#xff0c;包是由第三方个人或团队开发出来的&a…

跟踪数据集汇总

文章目录DanceTrack 运动跟踪数据集简介转为Labelme标注的物体检测数据集格式WiderPerson行人检测数据集简介转为Labelme标注的物体检测数据集格式DanceTrack 运动跟踪数据集 简介 DanceTrack 是一个大规模的多对象跟踪数据集。用于在遮挡、频繁交叉、同样服装和多样化身体姿…

DVWA之SQL注入漏洞与防御

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 耳熟能详的SQ注入是什么&#xff1f; 关于SQL注入漏洞&#xff0c;维基百科是这样解释的 …

传统离散制造行业的9个维度,你知道吗?

制造业是国家的经济基础&#xff0c;是立国之本、兴国之器、强国之基&#xff1b;作为我国实体经济的主体&#xff0c;是国民经济体系的重要组成部分。按照产品制造工艺过程特点&#xff0c;制造业总体上可以分为离散型制造、流程型制造、混合型制造。离散制造包括家电、家居、…

2022亚太杯数学建模E题(1月补赛)

占个位置吧&#xff0c;更新E题的详细思路代码&#xff0c;文章末尾名片获取&#xff01;ABC题已更新 持续为更新参考思路 E题思路分析&#xff1a; 第一问都是一些基础的数据分析问题&#xff0c;使用题目给出的数据稍加整理归纳即可得出结论。 E题给了4张表格数据&#x…