开始尝试从0写一个项目--后端(二)

实现学生管理

新增学生

接口设计

请求路径:/admin/student

请求方法:POST

请求参数:请求头:Headers:"Content-Type": "application/json"

请求体:Body:

id 学生id

idNumber 学生身份证

name 名字

phone 电话号码

sex 性别

username 用户名

返回数据:Result

student表设计

字段名

数据类型

说明

备注

id

bigint

主键

自增

name

varchar(32)

姓名

username

varchar(32)

用户名

唯一

password

varchar(64)

密码

phone

varchar(11)

手机号

sex

varchar(2)

性别

id_number

varchar(18)

身份证号

status

Int

账号状态

1正常 0锁定

create_time

Datetime

创建时间

update_time

datetime

最后修改时间

create_user

bigint

创建人id

update_user

bigint

最后修改人id

代码开发

设计DTO类接收前端传来的参数

sems-pojo/src/main/java/com/ljc/dto/StudentDTO.java

package com.ljc.dto;

import lombok.Data;

import java.io.Serializable;

@Data
public class StudentDTO implements Serializable {

    //学生id
    private Long id;

    //用户名
    private String username;

    //姓名
    private String name;

    //电话
    private String phone;

    //性别
    private String sex;

    //身份证号
    private String idNumber;

}

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

package com.ljc.controller.student;

import com.ljc.constant.JwtClaimsConstant;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
import com.ljc.properties.JwtProperties;
import com.ljc.result.Result;
import com.ljc.service.StudentService;
import com.ljc.utils.JwtUtil;
import com.ljc.vo.StudentLoginVO;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/admin/student")
@Slf4j
public class StudentController {
    @Autowired
    private StudentService studentService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param studentLoginDTO
     * @return
     */
    @ApiOperation("用户登录")
    @PostMapping("/login")
    public Result<StudentLoginVO> login(@RequestBody StudentLoginDTO studentLoginDTO) {
        log.info("学生登录:{}", studentLoginDTO);

        Student student = studentService.login(studentLoginDTO);

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, student.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        StudentLoginVO studentLoginVO = StudentLoginVO.builder()
                .id(student.getId())
                .userName(student.getUsername())
                .name(student.getName())
                .token(token)
                .build();

        return Result.success(studentLoginVO);
    }

    /**
     * 退出
     *
     * @return
     */
    @ApiOperation("用户退出")
    @PostMapping("/logout")
    public Result<String> logout() {
        return Result.success();
    }


    /**
     * 新增学生
     * @param studentDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增学生")
    public Result save(@RequestBody StudentDTO studentDTO){
        //日志,现在进行的是新增学生功能
        log.info("新增学生:{}", studentDTO);

        //调用service层(处理层)的方法进行新增
        studentService.save(studentDTO);

        return Result.success();
    }

}

Service层

sems-server/src/main/java/com/ljc/service/StudentService.java

package com.ljc.service;

import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;

public interface StudentService {

    /**
     * 学生登录
     * @param studentLoginDTO
     * @return
     */
    Student login(StudentLoginDTO studentLoginDTO);

    /**
     * 新增学生
     * @param studentDTO
     */
    void save(StudentDTO studentDTO);
}

ServiceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

package com.ljc.service.impl;

