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

大事件板块二

一、文章分类模块 Category

1. 业务五大功能

  1. 新增文章分类
  2. 查询当前用户自己创建的分类列表
  3. 根据 ID 查询分类详情
  4. 修改分类信息
  5. 删除分类

2. 实体类标准代码

java

运行

import lombok.Data; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import java.time.LocalDateTime; @Data public class Category { private Integer id; private String categoryName; private String categoryAlias; private Integer createUser; private LocalDateTime createTime; private LocalDateTime updateTime; // 定义分组接口 public interface Add extends Default {} public interface Update extends Default {} }

3. 实体字段分组校验配置

java

运行

// 只有修改需要ID非空,新增不需要 @NotNull(message = "分类ID不能为空", groups = Category.Update.class) private Integer id; // 新增、修改都要校验,不单独写分组,默认Default @NotEmpty(message = "分类名称不能为空") @Pattern(regexp = "^\\S{1,10}$",message = "名称1-10位,不能有空格") private String categoryName; @NotEmpty(message = "分类别名不能为空") @Pattern(regexp = "^\\S{1,10}$",message = "别名1-10位,不能有空格") private String categoryAlias;

4. 分组继承核心作用

Add、Update全部继承Default

  1. 所有没写 groups的注解,默认属于Default
  2. 使用@Validated(Add.class)既校验 Add 专属规则,又自动校验所有 Default 公共规则
  3. 使用@Validated(Update.class)既校验 Update 专属 id 非空,又自动校验名称、别名公共规则

5. Controller 控制层

java

运行

@RestController @RequestMapping("/category") public class CategoryController { @Autowired private CategoryService categoryService; // 新增 - 指定新增分组 @PostMapping public Result add(@RequestBody @Validated(Category.Add.class) Category category){ categoryService.add(category); return Result.success(); } // 查询列表 @GetMapping public Result<List<Category>> list(){ return Result.success(categoryService.list()); } // 详情查询 @GetMapping("/detail") public Result<Category> detail(Integer id){ return Result.success(categoryService.findById(id)); } // 修改 - 指定修改分组 @PutMapping public Result update(@RequestBody @Validated(Category.Update.class) Category category){ categoryService.update(category); return Result.success(); } // 删除 @DeleteMapping public Result delete(Integer id){ categoryService.delete(id); return Result.success(); } }

6. Service 业务层

java

运行

@Service public class CategoryServiceImpl implements CategoryService { @Autowired private CategoryMapper categoryMapper; @Override public void add(Category category) { // 获取当前登录用户ID category.setCreateUser(ThreadLocalUtil.getUserId()); category.setCreateTime(LocalDateTime.now()); category.setUpdateTime(LocalDateTime.now()); categoryMapper.add(category); } @Override public List<Category> list() { Integer userId = ThreadLocalUtil.getUserId(); return categoryMapper.list(userId); } @Override public Category findById(Integer id) { return categoryMapper.findById(id); } @Override public void update(Category category) { category.setUpdateTime(LocalDateTime.now()); categoryMapper.update(category); } @Override public void delete(Integer id) { categoryMapper.delete(id); } }

7. Mapper 与 XML

java

运行

public interface CategoryMapper { void add(Category category); List<Category> list(Integer userId); Category findById(Integer id); void update(Category category); void delete(Integer id); }

xml

<!--新增--> <insert id="add"> insert into category(category_name,category_alias,create_user,create_time,update_time) values(#{categoryName},#{categoryAlias},#{createUser},#{createTime},#{updateTime}) </insert> <!--查询个人列表--> <select id="list" resultType="Category"> select * from category where create_user=#{userId} </select> <!--根据ID查询--> <select id="findById" resultType="Category"> select * from category where id=#{id} </select> <!--修改--> <update id="update"> update category set category_name=#{categoryName},category_alias=#{categoryAlias},update_time=#{updateTime} where id=#{id} </update> <!--删除--> <delete id="delete"> delete from category where id=#{id} </delete>

8. ThreadLocal 作用

  1. 存储当前登录用户 ID
  2. 同一线程共享数据,不用前端反复传用户 ID
  3. 新增、查询个人数据自动绑定用户
  4. 请求结束清空,防止内存泄漏

二、分组校验全套核心规则(所有答疑全部整理)

1. 基础规则

  1. 校验注解不加 groups→ 默认归属Default 默认分组
  2. 校验注解加 groups=xxx.class→ 只属于指定分组,脱离 Default

2. @Validated 三种写法区别

  1. 只写 @Validated 不带任何分组只校验 Default 分组,所有自定义分组字段全部不校验
  2. @Validated(Add.class)Add 继承 Default → 新增专属校验 + 所有公共 Default 校验全部生效
  3. @Validated(Update.class)Update 继承 Default → 修改专属 id 校验 + 公共校验全部生效

3. 你最疑惑的核心矛盾点(重点背诵)

场景需求

某一个字段:绝大多数场景必须校验,只有唯一一种场景不需要校验 **

矛盾根源
  1. 如果你给这个字段单独绑定一个专属分组这个字段只属于这个组,其他所有 Add、Update、Default 全部校验不到它直接导致正常业务全部失效,完全不能用
  2. 如果你不给字段分组,默认归 Default所有继承 Default 的接口都会强制校验它,普通固定分组没办法单独跳过这一个字段
结论
  1. 普通固定分组做不到:只跳过单个字段,其他字段正常校验
  2. 普通分组只能实现两种效果
    • 全接口全部校验
    • 全接口全部不校验
  3. 想要单字段局部免校验方案 1:业务层手写 if 判断手动控制(最简单)方案 2:使用动态分组DefaultGroupSequenceProvider(注解实现)
反向需求

一个字段只在一种情况校验,其余全部不校验只能直接给字段绑定唯一指定分组弊端:除了这个场景,其余所有接口都无法校验该字段,业务局限性极大,尽量少用

4. 分组开发规范

  1. 新增接口统一用@Validated(Add.class)
  2. 修改接口统一用@Validated(Update.class)
  3. 公共通用校验全部不写 groups,依靠 Default 统一管理
  4. 只有新增 / 修改差异化字段,才单独指定 groups

三、自定义校验注解 @State(文章状态校验)

需求

文章状态只能为:已发布 / 草稿

1. 自定义注解

java

运行

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = StateValidator.class) public @interface State { String message() default "状态只能为已发布或草稿"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }

2. 校验规则类

java

运行

public class StateValidator implements ConstraintValidator<State,String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(value == null) return false; return value.equals("已发布") || value.equals("草稿"); } }

3. 实体使用方式(最优写法)

java

运行

// 不写groups,默认Default // 因为Add、Update都继承Default,新增修改自动全部校验状态 @State private String state;

4. 重点

不需要手动给@State加分组,依靠 Default 自动全局生效,代码最简洁


四、文件上传两大方式完整版

方式一:本地上传

1. 上传接口

java

运行

@PostMapping("/upload") public Result<String> upload(MultipartFile file) throws Exception { // 获取原始文件名 String originalName = file.getOriginalFilename(); // 截取后缀 String suffix = originalName.substring(originalName.lastIndexOf(".")); // UUID生成唯一文件名,防止重名覆盖 String newName = UUID.randomUUID() + suffix; // 保存到本地路径 file.transferTo(new File("D:/upload/" + newName)); return Result.success(newName); }
2. 配置文件限制大小

yaml

spring: servlet: multipart: max-file-size: 100MB max-request-size: 100MB
3. 缺点

路径固定、无法外网访问、服务器占用大、分布式项目不适用

方式二:阿里云 OSS 云端上传(课堂最终版)

1. 全套依赖

xml

<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.15.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.1</version> </dependency>
2. 标准工具类(传文件名 + 输入流,正确 URL 拼接)

java

运行

public class AliOssUtil { private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com"; private static final String ACCESS_KEY_ID = "填写自己"; private static final String ACCESS_KEY_SECRET = "填写自己"; private static final String BUCKET_NAME = "填写自己"; public static String upload(String originalFileName, InputStream inputStream){ // 截取后缀 String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); // 唯一文件名 String fileName = UUID.randomUUID() + suffix; // 创建客户端 OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET); // 上传 ossClient.putObject(BUCKET_NAME,fileName,inputStream); // 课堂标准正确拼接URL String url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.indexOf("/")+1)+"/"+fileName; ossClient.shutdown(); return url; } }
3. Controller 调用

java

运行

@PostMapping("/oss/upload") public Result<String> ossUpload(MultipartFile file) throws Exception{ String url = AliOssUtil.upload(file.getOriginalFilename(),file.getInputStream()); return Result.success(url); }
4. OSS 重点
  1. Bucket 名称全网唯一
  2. AK/SK 严禁泄露
  3. 返回外网 URL 直接存入数据库
  4. 工具类不依赖 MultipartFile,通用性极强
http://www.cnnetsun.cn/news/2434664.html

相关文章:

  • AI编程工程化:用.cursorrules文件规范Cursor编辑器代码生成
  • APK Installer:在Windows上安装安卓应用的终极解决方案
  • SpringBoot+Vue大学生创业项目信息管理系统源码+论文
  • 在taotoken控制台清晰查看各模型调用量与token消耗明细
  • 【会议征稿通知 | 南京师范大学主办 | IEEE出版 | EI 、Scopus稳定检索】第七届电气技术与自动控制国际学术会议(ICETAC 2026)
  • Concorde:CPU性能建模的革命性混合方法
  • OmenSuperHub:惠普OMEN游戏本性能优化终极指南 - 完全免费开源解决方案
  • 深度学习嵌入操作优化与DAE架构实践
  • Helm-Git:轻量级Kubernetes Chart分发方案,无缝集成Git工作流
  • LLM操作系统:从智能体框架到AI原生系统的技术实践
  • 东湖湖畔绣球盛放,柔色花团奏响初夏水岸温柔乐章
  • LinuxShell参数校验自动化巡检实践
  • LinuxSSH密钥轮换异常定位实战
  • 分享一套锋哥原创的基于Spring AI 2.0的RAG医疗健康知识智能问答系统(AI大模型 SpringBoot4+Vue3+Ollama)
  • 如何快速解决腾讯游戏卡顿问题:免费Windows优化工具完全指南
  • AgentOps:AI Agent可观测性平台,解决LLM应用开发调试难题
  • 从空白画布到专业思维导图:Freeplane-MindMap-Template如何让你3分钟变高手
  • ASO技能全解析:从关键词优化到数据驱动的应用商店增长实战
  • 重磅!全球市值 TOP50 企业出炉
  • 实测实在Agent如何靠“全生命周期预警”击穿信创孤岛
  • 从数字废墟到永恒珍藏:m4s-converter如何拯救你的B站记忆
  • RakkasJS深度解析:基于Bun的全栈React框架性能与迁移实践
  • Arduino IDE玩转RP2040:从入门到实战的完整指南
  • 基于MCP协议构建Reddit-AI智能体:原理、部署与实战应用
  • 华为云灾备方案深度解析:从分级保护到双活架构的定制化实践
  • 前端光标交互优化:从CSS定制到Canvas动态实现
  • Windows风扇控制完全指南:Fan Control免费软件从入门到精通
  • 3个技巧让你告别歌词烦恼:网易云QQ音乐歌词获取完整指南
  • Audacity:从零开始掌握开源音频编辑的艺术
  • Obsidian Dataview深度解析:构建个人知识管理的动态数据索引引擎