feat:增加登录和文件模块

This commit is contained in:
2025-06-25 23:49:07 +08:00
parent a7119309a2
commit b703d826ce
23 changed files with 856 additions and 27 deletions

View 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);
}
}

View File

@@ -6,10 +6,7 @@ import com.cxx.food.service.SessionService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/session") @RequestMapping("/session")
@@ -21,8 +18,14 @@ public class SessionController {
@Operation(summary = "创建会话") @Operation(summary = "创建会话")
@PostMapping("") @PostMapping("")
@SaIgnore @SaIgnore
public SessionDto createSession(@RequestParam("name") String name, public SessionDto create(@RequestParam("username") String username,
@RequestParam("password") String password) { @RequestParam("password") String password) {
return sessionService.create(name, password); return sessionService.create(username, password);
}
@Operation(summary = "退出会话")
@DeleteMapping("")
public Boolean abort(@RequestParam("username") String username) {
return sessionService.abort(username);
} }
} }

View 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);
}
}

View File

@@ -15,22 +15,24 @@ public interface FoodDao {
IPage<SummaryDto> querySummaryByPage(IPage<SummaryDto> page, IPage<SummaryDto> querySummaryByPage(IPage<SummaryDto> page,
@Param("query") FoodQueryVo foodQueryVo); @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> queryRecordById(@Param("id") Long id);
List<RecordDto> queryRecord(@Param("startDate") String startDate, 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, void insertMaterial(@Param("foodId") Long foodId,
@Param("list") List<MaterialDto> list); @Param("list") List<MaterialDto> list);

View File

@@ -1,6 +1,7 @@
package com.cxx.food.dto; package com.cxx.food.dto;
import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.SaTokenInfo;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -10,5 +11,8 @@ import lombok.Setter;
public class SessionDto { public class SessionDto {
@Schema(description = "令牌") @Schema(description = "令牌")
private SaTokenInfo saToken; private SaTokenInfo saToken;
@Schema(description = "用户信息")
private UserDto userInfo;
} }

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View File

@@ -16,7 +16,7 @@ public class Record extends AbstractIdEntity {
private Long foodId; private Long foodId;
@TableField("person") @TableField("person")
private Long person; private String person;
@TableField("date") @TableField("date")
private Date date; private Date date;

View 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> {
}

View 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);
}

View File

@@ -4,5 +4,7 @@ package com.cxx.food.service;
import com.cxx.food.dto.SessionDto; import com.cxx.food.dto.SessionDto;
public interface SessionService { public interface SessionService {
SessionDto create(String name, String password); SessionDto create(String username, String password);
Boolean abort(String username);
} }

View 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);
}

View 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;
}
}

View File

@@ -1,5 +1,6 @@
package com.cxx.food.service.impl; 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.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -90,12 +91,22 @@ public class FoodServiceImpl implements FoodService {
public Boolean addRecord(RecordDto recordDto) { public Boolean addRecord(RecordDto recordDto) {
LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Recipe> queryWrapper = new LambdaQueryWrapper<>();
Recipe recipe = recipeMapper.selectOne(queryWrapper.eq(Recipe::getName, recordDto.getName())); Recipe recipe = recipeMapper.selectOne(queryWrapper.eq(Recipe::getName, recordDto.getName()));
long recipeId;
if (recipe == null) { 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 record = new Record();
record.setFoodId(recipe.getId()); record.setFoodId(recipeId);
record.setPerson((String) StpUtil.getLoginId());
BeanUtils.copyProperties(recordDto, record); BeanUtils.copyProperties(recordDto, record);
recordMapper.insert(record); recordMapper.insert(record);
@@ -164,7 +175,7 @@ public class FoodServiceImpl implements FoodService {
@Override @Override
public List<SummaryDto> querySummary(FoodQueryVo foodQueryVo) { public List<SummaryDto> querySummary(FoodQueryVo foodQueryVo) {
return foodDao.querySummary(foodQueryVo); return foodDao.querySummary(foodQueryVo, StpUtil.getLoginIdAsString());
} }
@Override @Override
@@ -184,7 +195,7 @@ public class FoodServiceImpl implements FoodService {
@Override @Override
public List<RecordDto> queryRecord(String startDate, String endDate) { public List<RecordDto> queryRecord(String startDate, String endDate) {
return foodDao.queryRecord(startDate, endDate); return foodDao.queryRecord(startDate, endDate, StpUtil.getLoginIdAsString());
} }
@Override @Override
@@ -195,26 +206,26 @@ public class FoodServiceImpl implements FoodService {
@Override @Override
public List<String> queryCategory() { public List<String> queryCategory() {
return foodDao.queryCategory(); return foodDao.queryCategory(StpUtil.getLoginIdAsString());
} }
@Override @Override
public StatsDto queryStats() { public StatsDto queryStats() {
return foodDao.queryStats(); return foodDao.queryStats(StpUtil.getLoginIdAsString());
} }
@Override @Override
public List<StatsChartDto> queryRecordStats() { public List<StatsChartDto> queryRecordStats() {
return foodDao.queryRecordStats(); return foodDao.queryRecordStats(StpUtil.getLoginIdAsString());
} }
@Override @Override
public List<StatsChartDto> queryCategoryStats() { public List<StatsChartDto> queryCategoryStats() {
return foodDao.queryCategoryStats(); return foodDao.queryCategoryStats(StpUtil.getLoginIdAsString());
} }
@Override @Override
public List<StatsChartDto> queryRankStats() { public List<StatsChartDto> queryRankStats() {
return foodDao.queryRankStats(); return foodDao.queryRankStats(StpUtil.getLoginIdAsString());
} }
} }

View File

@@ -6,6 +6,9 @@ import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cxx.food.dto.SessionDto; 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.food.service.SessionService;
import com.cxx.framework.web.CustomException; import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -15,13 +18,42 @@ import org.springframework.stereotype.Service;
@Service @Service
public class SessionServiceImpl implements SessionService { public class SessionServiceImpl implements SessionService {
@Resource
private UserMapper userMapper;
@Override @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(); SessionDto sessionDto = new SessionDto();
StpUtil.login(645808454946885L); StpUtil.login(user.getId());
sessionDto.setSaToken(StpUtil.getTokenInfo()); sessionDto.setSaToken(StpUtil.getTokenInfo());
sessionDto.setUserInfo(userDto);
return sessionDto; 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;
}
} }

View 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;
}
}

View 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));
}
}
}