import com.ljc.constant.MessageConstant;
import com.ljc.constant.PasswordConstant;
import com.ljc.constant.StatusConstant;
import com.ljc.context.BaseContext;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
import com.ljc.exception.AccountLockedException;
import com.ljc.exception.AccountNotFoundException;
import com.ljc.exception.PasswordErrorException;
import com.ljc.mapper.StudentMapper;
import com.ljc.service.StudentService;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 学生登录
     *
     * @param studentLoginDTO
     * @return
     */
    public Student login(StudentLoginDTO studentLoginDTO) {
        String username = studentLoginDTO.getUsername();
        String password = studentLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Student student = studentMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (student == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // 进行md5加密,然后再进行比对
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(student.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (student.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return student;
    }


    /**
     *
     * 新增学生
     * @param studentDTO
     */
    @Override
    public void save(StudentDTO studentDTO) {
        //1. 创建一个学生对象
        Student student = new Student();

        //2. 将前端传后来的参数,即就是封装在DTO里面的数据转移到学生对象中
        //调用对象属性拷贝,括号里面参数,前面的是需要传递的数据,后面的是接收的数据
        BeanUtils.copyProperties(studentDTO,student);

        //3. 补充student里面的数据,前端没有传递回来的

        //3.1 前端没有传递账号状态,现在给设置账号状态:默认为正常,正常为1,异常为0;
        //由于1太不美观了,创建一个常量类
        /*student.setStatus(1);*/
        student.setStatus(StatusConstant.ENABLE);

        //3.2 设置密码:默认为123456,进行MD5加密
        //密码也设置一个常量:PasswordConstant.DEFAULT_PASSWORD
        /*student.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));*/
        student.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //3.3 设置创建时间和修改时间: 为当前时间
        student.setCreateTime(LocalDateTime.now());
        student.setUpdateTime(LocalDateTime.now());

        //3.4 设置创建人的id
        student.setCreateUser(BaseContext.getCurrentId());
        student.setUpdateUser(BaseContext.getCurrentId());

        //4 调用mapper层数据,连接数据库,对数据库进行操作
        studentMapper.insert(student);

    }

}

mapper层

sems-server/src/main/java/com/ljc/mapper/StudentMapper.java

package com.ljc.mapper;

import com.ljc.entity.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface StudentMapper {

    /**
     * 根据用户名查询学生
     * @param username
     * @return
     */
    @Select("select * from student where username = #{username}")
    Student getByUsername(String username);

    /**
     * 新增学生
     * @param student
     */
    @Insert("insert into student (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " +
            "values " +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void insert(Student student);
}

补充代码

这里面有几个常量值,和一个ThreadLocal来获取jwt令牌里面的用户名

封装了 ThreadLocal 操作的工具类:

sems-common/src/main/java/com/ljc/context/BaseContext.java

package com.ljc.context;

public class BaseContext {

    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }

    public static void removeCurrentId() {
        threadLocal.remove();
    }

}

常量类

密码

sems-common/src/main/java/com/ljc/constant/PasswordConstant.java

package com.ljc.constant;

/**
 * 密码常量
 */
public class PasswordConstant {

    public static final String DEFAULT_PASSWORD = "123456";

}

补充用户名重复常量

sems-common/src/main/java/com/ljc/constant/MessageConstant.java

package com.ljc.constant;

/**
 * 信息提示常量类
 */
public class MessageConstant {

    public static final String PASSWORD_ERROR = "密码错误";
    public static final String ACCOUNT_NOT_FOUND = "账号不存在";
    public static final String ACCOUNT_LOCKED = "账号被锁定";
    public static final String UNKNOWN_ERROR = "未知错误";
    public static final String USER_NOT_LOGIN = "用户未登录";
    public static final String CATEGORY_BE_RELATED_BY_SETMEAL = "当前分类关联了套餐,不能删除";
    public static final String CATEGORY_BE_RELATED_BY_DISH = "当前分类关联了菜品,不能删除";
    public static final String SHOPPING_CART_IS_NULL = "购物车数据为空,不能下单";
    public static final String ADDRESS_BOOK_IS_NULL = "用户地址为空,不能下单";
    public static final String LOGIN_FAILED = "登录失败";
    public static final String UPLOAD_FAILED = "文件上传失败";
    public static final String SETMEAL_ENABLE_FAILED = "套餐内包含未启售菜品,无法启售";
    public static final String PASSWORD_EDIT_FAILED = "密码修改失败";
    public static final String DISH_ON_SALE = "起售中的菜品不能删除";
    public static final String SETMEAL_ON_SALE = "起售中的套餐不能删除";
    public static final String DISH_BE_RELATED_BY_SETMEAL = "当前菜品关联了套餐,不能删除";
    public static final String ORDER_STATUS_ERROR = "订单状态错误";
    public static final String ORDER_NOT_FOUND = "订单不存在";

    public static final String ALREADY_EXISTS = "已存在";
}

处理用户名重复的异常

sems-server/src/main/java/com/ljc/handler/GlobalExceptionHandler.java

package com.ljc.handler;

import com.ljc.constant.MessageConstant;
import com.ljc.exception.BaseException;
import com.ljc.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常处理器,处理项目中抛出的业务异常
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获业务异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }

    /**
     * 捕获用户名重复
     * */
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        //Duplicate entry 'zhangsan' for key 'employee.idx_username'
        //获取异常信息
        String massage = ex.getMessage();

        //以” “分割异常信息;
        if (massage.contains("Duplicate entry")){
            String[] split = massage.split(" ");
            String name = split[2];
            String msg = name+ MessageConstant.ALREADY_EXISTS;//已存在已有常量,尽量避免字符串
            return Result.error(msg);
        }else {
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }


    }

}

