前言

今天学习的内容主要分为用户管理和分类管理。,都是一些crud的基本操作,看视频熟悉了项目结构之后并不难 ,毕竟是基础。希望看到这里的同学朋友们能跟我一起坚持下去,达到自己 理想的目标!!!


视频学习链接黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战_哔哩哔哩_bilibili

资料网盘链接https://pan.baidu.com/s/1MNDzXyVlr3mtmLgBjcPJVw&pwd=6633

本人项目远程仓库

sky-take-out: 黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战


视频当中采取的开发步骤分为四步走:•需求分析和设计  代码开发  功能测试  代码完善。首先通过接口开发文档对即将要开发的接口进行分析和设计,相当于把“大象塞进冰箱”的步骤细分,需要什么、需要干什么等。代码完善是对代码进行功能方面的完善,让代码不仅仅停留在crud层面上,更加人性化。

一、员工管理

(一)、新增员工

                1.需求分析和设计

首先我们进入添加用户的界面,发现就是一个填写信息的表单。那我们可以根据表单内容以及现实情况分析。其中账号通过mysql数据库UNIQUE限制手机号身份证号在前端校验(通过后续测试接口可得知)。

然后打开Apifox进入接口文档具体分析,也可以跟着视频中同步项目地址:苍穹外卖项目接口文档(启动项目后再访问哦~)

接口地址:/admin/employee  访问方式:Post

请求参数:json形式

返回参数

2.代码开发

视频中老师开发的顺序是由外到内,Controller -> Service -> ServiceImpl -> Mapper

注意:当前端传入数据与实体类对应属性相差太大时使用DTO接收封装数据

显然刚刚新增用户的表单中并没有给12个字段都输入信息,并且例如id一些字段不需要用户输入

/**    Controller层
     * 新增员工方法
     * @param employeeDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增员工")
    public  Result  save(@RequestBody EmployeeDTO employeeDTO) {
        //控制台打印信息
        log.info("新增员工{}",employeeDTO);
        
        employeeService.insert(employeeDTO);

        return Result.success();
    }

/**   Service层
     * 新增员工
     * @param employeeDTO
     */
void insert(EmployeeDTO employeeDTO);


/**    Service实现类
     * 新增员工
     * @param employeeDTO
     */
    public void insert(EmployeeDTO employeeDTO) {
        //创建实体类对象接受DTO属性,再进行其他属性设置
        Employee employee = new Employee();
        //1.BeanUtils的属性拷贝赋值
        BeanUtils.copyProperties(employeeDTO, employee);

        //2
        //2.1 默认状态正常;1表示正常,0表示禁用
        employee.setStatus(StatusConstant.ENABLE);

        //2.2默认密码为123456
        employee.setPassword(
            DigestUtils.md5DigestAsHex(
                PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        //2.3设置创建、修改时间
        LocalDateTime now = LocalDateTime.now();
        employee.setCreateTime(now);
        employee.setUpdateTime(now);

        //2.4设置创建、修改用户
        //TODO 代码完善中完成关于动态获取当前操作用户id问题
        employee.setCreateUser(10L);
        employee.setUpdateUser(10L);

        //3.调用服务层 接口
        employeeMapper.insert(employee);

    }

    /**Mapper层,单表插入语句 -> @Insert注解
     * 添加员工
     * @param employee
     */
    @Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user) " +
            "values (#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser})")
    void insert(Employee employee);
3.功能测试

①接口文档测试  ②前后端联调测试(把项目跑起来,模拟用户测试功能)

接口文档测试

这里我们直接使用apifox自动生成的数据进行测试,但是发现请求成功没有返回数据

因为项目中使用JWT令牌来拦截登录,通过校验用户登录的token进行拦截。token有效时间为2h

并且再次测试登录接口,发现确实会由token

我们将测试的token进行全局配置,注意:参数名必须为token否则扫描不到

之后,为开发方便,可以将token时间设置长一点(根据自己需要),否则两小时登录一次还是比较烦的。

前后端联调测试

会发现和上面接口文档测试不同,因为前端校验长度,手机号必须11位,身份证号必须18位

4.功能完善

问题:username字段unique属性,当用户名重复时会直接报错,用户体验差

返回给前端的只有异常。

解决:在项目全局异常处理中对异常信息进行捕获、处理

sky-server模块的handler包下的GlobalExceptionHandler类中重写异常处理方法

    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error("异常信息:{}",ex.getMessage());
        String message = ex.getMessage();
        //判断是否为触发unique的重复异常
        if( message.contains("Duplicate entry")){
            //根据异常信息可知重复值在索引2的位置,通过空格进行切片
            String[] s = message.split(" ");
            String username = s[2];
            //返回错误提示
            String msg = username + MessageConstant.ALREADY_EXISTS;
            return Result.error(msg);
        } else{
            return Result.error(MessageConstant.UNKNOWN_ERROR);
        }
    }

问题2:解决Service实现类中动态获取当前操作用户id问题

解决:使用ThreadLocal在JWT验证时存储用户id

· 关于ThreadLocal

ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

ThreadLocal常用方法:

public void set( T value)   设置当前线程的线程局部变量的值
public T get()   返回当前线程所对应的线程局部变量的值
public void remove()        移除当前线程的线程局部变量
注意 :客户端发送的每次请求,后端的 Tomcat 服务器都会分配一个单独的线程来处理请求

将service实体类中写死的数据换成localThread中存储的用户id

(二)、员工分页查询

1.需求分析和设计

根据产品原型对接口进行分析,发现如图三个特点。

