当前位置: 首页 > news >正文

苍穹外卖day2

添加员工

1.接口文档

1.请求参数

参数名称参数说明请求类型是否必须数据类型schema
employeeDTOemployeeDTObodytrueEmployeeDTOEmployeeDTO
└ idfalseinteger(int64)
└ idNumberfalsestring
└ namefalsestring
└ phonefalsestring
└ sexfalsestring
└ usernamefalsestring

2.相应参数

参数名称参数说明类型schema
codeinteger(int32)integer(int32)
dataobject
msgstring

3.请求示例

{"id":0,"idNumber":"","name":"","phone":"","sex":"","username":""}

4.响应示例

{"code":0,"data":{},"msg":""}

2.业务流程概览

新增员工的操作涉及到数据的接收、转换、增强(补全审计信息)以及持久化。

  • 前端传参EmployeeDTO(包含name,username,phone,idNumber,sex等)。
  • 后端处理
    1. Controller接收请求并打印日志。
    2. Service实现类进行属性拷贝,并手动补全数据库必需的审计字段(状态、密码、创建时间、创建人等)。
    3. Mapper执行 SQL 插入。
  • 异常处理
    1. 若用户存在返回“用户已存在”
    2. 否则返回未知错误

3.ThreadLocal 用户上下文

为了在Service层获取当前操作者的 ID,而又不通过方法参数层层传递,项目引入了ThreadLocal机制。

BaseContext 工具类

位于sky-common模块,负责存储当前线程的局部变量。

publicclassBaseContext{publicstaticThreadLocal<Long>threadLocal=newThreadLocal<>();publicstaticvoidsetCurrentId(Longid){threadLocal.set(id);}publicstaticLonggetCurrentId(){returnthreadLocal.get();}publicstaticvoidremoveCurrentId(){threadLocal.remove();}}

ThreadLocal 原理:

  • 线程隔离:每个线程维护自己的ThreadLocalMap,互不干扰。
  • 弱引用 Key:Key 是ThreadLocal对象的弱引用,Value 是强引用。
  • 生命周期:在拦截器存入 ID,在Service取用,在拦截器afterCompletion移除以防止内存泄漏。

4.代码实现

Controller 接口定义

@PostMapping@ApiOperation("新增员工")publicResultsave(@RequestBodyEmployeeDTOemployeeDTO){log.info("新增员工: {}",employeeDTO);employeeService.save(employeeDTO);returnResult.success();}

Service 实现类