View 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;
}
}

View File

@@ -92,6 +92,7 @@
LEFT JOIN recipe AS b ON a.food_id = b.id LEFT JOIN recipe AS b ON a.food_id = b.id
WHERE a.date <![CDATA[ >=]]> STR_TO_DATE(#{startDate}, '%Y-%m-%d') WHERE a.date <![CDATA[ >=]]> STR_TO_DATE(#{startDate}, '%Y-%m-%d')
AND a.date <![CDATA[ <=]]> STR_TO_DATE(#{endDate}, '%Y-%m-%d') AND a.date <![CDATA[ <=]]> STR_TO_DATE(#{endDate}, '%Y-%m-%d')
AND a.person = #{user}
ORDER BY date DESC ORDER BY date DESC
</select> </select>
@@ -132,7 +133,9 @@
<if test="query.category != null and query.category != ''"> <if test="query.category != null and query.category != ''">
a.category = #{query.category} a.category = #{query.category}
</if> </if>
AND a.create_by = #{user}
</where> </where>
ORDER BY a.update_time DESC;
</select> </select>
<select id="queryStats" resultType="com.cxx.food.dto.StatsDto"> <select id="queryStats" resultType="com.cxx.food.dto.StatsDto">
@@ -141,12 +144,14 @@
COUNT(b.id) AS workCount COUNT(b.id) AS workCount
FROM recipe a FROM recipe a
LEFT JOIN record b ON a.id = b.food_id LEFT JOIN record b ON a.id = b.food_id
WHERE a.create_by = #{user}
</select> </select>
<select id="queryRecordStats" resultType="com.cxx.food.dto.StatsChartDto"> <select id="queryRecordStats" resultType="com.cxx.food.dto.StatsChartDto">
SELECT DATE_FORMAT(date, '%Y-%m') AS name, SELECT DATE_FORMAT(date, '%Y-%m') AS name,
COUNT(id) AS value COUNT(id) AS value
FROM record FROM record
WHERE person = #{user}
GROUP BY DATE_FORMAT(date, '%Y-%m') GROUP BY DATE_FORMAT(date, '%Y-%m')
ORDER BY name DESC; ORDER BY name DESC;
</select> </select>
@@ -155,6 +160,7 @@
SELECT category AS name, SELECT category AS name,
COUNT(id) AS value COUNT(id) AS value
FROM recipe FROM recipe
WHERE create_by = #{user}
GROUP BY category GROUP BY category
ORDER BY value DESC; ORDER BY value DESC;
</select> </select>
@@ -165,6 +171,7 @@
FROM recipe a FROM recipe a
LEFT JOIN LEFT JOIN
record b ON a.id = b.food_id record b ON a.id = b.food_id
WHERE a.create_by = #{user}
GROUP BY a.id, a.name GROUP BY a.id, a.name
ORDER BY value DESC; ORDER BY value DESC;
</select> </select>
@@ -172,6 +179,7 @@
<select id="queryCategory" resultType="string"> <select id="queryCategory" resultType="string">
SELECT DISTINCT a.category AS category SELECT DISTINCT a.category AS category
FROM recipe a FROM recipe a
WHERE a.create_by = #{user}
</select> </select>
<insert id="insertMaterial"> <insert id="insertMaterial">