news 2026/5/29 6:43:00

♪苍穹外卖♪Day2 | 项目日记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
♪苍穹外卖♪Day2 | 项目日记

♪苍穹外卖♪Day2 | 项目日记

今日完成内容

今天主要完成了员工管理模块的完善分类管理模块的完整开发,另外开始搭建 AOP 公共字段自动填充的框架。


一、员工管理模块完善

昨天已经完成了员工的登录、退出、新增和分页查询,今天在此基础上补全了三个接口。

1. 启用/禁用员工账号

EmployeeController中新增接口:

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

Service 层使用 Builder 模式构建实体对象,只设置statusid,调用 Mapper 的update方法:

@OverridepublicvoidstartOrStop(Integerstatus,Longid){Employeeemployee=Employee.builder().status(status).id(id).build();employeeMapper.update(employee);}

这里的update使用的是动态 SQL,只更新非空字段,这是关键 —— 后面编辑员工信息也复用同一个 update 方法。

[!注意]
超级管理员(admin)不允许被禁用,这个业务约束在项目后续会加上。

2. 根据 ID 查询员工信息

@GetMapping("/{id}")@ApiOperation("根据id查询员工信息")publicResult<Employee>getById(@PathVariableLongid){Employeeemployee=employeeService.getById(id);returnResult.success(employee);}

在 Service 层做了密码脱敏处理,返回数据时将密码替换为****

@OverridepublicEmployeegetById(Longid){Employeeemployee=employeeMapper.getById(id);employee.setPassword("****");returnemployee;}

3. 编辑员工信息

@PutMapping@ApiOperation("编辑员工信息")publicResultupdate(@RequestBodyEmployeeDTOemployeeDTO){log.info("编辑员工信息",employeeDTO);employeeService.update(employeeDTO);returnResult.success();}

Service 层先用BeanUtils.copyProperties把 DTO 转成实体,然后设置修改时间和修改人:

@Overridepublicvoidupdate(EmployeeDTOemployeeDTO){Employeeemployee=newEmployee();BeanUtils.copyProperties(employeeDTO,employee);employee.setUpdateTime(LocalDateTime.now());employee.setUpdateUser(BaseContext.getCurrentId());employeeMapper.update(employee);}

4. 动态 SQL 更新

EmployeeMapper.xml中的 update 语句使用<set>+<if>实现动态更新:

<updateid="update"parameterType="Employee">update employee<set><iftest="username != null">username = #{username},</if><iftest="name != null">name = #{name},</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="status != null">status = #{status},</if><iftest="updateTime != null">update_time = #{updateTime},</if><iftest="updateUser != null">update_user = #{updateUser},</if></set>where id = #{id}</update>

这样启用禁用和编辑信息都能共用一个 update 方法,只需要设置需要修改的字段即可。


二、分类管理模块(完整 CRUD)

这是今天工作量最大的部分,从 Controller 到 Mapper 全部新建。

1. 新建分类

@PostMapping@ApiOperation("新增分类")publicResult<String>save(@RequestBodyCategoryDTOcategoryDTO){categoryService.save(categoryDTO);returnResult.success();}

新增分类默认状态为禁用(0),需要手动启用后才生效:

publicvoidsave(CategoryDTOcategoryDTO){Categorycategory=newCategory();BeanUtils.copyProperties(categoryDTO,category);category.setStatus(StatusConstant.DISABLE);// 设置审计字段category.setCreateTime(LocalDateTime.now());category.setUpdateTime(LocalDateTime.now());category.setCreateUser(BaseContext.getCurrentId());category.setUpdateUser(BaseContext.getCurrentId());categoryMapper.insert(category);}

Mapper 层使用注解方式直接写 SQL:

@Insert("insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)"+" VALUES"+" (#{type}, #{name}, #{sort}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")voidinsert(Categorycategory);

2. 分类分页查询

和员工分页查询一样的套路,PageHelper 一把梭:

publicPageResultpageQuery(CategoryPageQueryDTOcategoryPageQueryDTO){PageHelper.startPage(categoryPageQueryDTO.getPage(),categoryPageQueryDTO.getPageSize());Page<Category>page=categoryMapper.pageQuery(categoryPageQueryDTO);returnnewPageResult(page.getTotal(),page.getResult());}

XML 中的查询支持按分类名称类型筛选,按 sort 升序、创建时间降序排列:

<selectid="pageQuery"resultType="com.sky.entity.Category">select * from category<where><iftest="name != null and name != ''">and name like concat('%',#{name},'%')</if><iftest="type != null">and type = #{type}</if></where>order by sort asc , create_time desc</select>

3. 删除分类(含关联检查)

这是今天学到的一个业务细节 ——删除分类前必须检查是否有关联的菜品或套餐

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);}

