配置了阿里oss存储桶连接,实现了文件上传功能,现在前端能够正常上传图片到oss并显示

This commit is contained in:
puzvv
2025-12-30 19:44:05 +08:00
parent 90ecd78e8d
commit 5962461d86
8 changed files with 137 additions and 569 deletions

View File

@@ -61,7 +61,11 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
<!-- 工具类 --> <!-- 工具类 -->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>

View File

@@ -0,0 +1,78 @@
package icu.sunway.ai_spring_example.Common.Utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.UUID;
@Component
public class OSSUtil {
@Value("${aliyun.oss.endpoint}")
private String endpoint;
@Value("${aliyun.oss.accessKeyId}")
private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}")
private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")
private String bucketName;
@Value("${aliyun.oss.urlPrefix}")
private String urlPrefix;
/**
* 生成预签名上传 URL前端直传
*/
public String generatePresignedUrl(String fileName) {
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
String key = "images/" + LocalDate.now() + "/" + UUID.randomUUID() + getFileExtension(fileName);
Date expiration = new Date(System.currentTimeMillis() + 30 * 60 * 1000); // 30分钟过期
URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
ossClient.shutdown();
return url.toString();
}
/**
* 获取文件访问 URL
*/
public String getFileUrl(String fileName) {
String key = "images/" + LocalDate.now() + "/" + UUID.randomUUID() + getFileExtension(fileName);
return urlPrefix + key;
}
/**
* 后端直接上传文件到 OSS
*/
public String uploadFile(String originalFilename, InputStream inputStream) {
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
String key = "images/" + LocalDate.now() + "/" + UUID.randomUUID() + getFileExtension(originalFilename);
try {
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream);
ossClient.putObject(putObjectRequest);
return urlPrefix + key;
} finally {
ossClient.shutdown();
}
}
/**
* 获取文件扩展名
*/
private String getFileExtension(String fileName) {
if (fileName == null || !fileName.contains(".")) {
return "";
}
return fileName.substring(fileName.lastIndexOf("."));
}
}

View File

