mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 10:57:13 +08:00 
			
		
		
		
	refactor: 重构原有文件上传接口并优化配置文件配置格式
移除 ContiNew Starter 本地存储依赖
This commit is contained in:
		| @@ -64,12 +64,6 @@ | ||||
|             <artifactId>continew-starter-file-excel</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- ContiNew Starter 存储模块 - 本地存储 --> | ||||
|         <dependency> | ||||
|             <groupId>top.charles7c.continew</groupId> | ||||
|             <artifactId>continew-starter-storage-local</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- ContiNew Starter API 文档模块 --> | ||||
|         <dependency> | ||||
|             <groupId>top.charles7c.continew</groupId> | ||||
| @@ -93,7 +87,7 @@ | ||||
|             <groupId>org.dromara.x-file-storage</groupId> | ||||
|             <artifactId>x-file-storage-spring</artifactId> | ||||
|         </dependency> | ||||
|         <!-- Amazon Simple Storage Service(亚马逊简单存储服务,通用存储协议 S3,兼容主流云厂商对象存储) --> | ||||
|         <!-- Amazon S3(Amazon Simple Storage Service,亚马逊简单存储服务,通用存储协议 S3,兼容主流云厂商对象存储) --> | ||||
|         <dependency> | ||||
|             <groupId>com.amazonaws</groupId> | ||||
|             <artifactId>aws-java-sdk-s3</artifactId> | ||||
|   | ||||
| @@ -16,6 +16,8 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.config; | ||||
|  | ||||
| import java.util.Optional; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| @@ -25,6 +27,7 @@ import org.springframework.stereotype.Component; | ||||
|  | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.util.EscapeUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.charles7c.continew.admin.system.enums.FileTypeEnum; | ||||
| @@ -32,6 +35,7 @@ import top.charles7c.continew.admin.system.mapper.FileMapper; | ||||
| import top.charles7c.continew.admin.system.mapper.StorageMapper; | ||||
| import top.charles7c.continew.admin.system.model.entity.FileDO; | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
|  | ||||
| /** | ||||
|  * 文件记录实现类 | ||||
| @@ -50,7 +54,9 @@ public class FileRecorderImpl implements FileRecorder { | ||||
|     @Override | ||||
|     public boolean save(FileInfo fileInfo) { | ||||
|         FileDO file = new FileDO(); | ||||
|         file.setName(EscapeUtil.unescape(fileInfo.getOriginalFilename())); | ||||
|         String originalFilename = EscapeUtil.unescape(fileInfo.getOriginalFilename()); | ||||
|         file.setName(StrUtil.contains(originalFilename, StringConstants.DOT) | ||||
|             ? StrUtil.subBefore(originalFilename, StringConstants.DOT, true) : originalFilename); | ||||
|         file.setSize(fileInfo.getSize()); | ||||
|         file.setUrl(fileInfo.getUrl()); | ||||
|         file.setExtension(fileInfo.getExt()); | ||||
| @@ -66,17 +72,40 @@ public class FileRecorderImpl implements FileRecorder { | ||||
|  | ||||
|     @Override | ||||
|     public FileInfo getByUrl(String url) { | ||||
|         FileDO file = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).one(); | ||||
|         FileDO file = this.getFileByUrl(url); | ||||
|         if (null == file) { | ||||
|             return null; | ||||
|         } | ||||
|         FileInfo fileInfo = new FileInfo(); | ||||
|         fileInfo.setOriginalFilename(file.getName()); | ||||
|         String extension = file.getExtension(); | ||||
|         fileInfo.setOriginalFilename( | ||||
|             StrUtil.isNotBlank(extension) ? file.getName() + StringConstants.DOT + extension : file.getName()); | ||||
|         fileInfo.setSize(file.getSize()); | ||||
|         fileInfo.setUrl(file.getUrl()); | ||||
|         fileInfo.setExt(file.getExtension()); | ||||
|         fileInfo.setExt(extension); | ||||
|         fileInfo.setBasePath(StringConstants.EMPTY); | ||||
|         fileInfo.setPath(StringConstants.EMPTY); | ||||
|         fileInfo.setFilename(StrUtil.subAfter(url, StringConstants.SLASH, true)); | ||||
|         fileInfo.setPlatform(storageMapper.lambdaQuery().eq(StorageDO::getId, file.getStorageId()).one().getCode()); | ||||
|         return fileInfo; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean delete(String url) { | ||||
|         return fileMapper.lambdaUpdate().eq(FileDO::getUrl, url).remove(); | ||||
|         FileDO file = this.getFileByUrl(url); | ||||
|         return fileMapper.lambdaUpdate().eq(FileDO::getUrl, file.getUrl()).remove(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 根据 URL 查询文件 | ||||
|      * | ||||
|      * @param url | ||||
|      *            URL | ||||
|      * @return 文件信息 | ||||
|      */ | ||||
|     private FileDO getFileByUrl(String url) { | ||||
|         Optional<FileDO> fileOptional = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).oneOpt(); | ||||
|         return fileOptional.orElseGet(() -> fileMapper.lambdaQuery() | ||||
|             .eq(FileDO::getUrl, StrUtil.subAfter(url, StringConstants.SLASH, true)).oneOpt().orElse(null)); | ||||
|     } | ||||
| } | ||||
| @@ -41,7 +41,7 @@ import top.charles7c.continew.admin.system.service.StorageService; | ||||
|  * @since 2023/12/24 22:31 | ||||
|  */ | ||||
| @Slf4j | ||||
| //@Component | ||||
| @Component | ||||
| @RequiredArgsConstructor | ||||
| public class FileStorageConfigLoader implements ApplicationRunner { | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ package top.charles7c.continew.admin.system.service; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.FileInfo; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.model.query.FileQuery; | ||||
| @@ -39,9 +40,10 @@ public interface FileService extends BaseService<FileResp, FileDetailResp, FileQ | ||||
|      * | ||||
|      * @param file | ||||
|      *            文件信息 | ||||
|      * @return 文件信息 | ||||
|      */ | ||||
|     default void upload(MultipartFile file) { | ||||
|         upload(file, null); | ||||
|     default FileInfo upload(MultipartFile file) { | ||||
|         return upload(file, null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -51,8 +53,9 @@ public interface FileService extends BaseService<FileResp, FileDetailResp, FileQ | ||||
|      *            文件信息 | ||||
|      * @param storageCode | ||||
|      *            存储库编码 | ||||
|      * @return 文件信息 | ||||
|      */ | ||||
|     void upload(MultipartFile file, String storageCode); | ||||
|     FileInfo upload(MultipartFile file, String storageCode); | ||||
|  | ||||
|     /** | ||||
|      * 根据存储库 ID 列表查询 | ||||
|   | ||||
| @@ -30,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.core.util.URLUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.enums.StorageTypeEnum; | ||||
| import top.charles7c.continew.admin.system.mapper.FileMapper; | ||||
| import top.charles7c.continew.admin.system.model.entity.FileDO; | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| @@ -62,13 +63,16 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes | ||||
|     private final FileStorageService fileStorageService; | ||||
|  | ||||
|     @Override | ||||
|     public void upload(MultipartFile file, String storageCode) { | ||||
|     public FileInfo upload(MultipartFile file, String storageCode) { | ||||
|         StorageDO storage; | ||||
|         if (StrUtil.isBlank(storageCode)) { | ||||
|             StorageDO storage = storageService.getDefaultStorage(); | ||||
|             storage = storageService.getDefaultStorage(); | ||||
|             CheckUtils.throwIfNull(storage, "请先指定默认存储库"); | ||||
|             storageCode = storage.getCode(); | ||||
|         } else { | ||||
|             storage = storageService.getByCode(storageCode); | ||||
|             CheckUtils.throwIfNotExists(storage, "StorageDO", "Code", storageCode); | ||||
|         } | ||||
|         UploadPretreatment uploadPretreatment = fileStorageService.of(file).setPlatform(storageCode); | ||||
|         UploadPretreatment uploadPretreatment = fileStorageService.of(file).setPlatform(storage.getCode()); | ||||
|         uploadPretreatment.setProgressMonitor(new ProgressListener() { | ||||
|             @Override | ||||
|             public void start() { | ||||
| @@ -85,7 +89,11 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes | ||||
|                 log.info("上传结束"); | ||||
|             } | ||||
|         }); | ||||
|         uploadPretreatment.upload(); | ||||
|         // 处理本地存储文件 URL | ||||
|         FileInfo fileInfo = uploadPretreatment.upload(); | ||||
|         fileInfo.setUrl(StorageTypeEnum.LOCAL.equals(storage.getType()) | ||||
|             ? URLUtil.normalize(storage.getDomain() + StringConstants.SLASH + fileInfo.getUrl()) : fileInfo.getUrl()); | ||||
|         return fileInfo; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -196,7 +196,7 @@ public class StorageServiceImpl | ||||
|             new ResourceHandlerRegistry(applicationContext, servletContext, contentNegotiationManager, urlPathHelper); | ||||
|         // 重新注册相同 Pattern 的静态资源映射 | ||||
|         for (Map.Entry<String, String> entry : registerMapping.entrySet()) { | ||||
|             String pathPattern = StrUtil.appendIfMissing(entry.getKey(), "/**"); | ||||
|             String pathPattern = StrUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN); | ||||
|             String resourceLocations = StrUtil.appendIfMissing(entry.getValue(), StringConstants.SLASH); | ||||
|             // 移除之前注册过的相同 Pattern 映射 | ||||
|             handlerMap.remove(pathPattern); | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.service.impl; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.*; | ||||
|  | ||||
| @@ -24,15 +23,15 @@ import jakarta.annotation.Resource; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.FileInfo; | ||||
| import org.dromara.x.file.storage.core.FileStorageService; | ||||
| import org.springframework.cache.annotation.CacheConfig; | ||||
| import org.springframework.cache.annotation.Cacheable; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| import org.springframework.util.unit.DataSize; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.io.FileUtil; | ||||
| import cn.hutool.core.io.file.FileNameUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| @@ -51,17 +50,12 @@ import top.charles7c.continew.admin.system.model.req.UserReq; | ||||
| import top.charles7c.continew.admin.system.model.req.UserRoleUpdateReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.UserDetailResp; | ||||
| import top.charles7c.continew.admin.system.model.resp.UserResp; | ||||
| import top.charles7c.continew.admin.system.service.DeptService; | ||||
| import top.charles7c.continew.admin.system.service.RoleService; | ||||
| import top.charles7c.continew.admin.system.service.UserRoleService; | ||||
| import top.charles7c.continew.admin.system.service.UserService; | ||||
| import top.charles7c.continew.admin.system.service.*; | ||||
| import top.charles7c.continew.starter.core.constant.StringConstants; | ||||
| import top.charles7c.continew.starter.core.util.ExceptionUtils; | ||||
| import top.charles7c.continew.starter.core.util.FileUploadUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseServiceImpl; | ||||
| import top.charles7c.continew.starter.extension.crud.base.CommonUserService; | ||||
| import top.charles7c.continew.starter.storage.local.autoconfigure.LocalStorageProperties; | ||||
|  | ||||
| /** | ||||
|  * 用户业务实现 | ||||
| @@ -75,11 +69,12 @@ import top.charles7c.continew.starter.storage.local.autoconfigure.LocalStoragePr | ||||
| public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserResp, UserDetailResp, UserQuery, UserReq> | ||||
|     implements UserService, CommonUserService { | ||||
|  | ||||
|     private final UserRoleService userRoleService; | ||||
|     private final RoleService roleService; | ||||
|     private final LocalStorageProperties localStorageProperties; | ||||
|     @Resource | ||||
|     private DeptService deptService; | ||||
|     private final RoleService roleService; | ||||
|     private final UserRoleService userRoleService; | ||||
|     private final FileService fileService; | ||||
|     private final FileStorageService fileStorageService; | ||||
|  | ||||
|     @Override | ||||
|     public Long add(UserDO user) { | ||||
| @@ -164,26 +159,20 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public String uploadAvatar(MultipartFile avatarFile, Long id) { | ||||
|         LocalStorageProperties.LocalStorageMapping storageMapping = localStorageProperties.getMapping().get("AVATAR"); | ||||
|         DataSize maxFileSize = storageMapping.getMaxFileSize(); | ||||
|         CheckUtils.throwIf(avatarFile.getSize() > maxFileSize.toBytes(), "请上传小于 {}MB 的图片", maxFileSize.toMegabytes()); | ||||
|         String avatarImageType = FileNameUtil.extName(avatarFile.getOriginalFilename()); | ||||
|         String[] avatarSupportImgTypes = FileConstants.AVATAR_SUPPORTED_IMG_TYPES; | ||||
|         CheckUtils.throwIf(!StrUtil.equalsAnyIgnoreCase(avatarImageType, avatarSupportImgTypes), "头像仅支持 {} 格式的图片", | ||||
|             String.join(StringConstants.CHINESE_COMMA, avatarSupportImgTypes)); | ||||
|         // 上传新头像 | ||||
|         UserDO user = super.getById(id); | ||||
|         String avatarPath = storageMapping.getLocation(); | ||||
|         File newAvatarFile = FileUploadUtils.upload(avatarFile, avatarPath, false); | ||||
|         CheckUtils.throwIfNull(newAvatarFile, "上传头像失败"); | ||||
|         assert null != newAvatarFile; | ||||
|         FileInfo fileInfo = fileService.upload(avatarFile); | ||||
|         // 更新用户头像 | ||||
|         String newAvatar = newAvatarFile.getName(); | ||||
|         String newAvatar = fileInfo.getUrl(); | ||||
|         baseMapper.lambdaUpdate().set(UserDO::getAvatar, newAvatar).eq(UserDO::getId, id).update(); | ||||
|         // 删除原头像 | ||||
|         String oldAvatar = user.getAvatar(); | ||||
|         if (StrUtil.isNotBlank(oldAvatar)) { | ||||
|             FileUtil.del(avatarPath + oldAvatar); | ||||
|             fileStorageService.delete(oldAvatar); | ||||
|         } | ||||
|         return newAvatar; | ||||
|     } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   <div class="navbar"> | ||||
|     <div class="left-side"> | ||||
|       <a-space> | ||||
|         <img alt="logo" :src="getFile(appStore.getLogo)" height="33" /> | ||||
|         <img alt="logo" :src="appStore.getLogo" height="33" /> | ||||
|         <a-typography-title | ||||
|           :style="{ margin: 0, fontSize: '18px' }" | ||||
|           :heading="5" | ||||
| @@ -199,7 +199,6 @@ | ||||
|   import useUser from '@/hooks/user'; | ||||
|   import Menu from '@/components/menu/index.vue'; | ||||
|   import getAvatar from '@/utils/avatar'; | ||||
|   import getFile from '@/utils/file'; | ||||
|   import { setTimer } from '@/utils/auth'; | ||||
|   import MessageBox from '../message-box/index.vue'; | ||||
|  | ||||
|   | ||||
| @@ -7,14 +7,6 @@ export default function getAvatar( | ||||
|   gender: number | undefined, | ||||
| ) { | ||||
|   if (avatar) { | ||||
|     const baseUrl = import.meta.env.VITE_API_BASE_URL; | ||||
|     if ( | ||||
|       !avatar.startsWith('http://') && | ||||
|       !avatar.startsWith('https://') && | ||||
|       !avatar.startsWith('blob:') | ||||
|     ) { | ||||
|       return `${baseUrl}/avatar/${avatar}`; | ||||
|     } | ||||
|     return avatar; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
|   <div class="root"> | ||||
|     <div class="header"> | ||||
|       <img | ||||
|         :src="getFile(appStore.getLogo) ?? './logo.svg'" | ||||
|         alt="logo" | ||||
|         :src="appStore.getLogo ?? './logo.svg'" | ||||
|         alt="Logo" | ||||
|         height="33" | ||||
|       /> | ||||
|       <div class="logo-text">{{ appStore.getTitle }}</div> | ||||
| @@ -78,7 +78,6 @@ | ||||
| <script lang="ts" setup> | ||||
|   import { ref } from 'vue'; | ||||
|   import { useAppStore } from '@/store'; | ||||
|   import getFile from '@/utils/file'; | ||||
|   import useResponsive from '@/hooks/responsive'; | ||||
|   import { socialAuth } from '@/api/auth'; | ||||
|   import AccountLogin from './components/account-login.vue'; | ||||
|   | ||||
| @@ -33,7 +33,7 @@ | ||||
|                     v-if="faviconFile && faviconFile.url" | ||||
|                     class="arco-upload-list-picture custom-upload-avatar favicon" | ||||
|                   > | ||||
|                     <img :src="getFile(faviconFile.url)" /> | ||||
|                     <img :src="faviconFile.url" alt="favicon" /> | ||||
|                     <div | ||||
|                       v-if="isEdit" | ||||
|                       class="arco-upload-list-picture-mask favicon" | ||||
| @@ -77,7 +77,7 @@ | ||||
|                     v-if="logoFile && logoFile.url" | ||||
|                     class="arco-upload-list-picture custom-upload-avatar logo" | ||||
|                   > | ||||
|                     <img :src="getFile(logoFile.url)" /> | ||||
|                     <img :src="logoFile.url" alt="Logo" /> | ||||
|                     <div | ||||
|                       v-if="isEdit" | ||||
|                       class="arco-upload-list-picture-mask logo" | ||||
| @@ -181,7 +181,6 @@ | ||||
|     resetValue, | ||||
|   } from '@/api/system/config'; | ||||
|   import { upload } from '@/api/common'; | ||||
|   import getFile from '@/utils/file'; | ||||
|   import { useAppStore } from '@/store'; | ||||
|  | ||||
|   const { proxy } = getCurrentInstance() as any; | ||||
|   | ||||
| @@ -63,6 +63,7 @@ import top.charles7c.continew.starter.core.util.TemplateUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.ValidationUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.model.resp.R; | ||||
| import top.charles7c.continew.starter.log.common.annotation.Log; | ||||
| import top.charles7c.continew.starter.messaging.mail.util.MailUtils; | ||||
|  | ||||
| /** | ||||
| @@ -84,6 +85,7 @@ public class CaptchaController { | ||||
|     private final ProjectProperties projectProperties; | ||||
|     private final GraphicCaptchaProperties graphicCaptchaProperties; | ||||
|  | ||||
|     @Log(ignore = true) | ||||
|     @Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)") | ||||
|     @GetMapping("/behavior") | ||||
|     public R<Object> getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) { | ||||
| @@ -91,12 +93,14 @@ public class CaptchaController { | ||||
|         return R.ok(captchaService.get(captchaReq).getRepData()); | ||||
|     } | ||||
|  | ||||
|     @Log(ignore = true) | ||||
|     @Operation(summary = "校验行为验证码", description = "校验行为验证码") | ||||
|     @PostMapping("/behavior") | ||||
|     public R<Object> checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq) { | ||||
|         return R.ok(captchaService.check(captchaReq)); | ||||
|     } | ||||
|  | ||||
|     @Log(ignore = true) | ||||
|     @Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)") | ||||
|     @GetMapping("/img") | ||||
|     public R<CaptchaResp> getImageCaptcha() { | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.webapi.common; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
| @@ -32,8 +31,8 @@ import io.swagger.v3.oas.annotations.Parameter; | ||||
| import io.swagger.v3.oas.annotations.enums.ParameterIn; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.FileInfo; | ||||
| import org.springframework.cache.annotation.Cacheable; | ||||
| import org.springframework.util.unit.DataSize; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
| @@ -52,14 +51,11 @@ import top.charles7c.continew.admin.system.model.query.RoleQuery; | ||||
| import top.charles7c.continew.admin.system.model.resp.RoleResp; | ||||
| import top.charles7c.continew.admin.system.service.*; | ||||
| import top.charles7c.continew.starter.core.autoconfigure.project.ProjectProperties; | ||||
| import top.charles7c.continew.starter.core.util.FileUploadUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.charles7c.continew.starter.core.util.validate.ValidationUtils; | ||||
| import top.charles7c.continew.starter.data.mybatis.plus.base.IBaseEnum; | ||||
| import top.charles7c.continew.starter.extension.crud.model.query.SortQuery; | ||||
| import top.charles7c.continew.starter.extension.crud.model.resp.R; | ||||
| import top.charles7c.continew.starter.log.common.annotation.Log; | ||||
| import top.charles7c.continew.starter.storage.local.autoconfigure.LocalStorageProperties; | ||||
|  | ||||
| /** | ||||
|  * 公共 API | ||||
| @@ -75,26 +71,20 @@ import top.charles7c.continew.starter.storage.local.autoconfigure.LocalStoragePr | ||||
| @RequestMapping("/common") | ||||
| public class CommonController { | ||||
|  | ||||
|     private final ProjectProperties projectProperties; | ||||
|     private final FileService fileService; | ||||
|     private final DeptService deptService; | ||||
|     private final MenuService menuService; | ||||
|     private final RoleService roleService; | ||||
|     private final DictItemService dictItemService; | ||||
|     private final ProjectProperties projectProperties; | ||||
|     private final LocalStorageProperties localStorageProperties; | ||||
|     private final OptionService optionService; | ||||
|  | ||||
|     @Operation(summary = "上传文件", description = "上传文件") | ||||
|     @PostMapping("/file") | ||||
|     public R<String> upload(@NotNull(message = "文件不能为空") MultipartFile file) { | ||||
|         ValidationUtils.throwIf(file::isEmpty, "文件不能为空"); | ||||
|         LocalStorageProperties.LocalStorageMapping storageMapping = localStorageProperties.getMapping().get("FILE"); | ||||
|         DataSize maxFileSize = storageMapping.getMaxFileSize(); | ||||
|         CheckUtils.throwIf(file.getSize() > maxFileSize.toBytes(), "请上传小于 {}MB 的文件", maxFileSize.toMegabytes()); | ||||
|         String filePath = storageMapping.getLocation(); | ||||
|         File newFile = FileUploadUtils.upload(file, filePath, false); | ||||
|         CheckUtils.throwIfNull(newFile, "上传文件失败"); | ||||
|         assert null != newFile; | ||||
|         return R.ok("上传成功", newFile.getName()); | ||||
|         FileInfo fileInfo = fileService.upload(file); | ||||
|         return R.ok("上传成功", fileInfo.getUrl()); | ||||
|     } | ||||
|  | ||||
|     @Operation(summary = "查询部门树", description = "查询树结构的部门列表") | ||||
|   | ||||
| @@ -84,6 +84,22 @@ spring.cache: | ||||
|     cache-null-values: true | ||||
|  | ||||
| --- ### 验证码配置 | ||||
| continew-starter.captcha: | ||||
|   ## 行为验证码 | ||||
|   behavior: | ||||
|     enabled: true | ||||
|     cache-type: REDIS | ||||
|     water-mark: ${project.app-name} | ||||
|   ## 图形验证码 | ||||
|   graphic: | ||||
|     enabled: true | ||||
|     # 类型 | ||||
|     type: SPEC | ||||
|     # 内容长度 | ||||
|     length: 4 | ||||
|     # 过期时间 | ||||
|     expirationInMinutes: 2 | ||||
| ## 其他验证码配置 | ||||
| captcha: | ||||
|   ## 邮箱验证码配置 | ||||
|   mail: | ||||
| @@ -104,41 +120,33 @@ captcha: | ||||
|     # 模板 ID | ||||
|     templateId: 1 | ||||
|  | ||||
| --- ### ContiNew Starter 组件配置 | ||||
| continew-starter: | ||||
|   ## 验证码配置 | ||||
|   captcha: | ||||
|     # 行为验证码配置 | ||||
|     behavior: | ||||
|       enabled: true | ||||
|       cache-type: REDIS | ||||
|       water-mark: ${project.app-name} | ||||
|     # 图形验证码配置 | ||||
|     graphic: | ||||
|       enabled: true | ||||
|       # 类型 | ||||
|       type: SPEC | ||||
|       # 内容长度 | ||||
|       length: 4 | ||||
|       # 过期时间 | ||||
|       expirationInMinutes: 2 | ||||
|   ## 日志配置 | ||||
|   log: | ||||
| --- ### 日志配置 | ||||
| continew-starter.log: | ||||
|   # 是否打印日志,开启后可打印访问日志(类似于 Nginx access log) | ||||
|   is-print: true | ||||
|   ## 本地存储配置 | ||||
|   storage: | ||||
|     local: | ||||
| ## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置) | ||||
| logging: | ||||
|   level: | ||||
|     top.charles7c: DEBUG | ||||
|   file: | ||||
|     path: ./logs | ||||
|  | ||||
| --- ### 跨域配置 | ||||
| continew-starter.cors: | ||||
|   enabled: true | ||||
|   # 配置允许跨域的域名 | ||||
|   allowed-origins: '*' | ||||
|   # 配置允许跨域的请求方式 | ||||
|   allowed-methods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowed-headers: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposed-headers: '*' | ||||
|  | ||||
| --- ### 接口文档配置 | ||||
| springdoc: | ||||
|   swagger-ui: | ||||
|     enabled: true | ||||
|       mapping: | ||||
|         FILE: | ||||
|           path-pattern: /file/** | ||||
|           location: C:\${project.app-name}\data\file\ | ||||
|           max-file-size: 10MB | ||||
|         AVATAR: | ||||
|           path-pattern: /avatar/** | ||||
|           location: C:\${project.app-name}\data\avatar\ | ||||
|           max-file-size: 5MB | ||||
|  | ||||
| --- ### 短信配置 | ||||
| sms: | ||||
| @@ -169,23 +177,6 @@ spring.mail: | ||||
|           class: javax.net.ssl.SSLSocketFactory | ||||
|           port: 465 | ||||
|  | ||||
| --- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) | ||||
| rsa: | ||||
|   # 私钥 | ||||
|   privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV | ||||
|  | ||||
| --- ### 日志配置 | ||||
| logging: | ||||
|   level: | ||||
|     top.charles7c: DEBUG | ||||
|   file: | ||||
|     path: ./logs | ||||
|  | ||||
| --- ### 接口文档配置 | ||||
| springdoc: | ||||
|   swagger-ui: | ||||
|     enabled: true | ||||
|  | ||||
| --- ### Just Auth 配置 | ||||
| justauth: | ||||
|   enabled: true | ||||
| @@ -220,7 +211,6 @@ sa-token.extension: | ||||
|     - /swagger-resources/** | ||||
|     - /*/api-docs/** | ||||
|     # 本地存储资源 | ||||
|     - /avatar/** | ||||
|     - /file/** | ||||
|  | ||||
| --- ### 文件上传配置 | ||||
| @@ -232,14 +222,7 @@ spring.servlet: | ||||
|     # 单次总上传文件大小限制 | ||||
|     max-request-size: 20MB | ||||
|  | ||||
| --- ### 跨域配置 | ||||
| cors: | ||||
|   enabled: true | ||||
|   # 配置允许跨域的域名 | ||||
|   allowed-origins: '*' | ||||
|   # 配置允许跨域的请求方式 | ||||
|   allowed-methods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowed-headers: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposed-headers: '*' | ||||
| --- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) | ||||
| rsa: | ||||
|   # 私钥 | ||||
|   privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV | ||||
|   | ||||
| @@ -86,6 +86,22 @@ spring.cache: | ||||
|     cache-null-values: true | ||||
|  | ||||
| --- ### 验证码配置 | ||||
| continew-starter.captcha: | ||||
|   # 行为验证码 | ||||
|   behavior: | ||||
|     enabled: true | ||||
|     cache-type: REDIS | ||||
|     water-mark: ${project.app-name} | ||||
|   # 图形验证码 | ||||
|   graphic: | ||||
|     enabled: true | ||||
|     # 类型 | ||||
|     type: SPEC | ||||
|     # 内容长度 | ||||
|     length: 4 | ||||
|     # 过期时间 | ||||
|     expirationInMinutes: 2 | ||||
| ## 其他验证码配置 | ||||
| captcha: | ||||
|   ## 邮箱验证码配置 | ||||
|   mail: | ||||
| @@ -106,41 +122,38 @@ captcha: | ||||
|     # 模板 ID | ||||
|     templateId: 1 | ||||
|  | ||||
| --- ### ContiNew Starter 组件配置 | ||||
| continew-starter: | ||||
|   ## 验证码配置 | ||||
|   captcha: | ||||
|     # 行为验证码配置 | ||||
|     behavior: | ||||
|       enabled: true | ||||
|       cache-type: REDIS | ||||
|       water-mark: ${project.app-name} | ||||
|     # 图形验证码配置 | ||||
|     graphic: | ||||
|       enabled: true | ||||
|       # 类型 | ||||
|       type: SPEC | ||||
|       # 内容长度 | ||||
|       length: 4 | ||||
|       # 过期时间 | ||||
|       expirationInMinutes: 2 | ||||
|   ## 日志配置 | ||||
|   log: | ||||
| --- ### 日志配置 | ||||
| continew-starter.log: | ||||
|   # 是否打印日志,开启后可打印访问日志(类似于 Nginx access log) | ||||
|   is-print: false | ||||
|   ## 本地存储配置 | ||||
|   storage: | ||||
|     local: | ||||
| ## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置) | ||||
| logging: | ||||
|   level: | ||||
|     top.charles7c: INFO | ||||
|   file: | ||||
|     path: ../logs | ||||
|  | ||||
| --- ### 跨域配置 | ||||
| continew-starter.cors: | ||||
|   enabled: true | ||||
|       mapping: | ||||
|         FILE: | ||||
|           path-pattern: /file/** | ||||
|           location: ../data/file/ | ||||
|           max-file-size: 10MB | ||||
|         AVATAR: | ||||
|           path-pattern: /avatar/** | ||||
|           location: ../data/avatar/ | ||||
|           max-file-size: 5MB | ||||
|   # 配置允许跨域的域名 | ||||
|   allowed-origins: | ||||
|     - ${project.url} | ||||
|   # 配置允许跨域的请求方式 | ||||
|   allowed-methods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowed-headers: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposed-headers: '*' | ||||
|  | ||||
| --- ### 接口文档配置 | ||||
| springdoc: | ||||
|   swagger-ui: | ||||
|     enabled: false | ||||
| ## 接口文档增强配置 | ||||
| knife4j: | ||||
|   # 开启生产环境屏蔽 | ||||
|   production: true | ||||
|  | ||||
| --- ### 短信配置 | ||||
| sms: | ||||
| @@ -171,27 +184,6 @@ spring.mail: | ||||
|           class: javax.net.ssl.SSLSocketFactory | ||||
|           port: 465 | ||||
|  | ||||
| --- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) | ||||
| rsa: | ||||
|   # 私钥 | ||||
|   privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV | ||||
|  | ||||
| --- ### 日志配置 | ||||
| logging: | ||||
|   level: | ||||
|     top.charles7c: INFO | ||||
|   file: | ||||
|     path: ../logs | ||||
|  | ||||
| --- ### 接口文档配置 | ||||
| springdoc: | ||||
|   swagger-ui: | ||||
|     enabled: false | ||||
| ## 接口文档增强配置 | ||||
| knife4j: | ||||
|   # 开启生产环境屏蔽 | ||||
|   production: true | ||||
|  | ||||
| --- ### Just Auth 配置 | ||||
| justauth: | ||||
|   enabled: true | ||||
| @@ -219,7 +211,6 @@ sa-token.extension: | ||||
|     - /**/*.js | ||||
|     - /webSocket/** | ||||
|     # 本地存储资源 | ||||
|     - /avatar/** | ||||
|     - /file/** | ||||
|  | ||||
| --- ### 文件上传配置 | ||||
| @@ -231,15 +222,7 @@ spring.servlet: | ||||
|     # 单次总上传文件大小限制 | ||||
|     max-request-size: 20MB | ||||
|  | ||||
| --- ### 跨域配置 | ||||
| cors: | ||||
|   enabled: true | ||||
|   # 配置允许跨域的域名 | ||||
|   allowed-origins: | ||||
|     - ${project.url} | ||||
|   # 配置允许跨域的请求方式 | ||||
|   allowed-methods: '*' | ||||
|   # 配置允许跨域的请求头 | ||||
|   allowed-headers: '*' | ||||
|   # 配置允许跨域的响应头 | ||||
|   exposed-headers: '*' | ||||
| --- ### 非对称加密配置(例如:密码加密传输,前端公钥加密,后端私钥解密;在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) | ||||
| rsa: | ||||
|   # 私钥 | ||||
|   privateKey: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV | ||||
|   | ||||
| @@ -22,10 +22,7 @@ project: | ||||
|   # 是否启用本地解析 IP 归属地 | ||||
|   ip-addr-local-parse-enabled: true | ||||
|  | ||||
| --- ### 日志配置(重叠部分,优先级高于 logback-spring.xml 中的配置) | ||||
| logging: | ||||
|   config: classpath:logback-spring.xml | ||||
| ## 日志配置 | ||||
| --- ### 日志配置 | ||||
| continew-starter.log: | ||||
|   enabled: true | ||||
|   # 包含信息 | ||||
| @@ -39,6 +36,17 @@ continew-starter.log: | ||||
|     - OS | ||||
|     - RESPONSE_HEADERS | ||||
|     - RESPONSE_BODY | ||||
| ## 项目日志配置 | ||||
| logging: | ||||
|   config: classpath:logback-spring.xml | ||||
|  | ||||
| --- ### 线程池配置 | ||||
| continew-starter.thread-pool: | ||||
|   enabled: true | ||||
|   # 队列容量 | ||||
|   queue-capacity: 128 | ||||
|   # 活跃时间(单位:秒) | ||||
|   keep-alive-seconds: 300 | ||||
|  | ||||
| --- ### 接口文档配置 | ||||
| springdoc: | ||||
| @@ -195,14 +203,6 @@ management.health: | ||||
|     # 关闭邮箱健康检查(邮箱配置错误或邮箱服务器不可用时,健康检查会报错) | ||||
|     enabled: false | ||||
|  | ||||
| --- ### 线程池配置 | ||||
| thread-pool: | ||||
|   enabled: true | ||||
|   # 队列容量 | ||||
|   queue-capacity: 128 | ||||
|   # 活跃时间(单位:秒) | ||||
|   keep-alive-seconds: 300 | ||||
|  | ||||
| --- ### 代码生成器配置 | ||||
| generator: | ||||
|   # 排除数据表 | ||||
|   | ||||
| @@ -20,5 +20,5 @@ VALUES | ||||
| INSERT IGNORE INTO `sys_storage` | ||||
| (`id`, `name`, `code`, `type`, `access_key`, `secret_key`, `endpoint`, `bucket_name`, `domain`, `description`, `is_default`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`) | ||||
| VALUES | ||||
| (1, '本地存储-开发环境', 'local-dev', 2, NULL, NULL, NULL, 'C:/continew-admin/data/file', 'http://localhost:8000/file', '本地存储-开发环境', b'0', 1, 2, 1, NOW(), NULL, NULL), | ||||
| (2, '本地存储', 'local', 2, NULL, NULL, NULL, '../data/file/', 'http://api.charles7c.top/file', '本地存储', b'1', 1, 1, 1, NOW(), NULL, NULL); | ||||
| (1, '本地存储-开发环境', 'local-dev', 2, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file', '本地存储-开发环境', b'1', 1, 1, 1, NOW(), NULL, NULL), | ||||
| (2, '本地存储-生产环境', 'local-prod', 2, NULL, NULL, NULL, '../data/file/', 'http://api.charles7c.top/file', '本地存储-生产环境', b'0', 2, 2, 1, NOW(), NULL, NULL); | ||||
| @@ -55,7 +55,6 @@ services: | ||||
|     volumes: | ||||
|       - /docker/continew-admin/config/:/app/config/ | ||||
|       - /docker/continew-admin/data/file/:/app/data/file/ | ||||
|       - /docker/continew-admin/data/avatar/:/app/data/avatar/ | ||||
|       - /docker/continew-admin/logs/:/app/logs/ | ||||
|       - /docker/continew-admin/lib/:/app/lib/ | ||||
|     depends_on: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user