为此新建了DishMapperSetmealMapper中的countByCategoryId方法:

// DishMapper.java@Select("select count(id) from dish where category_id = #{categoryId}")IntegercountByCategoryId(LongcategoryId);// SetmealMapper.java@Select("select count(id) from setmeal where category_id = #{categoryId}")IntegercountByCategoryId(LongcategoryId);

如果有关联数据,抛出自定义的DeletionNotAllowedException,由全局异常处理器统一返回错误信息。

4. 修改分类

@PutMapping@ApiOperation("修改分类")publicResult<String>update(@RequestBodyCategoryDTOcategoryDTO){categoryService.update(categoryDTO);returnResult.success();}

XML 中同样使用动态 SQL,只更新传入的非空字段:

<updateid="update"parameterType="Category">update category<set><iftest="type != null">type = #{type},</if><iftest="name != null">name = #{name},</if><iftest="sort != null">sort = #{sort},</if><iftest="status != null">status = #{status},</if><iftest="updateTime != null">update_time = #{updateTime},</if><iftest="updateUser != null">update_user = #{updateUser}</if></set>where id = #{id}</update>

5. 启用/禁用分类

和员工的启用禁用一样,使用 Builder 模式构建:

publicvoidstartOrStop(Integerstatus,Longid){Categorycategory=Category.builder().id(id).status(status).updateTime(LocalDateTime.now()).updateUser(BaseContext.getCurrentId()).build();categoryMapper.update(category);}

6. 根据类型查询分类列表

这个接口是给前端用的,查询菜品分类(type=1)或套餐分类(type=2),只返回启用状态的数据:

@GetMapping("/list")@ApiOperation("根据类型查询分类")publicResult<List<Category>>list(Integertype){List<Category>list=categoryService.list(type);returnResult.success(list);}
<selectid="list"resultType="Category">select * from category where status = 1<iftest="type != null">and type = #{type}</if>order by sort asc,create_time desc</select>

三、AOP 公共字段自动填充(框架搭建)

今天还开始搭了 AOP 自动填充的框架,但反射赋值逻辑还没写完

