diff --git a/sql/createTable.sql b/sql/createTable.sql index 34f9cb2..e3b347a 100644 --- a/sql/createTable.sql +++ b/sql/createTable.sql @@ -141,4 +141,48 @@ CREATE TRIGGER set_t_tag_update_time FOR EACH ROW EXECUTE FUNCTION set_update_time(); -- ==================================================================================================================== +-- ==================================================================================================================== +-- ==================================================================================================================== +-- ==================================================================================================================== +CREATE TABLE t_blog_settings +( + -- id: 使用 BIGSERIAL 自动管理序列 + id BIGSERIAL PRIMARY KEY, + + -- logo: 图片路径可能很长,使用 TEXT 替代 VARCHAR(120),无性能损耗 + logo TEXT NOT NULL DEFAULT '', + + -- name: 博客名称通常较短,保留 VARCHAR 限制也是一种合理的业务约束 + name VARCHAR(60) NOT NULL DEFAULT '', + + -- author: 作者名同上 + author VARCHAR(20) NOT NULL DEFAULT '', + + -- introduction: 介绍语可能会变长,使用 TEXT 更灵活 + introduction TEXT NOT NULL DEFAULT '', + + -- avatar: 头像路径,使用 TEXT + avatar TEXT NOT NULL DEFAULT '', + + -- 下面的主页链接:原 MySQL 定义 varchar(60) 风险很高, + -- 现在的 URL 很容易超过 60 字符,PG 使用 TEXT 完美解决 + github_homepage TEXT NOT NULL DEFAULT '', + csdn_homepage TEXT NOT NULL DEFAULT '', + gitee_homepage TEXT NOT NULL DEFAULT '', + zhihu_homepage TEXT NOT NULL DEFAULT '' +); + +-- 添加注释 +COMMENT ON TABLE t_blog_settings IS '博客设置表'; +COMMENT ON COLUMN t_blog_settings.id IS 'id'; +COMMENT ON COLUMN t_blog_settings.logo IS '博客Logo'; +COMMENT ON COLUMN t_blog_settings.name IS '博客名称'; +COMMENT ON COLUMN t_blog_settings.author IS '作者名'; +COMMENT ON COLUMN t_blog_settings.introduction IS '介绍语'; +COMMENT ON COLUMN t_blog_settings.avatar IS '作者头像'; +COMMENT ON COLUMN t_blog_settings.github_homepage IS 'GitHub 主页访问地址'; +COMMENT ON COLUMN t_blog_settings.csdn_homepage IS 'CSDN 主页访问地址'; +COMMENT ON COLUMN t_blog_settings.gitee_homepage IS 'Gitee 主页访问地址'; +COMMENT ON COLUMN t_blog_settings.zhihu_homepage IS '知乎主页访问地址'; +-- ==================================================================================================================== -- ==================================================================================================================== \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminBlogSettingsController.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminBlogSettingsController.java new file mode 100644 index 0000000..6b8c124 --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminBlogSettingsController.java @@ -0,0 +1,43 @@ +package com.hanserwei.admin.controller; + +import com.hanserwei.admin.model.vo.setting.FindBlogSettingsRspVO; +import com.hanserwei.admin.model.vo.setting.UpdateBlogSettingsReqVO; +import com.hanserwei.admin.service.AdminBlogSettingsService; +import com.hanserwei.common.aspect.ApiOperationLog; +import com.hanserwei.common.utils.Response; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端博客设置控制器 + */ +@RestController +@RequestMapping("/admin/blog/settings") +public class AdminBlogSettingsController { + + @Resource + private AdminBlogSettingsService blogSettingsService; + + /** + * 博客基础信息修改 + */ + @PostMapping("/update") + @ApiOperationLog(description = "博客基础信息修改") + public Response updateBlogSettings(@RequestBody @Validated UpdateBlogSettingsReqVO updateBlogSettingsReqVO) { + return blogSettingsService.updateBlogSettings(updateBlogSettingsReqVO); + } + + /** + * 获取博客设置详情 + */ + @PostMapping("/detail") + @ApiOperationLog(description = "获取博客设置详情") + public Response findDetail() { + return blogSettingsService.findDetail(); + } + +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminFileController.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminFileController.java new file mode 100644 index 0000000..cc81e18 --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/controller/AdminFileController.java @@ -0,0 +1,33 @@ +package com.hanserwei.admin.controller; + +import com.hanserwei.admin.model.vo.file.UploadFileRspVO; +import com.hanserwei.admin.service.AdminFileService; +import com.hanserwei.common.aspect.ApiOperationLog; +import com.hanserwei.common.utils.Response; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 管理端文件控制器 + */ +@RestController +@RequestMapping("/admin") +public class AdminFileController { + + @Resource + private AdminFileService fileService; + + /** + * 文件上传 + */ + @PostMapping("/file/upload") + @ApiOperationLog(description = "文件上传") + public Response uploadFile(@RequestParam("file") MultipartFile file) { + return fileService.uploadFile(file); + } + +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/file/UploadFileRspVO.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/file/UploadFileRspVO.java new file mode 100644 index 0000000..eae65bb --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/file/UploadFileRspVO.java @@ -0,0 +1,19 @@ +package com.hanserwei.admin.model.vo.file; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UploadFileRspVO { + + /** + * 文件的访问链接 + */ + private String url; + +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/FindBlogSettingsRspVO.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/FindBlogSettingsRspVO.java new file mode 100644 index 0000000..35bd0d7 --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/FindBlogSettingsRspVO.java @@ -0,0 +1,62 @@ +package com.hanserwei.admin.model.vo.setting; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 查询博客设置响应 VO + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindBlogSettingsRspVO { + + /** + * 博客 Logo + */ + private String logo; + + /** + * 博客名称 + */ + private String name; + + /** + * 作者名称 + */ + private String author; + + /** + * 博客介绍 + */ + private String introduction; + + /** + * 作者头像 + */ + private String avatar; + + /** + * GitHub 主页地址 + */ + private String githubHomepage; + + /** + * CSDN 主页地址 + */ + private String csdnHomepage; + + /** + * Gitee 主页地址 + */ + private String giteeHomepage; + + /** + * 知乎主页地址 + */ + private String zhihuHomepage; +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/UpdateBlogSettingsReqVO.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/UpdateBlogSettingsReqVO.java new file mode 100644 index 0000000..7ce446b --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/model/vo/setting/UpdateBlogSettingsReqVO.java @@ -0,0 +1,68 @@ +package com.hanserwei.admin.model.vo.setting; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 更新博客设置请求 VO + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UpdateBlogSettingsReqVO { + + /** + * 博客 LOGO + */ + @NotBlank(message = "博客 LOGO 不能为空") + private String logo; + + /** + * 博客名称 + */ + @NotBlank(message = "博客名称不能为空") + private String name; + + /** + * 博客作者 + */ + @NotBlank(message = "博客作者不能为空") + private String author; + + /** + * 博客介绍语 + */ + @NotBlank(message = "博客介绍语不能为空") + private String introduction; + + /** + * 博客头像 + */ + @NotBlank(message = "博客头像不能为空") + private String avatar; + + /** + * GitHub 主页 + */ + private String githubHomepage; + + /** + * CSDN 主页 + */ + private String csdnHomepage; + + /** + * Gitee 主页 + */ + private String giteeHomepage; + + /** + * 知乎主页 + */ + private String zhihuHomepage; +} diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminBlogSettingsService.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminBlogSettingsService.java new file mode 100644 index 0000000..4dde49d --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminBlogSettingsService.java @@ -0,0 +1,22 @@ +package com.hanserwei.admin.service; + +import com.hanserwei.admin.model.vo.setting.FindBlogSettingsRspVO; +import com.hanserwei.admin.model.vo.setting.UpdateBlogSettingsReqVO; +import com.hanserwei.common.utils.Response; + +public interface AdminBlogSettingsService { + /** + * 更新博客设置信息 + * + * @param updateBlogSettingsReqVO 博客设置信息 + * @return 响应 + */ + Response updateBlogSettings(UpdateBlogSettingsReqVO updateBlogSettingsReqVO); + + /** + * 获取博客设置详情 + * + * @return 博客设置详情 + */ + Response findDetail(); +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminFileService.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminFileService.java new file mode 100644 index 0000000..e854f4d --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/AdminFileService.java @@ -0,0 +1,15 @@ +package com.hanserwei.admin.service; + +import com.hanserwei.admin.model.vo.file.UploadFileRspVO; +import com.hanserwei.common.utils.Response; +import org.springframework.web.multipart.MultipartFile; + +public interface AdminFileService { + /** + * 上传文件 + * + * @param file 文件 + * @return 访问地址 + */ + Response uploadFile(MultipartFile file); +} \ No newline at end of file diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminBlogSettingsServiceImpl.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminBlogSettingsServiceImpl.java new file mode 100644 index 0000000..2f08ebc --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminBlogSettingsServiceImpl.java @@ -0,0 +1,50 @@ +package com.hanserwei.admin.service.impl; + +import com.hanserwei.admin.model.vo.setting.FindBlogSettingsRspVO; +import com.hanserwei.admin.model.vo.setting.UpdateBlogSettingsReqVO; +import com.hanserwei.admin.service.AdminBlogSettingsService; +import com.hanserwei.common.domain.dataobject.BlogSettings; +import com.hanserwei.common.domain.repository.BlogSettingsRepository; +import com.hanserwei.common.utils.Response; +import jakarta.annotation.Resource; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + + +@Service +public class AdminBlogSettingsServiceImpl implements AdminBlogSettingsService { + + @Resource + private BlogSettingsRepository blogSettingsRepository; + + + @Override + public Response updateBlogSettings(UpdateBlogSettingsReqVO updateBlogSettingsReqVO) { + // 保存或更新博客设置 + blogSettingsRepository.findById(1L) + .ifPresentOrElse(existingSettings -> { + // 如果存在,则更新现有记录 + BeanUtils.copyProperties(updateBlogSettingsReqVO, existingSettings); + blogSettingsRepository.saveAndFlush(existingSettings); + }, () -> { + // 如果不存在,则创建新记录 + BlogSettings blogSettings = new BlogSettings(); + BeanUtils.copyProperties(updateBlogSettingsReqVO, blogSettings); + blogSettings.setId(1L); + blogSettingsRepository.saveAndFlush(blogSettings); + }); + return Response.success(); + } + + + @Override + public Response findDetail() { + return blogSettingsRepository.findById(1L) + .map(e -> { + FindBlogSettingsRspVO findBlogSettingsRspVO = new FindBlogSettingsRspVO(); + BeanUtils.copyProperties(e, findBlogSettingsRspVO); + return Response.success(findBlogSettingsRspVO); + }) + .orElse(Response.success(null)); + } +} diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminFileServiceImpl.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminFileServiceImpl.java new file mode 100644 index 0000000..3094c26 --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/service/impl/AdminFileServiceImpl.java @@ -0,0 +1,32 @@ +package com.hanserwei.admin.service.impl; + +import com.hanserwei.admin.model.vo.file.UploadFileRspVO; +import com.hanserwei.admin.service.AdminFileService; +import com.hanserwei.admin.utils.RustfsUtils; +import com.hanserwei.common.enums.ResponseCodeEnum; +import com.hanserwei.common.exception.BizException; +import com.hanserwei.common.utils.Response; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Slf4j +@Service +public class AdminFileServiceImpl implements AdminFileService { + + @Resource + private RustfsUtils rustfsUtils; + + @Override + public Response uploadFile(MultipartFile file) { + try { + // 上传文件 + String url = rustfsUtils.uploadFile(file); + return Response.success(UploadFileRspVO.builder().url(url).build()); + } catch (Exception e) { + log.error("==> 上传文件异常:{} ...", e.getMessage()); + throw new BizException(ResponseCodeEnum.FILE_UPLOAD_FAILED); + } + } +} diff --git a/weblog-module-admin/src/main/java/com/hanserwei/admin/utils/RustfsUtils.java b/weblog-module-admin/src/main/java/com/hanserwei/admin/utils/RustfsUtils.java new file mode 100644 index 0000000..ad30e52 --- /dev/null +++ b/weblog-module-admin/src/main/java/com/hanserwei/admin/utils/RustfsUtils.java @@ -0,0 +1,72 @@ + +package com.hanserwei.admin.utils; + +import com.hanserwei.common.config.RustfsProperties; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.util.UUID; + +@Component +@Slf4j +public class RustfsUtils { + + @Resource + private RustfsProperties rustfsProperties; + + @Resource + private S3Client s3Client; + + /** + * 上传文件 + * + * @param file 文件 + * @return 访问地址 + * @throws Exception 异常 + */ + public String uploadFile(MultipartFile file) throws Exception { + // 判断文件是否为空 + if (file == null || file.getSize() == 0) { + log.error("==> 上传文件异常:文件大小为空 ..."); + throw new RuntimeException("文件大小不能为空"); + } + + // 文件的原始名称 + String originalFileName = file.getOriginalFilename(); + // 文件的 Content-Type + String contentType = file.getContentType(); + + // 生成存储对象的名称(将 UUID 字符串中的 - 替换成空字符串) + String key = UUID.randomUUID().toString().replace("-", ""); + // 获取文件的后缀,如 .jpg + String suffix = null; + if (originalFileName != null) { + suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); + } + + // 拼接上文件后缀,即为要存储的文件名 + String objectName = String.format("%s%s", key, suffix); + + log.info("==> 开始上传文件至 Rustfs, ObjectName: {}", objectName); + + // 上传文件至 Rustfs + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .key(objectName) + .bucket(rustfsProperties.getBucketName()) + .contentType(contentType) + .contentLength(file.getSize()) + .build(); + + s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + + // 返回文件的访问链接 + String url = String.format("%s/%s/%s", rustfsProperties.getEndpoint(), rustfsProperties.getBucketName(), objectName); + log.info("==> 上传文件至 Rustfs 成功,访问路径: {}", url); + return url; + } +} diff --git a/weblog-module-common/build.gradle.kts b/weblog-module-common/build.gradle.kts index c3bcf76..881df0d 100644 --- a/weblog-module-common/build.gradle.kts +++ b/weblog-module-common/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { api("org.springframework.boot:spring-boot-starter-web") api("org.springframework.boot:spring-boot-starter-security") api("org.springframework.boot:spring-boot-starter-aop") + api("software.amazon.awssdk:s3:2.40.1") runtimeOnly("org.postgresql:postgresql") diff --git a/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsClientConfig.java b/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsClientConfig.java new file mode 100644 index 0000000..d151cf5 --- /dev/null +++ b/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsClientConfig.java @@ -0,0 +1,32 @@ +package com.hanserwei.common.config; + +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +import java.net.URI; + +@Component +public class RustfsClientConfig { + + @Resource + private RustfsProperties rustfsProperties; + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .endpointOverride(URI.create(rustfsProperties.getEndpoint())) + .region(Region.US_EAST_1) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(rustfsProperties.getAccessKey(), rustfsProperties.getSecretKey()) + ) + ) + .forcePathStyle(true) + .build(); + } +} diff --git a/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsProperties.java b/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsProperties.java new file mode 100644 index 0000000..f4ba81c --- /dev/null +++ b/weblog-module-common/src/main/java/com/hanserwei/common/config/RustfsProperties.java @@ -0,0 +1,15 @@ +package com.hanserwei.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@Data +@ConfigurationProperties(prefix = "rustfs") +public class RustfsProperties { + private String endpoint; + private String accessKey; + private String secretKey; + private String bucketName; +} diff --git a/weblog-module-common/src/main/java/com/hanserwei/common/domain/dataobject/BlogSettings.java b/weblog-module-common/src/main/java/com/hanserwei/common/domain/dataobject/BlogSettings.java new file mode 100644 index 0000000..268aaea --- /dev/null +++ b/weblog-module-common/src/main/java/com/hanserwei/common/domain/dataobject/BlogSettings.java @@ -0,0 +1,94 @@ +package com.hanserwei.common.domain.dataobject; + +import jakarta.persistence.*; +import lombok.*; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 博客设置表 + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Entity +@Table(name = "t_blog_settings") +public class BlogSettings implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 博客Logo + * 数据库类型为 TEXT,Java中映射为 String 即可 + */ + @Column(name = "logo", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String logo = ""; + + /** + * 博客名称 + */ + @Column(name = "name", length = 60, nullable = false) + @Builder.Default + private String name = ""; + + /** + * 作者名 + */ + @Column(name = "author", length = 20, nullable = false) + @Builder.Default + private String author = ""; + + /** + * 介绍语 + */ + @Column(name = "introduction", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String introduction = ""; + + /** + * 作者头像 + */ + @Column(name = "avatar", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String avatar = ""; + + /** + * GitHub 主页访问地址 + */ + @Column(name = "github_homepage", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String githubHomepage = ""; + + /** + * CSDN 主页访问地址 + */ + @Column(name = "csdn_homepage", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String csdnHomepage = ""; + + /** + * Gitee 主页访问地址 + */ + @Column(name = "gitee_homepage", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String giteeHomepage = ""; + + /** + * 知乎主页访问地址 + */ + @Column(name = "zhihu_homepage", nullable = false, columnDefinition = "TEXT") + @Builder.Default + private String zhihuHomepage = ""; +} \ No newline at end of file diff --git a/weblog-module-common/src/main/java/com/hanserwei/common/domain/repository/BlogSettingsRepository.java b/weblog-module-common/src/main/java/com/hanserwei/common/domain/repository/BlogSettingsRepository.java new file mode 100644 index 0000000..f9bc114 --- /dev/null +++ b/weblog-module-common/src/main/java/com/hanserwei/common/domain/repository/BlogSettingsRepository.java @@ -0,0 +1,8 @@ +package com.hanserwei.common.domain.repository; + +import com.hanserwei.common.domain.dataobject.BlogSettings; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BlogSettingsRepository extends JpaRepository { + +} diff --git a/weblog-module-common/src/main/java/com/hanserwei/common/enums/ResponseCodeEnum.java b/weblog-module-common/src/main/java/com/hanserwei/common/enums/ResponseCodeEnum.java index bc94f56..42d34da 100644 --- a/weblog-module-common/src/main/java/com/hanserwei/common/enums/ResponseCodeEnum.java +++ b/weblog-module-common/src/main/java/com/hanserwei/common/enums/ResponseCodeEnum.java @@ -20,7 +20,8 @@ public enum ResponseCodeEnum implements BaseExceptionInterface { USER_NOT_EXIST("2005", "有户不存在!"), CATEGORY_NAME_IS_EXISTED("20005", "该分类已存在,请勿重复添加!"), TAG_NOT_EXIST("20006", "标签不存在!"), - CATEGORY_NOT_EXIST("20007", "分类不存在!" ); + CATEGORY_NOT_EXIST("20007", "分类不存在!"), + FILE_UPLOAD_FAILED("20008", "上传文件失败!"); // 异常码 private final String errorCode;