diff --git a/home-service/pom.xml b/home-service/pom.xml
index 6bc88e1..808bfdd 100644
--- a/home-service/pom.xml
+++ b/home-service/pom.xml
@@ -38,6 +38,13 @@
cn.hutool
hutool-crypto
+
+
+
+ ws.schild
+ jave-all-deps
+ 3.5.0
+
diff --git a/home-service/src/main/java/com/cxx/home/config/AListProperties.java b/home-service/src/main/java/com/cxx/home/config/AListProperties.java
new file mode 100644
index 0000000..8bdad33
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/config/AListProperties.java
@@ -0,0 +1,15 @@
+package com.cxx.home.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "alist")
+@Getter
+@Setter
+public class AListProperties {
+ private String username;
+ private String password;
+}
diff --git a/home-service/src/main/java/com/cxx/home/config/RestClientConfig.java b/home-service/src/main/java/com/cxx/home/config/RestClientConfig.java
new file mode 100644
index 0000000..7ebb4ec
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/config/RestClientConfig.java
@@ -0,0 +1,25 @@
+package com.cxx.home.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.client.RestClient;
+
+@Configuration
+public class RestClientConfig {
+ final String baseUrl = "http://192.168.1.7:5244/";
+
+ @Bean
+ public RestClient aListClient() {
+
+ return RestClient.builder()
+ // 基础RURL
+ .baseUrl(baseUrl)
+ // 默认请求头 请求体为JSON格式
+ .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ // 默认请求头 响应体为JSON格式
+ .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
+ .build();
+ }
+}
diff --git a/home-service/src/main/java/com/cxx/home/controller/AuthController.java b/home-service/src/main/java/com/cxx/home/controller/AuthController.java
new file mode 100644
index 0000000..08d95da
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/controller/AuthController.java
@@ -0,0 +1,28 @@
+package com.cxx.home.controller;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.cxx.common.ReadView;
+import com.cxx.common.WriteView;
+import com.cxx.common.dto.StatsChartDto;
+import com.cxx.home.dto.anniversary.AnniversaryDto;
+import com.cxx.home.service.AnniversaryService;
+import com.fasterxml.jackson.annotation.JsonView;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/auth")
+@Tag(name = "认证")
+public class AuthController {
+ @Operation(summary = "查询在线人数")
+ @GetMapping("/online")
+ public List queryOnline() {
+ return StpUtil.searchSessionId("", 0, -1, true);
+ }
+}
+
\ No newline at end of file
diff --git a/home-service/src/main/java/com/cxx/home/controller/FileController.java b/home-service/src/main/java/com/cxx/home/controller/FileController.java
index 8717e38..5a84566 100644
--- a/home-service/src/main/java/com/cxx/home/controller/FileController.java
+++ b/home-service/src/main/java/com/cxx/home/controller/FileController.java
@@ -4,6 +4,7 @@ import com.cxx.home.dto.file.ChunkInfoDto;
import com.cxx.home.dto.file.ChunkResultDto;
import com.cxx.home.dto.file.FileInfoDto;
import com.cxx.home.dto.file.FileRecordDto;
+import com.cxx.home.service.AListService;
import com.cxx.home.service.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -20,6 +21,9 @@ public class FileController {
@Resource
private FileService fileService;
+ @Resource
+ private AListService aListService;
+
@Operation(summary = "上传文件")
@PostMapping(value = "", headers = "content-type=multipart/form-data")
public FileRecordDto uploadFile(@RequestPart("file") MultipartFile file,
@@ -28,6 +32,13 @@ public class FileController {
return fileService.uploadFile(file, path, md5);
}
+ @Operation(summary = "上传aList文件")
+ @PostMapping(value = "/alist", consumes = "multipart/form-data")
+ public void uploadAListFile(@RequestParam("file") MultipartFile file,
+ @RequestParam("filePath") String filePath) {
+ aListService.uploadFile(file, filePath);
+ }
+
@Operation(summary = "上传文件块")
@PostMapping("/chunk")
public Boolean uploadChunk(ChunkInfoDto chunkInfo,
diff --git a/home-service/src/main/java/com/cxx/home/dto/alist/AListLogin.java b/home-service/src/main/java/com/cxx/home/dto/alist/AListLogin.java
new file mode 100644
index 0000000..371a280
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/dto/alist/AListLogin.java
@@ -0,0 +1,11 @@
+package com.cxx.home.dto.alist;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class AListLogin {
+ private String username;
+ private String password;
+}
diff --git a/home-service/src/main/java/com/cxx/home/service/AListService.java b/home-service/src/main/java/com/cxx/home/service/AListService.java
new file mode 100644
index 0000000..465ca28
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/service/AListService.java
@@ -0,0 +1,7 @@
+package com.cxx.home.service;
+
+import org.springframework.web.multipart.MultipartFile;
+
+public interface AListService {
+ void uploadFile(MultipartFile file, String filePath);
+}
diff --git a/home-service/src/main/java/com/cxx/home/service/impl/AListServiceImpl.java b/home-service/src/main/java/com/cxx/home/service/impl/AListServiceImpl.java
new file mode 100644
index 0000000..80a90e2
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/service/impl/AListServiceImpl.java
@@ -0,0 +1,99 @@
+package com.cxx.home.service.impl;
+
+import com.cxx.framework.web.CustomException;
+import com.cxx.home.config.AListProperties;
+import com.cxx.home.dto.alist.AListLogin;
+import com.cxx.home.service.AListService;
+import com.cxx.home.util.VideoCompressor;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestClient;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@Slf4j
+public class AListServiceImpl implements AListService {
+
+ @Resource
+ private RestClient aListClient;
+
+ @Resource
+ private AListProperties aListProperties;
+
+ private String getAccessToken() {
+ AListLogin aListLogin = new AListLogin();
+ aListLogin.setUsername(aListProperties.getUsername());
+ aListLogin.setPassword(aListProperties.getPassword());
+
+ Map response = aListClient.post()
+ .uri("api/auth/login")
+ .body(aListLogin)
+ .retrieve()
+ .body(Map.class);
+
+ if (response == null) {
+ throw new CustomException("AList登录失败 请联系管理员");
+ }
+
+ HashMap data = (HashMap) response.get("data");
+ return (String) data.get("token");
+ }
+
+ @Override
+ public void uploadFile(MultipartFile file, String filePath) {
+ try {
+ String token = getAccessToken();
+
+ MultiValueMap body = new LinkedMultiValueMap<>();
+
+ File tempFile = convertMultipartFileToFile(file);
+ log.info("临时文件地址:{}", tempFile.toPath());
+
+ log.info("开始压缩文件:{}", tempFile.getName());
+ File compressFile = VideoCompressor.compressTo720p(tempFile);
+ log.info("完成压缩文件:{}", tempFile.getName());
+
+ log.info("压缩文件地址:{}", tempFile.toPath());
+
+ body.add("file", new FileSystemResource(compressFile));
+
+ aListClient.put()
+ .uri("api/fs/form")
+ .header(HttpHeaders.AUTHORIZATION, token)
+ .header("File-Path", filePath)
+ .contentType(MediaType.MULTIPART_FORM_DATA)
+ .body(body)
+ .retrieve()
+ .body(Map.class);
+
+ Files.deleteIfExists(compressFile.toPath());
+ Files.deleteIfExists(tempFile.toPath());
+ } catch (Exception e) {
+ log.error("文件上传失败{}", e.getMessage());
+ throw new RuntimeException("文件上传失败", e);
+ }
+ }
+
+ private File convertMultipartFileToFile(MultipartFile file) throws IOException {
+ String suffix = (file.getOriginalFilename() != null && file.getOriginalFilename().contains("."))
+ ? file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."))
+ : ".tmp";
+
+ Path tempFile = Files.createTempFile(System.currentTimeMillis() + "", suffix);
+ file.transferTo(tempFile);
+ return tempFile.toFile();
+ }
+}
diff --git a/home-service/src/main/java/com/cxx/home/service/impl/FoodServiceImpl.java b/home-service/src/main/java/com/cxx/home/service/impl/FoodServiceImpl.java
index 2cbd97e..5315dd5 100644
--- a/home-service/src/main/java/com/cxx/home/service/impl/FoodServiceImpl.java
+++ b/home-service/src/main/java/com/cxx/home/service/impl/FoodServiceImpl.java
@@ -1,5 +1,6 @@
package com.cxx.home.service.impl;
+import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -90,12 +91,21 @@ public class FoodServiceImpl implements FoodService {
public Boolean addFoodRecord(FoodRecordDto foodRecordDto) {
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
FoodRecipe foodRecipe = foodRecipeMapper.selectOne(queryWrapper.eq(FoodRecipe::getName, foodRecordDto.getName()));
+ long recipeId;
if (foodRecipe == null) {
- throw new CustomException("该菜谱不存在");
+ FoodRecipe newRecipe = new FoodRecipe();
+ newRecipe.setName(foodRecordDto.getName());
+ newRecipe.setCategory("其他");
+ newRecipe.setRecommendRate(0);
+ newRecipe.setRemark("");
+ foodRecipeMapper.insert(newRecipe);
+ recipeId = newRecipe.getId();
+ } else {
+ recipeId = foodRecipe.getId();
}
FoodRecord foodRecord = new FoodRecord();
- foodRecord.setFoodId(foodRecipe.getId());
+ foodRecord.setFoodId(recipeId);
BeanUtils.copyProperties(foodRecordDto, foodRecord);
foodRecordMapper.insert(foodRecord);
diff --git a/home-service/src/main/java/com/cxx/home/util/VideoCompressor.java b/home-service/src/main/java/com/cxx/home/util/VideoCompressor.java
new file mode 100644
index 0000000..2881cd7
--- /dev/null
+++ b/home-service/src/main/java/com/cxx/home/util/VideoCompressor.java
@@ -0,0 +1,153 @@
+package com.cxx.home.util;
+
+import lombok.extern.slf4j.Slf4j;
+import ws.schild.jave.Encoder;
+import ws.schild.jave.MultimediaObject;
+import ws.schild.jave.encode.AudioAttributes;
+import ws.schild.jave.encode.EncodingAttributes;
+import ws.schild.jave.encode.VideoAttributes;
+
+import java.io.File;
+
+/**
+ * 视频码率压缩工具类
+ * 只压缩码率,保持原始分辨率和宽高比
+ */
+@Slf4j
+public class VideoCompressor {
+
+ // 支持的视频格式
+ private static final String[] SUPPORTED_FORMATS = {
+ ".mp4", ".avi", ".mov", ".mkv", ".flv",
+ ".wmv", ".webm", ".mpeg", ".mpg", ".3gp"
+ };
+
+ // 码率压缩参数
+ private static final int TARGET_VIDEO_BITRATE = 1500000; // 1.5 Mbps
+ private static final int TARGET_AUDIO_BITRATE = 128000; // 128 kbps
+ private static final String VIDEO_CODEC = "libx264";
+ private static final String AUDIO_CODEC = "aac";
+ private static final String OUTPUT_FORMAT = "mp4";
+ private static final String OUTPUT_SUFFIX = "_compressed.mp4";
+
+ // 码率判断阈值
+ private static final int MAX_BITRATE_THRESHOLD = 2500000; // 2.5 Mbps
+ private static final int MIN_BITRATE_THRESHOLD = 1000000; // 1 Mbps
+
+ /**
+ * 压缩视频码率,保持原始分辨率和宽高比
+ *
+ * @param inputFile 输入视频文件
+ * @return 压缩后的视频文件,如果不需要压缩则返回原文件
+ * @throws Exception 压缩过程中可能出现的异常
+ */
+ public static File compressTo720p(File inputFile) throws Exception {
+ if (!isSupportedVideoFile(inputFile)) {
+ log.info("文件格式不支持或文件不存在,直接返回源文件");
+ return inputFile;
+ }
+
+ // 判断是否需要压缩码率
+ if (!needBitrateCompression(inputFile)) {
+ return inputFile;
+ }
+
+ File outputFile = createOutputFile(inputFile);
+ Encoder encoder = new Encoder();
+
+ // 配置编码参数
+ EncodingAttributes encodingAttributes = createBitrateEncodingAttributes();
+
+ log.info("开始压缩视频: {} -> {}", inputFile.getName(), outputFile.getName());
+ encoder.encode(new MultimediaObject(inputFile), outputFile, encodingAttributes);
+ log.info("视频压缩完成: {}", outputFile.getName());
+
+ return outputFile;
+ }
+
+ /**
+ * 判断视频是否需要压缩码率
+ */
+ public static boolean needBitrateCompression(File videoFile) throws Exception {
+ MultimediaObject media = new MultimediaObject(videoFile);
+
+ // 获取视频基本信息
+ double fileSizeMB = videoFile.length() / 1048576.0;
+ double duration = media.getInfo().getDuration() / 1000.0;
+ int originalBitrate = media.getInfo().getVideo().getBitRate();
+
+ // 获取视频分辨率
+ int width = media.getInfo().getVideo().getSize().getWidth();
+ int height = media.getInfo().getVideo().getSize().getHeight();
+
+ log.info("视频分析: 分辨率 {}x{}, 大小 {}MB, 时长 {}秒, 码率 {}bps", width, height, fileSizeMB, duration, originalBitrate);
+
+ // 如果原始码率超过阈值,就需要压缩
+ boolean needsCompression = originalBitrate > MAX_BITRATE_THRESHOLD;
+
+ if (needsCompression) {
+ log.info("视频码率({}bps)超过阈值,需要压缩", originalBitrate);
+ } else {
+ log.info("视频码率({}bps)在合理范围内,无需压缩", originalBitrate);
+ }
+
+ return needsCompression;
+ }
+
+ /**
+ * 创建码率压缩编码配置
+ */
+ private static EncodingAttributes createBitrateEncodingAttributes() {
+ // 音频配置
+ AudioAttributes audio = new AudioAttributes();
+ audio.setCodec(AUDIO_CODEC);
+ audio.setBitRate(TARGET_AUDIO_BITRATE);
+
+ // 视频配置
+ VideoAttributes video = new VideoAttributes();
+ video.setCodec(VIDEO_CODEC);
+ video.setBitRate(TARGET_VIDEO_BITRATE);
+
+ // 编码设置
+ EncodingAttributes attributes = new EncodingAttributes();
+ attributes.setOutputFormat(OUTPUT_FORMAT);
+ attributes.setAudioAttributes(audio);
+ attributes.setVideoAttributes(video);
+
+ return attributes;
+ }
+
+ /**
+ * 检查文件是否为支持的视频格式
+ */
+ private static boolean isSupportedVideoFile(File file) {
+ if (file == null || !file.exists()) {
+ return false;
+ }
+
+ String filename = file.getName().toLowerCase();
+ for (String format : SUPPORTED_FORMATS) {
+ if (filename.endsWith(format)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 创建输出文件对象
+ */
+ private static File createOutputFile(File inputFile) {
+ String baseName = getFileBaseName(inputFile.getName());
+ String outputFilename = baseName + OUTPUT_SUFFIX;
+ return new File(inputFile.getParent(), outputFilename);
+ }
+
+ /**
+ * 获取文件名(不含扩展名)
+ */
+ private static String getFileBaseName(String filename) {
+ int dotIndex = filename.lastIndexOf('.');
+ return (dotIndex == -1) ? filename : filename.substring(0, dotIndex);
+ }
+}
\ No newline at end of file
diff --git a/home-service/src/main/resources/application.yml b/home-service/src/main/resources/application.yml
index d422948..030daba 100644
--- a/home-service/src/main/resources/application.yml
+++ b/home-service/src/main/resources/application.yml
@@ -6,7 +6,7 @@ spring:
name: @project.artifactId@
version: @project.version@
profiles:
- active: dev
+ active: docker
mail:
host: smtp.163.com
port: 465
@@ -36,3 +36,7 @@ web-starter:
aes:
secret-key: "sG5p7t9wBdKnPqRsUvYx2D5F8H9JmQ=2"
+
+alist:
+ username: admin
+ password: 19940822Cxx
diff --git a/home-service/src/main/resources/mapper/FoodMapper.xml b/home-service/src/main/resources/mapper/FoodMapper.xml
index f681063..dff3f48 100644
--- a/home-service/src/main/resources/mapper/FoodMapper.xml
+++ b/home-service/src/main/resources/mapper/FoodMapper.xml
@@ -106,16 +106,17 @@