测试

完成后进入swagger界面测试一下

因为需要进行jwt令牌检测,所以

使用admin用户登录获取令牌

将合法的JWT令牌添加到全局参数中

检查一下添加上了没

然后开始测试

检查数据库添加上了没

学生分页查询

接口设计

请求路劲:/admin/student/page

请求方式:GET

请求参数:请求参数类型为Query,和前面的不一样,这是直接在网络地址上连接的

name 员工姓名;page 页码;pageSize 每页记录数

返回参数:

代码开发

DTO类设计

接收前端传来的数据

sems-pojo/src/main/java/com/ljc/dto/StudentPageQueryDTO.java

package com.ljc.dto;

import lombok.Data;

import java.io.Serializable;

@Data
public class StudentPageQueryDTO implements Serializable {

    //学生姓名
    private String name;

    //页码
    private int page;

    //每页显示记录数
    private int pageSize;

}

封装分页查询(特定返回给前端的实体类,后面所有分页查询都用得到)

分页查询后端返回的对象类型为: Result<PageResult>

sems-common/src/main/java/com/ljc/result/PageResult.java

package com.ljc.result;

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

import java.io.Serializable;
import java.util.List;

/**
 * 封装分页查询结果
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult implements Serializable {

    private long total; //总记录数

    private List records; //当前页数据集合

}

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

package com.ljc.controller.student;

import com.ljc.constant.JwtClaimsConstant;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.properties.JwtProperties;
import com.ljc.result.PageResult;
import com.ljc.result.Result;
import com.ljc.service.StudentService;
import com.ljc.utils.JwtUtil;
import com.ljc.vo.StudentLoginVO;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/admin/student")
@Slf4j
public class StudentController {
    @Autowired
    private StudentService studentService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param studentLoginDTO
     * @return
     */
    @ApiOperation("用户登录")
    @PostMapping("/login")
    public Result<StudentLoginVO> login(@RequestBody StudentLoginDTO studentLoginDTO) {
        log.info("学生登录:{}", studentLoginDTO);

        Student student = studentService.login(studentLoginDTO);

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, student.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        StudentLoginVO studentLoginVO = StudentLoginVO.builder()
                .id(student.getId())
                .userName(student.getUsername())
                .name(student.getName())
                .token(token)
                .build();

        return Result.success(studentLoginVO);
    }

    /**
     * 退出
     *
     * @return
     */
    @ApiOperation("用户退出")
    @PostMapping("/logout")
    public Result<String> logout() {
        return Result.success();
    }


    /**
     * 新增学生
     * @param studentDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增学生")
    public Result save(@RequestBody StudentDTO studentDTO){
        //日志,现在进行的是新增学生功能
        log.info("新增学生:{}", studentDTO);

        //调用service层(处理层)的方法进行新增
        studentService.save(studentDTO);

        return Result.success();
    }


    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("学生分页查询")
    public Result<PageResult> page(/*因为前端参数类型是Query,所以不需要注解*/StudentPageQueryDTO studentPageQueryDTO){
        log.info("学生分页查询:{}",studentPageQueryDTO);

        PageResult pageResult = studentService.page(studentPageQueryDTO);

        return Result.success(pageResult);

    }


}