publicvoidsave(EmployeeDTOemployeeDTO){Employeeemployee=newEmployee();// 1. 对象属性拷贝 (DTO -> Entity)BeanUtils.copyProperties(employeeDTO,employee);// 2. 设置账号状态 (1:启用, 0:锁定)employee.setStatus(StatusConstant.ENABLE);// 3. 设置默认密码 (123456) 并 MD5 加密employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));// 4. 设置审计时间employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());// 5. 设置审计人 (从 ThreadLocal 获取)employee.setCreateUser(BaseContext.getCurrentId());employee.setUpdateUser(BaseContext.getCurrentId());// 6. 调用持久层employeeMapper.insert(employee);}

Mapper层定义

/** * 插入员工数据 * @param employee */@Insert("insert into sky_take_out.employee(name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) "+"values"+"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")voidinsert(Employeeemployee);

插入数据异常处理

/** * 处理SQL异常 * @param ex * @return */@ExceptionHandlerpublicResultexceptionHandler(SQLIntegrityConstraintViolationExceptionex){Stringmessage=ex.getMessage();if(message.contains("Duplicate entry")){String[]split=message.split(" ");Stringusername=split[2];Stringmsg=username+MessageConstant.ALREADY_EXIST;returnResult.error(msg);}else{returnResult.error(MessageConstant.UNKNOWN_ERROR);}

员工分页查询

1.需求设计与接口文档

2.业务流程概览

  • 前端传参EmployeeDTO(包含name,username,phone,idNumber,sex等)。
  • 后端处理
    1. Controller接收请求并打印日志,封装结果为PageResult类返回给前端
    2. Service调用PageHelper存储页码每页记录数,并调用MapperpageQuery()方法
    3. Mapper层使用动态SQL实现按名字查询

3.PageHelper

  • PageHelper 原理:它是基于 MyBatis 的拦截器机制实现的。在执行查询前,从ThreadLocal中取出分页参数,动态改写 SQL 语句,增加LIMIT子句,因此在写动态SQL时不用增加LIMIT限制。
  • PageHelper 的有效范围:只对调用startPage()方法后的第一个查询方法有效。

4.代码实现

Contoller接口定义

/** * 分页查询 */@GetMapping("/page")@ApiOperation("分页查询")publicResult<PageResult>page(EmployeePageQueryDTOemployeePageQueryDTO){log.info("员工分页查询,参数为{}",employeePageQueryDTO);//封装为PageResult实体类返回前端PageResultpageResult=employeeService.pageQuery(employeePageQueryDTO);returnResult.success(pageResult);}

Service实现类

publicPageResultpageQuery(EmployeePageQueryDTOemployeePageQueryDTO){//调用PageHelper存储页码和记录数PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());//Page继承arraylist本质为一种list,用其存储查询返回的数据Page<Employee>page=employeeMapper.pageQuery(employeePageQueryDTO);//将总页码记录传回controllerlongtotal=page.getTotal();List<Employee>records=page.getResult();returnnewPageResult(total,records);}

Mapper

/** * 分页查询 * @param employeePageQueryDTO * @return */Page<Employee>pageQuery(EmployeePageQueryDTOemployeePageQueryDTO);
<selectid="pageQuery"resultType="com.sky.entity.Employee">select * from employee<where><iftest="name!=null and name !=' '">--字符串拼接预防sql注入 and name like concat ('%',#{name},'%')</if></where>order by create_time desc</select>

启用禁用员工账号

1.需求设计与接口文档

2.业务流程概览

  • 启用/禁用操作本质上是一个局部字段更新。前端通常传递两个参数:目标 ID 和 目标状态值(0 表示禁用,1 表示启用)。
  • 将id对应的实体类的status更改

3.代码实现

Contoller

/** * 启用禁用员工账号 */@PostMapping("/status/{status}")@ApiOperation("启用禁用员工账号")publicResultstartOrStop(@PathVariableIntegerstatus,longid){log.info("启用禁用员工账号:{},{}",status,id);employeeService.startOrStop(status,id);returnResult.success();}

Service实现类

/** * 编辑员工信息 * @param employeeDTO */publicvoidupdate(EmployeeDTOemployeeDTO){Employeeemployee=newEmployee();BeanUtils.copyProperties(employeeDTO,employee);employee.setUpdateTime(LocalDateTime.now());employee.setUpdateUser(BaseContext.getCurrentId());//再一次使用了BaseContextemployeeMapper.update(employee);//调用Mapper层的update}

Mapper

<updateid="update"parameterType="Employee">update employee<set><iftest="name!=null">name=#{name},</if><iftest="username!=null">username=#{username},</if><iftest="password!=null">password=#{password},</if><iftest="phone!=null">phone=#{phone},</if><iftest="sex!=null">sex=#{sex},</if><iftest="idNumber!=null">id_number=#{idNumber},</if><iftest="updateTime!=null">update_time=#{updateTime},</if><iftest="updateUser!=null">update_user=#{updateUser},</if><iftest="status!=null">status=#{status},</if></set>where id = #{id}</update>//update 语句可以供以后的开发共用,只许略微改动即可

编辑员工信息

1.需求分析与接口文档


2.业务流程概览

编辑操作通常分为两个步骤:回显(查询数据填入表单)和提交修改

数据回显:
  • 用户点击“编辑”按钮时,后端根据id查询详情,前端将数据填入表单。
  • CategoryController中,列表查询接口list或分页查询接口page已经承担了部分数据获取的功能。
修改信息:

参数接收:使用@RequestBody接收 DTO 对象(如CategoryDTOEmployeeDTO),因为编辑涉及的字段较多,JSON 传递更方便。

3. 代码实现

Controller

/** * 根据id查询员工信息 * @param id * @return */@GetMapping("/{id}")@ApiOperation("根据id查询员工信息")publicResult<Employee>getById(@PathVariableLongid){Employeeemployee=employeeService.getById(id);returnResult.success(employee);}/** * 编辑员工信息 * @param employeeDTO * @return */@PutMapping@ApiOperation("编辑员工信息")publicResultupdate(@RequestBodyEmployeeDTOemployeeDTO){log.info("编辑员工信息:{}",employeeDTO);employeeService.update(employeeDTO);returnResult.success();}

Service实现类

/** * 根据id查询员工信息 * @param id * @return */publicEmployeegetById(Longid){Employeeemployee=employeeMapper.getById(id);employee.setPassword("*****");//传回前端的密码也要加密防止泄露returnemployee;}/** * 编辑员工信息 * @param employeeDTO */publicvoidupdate(EmployeeDTOemployeeDTO){Employeeemployee=newEmployee();BeanUtils.copyProperties(employeeDTO,employee);//将已更改的DTO对象属性赋予给Employee对象employee.setUpdateTime(LocalDateTime.now());//更新未更改的属性employee.setUpdateUser(BaseContext.getCurrentId());employeeMapper.update(employee);}

Mapper

/** * 根据主键动态修改属性 * @param employee */voidupdate(Employeeemployee);//共用动态Sql的更新操作/** * 根据id查询员工信息 * @param id * @return */@Select("select * from employee where id =#{id}")EmployeegetById(Longid)

分类管理

1. 需求分析

业务需求

  • 支持分类的增加、删除、修改、分页查询。
  • 支持分类状态的灵活切换(启用/禁用)。
  • 约束条件:删除分类前必须校验其下是否挂载了菜品或套餐,若有则禁止删除。

2.业务流程概论

1.新增分类

前端发送POST请求,将分类信息以 JSON 格式传给后端。后端接收后进行属性拷贝,并默认将状态设置为禁用 (0),最后补充创建人、创建时间等审计信息并存入数据库。

2.分页查询

前端发送GET请求并携带分页及搜索参数。后端利用PageHelper 插件拦截 SQL 语句,结合 XML 中的动态 SQL(支持名称模糊匹配和类型过滤),自动实现数据的分页检索与排序。

3.启用与禁用

前端通过路径变量传递status,通过 Query 参数传递id。后端根据这些参数构建一个简化的实体对象,仅更新数据库中的status字段及相关的修改人、修改时间信息。

4.修改分类

前端发送PUT请求提交修改后的 JSON 数据。后端使用 MyBatis 的<set>动态标签,仅对前端传来的非空字段进行更新,确保未修改的数据保持不变。

5.根据 ID 删除

前端发送DELETE请求提交分类 ID。后端执行关联校验逻辑:先查询dish(菜品)和setmeal(套餐)表,若该分类下仍有数据则抛出异常禁止删除;只有在无关联数据时才执行物理删除。

3. 代码实现

代码与前面大同小异,值得注意的 删除分类前,必须先调用 DishMapper 和 SetmealMapper 统计关联数量

/** * 根据id删除分类 * @param id */publicvoiddeleteById(Longid){//查询当前分类是否关联了菜品,如果关联了就抛出业务异常Integercount=dishMapper.countByCategoryId(id);if(count>0){//当前分类下有菜品,不能删除thrownewDeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);}//查询当前分类是否关联了套餐,如果关联了就抛出业务异常count=setmealMapper.countByCategoryId(id);if(count>0){//当前分类下有菜品,不能删除thrownewDeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);}//删除分类数据categoryMapper.deleteById(id);}

总结

  • 感受到项目代码的复用率较高,CRUD的逻辑大部分相似。
  • 对于动态SQL的使用逐渐熟悉。
  • 熟悉了通过断点判断异常,以及编写自定义异常的逻辑。
  • 熟悉了BeanUtils,ThreadLocal的使用并感受到便利性
  • 对于前后端联调和前端发送请求后端响应的流程有些了解了
http://www.jsqmd.com/news/656990/

相关文章:

  • 奇偶判断:从取余到位运算的优雅解法
  • Excel公式格式化终极指南:如何让复杂公式一目了然
  • 开发者实战:2026年主流Claw工具技术对比与配置指南
  • Zotero文献格式化插件:3步打造规范学术文献库的终极指南
  • Claude Code 桌面版上线翻车:bug 多、结构失控,‘100% AI 编写’质量堪忧!
  • 生产刮刮卡定制制造商推荐
  • 从大模型到自主决策:AI Agent的核心进化路径
  • Boss-Key:你的Windows桌面隐身大师,一键隐藏所有敏感窗口
  • PHP线上死锁的庖丁解牛
  • 从零到一:用MK60单片机+鹰眼摄像头,手把手教你搭建一个能画方块的板球控制系统
  • Cursor Free VIP:解锁AI编程助手完整功能的终极方案
  • WinUtil:Windows系统优化与软件安装的终极解决方案
  • 移动端点 链接bing
  • 告别手动配置:用STM32CubeMX快速搞定STM32F407的DP83848以太网与LWIP初始化(附常见Ping不通问题排查)
  • 3步终极解锁VMware macOS虚拟机:开源工具Unlocker完整指南
  • A股沪指站稳4000点五连阳:银行股接棒主线,价值切换下的投资逻辑梳理
  • The 4th Universal Cup. Stage 13: Grand Prix of Ōokayama(无 DEL)
  • 树图管理化技术中的树图计划树图实施树图验证
  • GFS读写过程
  • 完全指南:高效使用开源工具突破Cursor AI Pro限制
  • 如何用LangChain开发一个Agent,20分钟搞定!
  • SAP接口集成-PO/PI-SLD配置实战:从系统格局到集成目录
  • 2026LINE养号:新号总被封?LINE账号养号与防封完整指南
  • 新年快乐 wp
  • Beyond Compare 5 密钥生成器:从技术原理到企业级部署指南
  • reverse2 wp
  • Element UI中国省市区级联数据:终极完整指南与实战教程
  • 独立站SEO流量增长:提高Google排名的优化方法
  • Eigen 3.4.90 矩阵操作实战 | C++高效线性代数指南(一)
  • 2026年美国投资移民机构排名及选择参考 - 品牌排行榜