@@ -1,282 +0,0 @@
package icu.sunway.ai_spring_example.Controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 用户管理后台接口
* 提供用户管理相关的功能
*/
@RestController
public class Admin {
/**
* 获取用户列表
*
* @param params 查询参数包含page、size、username、status等字段
* @return 用户列表数据
*/
@GetMapping("/admin/users")
public ResponseEntity<?> getUserList(@RequestParam Map<String, Object> params) {
// 从Map中获取查询参数
Integer page = Integer.valueOf((String) params.getOrDefault("page", "1"));
Integer size = Integer.valueOf((String) params.getOrDefault("size", "10"));
String username = (String) params.get("username");
Integer status = params.get("status") != null ? Integer.valueOf((String) params.get("status")) : null;
// TODO: 实现查询逻辑
// 1. 根据条件查询用户列表
// 2. 处理分页
// 模拟返回用户列表
List<Map<String, Object>> userList = List.of(
Map.of("id", 1, "username", "admin", "email", "admin@example.com", "status", 1, "role", "admin",
"createTime", "2023-01-01 10:00:00"),
Map.of("id", 2, "username", "user1", "email", "user1@example.com", "status", 1, "role", "user",
"createTime", "2023-01-02 10:00:00"));
Map<String, Object> responseData = new HashMap<>();
responseData.put("list", userList);
responseData.put("total", 2);
responseData.put("page", page);
responseData.put("size", size);
return ResponseEntity.success("获取用户列表成功", responseData);
}
/**
* 获取用户详情
*
* @param userId 用户ID
* @return 用户详细信息
*/
@GetMapping("/admin/users/{userId}")
public ResponseEntity<?> getUserDetail(@PathVariable Long userId) {
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// TODO: 实现查询逻辑
// 1. 根据用户ID查询用户信息
// 2. 构建响应数据
// 模拟返回用户详情
Map<String, Object> userInfo = Map.of(
"id", userId, "username", "admin", "email", "admin@example.com", "status", 1,
"role", "admin", "createTime", "2023-01-01 10:00:00", "updateTime", "2023-01-02 10:00:00");
return ResponseEntity.success("获取用户详情成功", userInfo);
}
/**
* 创建新用户
*
* @param userMap 用户信息包含username、password、email、role等字段
* @return 创建结果
*/
@PostMapping("/admin/users")
public ResponseEntity<?> createUser(@RequestBody Map<String, Object> userMap) {
// 从Map中获取用户信息
String username = (String) userMap.get("username");
String password = (String) userMap.get("password");
String email = (String) userMap.get("email");
String role = (String) userMap.get("role");
Integer status = (Integer) userMap.get("status");
// 参数验证
if (username == null || username.isEmpty()) {
return ResponseEntity.error(400, "用户名不能为空");
}
if (password == null || password.isEmpty()) {
return ResponseEntity.error(400, "密码不能为空");
}
if (email == null || email.isEmpty()) {
return ResponseEntity.error(400, "邮箱不能为空");
}
if (role == null || role.isEmpty()) {
return ResponseEntity.error(400, "角色不能为空");
}
// TODO: 实现创建逻辑
// 1. 检查用户名是否已存在
// 2. 检查邮箱是否已存在
// 3. 密码加密
// 4. 保存用户信息
// 模拟创建成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("id", 3);
responseData.put("username", username);
responseData.put("email", email);
return ResponseEntity.success("创建用户成功", responseData);
}
/**
* 更新用户信息
*
* @param userId 用户ID
* @param userMap 用户更新信息
* @return 更新结果
*/
@PutMapping("/admin/users/{userId}")
public ResponseEntity<?> updateUser(@PathVariable Long userId, @RequestBody Map<String, Object> userMap) {
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// 从Map中获取更新信息
String email = (String) userMap.get("email");
String role = (String) userMap.get("role");
Integer status = (Integer) userMap.get("status");
// TODO: 实现更新逻辑
// 1. 检查用户是否存在
// 2. 更新用户信息
// 模拟更新成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("id", userId);
responseData.put("email", email);
responseData.put("role", role);
responseData.put("status", status);
return ResponseEntity.success("更新用户信息成功", responseData);
}
/**
* 删除用户
*
* @param userId 用户ID
* @return 删除结果
*/
@DeleteMapping("/admin/users/{userId}")
public ResponseEntity<?> deleteUser(@PathVariable Long userId) {
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// TODO: 实现删除逻辑
// 1. 检查用户是否存在
// 2. 删除用户信息
// 模拟删除成功
return ResponseEntity.success("删除用户成功");
}
/**
* 修改用户密码
*
* @param userId 用户ID
* @param passwordMap 密码信息包含oldPassword和newPassword字段
* @return 修改结果
*/
@PutMapping("/admin/users/{userId}/password")
public ResponseEntity<?> changeUserPassword(@PathVariable Long userId,
@RequestBody Map<String, Object> passwordMap) {
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// 从Map中获取密码信息
String oldPassword = (String) passwordMap.get("oldPassword");
String newPassword = (String) passwordMap.get("newPassword");
// 参数验证
if (oldPassword == null || oldPassword.isEmpty()) {
return ResponseEntity.error(400, "旧密码不能为空");
}
if (newPassword == null || newPassword.isEmpty()) {
return ResponseEntity.error(400, "新密码不能为空");
}
// TODO: 实现修改密码逻辑
// 1. 检查用户是否存在
// 2. 验证旧密码
// 3. 更新新密码
// 模拟修改密码成功
return ResponseEntity.success("修改密码成功");
}
/**
* 更新用户状态
*
* @param userId 用户ID
* @param statusMap 状态信息包含status字段
* @return 更新结果
*/
@PutMapping("/admin/users/{userId}/status")
public ResponseEntity<?> updateUserStatus(@PathVariable Long userId, @RequestBody Map<String, Object> statusMap) {
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// 从Map中获取状态信息
Integer status = (Integer) statusMap.get("status");
// 参数验证
if (status == null) {
return ResponseEntity.error(400, "状态不能为空");
}
if (status != 0 && status != 1) {
return ResponseEntity.error(400, "状态值只能是0或1");
}
// TODO: 实现更新状态逻辑
// 1. 检查用户是否存在
// 2. 更新用户状态
// 模拟更新状态成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("id", userId);
responseData.put("status", status);
return ResponseEntity.success("更新用户状态成功", responseData);
}
/**
* 批量删除用户
*
* @param deleteMap 批量删除信息包含ids字段
* @return 删除结果
*/
@DeleteMapping("/admin/users/batch")
public ResponseEntity<?> batchDeleteUsers(@RequestBody Map<String, Object> deleteMap) {
// 从Map中获取删除信息
List<Long> userIds = (List<Long>) deleteMap.get("ids");
// 参数验证
if (userIds == null || userIds.isEmpty()) {
return ResponseEntity.error(400, "用户ID列表不能为空");
}
// TODO: 实现批量删除逻辑
// 1. 检查用户是否存在
// 2. 批量删除用户
// 模拟批量删除成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("deletedCount", userIds.size());
responseData.put("deletedIds", userIds);
return ResponseEntity.success("批量删除用户成功", responseData);
}
}

