Compare commits
5 Commits
f27b57ec8b
...
6606438862
| Author | SHA1 | Date | |
|---|---|---|---|
| 6606438862 | |||
| c9332b85db | |||
| 74d19bb9d6 | |||
| 57f97a490a | |||
| 746e5051cf |
@@ -42,6 +42,7 @@ dependencies {
|
||||
annotationProcessor 'org.projectlombok:lombok' // Lombok 注解处理
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test' // 测试框架
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' // thymeleaf模版引擎
|
||||
implementation 'com.aliyun.oss:aliyun-sdk-oss:3.15.1' // OSS SDK
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
39
src/main/java/com/corewing/app/config/CorsConfig.java
Normal file
39
src/main/java/com/corewing/app/config/CorsConfig.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.corewing.app.config;
|
||||
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration // 标记为配置类
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 配置跨域过滤器,优先级高于所有拦截器
|
||||
*/
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
// 1. 配置跨域参数
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
// 允许的前端域名(根据实际环境修改)
|
||||
config.addAllowedOriginPattern("*");
|
||||
// 允许携带 Cookie(前后端都需要开启)
|
||||
config.setAllowCredentials(true);
|
||||
// 允许的请求方法(包含预检请求 OPTIONS)
|
||||
config.addAllowedMethod("*");
|
||||
// 允许的请求头(* 表示所有)
|
||||
config.addAllowedHeader("*");
|
||||
// 预检请求缓存时间(1小时,减少重复验证)
|
||||
config.setMaxAge(3600L);
|
||||
|
||||
// 2. 配置路径匹配规则(对所有接口生效)
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
// 3. 返回过滤器(优先级最高)
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.corewing.app.dto.api;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TutorialListRequest {
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 教程分类
|
||||
@@ -76,4 +77,7 @@ public class TutorialCategory implements Serializable {
|
||||
public static final String typeCategory = "category";
|
||||
public static final String typeTag = "tag";
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<Tutorial> tutorials;
|
||||
|
||||
}
|
||||
|
||||
@@ -93,8 +93,8 @@ public class BizFirmwareController {
|
||||
*/
|
||||
@PostMapping("/uploadFile")
|
||||
@ResponseBody
|
||||
public Result<String> uploadFile(MultipartFile file) {
|
||||
return Result.success();
|
||||
public Result<String> uploadFile(MultipartFile file, Long id) {
|
||||
return Result.isBool(firmwareService.uploadFile(file, id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.corewing.app.common.Result;
|
||||
import com.corewing.app.dto.api.TutorialListRequest;
|
||||
import com.corewing.app.entity.Tutorial;
|
||||
import com.corewing.app.entity.TutorialCategory;
|
||||
import com.corewing.app.service.TutorialCategoryService;
|
||||
@@ -107,5 +108,4 @@ public class AppTutorialController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.corewing.app.common.Result;
|
||||
import com.corewing.app.entity.Firmware;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 固件 Service 接口
|
||||
@@ -24,4 +25,12 @@ public interface FirmwareService extends IService<Firmware> {
|
||||
* @return 固件信息
|
||||
*/
|
||||
Firmware getByFirmwareName(String firmwareName);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param file
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
boolean uploadFile(MultipartFile file, Long id);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ package com.corewing.app.service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.corewing.app.dto.api.TutorialListRequest;
|
||||
import com.corewing.app.entity.Tutorial;
|
||||
import com.corewing.app.entity.TutorialCategory;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TutorialService extends IService<Tutorial> {
|
||||
Page<Tutorial> pageList(Page<Tutorial> page, Long categoryId, String tutorialTitle, String lang);
|
||||
|
||||
|
||||
@@ -7,8 +7,12 @@ import com.corewing.app.common.page.PageContext;
|
||||
import com.corewing.app.entity.Firmware;
|
||||
import com.corewing.app.mapper.FirmwareMapper;
|
||||
import com.corewing.app.service.FirmwareService;
|
||||
import com.corewing.app.util.OSSUploadUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 固件 Service 实现类
|
||||
@@ -32,5 +36,18 @@ public class FirmwareServiceImpl extends ServiceImpl<FirmwareMapper, Firmware> i
|
||||
return this.getOne(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uploadFile(MultipartFile file, Long id) {
|
||||
try {
|
||||
String downloadUrl = OSSUploadUtil.uploadFile(file.getInputStream(), "/");
|
||||
Firmware firmware = getById(id);
|
||||
firmware.setDownloadUrl(downloadUrl);
|
||||
updateById(firmware);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,16 +5,20 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.corewing.app.common.page.PageContext;
|
||||
import com.corewing.app.dto.api.TutorialListRequest;
|
||||
import com.corewing.app.entity.Tutorial;
|
||||
import com.corewing.app.entity.TutorialCategory;
|
||||
import com.corewing.app.entity.TutorialCategoryRelation;
|
||||
import com.corewing.app.mapper.TutorialMapper;
|
||||
import com.corewing.app.service.TutorialCategoryRelationService;
|
||||
import com.corewing.app.service.TutorialCategoryService;
|
||||
import com.corewing.app.service.TutorialService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class TutorialServiceImpl extends ServiceImpl<TutorialMapper, Tutorial> implements TutorialService {
|
||||
@@ -25,6 +29,9 @@ public class TutorialServiceImpl extends ServiceImpl<TutorialMapper, Tutorial> i
|
||||
@Resource
|
||||
private TutorialCategoryRelationService tutorialCategoryRelationService;
|
||||
|
||||
@Resource
|
||||
private TutorialCategoryService tutorialCategoryService;
|
||||
|
||||
@Override
|
||||
public Page<Tutorial> pageList(Page<Tutorial> page, Long categoryId, String tutorialTitle, String lang) {
|
||||
return tutorialMapper.pageList(page, categoryId, tutorialTitle, lang);
|
||||
@@ -64,4 +71,5 @@ public class TutorialServiceImpl extends ServiceImpl<TutorialMapper, Tutorial> i
|
||||
removeById(id);
|
||||
return tutorialCategoryRelationService.remove(new LambdaQueryWrapper<TutorialCategoryRelation>().eq(TutorialCategoryRelation::getCategoryId, tutorial.getCategoryId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
}
|
||||
|
||||
// 验证密码(MD5加密)
|
||||
// String encryptPassword = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
|
||||
// 客户端已经使用加密
|
||||
if (!password.equals(user.getPassword())) {
|
||||
if (!encryptPassword.equals(user.getPassword())) {
|
||||
throw new RuntimeException(I18nUtil.getMessage("error.password.incorrect"));
|
||||
}
|
||||
|
||||
|
||||
86
src/main/java/com/corewing/app/util/OSSUploadUtil.java
Normal file
86
src/main/java/com/corewing/app/util/OSSUploadUtil.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package com.corewing.app.util;
|
||||
|
||||
import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSClientBuilder;
|
||||
import com.aliyun.oss.model.ObjectMetadata;
|
||||
import com.aliyun.oss.model.PutObjectRequest;
|
||||
import com.aliyun.oss.model.PutObjectResult;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Component
|
||||
public class OSSUploadUtil {
|
||||
|
||||
private static final String ENDPOINT = "oss-cn-shenzhen.aliyuncs.com"; // 地域节点
|
||||
private static final String ACCESS_KEY_ID = "your-access-key-id"; // 替换为你的 AccessKey ID
|
||||
private static final String ACCESS_KEY_SECRET = "your-access-key-secret"; // 替换为你的 AccessKey Secret
|
||||
private static final String BUCKET_NAME = "corewing-app"; // 替换为你的 Bucket 名称
|
||||
|
||||
/**
|
||||
* 上传文件到 OSS
|
||||
* @param inputStream 文件输入流
|
||||
* @param objectName OSS 中存储的文件路径(如 "firmware/20231104/update.bin")
|
||||
* @return 上传成功后的文件 URL
|
||||
*/
|
||||
public static String uploadFile(InputStream inputStream, String objectName) {
|
||||
// 创建 OSS 客户端实例
|
||||
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
|
||||
|
||||
try {
|
||||
// 设置文件元数据(可选,如 Content-Type)
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentType(getContentType(objectName)); // 自动识别文件类型
|
||||
|
||||
// 上传文件
|
||||
PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectName, inputStream, metadata);
|
||||
PutObjectResult result = ossClient.putObject(request);
|
||||
|
||||
// 生成文件访问 URL(公网访问需 Bucket 设为公共读)
|
||||
return String.format("https://%s.%s/%s", BUCKET_NAME, ENDPOINT, objectName);
|
||||
} finally {
|
||||
// 关闭 OSS 客户端,释放资源
|
||||
if (ossClient != null) {
|
||||
ossClient.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传本地文件到 OSS
|
||||
* @param localFilePath 本地文件路径(如 "D:/firmware/update.bin")
|
||||
* @param objectName OSS 中存储的文件路径
|
||||
* @return 上传成功后的文件 URL
|
||||
*/
|
||||
public static String uploadLocalFile(String localFilePath, String objectName) {
|
||||
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
|
||||
try {
|
||||
File file = new File(localFilePath);
|
||||
ossClient.putObject(BUCKET_NAME, objectName, file);
|
||||
return String.format("https://%s.%s/%s", BUCKET_NAME, ENDPOINT, objectName);
|
||||
} finally {
|
||||
if (ossClient != null) {
|
||||
ossClient.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件名获取 Content-Type
|
||||
* @param fileName 文件名(如 "update.bin")
|
||||
* @return Content-Type
|
||||
*/
|
||||
private static String getContentType(String fileName) {
|
||||
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
switch (suffix) {
|
||||
case "bin": return "application/octet-stream";
|
||||
case "jpg": case "jpeg": return "image/jpeg";
|
||||
case "png": return "image/png";
|
||||
case "txt": return "text/plain";
|
||||
// 其他文件类型可自行扩展
|
||||
default: return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,7 @@
|
||||
id="firmwareFile"
|
||||
name="file"
|
||||
accept=".bin"
|
||||
ref="firmwareFileRef"
|
||||
>
|
||||
<div class="form-text text-muted mt-1">支持 .bin 格式,单个文件不超过 100MB</div>
|
||||
</div>
|
||||
@@ -499,20 +500,59 @@
|
||||
},
|
||||
// 打开文件上传模态框
|
||||
openUploadModal(item) {
|
||||
this.addOrEditDto = item;
|
||||
this.modalInstances['uploadFileModal'].show();
|
||||
},
|
||||
uploadFile() {
|
||||
request.put("/biz/user/resetPasswordRequest", this.resetPasswordDto)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
$message.success('修改成功!', 500);
|
||||
this.modalInstances['resetPwdModal'].hide();
|
||||
} else {
|
||||
alert('修改失败!');
|
||||
async uploadFile() {
|
||||
// 1. 获取选中的文件
|
||||
const fileInput = this.$refs.firmwareFileRef;
|
||||
const file = fileInput.files[0];
|
||||
|
||||
// 2. 校验文件(格式、大小)
|
||||
if (!file) {
|
||||
alert("请选择 .bin 格式的固件文件");
|
||||
return;
|
||||
}
|
||||
if (file.type !== "application/octet-stream" && !file.name.endsWith(".bin")) {
|
||||
alert("仅支持 .bin 格式文件");
|
||||
return;
|
||||
}
|
||||
if (file.size > 100 * 1024 * 1024) { // 100MB
|
||||
alert("文件大小不能超过 100MB");
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("id", this.addOrEditDto.id);
|
||||
|
||||
// 4. 发送上传请求(Axios)
|
||||
try {
|
||||
const response = await axios({
|
||||
url: "/biz/firmware/uploadFile",
|
||||
method: "POST",
|
||||
data: formData,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
const progress = (progressEvent.loaded / progressEvent.total) * 100;
|
||||
console.log(`上传进度:${progress.toFixed(2)}%`);
|
||||
}
|
||||
this.resetPasswordDto = {};
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
$message.success('固件上传成功');
|
||||
this.$refs.firmwareFileRef.value = "";
|
||||
this.modalInstances['uploadFileModal'].hide();
|
||||
} else {
|
||||
$message.error("上传失败:" + response.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("上传出错:", error);
|
||||
$message.error("网络异常,上传失败");
|
||||
}
|
||||
|
||||
},
|
||||
// 打开新增模态框
|
||||
openAddModal() {
|
||||
|
||||
Reference in New Issue
Block a user