feat:增加登录和文件模块
This commit is contained in:
65
src/main/java/com/cxx/food/controller/FileController.java
Normal file
65
src/main/java/com/cxx/food/controller/FileController.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.cxx.food.controller;
|
||||
|
||||
import com.cxx.food.dto.file.ChunkInfoDto;
|
||||
import com.cxx.food.dto.file.ChunkResultDto;
|
||||
import com.cxx.food.dto.file.FileInfoDto;
|
||||
import com.cxx.food.dto.file.FileRecordDto;
|
||||
import com.cxx.food.service.FileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
@Tag(name = "文件管理")
|
||||
public class FileController {
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@Operation(summary = "上传文件")
|
||||
@PostMapping(value = "", headers = "content-type=multipart/form-data")
|
||||
public FileRecordDto uploadFile(@RequestPart("file") MultipartFile file,
|
||||
@RequestParam("path") String path,
|
||||
@RequestParam("md5") String md5) {
|
||||
return fileService.uploadFile(file, path, md5);
|
||||
}
|
||||
|
||||
@Operation(summary = "上传文件块")
|
||||
@PostMapping("/chunk")
|
||||
public Boolean uploadChunk(ChunkInfoDto chunkInfo,
|
||||
String path) {
|
||||
return fileService.uploadChunk(chunkInfo, path);
|
||||
}
|
||||
|
||||
@Operation(summary = "验证当前文件块是否上传")
|
||||
@GetMapping("/chunk")
|
||||
public ChunkResultDto checkChunk(@RequestParam String identifier,
|
||||
@RequestParam String filename,
|
||||
@RequestParam String path) {
|
||||
return fileService.checkChunk(identifier, filename, path);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除当前已上传的文件块")
|
||||
@DeleteMapping("/chunk")
|
||||
public Boolean deleteChunk(@RequestParam String identifier,
|
||||
@RequestParam String path) {
|
||||
return fileService.deleteChunk(identifier, path);
|
||||
}
|
||||
|
||||
@Operation(summary = "合并文件")
|
||||
@PostMapping("/chunk/merge")
|
||||
public FileRecordDto mergeFile(@RequestParam String filename,
|
||||
@RequestParam String path) {
|
||||
return fileService.mergeFile(filename, path);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取文件夹目录")
|
||||
@GetMapping("/catalog")
|
||||
public List<FileInfoDto> getFolderInfo(@RequestParam String folderName) {
|
||||
return fileService.getFolderInfo(folderName);
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,7 @@ import com.cxx.food.service.SessionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/session")
|
||||
@@ -21,8 +18,14 @@ public class SessionController {
|
||||
@Operation(summary = "创建会话")
|
||||
@PostMapping("")
|
||||
@SaIgnore
|
||||
public SessionDto createSession(@RequestParam("name") String name,
|
||||
@RequestParam("password") String password) {
|
||||
return sessionService.create(name, password);
|
||||
public SessionDto create(@RequestParam("username") String username,
|
||||
@RequestParam("password") String password) {
|
||||
return sessionService.create(username, password);
|
||||
}
|
||||
|
||||
@Operation(summary = "退出会话")
|
||||
@DeleteMapping("")
|
||||
public Boolean abort(@RequestParam("username") String username) {
|
||||
return sessionService.abort(username);
|
||||
}
|
||||
}
|
||||
27
src/main/java/com/cxx/food/controller/UserController.java
Normal file
27
src/main/java/com/cxx/food/controller/UserController.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.cxx.food.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.cxx.food.dto.ReadView;
|
||||
import com.cxx.food.dto.UserDto;
|
||||
import com.cxx.food.dto.WriteView;
|
||||
import com.cxx.food.service.UserService;
|
||||
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.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Tag(name = "用户管理")
|
||||
public class UserController {
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Operation(summary = "注册用户")
|
||||
@PostMapping("")
|
||||
@SaIgnore
|
||||
public Boolean createSession(@JsonView(WriteView.class) @RequestBody UserDto userDto) {
|
||||
return userService.register(userDto);
|
||||
}
|
||||
}
|
||||
@@ -15,22 +15,24 @@ public interface FoodDao {
|
||||
IPage<SummaryDto> querySummaryByPage(IPage<SummaryDto> page,
|
||||
@Param("query") FoodQueryVo foodQueryVo);
|
||||
|
||||
List<SummaryDto> querySummary(@Param("query") FoodQueryVo foodQueryVo);
|
||||
List<SummaryDto> querySummary(@Param("query") FoodQueryVo foodQueryVo,
|
||||
@Param("user") String user);
|
||||
|
||||
List<RecordDto> queryRecordById(@Param("id") Long id);
|
||||
|
||||
List<RecordDto> queryRecord(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
@Param("endDate") String endDate,
|
||||
@Param("user") String user);
|
||||
|
||||
StatsDto queryStats();
|
||||
StatsDto queryStats(@Param("user") String user);
|
||||
|
||||
List<StatsChartDto> queryRecordStats();
|
||||
List<StatsChartDto> queryRecordStats(@Param("user") String user);
|
||||
|
||||
List<StatsChartDto> queryCategoryStats();
|
||||
List<StatsChartDto> queryCategoryStats(@Param("user") String user);
|
||||
|
||||
List<StatsChartDto> queryRankStats();
|
||||
List<StatsChartDto> queryRankStats(@Param("user") String user);
|
||||
|
||||
List<String> queryCategory();
|
||||
List<String> queryCategory(@Param("user") String user);
|
||||
|
||||
void insertMaterial(@Param("foodId") Long foodId,
|
||||
@Param("list") List<MaterialDto> list);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cxx.food.dto;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -10,5 +11,8 @@ import lombok.Setter;
|
||||
public class SessionDto {
|
||||
@Schema(description = "令牌")
|
||||
private SaTokenInfo saToken;
|
||||
|
||||
@Schema(description = "用户信息")
|
||||
private UserDto userInfo;
|
||||
}
|
||||
|
||||
|
||||
73
src/main/java/com/cxx/food/dto/UserDto.java
Normal file
73
src/main/java/com/cxx/food/dto/UserDto.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com.cxx.food.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@JsonView(PlainView.class)
|
||||
public class UserDto {
|
||||
@Schema(description = "id")
|
||||
@JsonView(ReadView.class)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名称")
|
||||
@NotBlank(message = "用户名称不能为空")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@JsonView(WriteView.class)
|
||||
private String password;
|
||||
|
||||
// @Schema(description = "性别")
|
||||
// @JsonView(ReadView.class)
|
||||
// private Integer gender;
|
||||
//
|
||||
// @Schema(description = "身份证号")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String idNumber;
|
||||
//
|
||||
// @Schema(description = "手机号码")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String phoneNumber;
|
||||
//
|
||||
// @Schema(description = "电子邮件")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String email;
|
||||
//
|
||||
// @Schema(description = "生日")
|
||||
// @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
// private Date birthDate;
|
||||
|
||||
@Schema(description = "头像")
|
||||
@JsonView(ReadView.class)
|
||||
private String avatar;
|
||||
|
||||
// @Schema(description = "地区")
|
||||
// @JsonView(ReadView.class)
|
||||
// private List<String> area;
|
||||
//
|
||||
// @Schema(description = "地址")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String address;
|
||||
//
|
||||
// @Schema(description = "职业")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String job;
|
||||
//
|
||||
// @Schema(description = "标签")
|
||||
// @JsonView(ReadView.class)
|
||||
// private List<String> tags;
|
||||
//
|
||||
// @Schema(description = "描述")
|
||||
// @JsonView(ReadView.class)
|
||||
// private String description;
|
||||
}
|
||||
22
src/main/java/com/cxx/food/dto/file/ChunkInfoDto.java
Normal file
22
src/main/java/com/cxx/food/dto/file/ChunkInfoDto.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.cxx.food.dto.file;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ChunkInfoDto {
|
||||
/**
|
||||
* 当前文件块,从1开始
|
||||
*/
|
||||
private Integer chunkNumber;
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String filename;
|
||||
/**
|
||||
* 块内容
|
||||
*/
|
||||
private transient MultipartFile multipartFile;
|
||||
}
|
||||
20
src/main/java/com/cxx/food/dto/file/ChunkResultDto.java
Normal file
20
src/main/java/com/cxx/food/dto/file/ChunkResultDto.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.cxx.food.dto.file;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ChunkResultDto {
|
||||
/**
|
||||
* 是否跳过上传(已上传的可以直接跳过,达到秒传的效果)
|
||||
*/
|
||||
private boolean isSkipUpload;
|
||||
|
||||
/**
|
||||
* 已经上传的文件块编号,可以跳过,断点续传
|
||||
*/
|
||||
private List<Integer> uploadedChunkList;
|
||||
}
|
||||
17
src/main/java/com/cxx/food/dto/file/FileInfoDto.java
Normal file
17
src/main/java/com/cxx/food/dto/file/FileInfoDto.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.cxx.food.dto.file;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class FileInfoDto {
|
||||
private String name;
|
||||
private String type;
|
||||
private String size;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createDate;
|
||||
}
|
||||
14
src/main/java/com/cxx/food/dto/file/FileRecordDto.java
Normal file
14
src/main/java/com/cxx/food/dto/file/FileRecordDto.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.cxx.food.dto.file;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class FileRecordDto {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String md5;
|
||||
private String path;
|
||||
private String url;
|
||||
}
|
||||
24
src/main/java/com/cxx/food/entity/FileRecord.java
Normal file
24
src/main/java/com/cxx/food/entity/FileRecord.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.cxx.food.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.cxx.framework.data.AbstractEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("file_record")
|
||||
public class FileRecord extends AbstractEntity {
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@TableField("md5")
|
||||
private String md5;
|
||||
|
||||
@TableField("path")
|
||||
private String path;
|
||||
|
||||
@TableField("url")
|
||||
private String url;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class Record extends AbstractIdEntity {
|
||||
private Long foodId;
|
||||
|
||||
@TableField("person")
|
||||
private Long person;
|
||||
private String person;
|
||||
|
||||
@TableField("date")
|
||||
private Date date;
|
||||
|
||||
9
src/main/java/com/cxx/food/mapper/FileRecordMapper.java
Normal file
9
src/main/java/com/cxx/food/mapper/FileRecordMapper.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.cxx.food.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.cxx.food.entity.FileRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FileRecordMapper extends BaseMapper<FileRecord> {
|
||||
}
|
||||
48
src/main/java/com/cxx/food/service/FileService.java
Normal file
48
src/main/java/com/cxx/food/service/FileService.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.cxx.food.service;
|
||||
|
||||
import com.cxx.food.dto.file.ChunkInfoDto;
|
||||
import com.cxx.food.dto.file.ChunkResultDto;
|
||||
import com.cxx.food.dto.file.FileInfoDto;
|
||||
import com.cxx.food.dto.file.FileRecordDto;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface FileService {
|
||||
FileRecordDto uploadFile(MultipartFile file, String path, String md5);
|
||||
|
||||
/**
|
||||
* 上传文件块
|
||||
* @param chunkInfo 文件块信息
|
||||
* @param path 上传路径
|
||||
* @return 是否上传成功
|
||||
*/
|
||||
Boolean uploadChunk(ChunkInfoDto chunkInfo, String path);
|
||||
|
||||
/**
|
||||
* 检查文件块信息
|
||||
* @param identifier 唯一标识
|
||||
* @param filename 文件名
|
||||
* @param path 上传文件夹路径
|
||||
* @return 检查结果
|
||||
*/
|
||||
ChunkResultDto checkChunk(String identifier, String filename, String path);
|
||||
|
||||
/**
|
||||
* 删除文件块
|
||||
* @param identifier 唯一标识
|
||||
* @param path 上传文件夹路径
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteChunk(String identifier, String path);
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
* @param filename 文件名
|
||||
* @param path 上传文件夹路径
|
||||
* @return 是否合并成功
|
||||
*/
|
||||
FileRecordDto mergeFile(String filename, String path);
|
||||
|
||||
List<FileInfoDto> getFolderInfo(String folderName);
|
||||
}
|
||||
@@ -4,5 +4,7 @@ package com.cxx.food.service;
|
||||
import com.cxx.food.dto.SessionDto;
|
||||
|
||||
public interface SessionService {
|
||||
SessionDto create(String name, String password);
|
||||
SessionDto create(String username, String password);
|
||||
|
||||
Boolean abort(String username);
|
||||
}
|
||||
|
||||
9
src/main/java/com/cxx/food/service/UserService.java
Normal file
9
src/main/java/com/cxx/food/service/UserService.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.cxx.food.service;
|
||||
|
||||
|
||||
import com.cxx.food.dto.SessionDto;
|
||||
import com.cxx.food.dto.UserDto;
|
||||
|
||||
public interface UserService {
|
||||
Boolean register(UserDto userDto);
|
||||
}
|
||||
204
src/main/java/com/cxx/food/service/impl/FileServiceImpl.java
Normal file
204
src/main/java/com/cxx/food/service/impl/FileServiceImpl.java
Normal file
@@ -0,0 +1,204 @@
|
||||
package com.cxx.food.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.cxx.food.constant.FileConstant;
|
||||
import com.cxx.food.dto.file.ChunkInfoDto;
|
||||
import com.cxx.food.dto.file.ChunkResultDto;
|
||||
import com.cxx.food.dto.file.FileInfoDto;
|
||||
import com.cxx.food.dto.file.FileRecordDto;
|
||||
import com.cxx.food.entity.FileRecord;
|
||||
import com.cxx.food.mapper.FileRecordMapper;
|
||||
import com.cxx.food.service.FileService;
|
||||
import com.cxx.food.util.FileUtils;
|
||||
import com.cxx.food.util.UploadFileUtil;
|
||||
import com.cxx.framework.web.CustomException;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
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.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class FileServiceImpl implements FileService {
|
||||
@Value("${file.path}")
|
||||
private String fileRootPath;
|
||||
|
||||
@Resource
|
||||
private FileRecordMapper fileRecordMapper;
|
||||
|
||||
@Override
|
||||
public FileRecordDto uploadFile(MultipartFile multipartFile, String path, String md5) {
|
||||
UploadFileUtil.checkDirIsExist(getFilePath(path));
|
||||
|
||||
String filename = multipartFile.getOriginalFilename();
|
||||
String newPath = path + File.separator + md5 + "." + FilenameUtils.getExtension(filename);
|
||||
File newFile = new File(getFilePath(newPath));
|
||||
|
||||
saveFile(multipartFile, newFile);
|
||||
|
||||
// 这里的url需要加上 WebMvcConfig 中设置的前缀
|
||||
String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
|
||||
return getFileRecord(filename, md5, path, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean uploadChunk(ChunkInfoDto chunkInfo, String path) {
|
||||
MultipartFile file = chunkInfo.getMultipartFile();
|
||||
|
||||
try {
|
||||
// 1. 校验路径是否存在
|
||||
UploadFileUtil.checkDirIsExist(getFilePath(path));
|
||||
// 2. 获取文件块路径
|
||||
Path chunkPath = Paths.get(UploadFileUtil.generateChunkPath(getFilePath(path), chunkInfo));
|
||||
// 3. 将文件块写入指定路径
|
||||
Files.write(chunkPath, file.getBytes());
|
||||
} catch (IOException exception) {
|
||||
log.error("文件上传块失败: {}", exception.getMessage());
|
||||
throw new CustomException("文件上传块失败: " + exception.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkResultDto checkChunk(String identifier, String filename, String path) {
|
||||
ChunkResultDto chunkResult = new ChunkResultDto();
|
||||
List<Integer> uploadedChunkList = new ArrayList<>();
|
||||
|
||||
String folder = getFilePath(path) + File.separator + identifier;
|
||||
String file = folder + File.separator + filename;
|
||||
|
||||
// 判断文件夹是否存在
|
||||
if (UploadFileUtil.fileExists(folder)) {
|
||||
// 先判断整个文件是否已经上传过了,如果是,则告诉前端跳过上传,实现秒传
|
||||
if (UploadFileUtil.fileExists(file)) {
|
||||
chunkResult.setSkipUpload(true);
|
||||
chunkResult.setUploadedChunkList(uploadedChunkList);
|
||||
log.info("完整文件已存在,直接跳过上传,实现秒传");
|
||||
} else {
|
||||
chunkResult.setSkipUpload(false);
|
||||
// 获取已经上传的文件块
|
||||
chunkResult.setUploadedChunkList(UploadFileUtil.getUploadedChunkList(folder, filename));
|
||||
}
|
||||
} else {
|
||||
chunkResult.setSkipUpload(false);
|
||||
chunkResult.setUploadedChunkList(uploadedChunkList);
|
||||
}
|
||||
|
||||
return chunkResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteChunk(String identifier, String path) {
|
||||
String folder = getFilePath(path) + File.separator + identifier;
|
||||
log.info("开始删除文件: " + folder);
|
||||
|
||||
// 判断文件夹是否存在
|
||||
if (UploadFileUtil.fileExists(folder)) {
|
||||
UploadFileUtil.deleteDirectory(folder);
|
||||
} else {
|
||||
throw new CustomException("文件: " + folder + "不存在");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileRecordDto mergeFile(String filename, String path) {
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
String newFilename = uuid + "." + FilenameUtils.getExtension(filename);
|
||||
try {
|
||||
UploadFileUtil.mergeFile(newFilename, getFilePath(path));
|
||||
} catch (IOException exception) {
|
||||
throw new CustomException("合并文件: " + filename + " 失败");
|
||||
}
|
||||
|
||||
String newPath = path + File.separator + newFilename;
|
||||
String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
|
||||
return getFileRecord(filename, uuid, path, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileInfoDto> getFolderInfo(String folderName) {
|
||||
File folder = new File(folderName);
|
||||
List<FileInfoDto> fileInfoList = new ArrayList<>();
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
List<FileInfoDto> folderInfoList = new ArrayList<>();
|
||||
List<FileInfoDto> fileInfoSubList = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
FileInfoDto fileInfo = new FileInfoDto();
|
||||
fileInfo.setName(file.getName());
|
||||
if (file.isDirectory()) {
|
||||
fileInfo.setType("文件夹");
|
||||
fileInfo.setSize("-");
|
||||
folderInfoList.add(fileInfo);
|
||||
} else {
|
||||
fileInfo.setType(FileUtils.getFileType(file.getName()));
|
||||
fileInfo.setSize(FileUtils.formatSize(file.length()));
|
||||
fileInfoSubList.add(fileInfo);
|
||||
}
|
||||
fileInfo.setCreateDate(new Date(file.lastModified()));
|
||||
}
|
||||
fileInfoList.addAll(folderInfoList);
|
||||
fileInfoList.addAll(fileInfoSubList);
|
||||
}
|
||||
}
|
||||
return fileInfoList;
|
||||
}
|
||||
|
||||
private void saveFile(MultipartFile multipartFile, File file) {
|
||||
// 如果文件不存在
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
// 3. 如果不存在则创建文件
|
||||
multipartFile.transferTo(file);
|
||||
} catch (IOException e) {
|
||||
throw new CustomException("文件上传失败:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FileRecordDto getFileRecord(String filename, String md5, String path, String url) {
|
||||
FileRecord fileRecord = new FileRecord();
|
||||
fileRecord.setName(filename);
|
||||
fileRecord.setMd5(md5);
|
||||
fileRecord.setPath(path);
|
||||
fileRecord.setUrl(url);
|
||||
fileRecord.setId(saveDatabase(fileRecord, md5));
|
||||
|
||||
// 4. 返回文件信息
|
||||
FileRecordDto fileRecordDto = new FileRecordDto();
|
||||
BeanUtils.copyProperties(fileRecord, fileRecordDto);
|
||||
|
||||
return fileRecordDto;
|
||||
}
|
||||
|
||||
private Long saveDatabase(FileRecord fileRecord, String md5) {
|
||||
LambdaQueryWrapper<FileRecord> queryWrapper = Wrappers.lambdaQuery(FileRecord.class);
|
||||
FileRecord dbFileRecord = fileRecordMapper.selectOne(queryWrapper.eq(FileRecord::getMd5, md5));
|
||||
if (Objects.isNull(dbFileRecord)) {
|
||||
fileRecordMapper.insert(fileRecord);
|
||||
return fileRecord.getId();
|
||||
} else {
|
||||
return dbFileRecord.getId();
|
||||
}
|
||||
}
|
||||
|
||||
private String getFilePath(String path) {
|
||||
return fileRootPath + path;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.cxx.food.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,22 @@ public class FoodServiceImpl implements FoodService {
|
||||
public Boolean addRecord(RecordDto recordDto) {
|
||||
LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>();
|
||||
Recipe recipe = recipeMapper.selectOne(queryWrapper.eq(Recipe::getName, recordDto.getName()));
|
||||
long recipeId;
|
||||
if (recipe == null) {
|
||||
throw new CustomException("该菜谱不存在");
|
||||
Recipe newRecipe = new Recipe();
|
||||
newRecipe.setName(recordDto.getName());
|
||||
newRecipe.setCategory("其他");
|
||||
newRecipe.setRecommendRate(0);
|
||||
newRecipe.setRemark("");
|
||||
recipeMapper.insert(newRecipe);
|
||||
recipeId = newRecipe.getId();
|
||||
} else {
|
||||
recipeId = recipe.getId();
|
||||
}
|
||||
|
||||
Record record = new Record();
|
||||
record.setFoodId(recipe.getId());
|
||||
record.setFoodId(recipeId);
|
||||
record.setPerson((String) StpUtil.getLoginId());
|
||||
BeanUtils.copyProperties(recordDto, record);
|
||||
recordMapper.insert(record);
|
||||
|
||||
@@ -164,7 +175,7 @@ public class FoodServiceImpl implements FoodService {
|
||||
|
||||
@Override
|
||||
public List<SummaryDto> querySummary(FoodQueryVo foodQueryVo) {
|
||||
return foodDao.querySummary(foodQueryVo);
|
||||
return foodDao.querySummary(foodQueryVo, StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,7 +195,7 @@ public class FoodServiceImpl implements FoodService {
|
||||
|
||||
@Override
|
||||
public List<RecordDto> queryRecord(String startDate, String endDate) {
|
||||
return foodDao.queryRecord(startDate, endDate);
|
||||
return foodDao.queryRecord(startDate, endDate, StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,26 +206,26 @@ public class FoodServiceImpl implements FoodService {
|
||||
|
||||
@Override
|
||||
public List<String> queryCategory() {
|
||||
return foodDao.queryCategory();
|
||||
return foodDao.queryCategory(StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDto queryStats() {
|
||||
return foodDao.queryStats();
|
||||
return foodDao.queryStats(StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StatsChartDto> queryRecordStats() {
|
||||
return foodDao.queryRecordStats();
|
||||
return foodDao.queryRecordStats(StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StatsChartDto> queryCategoryStats() {
|
||||
return foodDao.queryCategoryStats();
|
||||
return foodDao.queryCategoryStats(StpUtil.getLoginIdAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StatsChartDto> queryRankStats() {
|
||||
return foodDao.queryRankStats();
|
||||
return foodDao.queryRankStats(StpUtil.getLoginIdAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.cxx.food.dto.SessionDto;
|
||||
import com.cxx.food.dto.UserDto;
|
||||
import com.cxx.food.entity.User;
|
||||
import com.cxx.food.mapper.UserMapper;
|
||||
import com.cxx.food.service.SessionService;
|
||||
import com.cxx.framework.web.CustomException;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -15,13 +18,42 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SessionServiceImpl implements SessionService {
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public SessionDto create(String name, String password) {
|
||||
public SessionDto create(String username, String password) {
|
||||
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getUsername, username);
|
||||
User user = userMapper.selectOne(queryWrapper);
|
||||
if (user == null) {
|
||||
throw new CustomException("该用户不存在");
|
||||
}
|
||||
|
||||
if (!BCrypt.checkpw(password, user.getPassword())) {
|
||||
throw new CustomException("密码不正确");
|
||||
}
|
||||
|
||||
UserDto userDto = new UserDto();
|
||||
BeanUtils.copyProperties(user, userDto);
|
||||
|
||||
SessionDto sessionDto = new SessionDto();
|
||||
StpUtil.login(645808454946885L);
|
||||
StpUtil.login(user.getId());
|
||||
sessionDto.setSaToken(StpUtil.getTokenInfo());
|
||||
sessionDto.setUserInfo(userDto);
|
||||
|
||||
return sessionDto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean abort(String username) {
|
||||
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getUsername, username);
|
||||
User user = userMapper.selectOne(queryWrapper);
|
||||
if (user == null) {
|
||||
throw new CustomException("该用户不存在");
|
||||
}
|
||||
|
||||
StpUtil.logout(user.getId());
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
34
src/main/java/com/cxx/food/service/impl/UserServiceImpl.java
Normal file
34
src/main/java/com/cxx/food/service/impl/UserServiceImpl.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.cxx.food.service.impl;
|
||||
|
||||
|
||||
import cn.dev33.satoken.secure.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.cxx.food.dto.UserDto;
|
||||
import com.cxx.food.entity.User;
|
||||
import com.cxx.food.mapper.UserMapper;
|
||||
import com.cxx.food.service.UserService;
|
||||
import com.cxx.framework.web.CustomException;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public Boolean register(UserDto userDto) {
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (userMapper.exists(queryWrapper.eq(User::getUsername, userDto.getUsername()))) {
|
||||
throw new CustomException("该用户已注册");
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(userDto, user);
|
||||
user.setPassword(BCrypt.hashpw(userDto.getPassword(), BCrypt.gensalt()));
|
||||
|
||||
return userMapper.insert(user) == 1;
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/cxx/food/util/FileUtils.java
Normal file
28
src/main/java/com/cxx/food/util/FileUtils.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.cxx.food.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class FileUtils {
|
||||
public static String getFileType(String fileName) {
|
||||
int lastIndex = fileName.lastIndexOf('.');
|
||||
if (lastIndex != -1) {
|
||||
return fileName.substring(lastIndex + 1);
|
||||
}
|
||||
return "未知类型";
|
||||
}
|
||||
|
||||
public static String formatSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format("%.2f KB", (double) size / 1024);
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return String.format("%.2f MB", (double) size / (1024 * 1024));
|
||||
} else {
|
||||
return String.format("%.2f GB", (double) size / (1024 * 1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
173
src/main/java/com/cxx/food/util/UploadFileUtil.java
Normal file
173
src/main/java/com/cxx/food/util/UploadFileUtil.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package com.cxx.food.util;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import com.cxx.food.dto.file.ChunkInfoDto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @Author: chenxiuxiang
|
||||
* @Date: 2023/7/24 14:49
|
||||
* @Description: 上传文件工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class UploadFileUtil {
|
||||
private static final String FILE_SEPARATOR = "_";
|
||||
|
||||
/**
|
||||
* 校验文件夹是否存在
|
||||
*
|
||||
* @param path 路径
|
||||
*/
|
||||
public static void checkDirIsExist(String path) {
|
||||
File dir = new File(path);
|
||||
if (!dir.exists() && !dir.isDirectory()) {
|
||||
boolean mkdir = dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件路径
|
||||
*
|
||||
* @param path 上传文件夹路径
|
||||
* @param chunkInfo 分片文件信息
|
||||
* @return 文件路径
|
||||
*/
|
||||
public static String generateChunkPath(String path, ChunkInfoDto chunkInfo) {
|
||||
return path + File.separator + chunkInfo.getFilename() + FILE_SEPARATOR + chunkInfo.getChunkNumber();
|
||||
}
|
||||
|
||||
public static List<Integer> getUploadedChunkList(String folder, String filename) {
|
||||
List<Integer> uploadedChunkList = new ArrayList<>();
|
||||
|
||||
// 获取文件夹下所有的文件
|
||||
try (Stream<Path> list = Files.list(Paths.get(folder))) {
|
||||
// 去除需要合并的文件
|
||||
list.filter(path -> !path.getFileName().toString().equals(filename))
|
||||
// 循环遍历文件 将已经上传的文件序号添加至列表中
|
||||
.forEach(path -> {
|
||||
String chunkPath = path.getFileName().toString();
|
||||
int index = chunkPath.lastIndexOf(FILE_SEPARATOR);
|
||||
uploadedChunkList.add(Integer.valueOf(chunkPath.substring(index + 1)));
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return uploadedChunkList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件合并
|
||||
*/
|
||||
public static void mergeFile(String filename, String path) throws IOException {
|
||||
// 判断文件是否存在
|
||||
String file = path + File.separator + filename;
|
||||
if (fileExists(file)) {
|
||||
deleteFile(file);
|
||||
}
|
||||
|
||||
// 不存在的话,进行合并
|
||||
Files.createFile(Paths.get(file));
|
||||
|
||||
// 获取文件夹下所有的文件
|
||||
try (Stream<Path> list = Files.list(Paths.get(path))) {
|
||||
// 保留后缀包含分隔符的
|
||||
list.filter(chunkPath -> FileNameUtil.getSuffix(chunkPath.getFileName().toString()).contains(FILE_SEPARATOR))
|
||||
// 按照文件名排序
|
||||
.sorted((o1, o2) -> {
|
||||
String p1 = o1.getFileName().toString();
|
||||
String p2 = o2.getFileName().toString();
|
||||
int i1 = p1.lastIndexOf(FILE_SEPARATOR);
|
||||
int i2 = p2.lastIndexOf(FILE_SEPARATOR);
|
||||
return Integer.valueOf(p1.substring(i1 + 1))
|
||||
.compareTo(Integer.valueOf(p2.substring(i2 + 1)));
|
||||
})
|
||||
// 循环写入到文件中
|
||||
.forEach(chunkPath -> {
|
||||
try {
|
||||
// 以追加的形式写入文件
|
||||
Files.write(Paths.get(file), Files.readAllBytes(chunkPath), StandardOpenOption.APPEND);
|
||||
// 合并后删除该块
|
||||
Files.delete(chunkPath);
|
||||
} catch (IOException exception) {
|
||||
log.error("写入文件失败: " + exception.getMessage());
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件的全路径名判断文件是否存在
|
||||
*/
|
||||
public static boolean fileExists(String file) {
|
||||
Path path = Paths.get(file);
|
||||
return Files.exists(path, LinkOption.NOFOLLOW_LINKS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录(文件夹)以及目录下的文件
|
||||
*
|
||||
* @param sPath 被删除目录的文件路径
|
||||
* @return 目录删除成功返回true,否则返回false
|
||||
*/
|
||||
public static boolean deleteDirectory(String sPath) {
|
||||
// 如果sPath不以文件分隔符结尾,自动添加文件分隔符
|
||||
if (!sPath.endsWith(File.separator)) {
|
||||
sPath = sPath + File.separator;
|
||||
}
|
||||
File dirFile = new File(sPath);
|
||||
// 如果dir对应的文件不存在,或者不是一个目录,则退出
|
||||
if (!dirFile.exists() || !dirFile.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean flag = true;
|
||||
// 删除文件夹下的所有文件(包括子目录)
|
||||
File[] files = dirFile.listFiles();
|
||||
for (File file : Objects.requireNonNull(files)) {
|
||||
// 删除子文件
|
||||
if (file.isFile()) {
|
||||
flag = deleteFile(file.getAbsolutePath());
|
||||
}
|
||||
// 删除子目录
|
||||
else {
|
||||
flag = deleteDirectory(file.getAbsolutePath());
|
||||
}
|
||||
if (!flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("文件删除成功");
|
||||
// 删除当前目录
|
||||
return dirFile.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个文件
|
||||
*
|
||||
* @param sPath 被删除文件的文件名
|
||||
* @return 单个文件删除成功返回true,否则返回false
|
||||
*/
|
||||
public static boolean deleteFile(String sPath) {
|
||||
boolean flag = false;
|
||||
File file = new File(sPath);
|
||||
// 路径为文件且不为空则进行删除
|
||||
if (file.isFile() && file.exists()) {
|
||||
flag = file.delete();
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@
|
||||
LEFT JOIN recipe AS b ON a.food_id = b.id
|
||||
WHERE a.date <![CDATA[ >=]]> STR_TO_DATE(#{startDate}, '%Y-%m-%d')
|
||||
AND a.date <![CDATA[ <=]]> STR_TO_DATE(#{endDate}, '%Y-%m-%d')
|
||||
AND a.person = #{user}
|
||||
ORDER BY date DESC
|
||||
</select>
|
||||
|
||||
@@ -132,7 +133,9 @@
|
||||
<if test="query.category != null and query.category != ''">
|
||||
a.category = #{query.category}
|
||||
</if>
|
||||
AND a.create_by = #{user}
|
||||
</where>
|
||||
ORDER BY a.update_time DESC;
|
||||
</select>
|
||||
|
||||
<select id="queryStats" resultType="com.cxx.food.dto.StatsDto">
|
||||
@@ -141,12 +144,14 @@
|
||||
COUNT(b.id) AS workCount
|
||||
FROM recipe a
|
||||
LEFT JOIN record b ON a.id = b.food_id
|
||||
WHERE a.create_by = #{user}
|
||||
</select>
|
||||
|
||||
<select id="queryRecordStats" resultType="com.cxx.food.dto.StatsChartDto">
|
||||
SELECT DATE_FORMAT(date, '%Y-%m') AS name,
|
||||
COUNT(id) AS value
|
||||
FROM record
|
||||
WHERE person = #{user}
|
||||
GROUP BY DATE_FORMAT(date, '%Y-%m')
|
||||
ORDER BY name DESC;
|
||||
</select>
|
||||
@@ -155,6 +160,7 @@
|
||||
SELECT category AS name,
|
||||
COUNT(id) AS value
|
||||
FROM recipe
|
||||
WHERE create_by = #{user}
|
||||
GROUP BY category
|
||||
ORDER BY value DESC;
|
||||
</select>
|
||||
@@ -165,6 +171,7 @@
|
||||
FROM recipe a
|
||||
LEFT JOIN
|
||||
record b ON a.id = b.food_id
|
||||
WHERE a.create_by = #{user}
|
||||
GROUP BY a.id, a.name
|
||||
ORDER BY value DESC;
|
||||
</select>
|
||||
@@ -172,6 +179,7 @@
|
||||
<select id="queryCategory" resultType="string">
|
||||
SELECT DISTINCT a.category AS category
|
||||
FROM recipe a
|
||||
WHERE a.create_by = #{user}
|
||||
</select>
|
||||
|
||||
<insert id="insertMaterial">
|
||||
|
||||
Reference in New Issue
Block a user