View File

@@ -1,87 +0,0 @@
package icu.sunway.ai_spring_example.Controllers;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 认证控制器
* 提供登录和注册接口
*/
@RestController
public class Auth {
/**
* 登录接口
*
* @param loginMap 登录请求参数包含username和password字段
* @return 登录成功返回 JWT 令牌,失败返回错误信息
*/
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, Object> loginMap) {
// 从Map中获取登录参数
String username = (String) loginMap.get("username");
String password = (String) loginMap.get("password");
// 参数验证
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
return ResponseEntity.error(400, "用户名和密码不能为空");
}
// TODO: 实现实际的登录逻辑
// 1. 根据用户名查询用户
// 2. 验证密码
// 3. 生成JWT令牌
// 模拟登录成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("token", "mock-jwt-token-123456");
responseData.put("username", username);
responseData.put("role", "user");
return ResponseEntity.success("登录成功", responseData);
}
/**
* 注册接口
*
* @param registerMap 注册请求参数包含username、password、email等字段
* @return 注册成功返回成功信息,失败返回错误信息
*/
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody Map<String, Object> registerMap) {
// 从Map中获取注册参数
String username = (String) registerMap.get("username");
String password = (String) registerMap.get("password");
String email = (String) registerMap.get("email");
// 参数验证
if (username == null || username.isEmpty()) {
return ResponseEntity.error(400, "用户名不能为空");
}
if (password == null || password.isEmpty()) {
return ResponseEntity.error(400, "密码不能为空");
}
if (email == null || email.isEmpty()) {
return ResponseEntity.error(400, "邮箱不能为空");
}
// TODO: 实现实际的注册逻辑
// 1. 检查用户名是否已存在
// 2. 检查邮箱是否已存在
// 3. 密码加密
// 4. 保存用户信息
// 模拟注册成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("username", username);
responseData.put("email", email);
return ResponseEntity.success("注册成功", responseData);
}
}

View File