service层

sems-server/src/main/java/com/ljc/service/StudentService.java

package com.ljc.service;

import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.result.PageResult;

public interface StudentService {

    /**
     * 学生登录
     * @param studentLoginDTO
     * @return
     */
    Student login(StudentLoginDTO studentLoginDTO);

    /**
     * 新增学生
     * @param studentDTO
     */
    void save(StudentDTO studentDTO);

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    PageResult page(StudentPageQueryDTO studentPageQueryDTO);
}

ServiceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

package com.ljc.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ljc.constant.MessageConstant;
import com.ljc.constant.PasswordConstant;
import com.ljc.constant.StatusConstant;
import com.ljc.context.BaseContext;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.exception.AccountLockedException;
import com.ljc.exception.AccountNotFoundException;
import com.ljc.exception.PasswordErrorException;
import com.ljc.mapper.StudentMapper;
import com.ljc.result.PageResult;
import com.ljc.service.StudentService;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 学生登录
     *
     * @param studentLoginDTO
     * @return
     */
    public Student login(StudentLoginDTO studentLoginDTO) {
        String username = studentLoginDTO.getUsername();
        String password = studentLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Student student = studentMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (student == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // 进行md5加密,然后再进行比对
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(student.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (student.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return student;
    }


    /**
     *
     * 新增学生
     * @param studentDTO
     */
    @Override
    public void save(StudentDTO studentDTO) {
        //1. 创建一个学生对象
        Student student = new Student();

        //2. 将前端传后来的参数,即就是封装在DTO里面的数据转移到学生对象中
        //调用对象属性拷贝,括号里面参数,前面的是需要传递的数据,后面的是接收的数据
        BeanUtils.copyProperties(studentDTO,student);

        //3. 补充student里面的数据,前端没有传递回来的

        //3.1 前端没有传递账号状态,现在给设置账号状态:默认为正常,正常为1,异常为0;
        //由于1太不美观了,创建一个常量类
        /*student.setStatus(1);*/
        student.setStatus(StatusConstant.ENABLE);

        //3.2 设置密码:默认为123456,进行MD5加密
        //密码也设置一个常量:PasswordConstant.DEFAULT_PASSWORD
        /*student.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));*/
        student.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //3.3 设置创建时间和修改时间: 为当前时间
        student.setCreateTime(LocalDateTime.now());
        student.setUpdateTime(LocalDateTime.now());

        //3.4 设置创建人的id
        student.setCreateUser(BaseContext.getCurrentId());
        student.setUpdateUser(BaseContext.getCurrentId());

        //4 调用mapper层数据,连接数据库,对数据库进行操作
        studentMapper.insert(student);

    }

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    @Override
    public PageResult page(StudentPageQueryDTO studentPageQueryDTO) {
        //1. 设置分页参数
        //第一个参数是分页查询的当前页码,第二个是当前页需要展示的数据大小
        PageHelper.startPage(studentPageQueryDTO.getPage(),studentPageQueryDTO.getPageSize());

        //2. 执行查询、
        Page<Student> page = studentMapper.pageQuery(studentPageQueryDTO);
        //3. 使用PageInfo封装分页结果
        long total = page.getTotal();
        List<Student> records = page.getResult();

        //4. 返回封装的PageResult对象
        return new PageResult(total,records);
    }

}

mapper层

sems-server/src/main/java/com/ljc/mapper/StudentMapper.java

package com.ljc.mapper;

import com.github.pagehelper.Page;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.result.PageResult;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface StudentMapper {

    /**
     * 根据用户名查询学生
     * @param username
     * @return
     */
    @Select("select * from student where username = #{username}")
    Student getByUsername(String username);

    /**
     * 新增学生
     * @param student
     */
    @Insert("insert into student (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " +
            "values " +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void insert(Student student);

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    //查询在xml文件里面动态查询;
    Page<Student> pageQuery(StudentPageQueryDTO studentPageQueryDTO);
}

sems-server/src/main/resources/mapper/StudentMapper.xml

<?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.ljc.mapper.StudentMapper">

    <select id="pageQuery" resultType="com.ljc.entity.Student" parameterType="com.ljc.dto.StudentPageQueryDTO">
        select * from student
        <where>
            <if test="name != null and name != ''">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>

日期统一格式

在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理

sems-server/src/main/java/com/ljc/config/WebMvcConfiguration.java

package com.ljc.config;

import com.ljc.interceptor.JwtTokenAdminInterceptor;
import com.ljc.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.List;

/**
 * 配置类,注册web层相关组件
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin/student/login");
    }

    /**
     * 通过knife4j生成接口文档
     * @return
     */
    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("体育器材项目接口文档")
                .version("1.0")
                .description("体育器材项目接口文档")
                .build();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.ljc.controller"))
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    /**
     * 设置静态资源映射
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    /**
     * 扩展Spring MVC框架的消息转化器
     * @param converters
     */
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转化器加入容器中
        converters.add(0,converter);
    }
}

同时定义一个时间格式类(把常见的都写进去)

sems-common/src/main/java/com/ljc/json/JacksonObjectMapper.java

package com.ljc.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

测试

PS:如果token过期了,记得去用户登录里重新获取

启用禁用学生状态

接口设计

请求路径:/admin/student/status/{status}

请求方法:POST

请求参数:Header:application/json

路径参数:status

query:id

返回参数:code;msg;data

代码开发

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

/**
     * 启用禁用学生状态
     * @param status
     * @param id
     * @return
     */
    @GetMapping("/status/{status}")
    @ApiOperation("启用禁止学生状态")
    public Result startOrStopStatus(@PathVariable Integer status,Long id){
        log.info("启用禁用学生状态:{},{}",status,id);

        studentService.starOrStopStatus(status,id);
        
        return Result.success();
    }

service层

sems-server/src/main/java/com/ljc/service/StudentService.java

 /**
     * 启用禁用学生状态
     * @param status
     * @param id
     */
    void starOrStopStatus(Integer status, Long id);

serviceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

/**
     * 启用禁用学生状态
     * @param status
     * @param id
     */
    @Override
    public void starOrStopStatus(Integer status, Long id) {
        Student student = Student
                .builder()
                .id(id)
                .status(status)
                .build();

        studentMapper.updata(student);
    }

mapper层

sems-server/src/main/java/com/ljc/mapper/StudentMapper.java

/**
     * 启用禁用学生状态
     * @param student
     */
    void updata(Student student);

sems-server/src/main/resources/mapper/StudentMapper.xml

<update id="updata" parameterType="com.ljc.entity.Student">
        update student
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="sex != null">sex = #{sex},</if>
            <if test="idNumber != null">id_Number = #{idNumber},</if>
            <if test="updateTime != null">update_Time = #{updateTime},</if>
            <if test="updateUser != null">update_User = #{updateUser},</if>
            <if test="status != null">status = #{status},</if>
        </set>
        where id = #{id}
    </update>

测试

编辑学生信息

接口设计

根据id查询学生

请求路径:/admin/student/{id}

请求方法:GET

请求参数:id

返回数据:code;msg;data(student类)

编辑学生信息

请求路径:/admin/student

请求方法:PUT

请求参数:Headers:application/json

Body:id,idNumber,name,phone,sex,username

返回数据:code;msg;data

代码开发

根据id查询学生

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

/**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询学生信息")
    public Result<Student> getById(@PathVariable Long id){
        log.info("根据id查询学生信息:{}",id);

        Student student = studentService.getById(id);

        return Result.success(student);
    }
service层

sems-server/src/main/java/com/ljc/service/StudentService.java

/**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    Student getById(Long id);
ServiceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

/**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @Override
    public Student getById(Long id) {
        //根据mapper层查询数据
        Student student = studentMapper.getById(id);

        //将查询回来的密码进行保护
        student.setPassword("*****");

        return student;
    }
mapper层

sems-server/src/main/java/com/ljc/mapper/StudentMapper.java

/**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @Select("select * from student where id = #{id}")
    Student getById(Long id);
测试

编辑学生信息

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

/**
     * 编辑学生信息
     * @param studentDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑学生信息")
    public Result update(@RequestBody StudentDTO studentDTO){
        log.info("编辑学生信息:{}", studentDTO);
        studentService.update(studentDTO);
        return Result.success();
    }
service层

sems-server/src/main/java/com/ljc/service/StudentService.java

 /**
     * 编辑学生信息
     * @param studentDTO
     */
    void update(StudentDTO studentDTO);
ServiceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

/**
     * 编辑学生信息
     * @param studentDTO
     */
    @Override
    public void update(StudentDTO studentDTO) {
        Student student = new Student();

        BeanUtils.copyProperties(studentDTO,student);

        //补充前端没有传回来的数据
        student.setUpdateTime(LocalDateTime.now());
        student.setUpdateUser(BaseContext.getCurrentId());

        studentMapper.updata(student);//之前启用禁用学生状态板块以及实现;
    }

测试

目前为止整体代码

controller层

sems-server/src/main/java/com/ljc/controller/student/StudentController.java

package com.ljc.controller.student;

import com.ljc.constant.JwtClaimsConstant;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.properties.JwtProperties;
import com.ljc.result.PageResult;
import com.ljc.result.Result;
import com.ljc.service.StudentService;
import com.ljc.utils.JwtUtil;
import com.ljc.vo.StudentLoginVO;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/admin/student")
@Slf4j
public class StudentController {
    @Autowired
    private StudentService studentService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param studentLoginDTO
     * @return
     */
    @ApiOperation("用户登录")
    @PostMapping("/login")
    public Result<StudentLoginVO> login(@RequestBody StudentLoginDTO studentLoginDTO) {
        log.info("学生登录:{}", studentLoginDTO);

        Student student = studentService.login(studentLoginDTO);

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, student.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        StudentLoginVO studentLoginVO = StudentLoginVO.builder()
                .id(student.getId())
                .userName(student.getUsername())
                .name(student.getName())
                .token(token)
                .build();

        return Result.success(studentLoginVO);
    }

    /**
     * 退出
     *
     * @return
     */
    @ApiOperation("用户退出")
    @PostMapping("/logout")
    public Result<String> logout() {
        return Result.success();
    }


    /**
     * 新增学生
     * @param studentDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增学生")
    public Result save(@RequestBody StudentDTO studentDTO){
        //日志,现在进行的是新增学生功能
        log.info("新增学生:{}", studentDTO);

        //调用service层(处理层)的方法进行新增
        studentService.save(studentDTO);

        return Result.success();
    }


    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("学生分页查询")
    public Result<PageResult> page(/*因为前端参数类型是Query,所以不需要注解*/StudentPageQueryDTO studentPageQueryDTO){
        log.info("学生分页查询:{}",studentPageQueryDTO);

        PageResult pageResult = studentService.page(studentPageQueryDTO);

        return Result.success(pageResult);

    }


    /**
     * 启用禁用学生状态
     * @param status
     * @param id
     * @return
     */
    @GetMapping("/status/{status}")
    @ApiOperation("启用禁止学生状态")
    public Result startOrStopStatus(@PathVariable Integer status,Long id){
        log.info("启用禁用学生状态:{},{}",status,id);

        studentService.starOrStopStatus(status,id);

        return Result.success();
    }


    /**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询学生信息")
    public Result<Student> getById(@PathVariable Long id){
        log.info("根据id查询学生信息:{}",id);

        Student student = studentService.getById(id);

        return Result.success(student);
    }

    /**
     * 编辑学生信息
     * @param studentDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑学生信息")
    public Result update(@RequestBody StudentDTO studentDTO){
        log.info("编辑学生信息:{}", studentDTO);
        studentService.update(studentDTO);
        return Result.success();
    }
}

service层

sems-server/src/main/java/com/ljc/service/StudentService.java

package com.ljc.service;

import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.result.PageResult;

public interface StudentService {

    /**
     * 学生登录
     * @param studentLoginDTO
     * @return
     */
    Student login(StudentLoginDTO studentLoginDTO);

    /**
     * 新增学生
     * @param studentDTO
     */
    void save(StudentDTO studentDTO);

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    PageResult page(StudentPageQueryDTO studentPageQueryDTO);

    /**
     * 启用禁用学生状态
     * @param status
     * @param id
     */
    void starOrStopStatus(Integer status, Long id);

    /**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    Student getById(Long id);

    /**
     * 编辑学生信息
     * @param studentDTO
     */
    void update(StudentDTO studentDTO);
}