自定义注解@AutoFill

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceAutoFill{//数据库操作类型:插入INSERT,更新UPDATEOperationTypevalue();}

切面类AutoFillAspect

@Aspect@Component@Slf4jpublicclassAutoFillAspect{@Pointcut("execution(* com.sky.mapper.*.*(..))&&@annotation(com.sky.annotation.AutoFill)")publicvoidautoFillPointCut(){}@Before("autoFillPointCut()")publicvoidautoFill(JoinPointjoinPoint){log.info("开始进行公共字段自动填充...");//获取当前被拦截的数据库操作类型MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();AutoFillautoFill=signature.getMethod().getAnnotation(AutoFill.class);OperationTypeoperationType=autoFill.value();//获取当前被拦截的方法参数---实体对象Object[]args=joinPoint.getArgs();//准备赋值的数据//根据当前不同的操作类型,为对应的属性通过反射赋值}}

思路是:在 Mapper 的 insert/update 方法上加@AutoFill注解,AOP 拦截后通过反射自动给createTimeupdateTimecreateUserupdateUser赋值,就不用每次在 Service 里手动设置了。明天把反射赋值逻辑补完。


完成的功能列表

功能状态
员工启用/禁用✅ 已完成
根据 ID 查询员工✅ 已完成
编辑员工信息✅ 已完成
分类新增✅ 已完成
分类分页查询✅ 已完成
分类删除(含关联检查)✅ 已完成
分类修改✅ 已完成
分类启用/禁用✅ 已完成
根据类型查询分类列表✅ 已完成
AOP 公共字段自动填充🔲 框架搭建完成,反射赋值待补

遇到的问题

1. 动态 SQL update 的复用问题

员工的启用禁用和编辑信息都需要 update,但要更新的字段不同。一开始想写两个方法,后来发现用<set>+<if>动态 SQL 就能完美复用一个 update,只需要在 Service 层设置不同的字段就行。

2. 删除分类的业务约束

最开始直接写了个deleteById,后来发现分类下可能关联了菜品和套餐,直接删除会导致外键关联问题。需要先查dish表和setmeal表,有关联数据就抛异常。

3. AOP 切入点表达式

切入点写的是execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill),这样只会拦截 Mapper 层中标注了@AutoFill的方法,不会影响其他方法。


学习收获

  1. MyBatis 动态 SQL——<set>+<if>标签可以实现按需更新,避免每次写不同的 update 方法
  2. AOP + 自定义注解—— 通过自定义注解标记需要自动填充的方法,AOP 统一拦截处理,实现了解耦
  3. 业务删除的关联检查—— 删除操作不能简单执行,需要先检查关联关系,保证数据完整性
  4. Builder 模式—— 只需要更新少量字段时,用@Builder注解构建对象比 new + 全部 setter 更简洁
  5. 密码脱敏—— 查询员工信息返回前端时,密码需要替换为****,不能明文暴露
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 6:38:59

别再用‘暴力法’了!解密‘加密的病历单’时,聊聊C++中字符串处理的那些高效技巧与易错点

别再用‘暴力法’了&#xff01;解密‘加密的病历单’时&#xff0c;聊聊C中字符串处理的那些高效技巧与易错点在代码审查工作坊中&#xff0c;我们常遇到这样的场景&#xff1a;一个看似简单的字符串处理任务&#xff0c;却因为不当的实现方式导致性能瓶颈或隐蔽的边界错误。以…

作者头像 李华
网站建设 2026/5/29 6:36:58

AI赋能销售:ChatGPT构建高效沟通系统与话术生成实战

1. 项目概述&#xff1a;当AI成为你的金牌销售助理 “销售话术”这个词&#xff0c;听起来有点老套&#xff0c;但每个一线销售都知道&#xff0c;它直接决定了客户的去留。无论是初次破冰的邮件、跟进客户的微信消息&#xff0c;还是产品演示后的临门一脚&#xff0c;每一句话…

作者头像 李华
网站建设 2026/5/29 6:34:12

金融科技数据驱动实战:从湖仓一体到智能风控与精准营销

1. 项目概述&#xff1a;当数据成为金融科技的“新石油”在金融行业摸爬滚打十几年&#xff0c;我亲眼见证了一个核心驱动力的变迁&#xff1a;从最初的网点为王&#xff0c;到后来的渠道为王&#xff0c;再到今天的数据为王。我们谈论的“Meet Data: The Driving Power of Fin…

作者头像 李华
网站建设 2026/5/29 6:29:22

AI编程助手架构设计:安全、效率与扩展性的工程权衡

1. 项目概述&#xff1a;AI编程助手架构的核心张力最近几年&#xff0c;AI编程助手从简单的代码补全插件&#xff0c;进化成了能理解复杂需求、自主调用工具、甚至独立完成小型开发任务的智能体&#xff08;Agent&#xff09;。作为一名在软件开发一线摸爬滚打了十几年的工程师…

作者头像 李华
网站建设 2026/5/29 6:27:05

从图像压缩到噪声过滤:Haar小波在OpenCV与数字图像处理中的实战指南

从图像压缩到噪声过滤&#xff1a;Haar小波在OpenCV与数字图像处理中的实战指南当你面对一张布满噪点的照片&#xff0c;或是需要压缩上传的医学影像时&#xff0c;传统方法往往在细节保留和压缩效率之间难以平衡。而Haar小波变换&#xff0c;这个诞生于20世纪初的数学工具&…

作者头像 李华