fix:文件上传优化

This commit is contained in:
zhangzq
2026-02-24 19:23:19 +08:00
parent 2e2b36ddf8
commit 2cbf0e063c
15 changed files with 143 additions and 61 deletions

View File

@@ -18,6 +18,11 @@
<groupId>org.nl</groupId> <groupId>org.nl</groupId>
<artifactId>nl-common</artifactId> <artifactId>nl-common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.nl</groupId>
<artifactId>nl-plugin-dev-api</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -6,7 +6,7 @@ import java.util.List;
@Data @Data
public class MapLayout { public class MapLayout {
private Long mapId; private String mapId;
private Integer width; private Integer width;
private Integer height; private Integer height;
private MapOrigin origin; private MapOrigin origin;

View File

@@ -3,6 +3,8 @@ package org.nl.module.layout.service.impl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.nl.common.localStorage.service.LocalStorageService; import org.nl.common.localStorage.service.LocalStorageService;
import org.nl.common.localStorage.service.entity.LocalStorage; import org.nl.common.localStorage.service.entity.LocalStorage;
import org.nl.dev.api.DevFileApi;
import org.nl.dev.dto.DevFileApiDto;
import org.nl.module.layout.service.AgvLayoutService; import org.nl.module.layout.service.AgvLayoutService;
import org.nl.module.layout.service.dto.DeviceLayout; import org.nl.module.layout.service.dto.DeviceLayout;
import org.nl.module.layout.service.dto.MapLayout; import org.nl.module.layout.service.dto.MapLayout;
@@ -22,18 +24,18 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@Service @Service
public class AgvLayoutServiceImpl extends ServiceImpl<AgvLayoutMapMapper, AgvLayoutMap> implements AgvLayoutService { public class AgvLayoutServiceImpl extends ServiceImpl<AgvLayoutMapMapper, AgvLayoutMap> implements AgvLayoutService {
@Autowired @Autowired
private LocalStorageService localStorageService; private DevFileApi devFileApi;
@Resource @Resource
private AgvLayoutDeviceMapper agvLayoutDeviceMapper; private AgvLayoutDeviceMapper agvLayoutDeviceMapper;
@Override @Override
@Transactional @Transactional
public void saveLayout(MapLayout layout) { public void saveLayout(MapLayout layout) {
LocalStorage storage = localStorageService.getById(layout.getMapId()); DevFileApiDto storage = devFileApi.getFileInfoById(layout.getMapId());
AgvLayoutMap layoutMap = AgvLayoutMap.builder() AgvLayoutMap layoutMap = AgvLayoutMap.builder()
.mapId(layout.getMapId()) .mapId(layout.getMapId())
.mapName(storage.getName()) .mapName(storage.getName())
.url(storage.getPath()) .url(storage.getStoragePath())
.width(layout.getWidth()) .width(layout.getWidth())
.height(layout.getHeight()) .height(layout.getHeight())
.x(layout.getOrigin().getX()) .x(layout.getOrigin().getX())

View File

@@ -12,6 +12,7 @@
*/ */
package org.nl.module.device.controller; package org.nl.module.device.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -47,6 +48,7 @@ public class BaseDataDeviceController {
*/ */
@Operation(summary = "获取设备列表") @Operation(summary = "获取设备列表")
@GetMapping("/list") @GetMapping("/list")
@SaIgnore
public CommonResult list(BaseDeviceQuery query,PageQuery page) { public CommonResult list(BaseDeviceQuery query,PageQuery page) {
Page<BaseDataDevice> result = baseDataDeviceService.page(page.build(), query.build()); Page<BaseDataDevice> result = baseDataDeviceService.page(page.build(), query.build());
return CommonResult.data(result); return CommonResult.data(result);
@@ -83,6 +85,7 @@ public class BaseDataDeviceController {
} }
@Operation(summary = "设备组件列表") @Operation(summary = "设备组件列表")
@GetMapping("compentList") @GetMapping("compentList")
@SaIgnore
public CommonResult<List> compentList() { public CommonResult<List> compentList() {
return CommonResult.data(baseDataDeviceService.list()); return CommonResult.data(baseDataDeviceService.list());
} }

View File

@@ -59,8 +59,8 @@ public class BaseDataDevice implements Serializable {
@Schema(description = "图标地址") @Schema(description = "图标地址")
private String icon; private String icon;
@Schema(description = "图标路径地址") @Schema(description = "图标文件ID")
private String path; private String fileId;
@Schema(description = "创建时间") @Schema(description = "创建时间")
private Date createTime; private Date createTime;

View File

@@ -13,6 +13,7 @@
package org.nl.dev.api; package org.nl.dev.api;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import org.nl.dev.dto.DevFileApiDto;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
/** /**
@@ -109,7 +110,7 @@ public interface DevFileApi {
* @author chengchuanyao * @author chengchuanyao
* @date 2024/7/26 16:10 * @date 2024/7/26 16:10
*/ */
JSONObject getFileInfoById(String id); DevFileApiDto getFileInfoById(Long id);
/** /**
* 通过文件地址获取文件信息 * 通过文件地址获取文件信息

View File

@@ -0,0 +1,56 @@
package org.nl.dev.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class DevFileApiDto {
/** id */
@Schema(description = "id")
private String id;
/** 存储引擎 */
@Schema(description = "存储引擎")
private String engine;
/** 存储桶 */
@Schema(description = "存储桶")
private String bucket;
/** 文件名称 */
@Schema(description = "文件名称")
private String name;
/** 文件后缀 */
@Schema(description = "文件后缀")
private String suffix;
/** 文件大小kb */
@Schema(description = "文件大小kb")
private String sizeKb;
/** 文件大小(格式化后) */
@Schema(description = "文件大小(格式化后)")
private String sizeInfo;
/** 文件的对象名(唯一名称) */
@Schema(description = "文件的对象名(唯一名称)")
private String objName;
/** 文件存储路径 */
@Schema(description = "文件存储路径")
private String storagePath;
/** 文件下载路径 */
@Schema(description = "文件下载路径")
private String downloadPath;
/** 图片缩略图 */
@Schema(description = "图片缩略图")
private String thumbnail;
/** 扩展信息 */
@Schema(description = "扩展信息")
private String extJson;
}

View File

@@ -12,6 +12,8 @@
*/ */
package org.nl.dev.modular.file.entity; package org.nl.dev.modular.file.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter; import lombok.Getter;
@@ -30,8 +32,8 @@ import org.nl.common.pojo.CommonEntity;
public class DevFile extends CommonEntity { public class DevFile extends CommonEntity {
/** id */ /** id */
@Schema(description = "id") @TableId(type = IdType.AUTO)
private String id; private Long id;
/** 存储引擎 */ /** 存储引擎 */
@Schema(description = "存储引擎") @Schema(description = "存储引擎")

View File

@@ -22,4 +22,9 @@ import org.nl.dev.modular.file.entity.DevFile;
* @date 2022/2/23 18:40 * @date 2022/2/23 18:40
**/ **/
public interface DevFileMapper extends BaseMapper<DevFile> { public interface DevFileMapper extends BaseMapper<DevFile> {
@Insert("INSERT INTO local_storage (engine,bucket,name, suffix,sizeKb,sizeInfo,objName, storagePath, downloadPath, thumbnail, extJson) VALUES (#{name}, #{suffix}, #{path}, #{type}, #{size}, #{orderCode}, #{createTime}, #{createName}, #{updateTime})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUseGeneratedKeys(LocalStorage user);
} }

View File

@@ -16,8 +16,10 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.nl.dev.dto.DevFileApiDto;
import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.entity.DevFile;
import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum; import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.nl.dev.api.DevFileApi; import org.nl.dev.api.DevFileApi;
@@ -78,10 +80,11 @@ public class DevFileApiProvider implements DevFileApi {
} }
@Override @Override
public JSONObject getFileInfoById(String id) { public DevFileApiDto getFileInfoById(Long id) {
return Optional.ofNullable(devFileService.getById(id)) DevFile byId = devFileService.getById(id);
.map(JSONUtil::parseObj) DevFileApiDto dto = new DevFileApiDto();
.orElse(new JSONObject()); BeanUtils.copyProperties(dto,byId);
return dto;
} }
@Override @Override

View File

@@ -141,7 +141,7 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
} }
// 生成id // 生成id
String fileId = IdWorker.getIdStr(); Long fileId = IdWorker.getId();
// 存储桶名称 // 存储桶名称
String bucketName = null; String bucketName = null;
@@ -210,7 +210,7 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
if(ObjectUtil.isEmpty(apiUrl)) { if(ObjectUtil.isEmpty(apiUrl)) {
throw new CommonException("后端域名地址未正确配置snowy.config.common.backend-url为空"); throw new CommonException("后端域名地址未正确配置snowy.config.common.backend-url为空");
} }
downloadUrl= apiUrl + "/dev/file/download?id=" + fileId; downloadUrl= apiUrl + "/api/localStorage/download?storageId=" + fileId;
devFile.setDownloadPath(downloadUrl); devFile.setDownloadPath(downloadUrl);
} else { } else {
// 阿里云、腾讯云、MINIO可以直接使用存储地址公网作为下载地址 // 阿里云、腾讯云、MINIO可以直接使用存储地址公网作为下载地址
@@ -233,7 +233,7 @@ public class DevFileServiceImpl extends ServiceImpl<DevFileMapper, DevFile> impl
* @author xuyuxiang * @author xuyuxiang
* @date 2022/4/22 15:58 * @date 2022/4/22 15:58
**/ **/
public String genFileKey(String fileId, MultipartFile file) { public String genFileKey(Long fileId, MultipartFile file) {
// 获取文件原始名称 // 获取文件原始名称
String originalFileName = file.getOriginalFilename(); String originalFileName = file.getOriginalFilename();

View File

@@ -325,6 +325,7 @@ function addDeviceFromTemplate(template, x, y) {
name: `${template.name}`, name: `${template.name}`,
type: template.type, type: template.type,
icon: template.icon, icon: template.icon,
path: template.path,
x: x, x: x,
y: y, y: y,
angle: 0, angle: 0,

View File

@@ -190,8 +190,8 @@
if (res) { if (res) {
// 保存文件ID到表单数据 // 保存文件ID到表单数据
const fileId = res.id || res const fileId = res.id || res
formData.value.icon = fileId formData.value.fileId = fileId
formData.value.path = res.storagePath formData.value.icon = res.storagePath
message.success('上传成功') message.success('上传成功')
// 加载新上传的图片预览 // 加载新上传的图片预览

View File

@@ -48,9 +48,9 @@
<template v-if="column.dataIndex === 'code'"> <template v-if="column.dataIndex === 'code'">
<span>{{ record.code }}</span> <span>{{ record.code }}</span>
</template> </template>
<template v-if="column.dataIndex === 'icon'"> <template v-if="column.dataIndex === 'fileId'">
<a-image v-if="record.icon && getImageUrl(record.icon)" :width="50" :src="getImageUrl(record.icon)" /> <a-image v-if="record.fileId && getImageUrl(record.fileId)" :width="50" :src="getImageUrl(record.fileId)" />
<a-spin v-else-if="record.icon" size="small" /> <a-spin v-else-if="record.fileId" size="small" />
<span v-else>-</span> <span v-else>-</span>
</template> </template>
<template v-if="column.dataIndex === 'action'"> <template v-if="column.dataIndex === 'action'">
@@ -104,8 +104,8 @@
dataIndex: 'editParam' dataIndex: 'editParam'
}, },
{ {
title: '图片路径', title: '图片文件ID',
dataIndex: 'path' dataIndex: 'fileId'
}, },
{ {
title: '所属区域', title: '所属区域',
@@ -191,8 +191,8 @@
// 加载所有图片 // 加载所有图片
if (res.records && res.records.length > 0) { if (res.records && res.records.length > 0) {
res.records.forEach((item) => { res.records.forEach((item) => {
if (item.icon) { if (item.fileId) {
loadImageById(item.icon) loadImageById(item.fileId)
} }
}) })
} }

View File

@@ -16,6 +16,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaResponse; import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.filter.SaServletFilter; import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaHttpMethod; import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
@@ -245,39 +246,39 @@ public class GlobalConfigure implements WebMvcConfigurer {
// 指定拦截路由 // 指定拦截路由
.addInclude("/**") .addInclude("/**")
// 设置鉴权的接口 // // 设置鉴权的接口
.setAuth(r -> { // .setAuth(r -> {
// B端的接口校验B端登录 // // B端的接口校验B端登录
SaRouter.match("/**") // SaRouter.match("/**")
// 排除无需登录接口 // // 排除无需登录接口
.notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR)) // .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
// 排除C端认证接口 // // 排除C端认证接口
.notMatch(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR)) // .notMatch(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR))
// 校验B端登录 // // 校验B端登录
.check(r1 -> { // .check(r1 -> {
StpUtil.checkLogin(); // StpUtil.checkLogin();
// 更新过期时间 // // 更新过期时间
StpUtil.renewTimeout(saTokenConfig.getTimeout()); // StpUtil.renewTimeout(saTokenConfig.getTimeout());
}); // });
//
// C端的接口校验C端登录 // // C端的接口校验C端登录
SaRouter.match("/**") // SaRouter.match("/**")
// 排除无需登录接口 // // 排除无需登录接口
.notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR)) // .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
// 匹配C端认证接口 // // 匹配C端认证接口
.match(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR)) // .match(CollectionUtil.newArrayList(CLIENT_USER_PERMISSION_PATH_ARR))
// 校验C端登录 // // 校验C端登录
.check(r1 -> StpClientUtil.checkLogin()); // .check(r1 -> StpClientUtil.checkLogin());
//
// B端的超管接口校验B端超管角色 // // B端的超管接口校验B端超管角色
SaRouter.match("/**") // SaRouter.match("/**")
// 排除无需登录接口 // // 排除无需登录接口
.notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR)) // .notMatch(CollectionUtil.newArrayList(NO_LOGIN_PATH_ARR))
// 匹配超管接口 // // 匹配超管接口
.match(CollectionUtil.newArrayList(SUPER_PERMISSION_PATH_ARR)) // .match(CollectionUtil.newArrayList(SUPER_PERMISSION_PATH_ARR))
// 校验B端超管角色 // // 校验B端超管角色
.check(r1 -> StpUtil.checkRole(SysBuildInEnum.BUILD_IN_ROLE_CODE.getValue())); // .check(r1 -> StpUtil.checkRole(SysBuildInEnum.BUILD_IN_ROLE_CODE.getValue()));
}) // })
// 前置函数:在每次认证函数之前执行 // 前置函数:在每次认证函数之前执行
.setBeforeAuth(obj -> { .setBeforeAuth(obj -> {
@@ -712,6 +713,9 @@ public class GlobalConfigure implements WebMvcConfigurer {
*/ */
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns(NO_LOGIN_PATH_ARR); // 白名单
registry.addInterceptor(mockInterceptor) registry.addInterceptor(mockInterceptor)
.addPathPatterns("/**") // 拦截所有路径 .addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns(CollectionUtil.newArrayList(NO_CHECK_MOCK)); .excludePathPatterns(CollectionUtil.newArrayList(NO_CHECK_MOCK));