苍穹外卖day2

张开发
2026/4/17 19:27:50 15 分钟阅读

分享文章

苍穹外卖day2
添加员工1.接口文档1.请求参数参数名称参数说明请求类型是否必须数据类型schemaemployeeDTOemployeeDTObodytrueEmployeeDTOEmployeeDTO└ idfalseinteger(int64)└ idNumberfalsestring└ namefalsestring└ phonefalsestring└ sexfalsestring└ usernamefalsestring2.相应参数参数名称参数说明类型schemacodeinteger(int32)integer(int32)dataobjectmsgstring3.请求示例{id:0,idNumber:,name:,phone:,sex:,username:}4.响应示例{code:0,data:{},msg:}2.业务流程概览新增员工的操作涉及到数据的接收、转换、增强补全审计信息以及持久化。前端传参EmployeeDTO包含name,username,phone,idNumber,sex等。后端处理Controller接收请求并打印日志。Service实现类进行属性拷贝并手动补全数据库必需的审计字段状态、密码、创建时间、创建人等。Mapper执行 SQL 插入。异常处理若用户存在返回“用户已存在”否则返回未知错误3.ThreadLocal 用户上下文为了在Service层获取当前操作者的 ID而又不通过方法参数层层传递项目引入了ThreadLocal机制。BaseContext 工具类位于sky-common模块负责存储当前线程的局部变量。publicclassBaseContext{publicstaticThreadLocalLongthreadLocalnewThreadLocal();publicstaticvoidsetCurrentId(Longid){threadLocal.set(id);}publicstaticLonggetCurrentId(){returnthreadLocal.get();}publicstaticvoidremoveCurrentId(){threadLocal.remove();}}ThreadLocal 原理线程隔离每个线程维护自己的ThreadLocalMap互不干扰。弱引用 KeyKey 是ThreadLocal对象的弱引用Value 是强引用。生命周期在拦截器存入 ID在Service取用在拦截器afterCompletion移除以防止内存泄漏。4.代码实现Controller 接口定义PostMappingApiOperation(新增员工)publicResultsave(RequestBodyEmployeeDTOemployeeDTO){log.info(新增员工: {},employeeDTO);employeeService.save(employeeDTO);returnResult.success();}Service 实现类publicvoidsave(EmployeeDTOemployeeDTO){EmployeeemployeenewEmployee();// 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){Stringmessageex.getMessage();if(message.contains(Duplicate entry)){String[]splitmessage.split( );Stringusernamesplit[2];StringmsgusernameMessageConstant.ALREADY_EXIST;returnResult.error(msg);}else{returnResult.error(MessageConstant.UNKNOWN_ERROR);}员工分页查询1.需求设计与接口文档2.业务流程概览前端传参EmployeeDTO包含name,username,phone,idNumber,sex等。后端处理Controller接收请求并打印日志封装结果为PageResult类返回给前端Service调用PageHelper存储页码和每页记录数并调用Mapper层pageQuery()方法Mapper层使用动态SQL实现按名字查询3.PageHelperPageHelper 原理它是基于 MyBatis 的拦截器机制实现的。在执行查询前从ThreadLocal中取出分页参数动态改写 SQL 语句增加LIMIT子句因此在写动态SQL时不用增加LIMIT限制。PageHelper 的有效范围只对调用startPage()方法后的第一个查询方法有效。4.代码实现Contoller接口定义/** * 分页查询 */GetMapping(/page)ApiOperation(分页查询)publicResultPageResultpage(EmployeePageQueryDTOemployeePageQueryDTO){log.info(员工分页查询参数为{},employeePageQueryDTO);//封装为PageResult实体类返回前端PageResultpageResultemployeeService.pageQuery(employeePageQueryDTO);returnResult.success(pageResult);}Service实现类publicPageResultpageQuery(EmployeePageQueryDTOemployeePageQueryDTO){//调用PageHelper存储页码和记录数PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());//Page继承arraylist本质为一种list用其存储查询返回的数据PageEmployeepageemployeeMapper.pageQuery(employeePageQueryDTO);//将总页码记录传回controllerlongtotalpage.getTotal();ListEmployeerecordspage.getResult();returnnewPageResult(total,records);}Mapper/** * 分页查询 * param employeePageQueryDTO * return */PageEmployeepageQuery(EmployeePageQueryDTOemployeePageQueryDTO);selectidpageQueryresultTypecom.sky.entity.Employeeselect * from employeewhereiftestname!null and name ! --字符串拼接预防sql注入 and name like concat (%,#{name},%)/if/whereorder 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){EmployeeemployeenewEmployee();BeanUtils.copyProperties(employeeDTO,employee);employee.setUpdateTime(LocalDateTime.now());employee.setUpdateUser(BaseContext.getCurrentId());//再一次使用了BaseContextemployeeMapper.update(employee);//调用Mapper层的update}MapperupdateidupdateparameterTypeEmployeeupdate employeesetiftestname!nullname#{name},/ififtestusername!nullusername#{username},/ififtestpassword!nullpassword#{password},/ififtestphone!nullphone#{phone},/ififtestsex!nullsex#{sex},/ififtestidNumber!nullid_number#{idNumber},/ififtestupdateTime!nullupdate_time#{updateTime},/ififtestupdateUser!nullupdate_user#{updateUser},/ififteststatus!nullstatus#{status},/if/setwhere id #{id}/update//update 语句可以供以后的开发共用只许略微改动即可编辑员工信息1.需求分析与接口文档2.业务流程概览编辑操作通常分为两个步骤回显查询数据填入表单和提交修改。数据回显用户点击“编辑”按钮时后端根据id查询详情前端将数据填入表单。在CategoryController中列表查询接口list或分页查询接口page已经承担了部分数据获取的功能。修改信息参数接收使用RequestBody接收 DTO 对象如CategoryDTO或EmployeeDTO因为编辑涉及的字段较多JSON 传递更方便。3. 代码实现Controller/** * 根据id查询员工信息 * param id * return */GetMapping(/{id})ApiOperation(根据id查询员工信息)publicResultEmployeegetById(PathVariableLongid){EmployeeemployeeemployeeService.getById(id);returnResult.success(employee);}/** * 编辑员工信息 * param employeeDTO * return */PutMappingApiOperation(编辑员工信息)publicResultupdate(RequestBodyEmployeeDTOemployeeDTO){log.info(编辑员工信息:{},employeeDTO);employeeService.update(employeeDTO);returnResult.success();}Service实现类/** * 根据id查询员工信息 * param id * return */publicEmployeegetById(Longid){EmployeeemployeeemployeeMapper.getById(id);employee.setPassword(*****);//传回前端的密码也要加密防止泄露returnemployee;}/** * 编辑员工信息 * param employeeDTO */publicvoidupdate(EmployeeDTOemployeeDTO){EmployeeemployeenewEmployee();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){//查询当前分类是否关联了菜品如果关联了就抛出业务异常IntegercountdishMapper.countByCategoryId(id);if(count0){//当前分类下有菜品不能删除thrownewDeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);}//查询当前分类是否关联了套餐如果关联了就抛出业务异常countsetmealMapper.countByCategoryId(id);if(count0){//当前分类下有菜品不能删除thrownewDeletionNotAllowedException(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);}//删除分类数据categoryMapper.deleteById(id);}总结感受到项目代码的复用率较高CRUD的逻辑大部分相似。对于动态SQL的使用逐渐熟悉。熟悉了通过断点判断异常以及编写自定义异常的逻辑。熟悉了BeanUtilsThreadLocal的使用并感受到便利性对于前后端联调和前端发送请求后端响应的流程有些了解了

更多文章