@@ -1,193 +0,0 @@
package icu.sunway.ai_spring_example.Controllers;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
/**
* 课程控制器
* 提供课程展示和报名相关的功能
*/
@RestController
public class Course {
/**
* 获取课程列表
*
* @param params 查询参数包含page、size、name、category、status等字段
* @return 课程列表数据
*/
@GetMapping("/courses")
public ResponseEntity<?> getCourseList(@RequestParam Map<String, Object> params) {
// 从Map中获取查询参数
Integer page = Integer.valueOf((String) params.getOrDefault("page", "1"));
Integer size = Integer.valueOf((String) params.getOrDefault("size", "10"));
String name = (String) params.get("name");
String category = (String) params.get("category");
Integer status = params.get("status") != null ? Integer.valueOf((String) params.get("status")) : null;
// TODO: 实现查询逻辑
// 1. 根据条件查询课程列表
// 2. 处理分页
// 模拟返回课程列表
List<Map<String, Object>> courseList = List.of(
Map.of("id", 1, "name", "Java基础课程", "category", "编程", "teacher", "张老师", "price", 99.00, "students", 150,
"status", 1, "description", "Java入门到精通课程"),
Map.of("id", 2, "name", "Python数据分析", "category", "数据科学", "teacher", "李老师", "price", 199.00, "students",
80, "status", 1, "description", "Python数据分析实战课程"));
Map<String, Object> responseData = new HashMap<>();
responseData.put("list", courseList);
responseData.put("total", 2);
responseData.put("page", page);
responseData.put("size", size);
return ResponseEntity.success("获取课程列表成功", responseData);
}
/**
* 获取课程详情
*
* @param courseId 课程ID
* @return 课程详细信息
*/
@GetMapping("/courses/{courseId}")
public ResponseEntity<?> getCourseDetail(@PathVariable Long courseId) {
// 参数验证
if (courseId == null) {
return ResponseEntity.error(400, "课程ID不能为空");
}
// TODO: 实现查询逻辑
// 1. 根据课程ID查询课程信息
// 2. 查询课程章节、评价等关联信息
// 模拟返回课程详情
Map<String, Object> courseInfo = Map.of(
"id", courseId, "name", "Java基础课程", "category", "编程", "teacher", "张老师", "price", 99.00,
"students", 150, "status", 1, "description", "Java入门到精通课程",
"chapters", List.of(
Map.of("id", 1, "name", "Java简介", "duration", "30分钟"),
Map.of("id", 2, "name", "变量与数据类型", "duration", "45分钟")),
"ratings", Map.of("avg", 4.8, "count", 50));
return ResponseEntity.success("获取课程详情成功", courseInfo);
}
/**
* 课程报名
*
* @param enrollMap 报名信息包含courseId、userId等字段
* @return 报名结果
*/
@PostMapping("/courses/enroll")
public ResponseEntity<?> enrollCourse(@RequestBody Map<String, Object> enrollMap) {
// 从Map中获取报名信息
Long courseId = (Long) enrollMap.get("courseId");
Long userId = (Long) enrollMap.get("userId");
// 参数验证
if (courseId == null) {
return ResponseEntity.error(400, "课程ID不能为空");
}
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// TODO: 实现报名逻辑
// 1. 检查课程是否存在且可报名
// 2. 检查用户是否已报名该课程
// 3. 记录报名信息
// 4. 更新课程报名人数
// 模拟报名成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("courseId", courseId);
responseData.put("userId", userId);
responseData.put("enrollTime", "2023-05-20 10:00:00");
return ResponseEntity.success("课程报名成功", responseData);
}
/**
* 获取用户报名的课程列表
*
* @param params 查询参数包含userId、page、size等字段
* @return 用户报名的课程列表
*/
@GetMapping("/user/courses")
public ResponseEntity<?> getUserCourses(@RequestParam Map<String, Object> params) {
// 从Map中获取查询参数
Long userId = Long.valueOf((String) params.get("userId"));
Integer page = Integer.valueOf((String) params.getOrDefault("page", "1"));
Integer size = Integer.valueOf((String) params.getOrDefault("size", "10"));
// 参数验证
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// TODO: 实现查询逻辑
// 1. 根据用户ID查询已报名课程
// 2. 处理分页
// 模拟返回用户报名的课程列表
List<Map<String, Object>> userCourseList = List.of(
Map.of("id", 1, "courseId", 1, "courseName", "Java基础课程", "teacher", "张老师", "enrollTime",
"2023-05-20 10:00:00", "status", "learning"),
Map.of("id", 2, "courseId", 2, "courseName", "Python数据分析", "teacher", "李老师", "enrollTime",
"2023-05-15 14:30:00", "status", "completed"));
Map<String, Object> responseData = new HashMap<>();
responseData.put("list", userCourseList);
responseData.put("total", 2);
responseData.put("page", page);
responseData.put("size", size);
return ResponseEntity.success("获取用户课程列表成功", responseData);
}
/**
* 取消课程报名
*
* @param courseId 课程ID
* @param params 参数包含userId字段
* @return 取消报名结果
*/
@DeleteMapping("/courses/{courseId}/enroll")
public ResponseEntity<?> cancelEnroll(@PathVariable Long courseId, @RequestParam Map<String, Object> params) {
// 从Map中获取用户ID
Long userId = Long.valueOf((String) params.get("userId"));
// 参数验证
if (courseId == null) {
return ResponseEntity.error(400, "课程ID不能为空");
}
if (userId == null) {
return ResponseEntity.error(400, "用户ID不能为空");
}
// TODO: 实现取消报名逻辑
// 1. 检查报名记录是否存在
// 2. 删除报名记录
// 3. 更新课程报名人数
// 模拟取消报名成功
Map<String, Object> responseData = new HashMap<>();
responseData.put("courseId", courseId);
responseData.put("userId", userId);
return ResponseEntity.success("取消课程报名成功", responseData);
}
}

