1 Star 0 Fork 0

Lance/firstHtml

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
.cursorrules 25.31 KB
一键复制 编辑 原始数据 按行查看 历史
497208709@qq.com 提交于 2025-04-19 15:03 +08:00 . 修改好图片裁剪功能
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
# 在线工具平台 - Vue 和 JavaScript 代码规范
## 文件命名规范
- 所有文件使用小写字母命名,单词之间使用连字符(kebab-case)分隔
- 示例: `file-upload.vue`, `image-processor.js`, `api-service.js`
- Vue组件文件使用 `.vue` 扩展名
- 工具类和辅助函数使用 `.js` 扩展名
- 类型定义文件使用 `.d.ts` 扩展名(如使用TypeScript)
## Vue 组件规范
### 组件命名
- 组件名应该始终是多个单词的,根组件 `App` 除外
- 组件名应该以高级别的单词开头,以描述性的修饰词结尾
- 示例: `FileUpload`, `ImageCompressor`, `NavigationBar`
- 页面级组件使用 `Page` 后缀
- 示例: `HomePage.vue`, `FileToolPage.vue`, `ImageToolPage.vue`
- 基础组件使用 `Base` 前缀或特定前缀
- 示例: `BaseButton.vue`, `BaseInput.vue`, `UiCard.vue`
### 组件结构
- 组件选项顺序:
```js
export default {
name: '',
components: {},
props: {},
data() {},
computed: {},
watch: {},
created() {},
mounted() {},
methods: {}
}
```
- Props 定义应该尽量详细,至少指定类型
```js
props: {
status: {
type: String,
required: true,
validator: function(value) {
return ['success', 'warning', 'error'].indexOf(value) !== -1
}
}
}
```
- 组件模板应该只包含简单的表达式,复杂的表达式应该重构为计算属性或方法
### 组件通信
- 父子组件通信:
- 父组件通过 props 向子组件传递数据
- 子组件通过 events 向父组件发送消息
- 避免使用 `this.$parent` 或 `this.$refs` 直接访问组件实例
- 使用Vuex进行复杂组件间的状态管理
## JavaScript/ECMAScript 规范
### 语法规范
- 使用ES6+语法
- 使用 `const` 和 `let` 声明变量,避免使用 `var`
- 优先使用箭头函数,特别是在回调函数中
```js
// 推荐
[1, 2, 3].map(x => x * 2)
// 避免
[1, 2, 3].map(function(x) { return x * 2 })
```
- 使用模板字符串替代字符串拼接
```js
// 推荐
const message = `Hello, ${name}!`
// 避免
const message = 'Hello, ' + name + '!'
```
- 使用对象和数组解构
```js
// 推荐
const { name, age } = user
const [first, second] = items
```
### 命名规范
- 变量和函数名使用驼峰式命名(camelCase)
```js
const userName = 'John'
function getUserData() { ... }
```
- 常量使用大写字母和下划线(SNAKE_CASE)
```js
const API_BASE_URL = 'https://api.example.com'
const MAX_FILE_SIZE = 10 * 1024 * 1024
```
- 类名和构造函数使用大驼峰式命名(PascalCase)
```js
class FileUploader { ... }
```
- 私有属性和方法在名称前加下划线
```js
this._privateMethod() { ... }
```
### 代码格式
- 使用2个空格缩进
- 每行代码不超过80个字符
- 在运算符前后、逗号后、冒号和分号后添加空格
- 代码块使用大括号,即使只有一行
```js
// 推荐
if (condition) {
doSomething()
}
// 避免
if (condition) doSomething()
```
- 在文件末尾保留一个空行
## API和服务规范
- API服务统一放在 `services` 目录下
- 每个服务模块负责特定的功能领域
```
services/
├── file-service.js
├── image-service.js
└── user-service.js
```
- 使用Axios实例设置通用配置
```js
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_URL,
timeout: 10000,
headers: {'Content-Type': 'application/json'}
})
```
- 所有API调用应返回Promise
## 注释规范
- 使用JSDoc风格的注释
```js
/**
* 压缩图片函数
* @param {File} file - 要压缩的图片文件
* @param {number} quality - 压缩质量(0-1)
* @returns {Promise<Blob>} 压缩后的图片Blob
*/
function compressImage(file, quality) { ... }
```
- 复杂的函数应该有注释说明其功能和参数
- 任何不是一目了然的代码都应该有注释
- 使用TODO注释标记待完成的功能
```js
// TODO: 添加文件类型验证
```
## CSS/SCSS 规范
- 使用BEM命名方法论
```css
.block__element--modifier { ... }
```
- 或使用基于组件的命名,避免样式冲突
```css
.file-uploader { ... }
.file-uploader__input { ... }
.file-uploader--active { ... }
```
- 使用SCSS嵌套,但避免超过3层
- 优先使用类选择器,避免使用ID选择器和标签选择器
- 使用变量存储颜色、字体等通用样式
```scss
$primary-color: #3498db;
$error-color: #e74c3c;
```
## 性能优化
- 组件应该按需导入,避免全局导入
```js
// 推荐
import { Button, Input } from 'ant-design-vue'
// 避免
import AntDesign from 'ant-design-vue'
```
- 大型依赖应该使用动态导入
```js
const OcrProcessor = () => import('./components/ocr-processor')
```
- 图片压缩、OCR等计算密集型任务应该使用Web Workers
- 组件应该在合适的时机销毁和清理资源
```js
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
this.someObject.dispose()
}
```
## 测试规范
- 单元测试文件应与被测试文件放在同一目录下,或放在专门的 `tests` 目录
- 测试文件名后缀为 `.spec.js` 或 `.test.js`
- 每个组件至少应该有一个测试用例
- 测试描述应该清晰描述被测功能
```js
describe('ImageCompressor', () => {
it('should compress images without losing too much quality', () => { ... })
it('should reject files larger than the maximum size', () => { ... })
})
```
## Git 提交规范
- 使用约定式提交格式
```
<type>(<scope>): <subject>
<body>
<footer>
```
- 常见type类型:
- feat: 新功能
- fix: 修复bug
- docs: 文档更新
- style: 代码格式调整,不影响功能
- refactor: 代码重构,不新增功能或修复bug
- perf: 性能优化
- test: 添加或修改测试
- build: 构建系统或外部依赖变更
- ci: CI配置文件或脚本变更
## 安全规范
- 不在前端存储敏感信息
- 所有用户输入都应该进行验证和清洗
- 使用 `v-html` 指令时注意防范XSS攻击
- API请求应该有超时和错误处理
- 遵循最小权限原则
# Java后端代码规范
## 项目分层架构
后端必须严格遵循以下分层结构,确保代码的清晰性和可维护性:
```
com.example.project
├── controller/ # 控制层,处理HTTP请求
├── service/ # 业务逻辑层
│ ├── impl/ # 接口实现
│ └── 接口定义
├── dao/ # 数据访问层
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── vo/ # 视图对象
├── config/ # 配置类
├── util/ # 工具类
└── exception/ # 自定义异常
```
## 命名规范
### 包命名
- 包名全部小写,使用点分隔符
- 示例:`com.example.project.controller`
### 类命名
- 使用大驼峰命名法(PascalCase)
- 控制器类以`Controller`结尾
- 示例:`FileController`, `ImageController`
- 服务接口不使用前缀,以`Service`结尾
- 示例:`FileService`, `ImageService`
- 服务实现类以`ServiceImpl`结尾
- 示例:`FileServiceImpl`, `ImageServiceImpl`
- DAO接口以`Dao`或`Repository`结尾
- 示例:`FileDao`, `ImageRepository`
- 实体类不使用后缀,使用有意义的名词
- 示例:`User`, `FileInfo`, `ImageData`
- 数据传输对象以`DTO`结尾
- 示例:`UserDTO`, `FileUploadDTO`
- 视图对象以`VO`结尾
- 示例:`UserVO`, `FileInfoVO`
### 方法命名
- 使用小驼峰命名法(camelCase)
- 方法名应该是动词或动词短语
- 获取数据:以`get`或`find`或`query`开头
- 保存数据:以`save`或`insert`开头
- 更新数据:以`update`开头
- 删除数据:以`delete`或`remove`开头
- 转换数据:以`convert`或`to`开头
- 示例:`getUserById`, `saveFile`, `updateImage`, `deleteFileById`
### 变量命名
- 使用小驼峰命名法(camelCase)
- 布尔类型变量应以`is`或`has`开头
- 示例:`isEnabled`, `hasPermission`
- 常量使用大写字母和下划线(SNAKE_CASE)
- 示例:`MAX_FILE_SIZE`, `DEFAULT_TIMEOUT`
## 代码结构规范
### 控制层 (Controller)
- 控制器类只负责处理HTTP请求、参数验证和返回结果
- 不应包含业务逻辑,业务逻辑应委托给服务层
- 统一使用`@RestController`和`@RequestMapping`注解
- 请求方法应使用对应的HTTP方法注解(`@GetMapping`, `@PostMapping`等)
- 统一的返回结果格式
```java
@RestController
@RequestMapping("/api/files")
public class FileController {
@Autowired
private FileService fileService;
@PostMapping("/upload")
public ResponseEntity<FileInfoVO> uploadFile(@RequestParam("file") MultipartFile file) {
// 参数验证
if (file.isEmpty()) {
throw new BadRequestException("文件不能为空");
}
// 调用服务层
FileInfo fileInfo = fileService.saveFile(file);
// 转换为VO并返回
return ResponseEntity.ok(convertToVO(fileInfo));
}
}
```
### 服务层 (Service)
- 服务接口定义业务操作,实现类包含具体业务逻辑
- 事务管理应该在服务层处理
- 服务层方法应该是原子的、具有业务含义的操作
```java
// 服务接口
public interface FileService {
FileInfo saveFile(MultipartFile file);
FileInfo getFileById(Long id);
void deleteFile(Long id);
}
// 服务实现
@Service
public class FileServiceImpl implements FileService {
@Autowired
private FileDao fileDao;
@Override
@Transactional
public FileInfo saveFile(MultipartFile file) {
// 业务逻辑处理
// ...
FileInfo fileInfo = new FileInfo();
// 设置文件信息
// 调用DAO层保存
return fileDao.save(fileInfo);
}
}
```
### 数据访问层 (DAO/Repository)
- 使用JPA、MyBatis等框架时,定义接口继承框架提供的基础接口
- 自定义查询方法应遵循框架的命名规范
- 复杂查询应使用注解或XML配置
```java
// 使用Spring Data JPA
public interface FileDao extends JpaRepository<FileInfo, Long> {
List<FileInfo> findByUserId(Long userId);
@Query("SELECT f FROM FileInfo f WHERE f.size > :minSize")
List<FileInfo> findLargeFiles(@Param("minSize") long minSize);
}
// 使用MyBatis
public interface ImageDao {
@Select("SELECT * FROM image WHERE id = #{id}")
Image getById(Long id);
@Insert("INSERT INTO image(name, url, size) VALUES(#{name}, #{url}, #{size})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Image image);
}
```
### 实体类 (Entity)
- 使用JPA注解或MyBatis注解映射数据库表
- 实体类应包含所有数据库字段
- 提供合适的构造函数、getter和setter方法
- 实现合适的`equals`、`hashCode`和`toString`方法
```java
@Entity
@Table(name = "file_info")
public class FileInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String filename;
@Column(nullable = false)
private String path;
@Column(nullable = false)
private long size;
@Column(name = "upload_time")
private LocalDateTime uploadTime;
// 构造函数、getter和setter、equals和hashCode等
}
```
### 数据传输对象 (DTO)
- DTO用于在不同层之间传输数据
- 应只包含需要传输的数据,不包含业务逻辑
- 可以使用validation注解进行验证
```java
public class FileUploadDTO {
@NotBlank(message = "文件名不能为空")
private String filename;
@NotNull(message = "文件内容不能为空")
private byte[] content;
// getter和setter
}
```
### 视图对象 (VO)
- VO用于向前端返回数据
- 只包含需要展示的字段
```java
public class FileInfoVO {
private Long id;
private String filename;
private long size;
private String uploadTime;
private String downloadUrl;
// getter和setter
}
```
## 异常处理
- 使用自定义异常表达业务异常
- 全局异常处理器统一处理异常
```java
// 自定义异常
public class FileProcessingException extends RuntimeException {
public FileProcessingException(String message) {
super(message);
}
}
// 全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(FileProcessingException.class)
public ResponseEntity<ErrorResponse> handleFileProcessingException(FileProcessingException ex) {
ErrorResponse error = new ErrorResponse("file_processing_error", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
```
## 注释规范
- 类上方使用Javadoc注释,说明类的用途
- 方法上方使用Javadoc注释,说明方法的功能、参数和返回值
- 复杂的业务逻辑处应有详细注释
```java
/**
* 文件服务,处理文件上传、下载和处理相关的业务逻辑
*/
@Service
public class FileServiceImpl implements FileService {
/**
* 保存文件并返回文件信息
*
* @param file 上传的文件
* @return 保存后的文件信息
* @throws FileProcessingException 当文件处理失败时抛出
*/
@Override
public FileInfo saveFile(MultipartFile file) {
// 实现...
}
}
```
## 日志规范
- 使用SLF4J + Logback作为日志框架
- 在类的开头定义静态日志对象
- 合理使用不同级别的日志
- 避免在日志中暴露敏感信息
```java
@Service
public class FileServiceImpl implements FileService {
private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
@Override
public FileInfo saveFile(MultipartFile file) {
logger.info("开始保存文件: {}", file.getOriginalFilename());
try {
// 业务逻辑
logger.debug("文件大小: {}", file.getSize());
// 更多业务逻辑
logger.info("文件保存成功: {}", file.getOriginalFilename());
return fileInfo;
} catch (Exception e) {
logger.error("保存文件失败: {}", file.getOriginalFilename(), e);
throw new FileProcessingException("保存文件失败: " + e.getMessage());
}
}
}
```
## 数据库规范
### 表设计
- 表名使用小写字母,单词间用下划线分隔
- 主键通常命名为`id`,类型为`BIGINT`
- 创建和更新时间字段分别命名为`create_time`和`update_time`
- 软删除字段命名为`is_deleted`,类型为`TINYINT`或`BOOLEAN`
### SQL编写
- 关键字使用大写,表名和字段名使用小写
- 复杂查询应该格式化并添加注释
- 避免使用`SELECT *`,明确指定需要的列
- 大型列表查询必须分页
```sql
-- 查询用户上传的文件,按上传时间降序排序
SELECT
f.id,
f.filename,
f.path,
f.size,
f.upload_time
FROM
file_info f
WHERE
f.user_id = 123
AND f.is_deleted = 0
ORDER BY
f.upload_time DESC
LIMIT 0, 20;
```
## 并发与性能规范
- 避免长时间持有锁或事务
- 分布式锁使用Redis或Zookeeper实现
- 大文件处理应使用异步方式
- 频繁访问的数据应使用缓存
- API接口应有合理的超时设置
```java
@Service
public class FileProcessingServiceImpl implements FileProcessingService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void processFile(Long fileId) {
String lockKey = "file_lock:" + fileId;
boolean locked = false;
try {
// 尝试获取分布式锁
locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);
if (!locked) {
throw new FileProcessingException("文件正在被处理,请稍后再试");
}
// 处理文件的业务逻辑
} finally {
// 释放锁
if (locked) {
redisTemplate.delete(lockKey);
}
}
}
}
```
## 安全规范
- 密码必须加密存储,推荐使用BCrypt
- 防止SQL注入,使用参数化查询
- 防止XSS攻击,过滤用户输入
- 敏感信息不记录到日志
- API权限控制使用Spring Security
- 文件上传验证文件类型和大小
```java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User createUser(String username, String password) {
// 密码加密
String encodedPassword = passwordEncoder.encode(password);
User user = new User();
user.setUsername(username);
user.setPassword(encodedPassword);
return userDao.save(user);
}
}
```
# 控制层返回实体对象规范
## 统一响应结构
所有API接口必须使用统一的响应结构,确保前端处理的一致性。
### 响应实体结构
```java
/**
* 统一API响应结果封装
*/
public class ApiResponse<T> {
/**
* 状态码
*/
private Integer code;
/**
* 消息
*/
private String message;
/**
* 数据
*/
private T data;
/**
* 时间戳
*/
private long timestamp;
// 构造方法、getter和setter方法省略
/**
* 成功响应
*/
public static <T> ApiResponse<T> success() {
return new ApiResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
}
/**
* 成功响应(带数据)
*/
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 失败响应
*/
public static <T> ApiResponse<T> fail(Integer code, String message) {
return new ApiResponse<>(code, message, null);
}
/**
* 系统异常响应
*/
public static <T> ApiResponse<T> error() {
return new ApiResponse<>(ResultCode.SERVER_ERROR.getCode(), ResultCode.SERVER_ERROR.getMessage(), null);
}
}
```
### 返回码定义
```java
/**
* API响应码枚举
*/
public enum ResultCode {
// 成功
SUCCESS(200, "操作成功"),
// 客户端错误
BAD_REQUEST(400, "请求参数错误"),
UNAUTHORIZED(401, "未授权"),
FORBIDDEN(403, "禁止访问"),
NOT_FOUND(404, "资源不存在"),
METHOD_NOT_ALLOWED(405, "请求方法不允许"),
REQUEST_TIMEOUT(408, "请求超时"),
PARAM_VALID_ERROR(4001, "参数校验失败"),
FILE_TYPE_ERROR(4002, "文件类型错误"),
FILE_SIZE_ERROR(4003, "文件大小超出限制"),
// 服务器错误
SERVER_ERROR(500, "服务器内部错误"),
SERVICE_UNAVAILABLE(503, "服务不可用"),
// 业务错误 (6000-6999)
FILE_UPLOAD_ERROR(6001, "文件上传失败"),
FILE_PROCESS_ERROR(6002, "文件处理失败"),
IMAGE_PROCESS_ERROR(6003, "图片处理失败"),
OCR_PROCESS_ERROR(6004, "OCR识别失败");
private final Integer code;
private final String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
// getter方法省略
}
```
## 使用规范
### 在控制器中的使用方式
```java
@RestController
@RequestMapping("/api/images")
public class ImageController {
@Autowired
private ImageService imageService;
/**
* 上传并压缩图片
*/
@PostMapping("/compress")
public ApiResponse<ImageInfoVO> compressImage(@RequestParam("file") MultipartFile file,
@RequestParam(value = "quality", defaultValue = "80") Integer quality) {
// 参数校验
if (file.isEmpty()) {
return ApiResponse.fail(ResultCode.PARAM_VALID_ERROR.getCode(), "图片文件不能为空");
}
// 文件类型校验
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
return ApiResponse.fail(ResultCode.FILE_TYPE_ERROR.getCode(), "只支持图片文件");
}
try {
// 调用服务层处理业务
ImageInfo imageInfo = imageService.compressImage(file, quality);
// 转换为VO并返回
ImageInfoVO vo = convertToVO(imageInfo);
return ApiResponse.success(vo);
} catch (FileProcessException e) {
logger.error("图片压缩失败", e);
return ApiResponse.fail(ResultCode.IMAGE_PROCESS_ERROR.getCode(), e.getMessage());
} catch (Exception e) {
logger.error("图片压缩过程中发生未知错误", e);
return ApiResponse.error();
}
}
/**
* 获取图片信息
*/
@GetMapping("/{id}")
public ApiResponse<ImageInfoVO> getImageInfo(@PathVariable Long id) {
try {
ImageInfo imageInfo = imageService.getImageById(id);
if (imageInfo == null) {
return ApiResponse.fail(ResultCode.NOT_FOUND.getCode(), "图片不存在");
}
ImageInfoVO vo = convertToVO(imageInfo);
return ApiResponse.success(vo);
} catch (Exception e) {
logger.error("获取图片信息失败", e);
return ApiResponse.error();
}
}
// 其他接口方法...
}
```
### 全局异常处理中的使用
```java
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理参数验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
String errorMessage = ex.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining("; "));
return ApiResponse.fail(ResultCode.PARAM_VALID_ERROR.getCode(), errorMessage);
}
/**
* 处理文件上传异常
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ApiResponse<String> handleMaxSizeException(MaxUploadSizeExceededException ex) {
return ApiResponse.fail(ResultCode.FILE_SIZE_ERROR.getCode(), "上传的文件大小超出限制");
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public ApiResponse<String> handleBusinessException(BusinessException ex) {
return ApiResponse.fail(ex.getCode(), ex.getMessage());
}
/**
* 处理所有未捕获的异常
*/
@ExceptionHandler(Exception.class)
public ApiResponse<String> handleAllExceptions(Exception ex) {
logger.error("系统未知异常", ex);
return ApiResponse.error();
}
}
```
## 自定义业务异常
```java
/**
* 业务异常基类
*/
public class BusinessException extends RuntimeException {
private final Integer code;
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
// getter方法
}
/**
* 文件处理异常
*/
public class FileProcessException extends BusinessException {
public FileProcessException(String message) {
super(ResultCode.FILE_PROCESS_ERROR.getCode(), message);
}
}
/**
* 图片处理异常
*/
public class ImageProcessException extends BusinessException {
public ImageProcessException(String message) {
super(ResultCode.IMAGE_PROCESS_ERROR.getCode(), message);
}
}
```
## 前端处理响应规范
在前端处理API响应时,应当遵循以下规范:
```javascript
// axios请求拦截器处理
axios.interceptors.response.use(
response => {
const res = response.data;
// 判断状态码
if (res.code !== 200) {
// 根据不同状态码处理不同情况
if (res.code === 401) {
// 未授权,处理登录超时
router.push('/login');
} else if (res.code === 403) {
// 权限不足
Message.error('您没有访问权限');
} else {
// 其他错误
Message.error(res.message || '请求失败');
}
// 直接拒绝掉Promise,进入catch
return Promise.reject(new Error(res.message || '请求失败'));
} else {
// 成功返回数据
return res.data;
}
},
error => {
// 网络错误等情况
Message.error('网络错误,请稍后重试');
return Promise.reject(error);
}
);
```
## HTTP状态码与业务状态码的关系
- HTTP状态码用于表示请求的处理结果,主要作用是告诉客户端请求是否成功。
- 业务状态码用于表示具体的业务处理结果,主要作用是告诉客户端业务处理的具体情况。
实践建议:
1. 正常情况下,所有API接口都应返回HTTP 200状态码,同时在响应体中包含业务状态码。
2. 对于授权类错误(未登录、权限不足等),可以返回对应的HTTP状态码(401/403)。
3. 对于严重的服务器错误,可以返回HTTP 500状态码。
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/lance521/first-html.git
git@gitee.com:lance521/first-html.git
lance521
first-html
firstHtml
master

搜索帮助