serviceImpl层

sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java

package com.ljc.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ljc.constant.MessageConstant;
import com.ljc.constant.PasswordConstant;
import com.ljc.constant.StatusConstant;
import com.ljc.context.BaseContext;
import com.ljc.dto.StudentDTO;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.exception.AccountLockedException;
import com.ljc.exception.AccountNotFoundException;
import com.ljc.exception.PasswordErrorException;
import com.ljc.mapper.StudentMapper;
import com.ljc.result.PageResult;
import com.ljc.service.StudentService;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 学生登录
     *
     * @param studentLoginDTO
     * @return
     */
    public Student login(StudentLoginDTO studentLoginDTO) {
        String username = studentLoginDTO.getUsername();
        String password = studentLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Student student = studentMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (student == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // 进行md5加密,然后再进行比对
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(student.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (student.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return student;
    }


    /**
     *
     * 新增学生
     * @param studentDTO
     */
    @Override
    public void save(StudentDTO studentDTO) {
        //1. 创建一个学生对象
        Student student = new Student();

        //2. 将前端传后来的参数,即就是封装在DTO里面的数据转移到学生对象中
        //调用对象属性拷贝,括号里面参数,前面的是需要传递的数据,后面的是接收的数据
        BeanUtils.copyProperties(studentDTO,student);

        //3. 补充student里面的数据,前端没有传递回来的

        //3.1 前端没有传递账号状态,现在给设置账号状态:默认为正常,正常为1,异常为0;
        //由于1太不美观了,创建一个常量类
        /*student.setStatus(1);*/
        student.setStatus(StatusConstant.ENABLE);

        //3.2 设置密码:默认为123456,进行MD5加密
        //密码也设置一个常量:PasswordConstant.DEFAULT_PASSWORD
        /*student.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));*/
        student.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //3.3 设置创建时间和修改时间: 为当前时间
        student.setCreateTime(LocalDateTime.now());
        student.setUpdateTime(LocalDateTime.now());

        //3.4 设置创建人的id
        student.setCreateUser(BaseContext.getCurrentId());
        student.setUpdateUser(BaseContext.getCurrentId());

        //4 调用mapper层数据,连接数据库,对数据库进行操作
        studentMapper.insert(student);

    }

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    @Override
    public PageResult page(StudentPageQueryDTO studentPageQueryDTO) {
        //1. 设置分页参数
        //第一个参数是分页查询的当前页码,第二个是当前页需要展示的数据大小
        PageHelper.startPage(studentPageQueryDTO.getPage(),studentPageQueryDTO.getPageSize());

        //2. 执行查询、
        Page<Student> page = studentMapper.pageQuery(studentPageQueryDTO);
        //3. 使用PageInfo封装分页结果
        long total = page.getTotal();
        List<Student> records = page.getResult();

        //4. 返回封装的PageResult对象
        return new PageResult(total,records);
    }

    /**
     * 启用禁用学生状态
     * @param status
     * @param id
     */
    @Override
    public void starOrStopStatus(Integer status, Long id) {
        Student student = Student
                .builder()
                .id(id)
                .status(status)
                .build();

        studentMapper.updata(student);
    }

    /**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @Override
    public Student getById(Long id) {
        //根据mapper层查询数据
        Student student = studentMapper.getById(id);

        //将查询回来的密码进行保护
        student.setPassword("*****");

        return student;
    }

    /**
     * 编辑学生信息
     * @param studentDTO
     */
    @Override
    public void update(StudentDTO studentDTO) {
        Student student = new Student();

        BeanUtils.copyProperties(studentDTO,student);

        //补充前端没有传回来的数据
        student.setUpdateTime(LocalDateTime.now());
        student.setUpdateUser(BaseContext.getCurrentId());

        studentMapper.updata(student);//之前启用禁用学生状态板块以及实现;
    }


}

mapper层

sems-server/src/main/java/com/ljc/mapper/StudentMapper.java

package com.ljc.mapper;

import com.github.pagehelper.Page;
import com.ljc.dto.StudentPageQueryDTO;
import com.ljc.entity.Student;
import com.ljc.result.PageResult;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface StudentMapper {

    /**
     * 根据用户名查询学生
     * @param username
     * @return
     */
    @Select("select * from student where username = #{username}")
    Student getByUsername(String username);

    /**
     * 新增学生
     * @param student
     */
    @Insert("insert into student (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " +
            "values " +
            "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")
    void insert(Student student);

    /**
     * 学生分页查询
     * @param studentPageQueryDTO
     * @return
     */
    //查询在xml文件里面动态查询;
    Page<Student> pageQuery(StudentPageQueryDTO studentPageQueryDTO);

    /**
     * 启用禁用学生状态
     * @param student
     */
    void updata(Student student);

    /**
     * 根据id查询学生信息
     * @param id
     * @return
     */
    @Select("select * from student where id = #{id}")
    Student getById(Long id);
}

sems-server/src/main/resources/mapper/StudentMapper.xml

<?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.ljc.mapper.StudentMapper">
    <update id="updata" parameterType="com.ljc.entity.Student">
        update student
        <set>
            <if test="name != null">name = #{name},</if>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="sex != null">sex = #{sex},</if>
            <if test="idNumber != null">id_Number = #{idNumber},</if>
            <if test="updateTime != null">update_Time = #{updateTime},</if>
            <if test="updateUser != null">update_User = #{updateUser},</if>
            <if test="status != null">status = #{status},</if>
        </set>
        where id = #{id}
    </update>

    <select id="pageQuery" resultType="com.ljc.entity.Student" parameterType="com.ljc.dto.StudentPageQueryDTO">
        select * from student
        <where>
            <if test="name != null and name != ''">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>
</mapper>

学生管理版本基本搞定,现在去完成前端相关的板块

相关推荐

  1. rust0开始项目-04-多样化错误处理

    2024-07-10 12:50:06       43 阅读
  2. 0开始android

    2024-07-10 12:50:06       40 阅读
  3. rust0开始项目-06-如何接受命令行参数clap-01

    2024-07-10 12:50:06       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 12:50:06       4 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 12:50:06       5 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 12:50:06       4 阅读
  4. Python语言-面向对象

    2024-07-10 12:50:06       5 阅读

热门阅读

  1. 小程序的制作费用很贵么

    2024-07-10 12:50:06       8 阅读
  2. c#实现23种常见的设计模式--动态更新

    2024-07-10 12:50:06       6 阅读
  3. 银河麒麟(V10SP1)-arm版交叉编译-qt-5.12.12源码

    2024-07-10 12:50:06       7 阅读
  4. 华为机考真题 -- 游戏分组

    2024-07-10 12:50:06       10 阅读
  5. Linux 期末速成(知识点+例题)

    2024-07-10 12:50:06       10 阅读
  6. 【基础篇】1.8 C语言基础(二)

    2024-07-10 12:50:06       8 阅读
  7. element ui form添加校验规则

    2024-07-10 12:50:06       8 阅读
  8. splice方法的使用#Vue3

    2024-07-10 12:50:06       9 阅读
  9. 使用Dockerfile和ENTRYPOINT运行Python 3脚本

    2024-07-10 12:50:06       9 阅读
  10. 黑龙江等保测评对中小企业成本效益分析

    2024-07-10 12:50:06       9 阅读