View File

@@ -0,0 +1,38 @@
package icu.sunway.ai_spring_example.Controllers;
import icu.sunway.ai_spring_example.Common.Exception.BusinessException;
import icu.sunway.ai_spring_example.Common.Response.ResponseEntity;
import icu.sunway.ai_spring_example.Common.Utils.OSSUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Map;
@RestController
@RequestMapping("/api/upload")
public class UploadController {
@Autowired
private OSSUtil ossUtil;
// 方式1生成预签名 URL 给前端直传
@GetMapping("/presigned-url")
public ResponseEntity getPresignedUrl(@RequestParam String fileName) {
String presignedUrl = ossUtil.generatePresignedUrl(fileName);
String fileUrl = ossUtil.getFileUrl(fileName);
return ResponseEntity.success(Map.of("uploadUrl", presignedUrl, "fileUrl", fileUrl));
}
// 方式2后端接收文件并上传
@PostMapping("/file")
public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) {
try {
String fileUrl = ossUtil.uploadFile(file.getOriginalFilename(), file.getInputStream());
return ResponseEntity.success(Map.of("msg", "上传成功", "fileUrl", fileUrl));
} catch (IOException e) {
return ResponseEntity.error("文件上传失败");
}
}
}

View File

@@ -105,12 +105,14 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
// 4. 重新插入新规格 // 4. 重新插入新规格
List<ProductSku> skuList = new ArrayList<>(); List<ProductSku> skuList = new ArrayList<>();
if(productDTO.getSkuList() != null){
for (ProductSkuDTO skuDTO : productDTO.getSkuList()) { for (ProductSkuDTO skuDTO : productDTO.getSkuList()) {
ProductSku sku = new ProductSku(); ProductSku sku = new ProductSku();
BeanUtils.copyProperties(skuDTO, sku); BeanUtils.copyProperties(skuDTO, sku);
sku.setProductId(product.getId()); sku.setProductId(product.getId());
skuList.add(sku); skuList.add(sku);
} }
}
if (!skuList.isEmpty()) { if (!skuList.isEmpty()) {
productSkuMapper.batchInsert(skuList); productSkuMapper.batchInsert(skuList);
} }

View File

@@ -36,3 +36,11 @@ zhiwei:
user-ttl: 7200000 user-ttl: 7200000
# 设置前端传递过来的令牌名称 # 设置前端传递过来的令牌名称
user-token-name: token user-token-name: token
aliyun:
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
accessKeyId: LTAI5tEajTmhB2RCHuUwo3QW
accessKeySecret: G1x7ZoS8PkjjRNtoPMoLaJz5T6VGep
bucketName: puzv
urlPrefix: https://puzv.oss-cn-hangzhou.aliyuncs.com/