根据接口文档分析

接口地址:/admin/employee/page 请求方式:Get

2.代码开发

分页查询对应DTO:EmployeePageQueryDTO

(再次强调,迎合前端数据专门设计的封装类xxxDTO)

分页查询对应的返回类PageResult(观察项目结构发现,result包下就Result和PageResult)

注意:此处使用 mybatis 的分页插件 PageHelper 来简化分页代码的开发。底层基于 mybatis 的拦截器实现。(相当于帮我们加limit关键字)

/**    Controller层
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @GetMapping("/page")
    @ApiOperation("分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {
        log.info("分页查询{}",employeePageQueryDTO);
        PageResult pageResult = employeeService.page(employeePageQueryDTO);
        return Result.success(pageResult);
    }

/**    Service层
     * 员工分页查询
     *
     * @param employeePageQueryDTO
     * @return
     */
     PageResult page(EmployeePageQueryDTO employeePageQueryDTO);

    /** Service实现类
     * 员工分页查询
     *
     * @param employeePageQueryDTO
     * @return
     */
    public PageResult page(EmployeePageQueryDTO employeePageQueryDTO) {
        //0.mybatis提供的分页插件工具PageHelper
        //相当于select * from employee limit 0,10;
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());

        //1.进行分页查询
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult());
    }

    /**    Mapper
     * 员工分页查询;设计动态数据 -> 使用xml注入
     * @param employeePageQueryDTO
     * @return
     */
    Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);



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

3.功能测试

问题

测试时发现,展示时间时数据全都挤在一块,可读性很差

解决

①:使用@JsonFormat注解对日期格式化(量少方便)

②:拓展mvc消息转换器,对日期类型的数据统一格式化处理(量大方便)

/**    WebMvcConfiguration类
     * 拓展消息转换器:将时间类型属性自动转换为json格式
     * @param converters
     */
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("消息转换器....");
        //1.创建自己的消息转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //2.为消息转换器添加转换规则(JacksonObjectMapper在项目common包中json提供)
        //2.1JacksonObjectMapper里面有转换规则:将java当中多个时间类转换成对应格式
        converter.setObjectMapper(new JacksonObjectMapper());
        //3.将自己的转换器加入mvc转换器列表
        //注意:add将我们转换器默认加到末尾,可能不生效 ——> 指定位置
        converters.add(0,converter);
    }

(三)、启用禁用员工账号

1.需求分析和设计

业务规则:

可以对状态为“启用” 的员工账号进行“禁用”操作
可以对状态为“禁用”的员工账号进行“启用”操作
状态为“禁用”的员工账号不能登录系统

接口设计

2.代码开发

    /**    Controller
     * 员工状态禁启用
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("员工状态禁启用")
    public Result startOrStop(@PathVariable Integer status,Long id) {
        log.info("员工状态禁启用{},{}",status,id);

        employeeService.startOrStop(status,id);
        return Result.success();
    }

    /**    Service
     * 启用员工账号
     * @param status
     * @param id
     */
    void startOrStop(Integer status, Long id);


/**    Service实现
     * 员工禁启用方法
     * @param status
     * @param id
     */
    public void startOrStop(Integer status, Long id) {
        Employee employee = Employee.builder()
                .id(id)
                .status(status)
                .build();
        employeeMapper.update(employee);
    }

    
/**    Mapper
     * 根据id修改员工账号状态
     * @param employee
     */
    void update(Employee employee);

单单更新一个用户状态没必要单独写一个更新方法,为后面编辑用户功能方便

<update id="update" parameterType="com.sky.entity.Employee">
        update employee
        <set>
            <if test="username != null">username = #{username},</if>
            <if test="name != null">name = #{name},</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>

(四)、编辑员工

1.需求分析和设计

编辑员工功能涉及到两个接口:

根据 id 查询员工信息(表单 回显 用户原先数据)
编辑员工信息

接口设计

2.代码开发

1.getById

/**    Controller
     * 修改员工信息前回显员工原有数据
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询员工")
    public Result<Employee>  getById(@PathVariable Long id) {
        log.info("根据id查询员工{}",id);
        Employee employee = employeeService.getById(id);
        return Result.success(employee);
    }


/**    Service
     * 根据id查询员工
     * @param id
     * @return
     */
    Employee getById(Long id);

/**    ServiceImpl
     * 根据id查询员工
     * @param id
     * @return
     */
    public Employee getById(Long id) {
        Employee employee = employeeMapper.getById(id);
        return employee;
    }

/**    Mapper
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @Select("select * from employee where id = #{id}")
    Employee getById(Long id);
/**    Controller
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑员工信息")
    public Result update(@RequestBody EmployeeDTO employeeDTO) {
        log.info("编辑员工信息{}",employeeDTO);

        employeeService.update(employeeDTO);
        return Result.success();
    }

/**    Service
     * 根据id修改员工信息
     * @param employeeDTO
     */
    void update(EmployeeDTO employeeDTO);


/**    ServiceImpl
     * 根据id修改员工信息
     * @param employeeDTO
     */
    public void update(EmployeeDTO employeeDTO) {
        //update set (列 = 属性,.....) where id =
        Employee employee = new Employee();
        //1.拷贝传入参数属性
        BeanUtils.copyProperties(employeeDTO, employee);
        //2.更新修改时间及修改人
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(BaseContext.getCurrentId());
        //3.修改员工信息
        employeeMapper.update(employee);
    }

(五)、导入分类管理类功能代码

将资料中的代码导入到响应位置即可

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