This commit is contained in:
jiangh277 2025-08-04 16:51:13 +08:00
parent f8fb9b561c
commit eba0eb085e
41 changed files with 451 additions and 73 deletions

View File

@ -39,7 +39,11 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@ -39,6 +39,17 @@
<version>3.5.17</version> <version>3.5.17</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View File

@ -1,4 +1,4 @@
package com.timeline.aop; package com.timeline.common.aop;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@ -0,0 +1,26 @@
package com.timeline.common.config;
import com.github.pagehelper.PageInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class PageHelperConfig {
@Bean(name = "pageHelper")
public PageInterceptor pageInterceptor() {
PageInterceptor interceptor = new PageInterceptor();
Properties properties = new Properties();
// 配置数据库方言
properties.setProperty("helperDialect", "mysql");
// 分页合理化参数默认值为false
properties.setProperty("reasonable", "true");
// 支持通过Mapper接口参数来传递分页参数
properties.setProperty("supportMethodsArguments", "true");
// always总是返回PageInfo类型
properties.setProperty("returnPageInfo", "check");
interceptor.setProperties(properties);
return interceptor;
}
}

View File

@ -1,4 +1,4 @@
package com.timeline.constants; package com.timeline.common.constants;
public class CommonConstants { public class CommonConstants {
private static final int RELATION_USER_AND_STORY = 1; private static final int RELATION_USER_AND_STORY = 1;
@ -9,4 +9,8 @@ public class CommonConstants {
public static final int RELATION_STORY_ITEM_AND_COVER = 6; public static final int RELATION_STORY_ITEM_AND_COVER = 6;
public static final int STORY_ITEM_IS_ROOT = 1; public static final int STORY_ITEM_IS_ROOT = 1;
public static final int STORY_ITEM_IS_NOT_ROOT = 0; public static final int STORY_ITEM_IS_NOT_ROOT = 0;
public static final int DELETED = 1;
public static final int NOT_DELETED = 0;
} }

View File

@ -1,4 +1,4 @@
package com.timeline.dto; package com.timeline.common.dto;
import lombok.Data; import lombok.Data;

View File

@ -0,0 +1,23 @@
package com.timeline.common.dto;
import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class PageResult<T> {
private long total;
private int pageNum;
private int pageSize;
private int pages;
private List<T> list;
public static <T> PageResult<T> of(List<T> list) {
PageInfo<T> pageInfo = new PageInfo<>(list);
return new PageResult<T>(pageInfo.getTotal(), pageInfo.getPageNum(),
pageInfo.getPageSize(), pageInfo.getPages(), pageInfo.getList());
}
}

View File

@ -1,4 +1,4 @@
package com.timeline.enums; package com.timeline.common.enums;
public enum RelationTypeEnum { public enum RelationTypeEnum {

View File

@ -1,6 +1,6 @@
package com.timeline.exception; package com.timeline.common.exception;
import com.timeline.response.ResponseEnum; import com.timeline.common.response.ResponseEnum;
public class CustomException extends RuntimeException { public class CustomException extends RuntimeException {
private final int code; private final int code;

View File

@ -1,8 +1,8 @@
package com.timeline.handler; package com.timeline.common.handler;
import com.timeline.exception.CustomException; import com.timeline.common.exception.CustomException;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import com.timeline.response.ResponseEnum; import com.timeline.common.response.ResponseEnum;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;

View File

@ -1,4 +1,4 @@
package com.timeline.response; package com.timeline.common.response;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.timeline.response; package com.timeline.common.response;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@ -25,7 +25,10 @@ public enum ResponseEnum {
NOT_IMPLEMENTED(501, "功能未实现"), NOT_IMPLEMENTED(501, "功能未实现"),
BAD_GATEWAY(502, "网关错误"), BAD_GATEWAY(502, "网关错误"),
SERVICE_UNAVAILABLE(503, "服务不可用"), SERVICE_UNAVAILABLE(503, "服务不可用"),
GATEWAY_TIMEOUT(504, "网关超时"); GATEWAY_TIMEOUT(504, "网关超时"),
// 操作错误
SEARCH_ERROR(4001, "查询数据库错误");
private final int code; private final int code;
private final String message; private final String message;

View File

@ -0,0 +1,20 @@
package com.timeline.common.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CommonUtils {
public static String calculateFileHash(MultipartFile file) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] fileBytes = file.getBytes();
byte[] hashBytes = digest.digest(fileBytes);
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}

View File

@ -1,4 +1,4 @@
package com.timeline.utils; package com.timeline.common.utils;
import java.util.UUID; import java.util.UUID;

View File

@ -0,0 +1,49 @@
package com.timeline.common.utils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.timeline.common.exception.CustomException;
import com.timeline.common.response.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class PageUtils {
public PageUtils() {
}
public static Map pageQuery(Integer pageNum, Integer pageSize, Class clas, String methodName, Object params, String listKey) {
Map pageResult = new HashMap();
if (pageNum == null) {
pageNum = 0;
}
if (pageSize == null) {
pageSize = 0;
}
try {
if (pageNum >= 1 && pageSize >= 1) {
PageHelper.startPage(pageNum, pageSize);
List list = (List)ReflectUtils.call(clas, methodName, params);
PageInfo pageInfo = new PageInfo(list);
pageResult.put("pageNumber", pageInfo.getPageNum());
pageResult.put("pageSize", pageInfo.getPageSize());
pageResult.put("total", pageInfo.getTotal());
pageResult.put("pages", pageInfo.getPages());
pageResult.put(listKey, pageInfo.getList());
return pageResult;
} else {
List list = (List)ReflectUtils.call(clas, methodName, params);
pageResult.put(listKey, list);
return pageResult;
}
} catch (Exception e) {
log.error("查询失败", e);
throw new CustomException(ResponseEnum.SEARCH_ERROR);
}
}
}

View File

@ -0,0 +1,58 @@
package com.timeline.common.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class ReflectUtils {
public ReflectUtils() {
}
public static Object persistenceData(String entityPath, String persisPath, String methodName, Map params) throws Exception {
Object result = null;
try {
if (!StringUtils.isEmpty(entityPath) && !StringUtils.isEmpty(persisPath)) {
Class entityClass = Class.forName(entityPath);
Class persisClass = Class.forName(persisPath);
Object daoBean = SpringContextUtils.getBean(persisClass);
Object o = JSONObject.toJavaObject(JSON.parseObject(JSONObject.toJSONString(params)), entityClass);
Method method = persisClass.getMethod(methodName, entityClass);
result = method.invoke(daoBean, o);
return result;
} else {
return null;
}
} catch (Exception e) {
throw e;
}
}
public static Object call(Class clas, String methodName, Object params) throws Exception {
Object bean = SpringContextUtils.getBean(clas);
Method method = clas.getMethod(methodName, Map.class);
Object result = method.invoke(bean, params);
return result;
}
public static Object call(String className, String methodName, String parameterType, JSONObject parameter) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
Class clas = Class.forName(className);
Class parameterTypeClass = Class.forName(parameterType);
Object daoBean = SpringContextUtils.getBean(clas);
Object o = JSONObject.toJavaObject(parameter, parameterTypeClass);
Method method = clas.getMethod(methodName, parameterTypeClass);
return method.invoke(daoBean, o);
}
public static Object call(String className, String methodName, String parameterType, Object parameter) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
Class clas = Class.forName(className);
Class parameterTypeClass = Class.forName(parameterType);
Object daoBean = SpringContextUtils.getBean(clas);
Method method = clas.getMethod(methodName, parameterTypeClass);
return method.invoke(daoBean, parameter);
}
}

View File

@ -0,0 +1,30 @@
package com.timeline.common.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public SpringContextUtils() {
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static <T> T getBean(String name) throws BeansException {
return (T)applicationContext.getBean(name);
}
public static <T> T getBean(Class<?> clz) throws BeansException {
return (T)applicationContext.getBean(clz);
}
}

View File

@ -0,0 +1,9 @@
package com.timeline.common.vo;
import lombok.Data;
@Data
public class CommonVo {
private Integer pageSize;
private Integer current;
}

View File

@ -77,6 +77,10 @@
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,21 +1,22 @@
package com.timeline.file.controller; package com.timeline.file.controller;
import com.timeline.file.entity.ImageInfo;
import com.timeline.file.service.FileService; import com.timeline.file.service.FileService;
import com.timeline.file.service.impl.FileServiceImpl;
import com.timeline.file.vo.ImageInfoVo; import com.timeline.file.vo.ImageInfoVo;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.utils.IOUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@RestController @RestController
@ -47,13 +48,13 @@ public class FileController {
fileService.saveFileMetadata(imageInfoVo); fileService.saveFileMetadata(imageInfoVo);
return ResponseEntity.success("上传成功"); return ResponseEntity.success("上传成功");
} }
@PostMapping("/upload-cover") @PostMapping("/upload-image")
public ResponseEntity<String> uploadCover(@RequestPart("cover") MultipartFile cover) throws Throwable { public ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image) throws Throwable {
String objectKey = fileService.uploadCover(cover); String objectKey = fileService.uploadCover(image);
return ResponseEntity.success(objectKey); return ResponseEntity.success(objectKey);
} }
@RequestMapping(value = "/download/cover/{coverInstanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE) @RequestMapping(value = "/image/{coverInstanceId}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void downloadCover(@PathVariable String coverInstanceId, HttpServletResponse response) throws Throwable { public void downloadCover(@PathVariable String coverInstanceId, HttpServletResponse response) throws Throwable {
log.info("downloadCover"); log.info("downloadCover");
InputStream inputStream = fileService.downloadCover(coverInstanceId); InputStream inputStream = fileService.downloadCover(coverInstanceId);
@ -100,4 +101,15 @@ public class FileController {
fileService.removeImageFromStoryItem(imageInstanceId, storyItemId); fileService.removeImageFromStoryItem(imageInstanceId, storyItemId);
return ResponseEntity.success("图片已从故事项中移除"); return ResponseEntity.success("图片已从故事项中移除");
} }
@GetMapping("/image/list")
public ResponseEntity<Map> getImagesListByOwnerId(@SpringQueryMap ImageInfoVo imageInfoVo) throws Throwable {
imageInfoVo.setOwnerId("9999");
Map images = fileService.getImagesListByOwnerId(imageInfoVo);
return ResponseEntity.success(images);
}
@DeleteMapping("/image/{imageInstanceId}")
public ResponseEntity<String> deleteImage(@PathVariable String imageInstanceId) throws Throwable {
fileService.deleteImage(imageInstanceId);
return ResponseEntity.success("图片已删除");
}
} }

View File

@ -1,6 +1,6 @@
package com.timeline.file.dao; package com.timeline.file.dao;
import com.timeline.dto.CommonRelationDTO; import com.timeline.common.dto.CommonRelationDTO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,15 @@
package com.timeline.file.dao;
import com.timeline.file.entity.FileHash;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface FileHashMapper {
void insertFileHash(FileHash fileHash);
List<FileHash> getFileHashByFileHash(String fileHash);
List<FileHash> getFileHashByInstanceId(String instanceId);
List<FileHash> getOtherFileHashByInstanceId(String instanceId);
void deleteFileHash(String instanceId);
}

View File

@ -1,11 +1,18 @@
package com.timeline.file.dao; package com.timeline.file.dao;
import com.timeline.file.entity.ImageInfo; import com.timeline.file.entity.ImageInfo;
import com.timeline.file.vo.ImageInfoVo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper @Mapper
public interface ImageInfoMapper { public interface ImageInfoMapper {
void insert(ImageInfo imageInfo); void insert(ImageInfo imageInfo);
void update(ImageInfo imageInfo);
String selectObjectKeyById(String objectKey); String selectObjectKeyById(String objectKey);
void delete(String objectKey); void delete(String objectKey);
List<ImageInfo> selectListByOwnerId(Map map);
ImageInfo selectByInstanceId(String instanceId);
} }

View File

@ -0,0 +1,18 @@
package com.timeline.file.entity;
import lombok.Data;
@Data
public class FileHash {
private Integer id;
private String instanceId;
private String hashValue;
private Integer isDeleted;
public FileHash(String instanceId, String hashValue) {
this.id = null;
this.instanceId = instanceId;
this.hashValue = hashValue;
this.isDeleted = 0;
}
}

View File

@ -15,4 +15,5 @@ public class ImageInfo {
private String contentType; private String contentType;
private String userId; private String userId;
private Integer isDeleted; private Integer isDeleted;
private LocalDateTime updateTime;
} }

View File

@ -8,6 +8,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
@Service @Service
public interface FileService { public interface FileService {
@ -23,4 +24,5 @@ public interface FileService {
ArrayList<String> getAllImageUrls(List<String> images) throws Throwable; ArrayList<String> getAllImageUrls(List<String> images) throws Throwable;
String uploadCover(MultipartFile cover) throws Throwable; String uploadCover(MultipartFile cover) throws Throwable;
InputStream downloadCover(String coverKey) throws Throwable; InputStream downloadCover(String coverKey) throws Throwable;
Map getImagesListByOwnerId(ImageInfoVo imageInfoVo);
} }

View File

@ -1,13 +1,18 @@
package com.timeline.file.service.impl; package com.timeline.file.service.impl;
import com.timeline.exception.CustomException; import com.timeline.common.constants.CommonConstants;
import com.timeline.common.exception.CustomException;
import com.timeline.common.utils.CommonUtils;
import com.timeline.common.utils.PageUtils;
import com.timeline.file.config.MinioConfig; import com.timeline.file.config.MinioConfig;
import com.timeline.file.dao.CommonRelationMapper; import com.timeline.file.dao.CommonRelationMapper;
import com.timeline.file.dao.FileHashMapper;
import com.timeline.file.dao.ImageInfoMapper; import com.timeline.file.dao.ImageInfoMapper;
import com.timeline.file.entity.FileHash;
import com.timeline.file.entity.ImageInfo; import com.timeline.file.entity.ImageInfo;
import com.timeline.file.service.FileService; import com.timeline.file.service.FileService;
import com.timeline.file.vo.ImageInfoVo; import com.timeline.file.vo.ImageInfoVo;
import com.timeline.utils.IdUtils; import com.timeline.common.utils.IdUtils;
import io.minio.*; import io.minio.*;
import io.minio.errors.MinioException; import io.minio.errors.MinioException;
import io.minio.http.Method; import io.minio.http.Method;
@ -18,8 +23,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream; import java.io.InputStream;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.*;
import java.util.List;
@Slf4j @Slf4j
@Service @Service
@ -32,6 +36,8 @@ public class FileServiceImpl implements FileService {
@Autowired @Autowired
private CommonRelationMapper commonRelationMapper; private CommonRelationMapper commonRelationMapper;
@Autowired @Autowired
private FileHashMapper fileHashMapper;
@Autowired
public FileServiceImpl(MinioClient minioClient, MinioConfig minioConfig) { public FileServiceImpl(MinioClient minioClient, MinioConfig minioConfig) {
this.minioClient = minioClient; this.minioClient = minioClient;
this.minioConfig = minioConfig; this.minioConfig = minioConfig;
@ -96,16 +102,26 @@ public class FileServiceImpl implements FileService {
} }
@Override @Override
public void deleteImage(String objectKey) throws Throwable { public void deleteImage(String instanceId) throws Throwable {
try { try {
// 删除 MinIO 中的对象 List<FileHash> otherFileHashByInstanceId = fileHashMapper.getOtherFileHashByInstanceId(instanceId);
ImageInfo imageInfo = imageInfoMapper.selectByInstanceId(instanceId);
if (otherFileHashByInstanceId != null && !otherFileHashByInstanceId.isEmpty()) {
} else {
// 不存在其他image_info使用则删除 MinIO 中的对象
minioClient.removeObject(RemoveObjectArgs.builder() minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(minioConfig.getBucketName()) .bucket(minioConfig.getBucketName())
.object(objectKey) .object(imageInfo.getObjectKey())
.build()); .build());
}
// 删除file_hash
fileHashMapper.deleteFileHash(instanceId);
// 删除 MySQL 记录 // 删除image_info
imageInfoMapper.delete(objectKey); imageInfo.setIsDeleted(CommonConstants.DELETED);
imageInfo.setUpdateTime(LocalDateTime.now());
imageInfoMapper.update(imageInfo);
} catch (Exception e) { } catch (Exception e) {
log.error("删除图片失败", e); log.error("删除图片失败", e);
throw new CustomException(500, "删除图片失败"); throw new CustomException(500, "删除图片失败");
@ -149,25 +165,33 @@ public class FileServiceImpl implements FileService {
} }
@Override @Override
public String uploadCover(MultipartFile cover) throws Throwable { public String uploadCover(MultipartFile image) throws Throwable {
String fileName = "cover-" + IdUtils.randomUuidUpper() + ".jpg"; String suffix = Objects.requireNonNull(image.getOriginalFilename()).substring(image.getOriginalFilename().lastIndexOf("."));
// 1. 上传到 MinIO String hash = CommonUtils.calculateFileHash(image);
minioClient.putObject(PutObjectArgs.builder() String objectKey = hash + suffix;
.bucket(minioConfig.getBucketName()) log.info("上传图片的ObjectKey值为{}", objectKey);
.object(fileName) List<FileHash> hashByFileHash = fileHashMapper.getFileHashByFileHash(hash);
.stream(cover.getInputStream(), cover.getSize(), -1)
.contentType(cover.getContentType())
.build());
// 2. 保存元数据到 MySQL // 2. 保存元数据到 MySQL
ImageInfo imageInfo = new ImageInfo(); ImageInfo imageInfo = new ImageInfo();
imageInfo.setInstanceId(IdUtils.randomUuidUpper()); imageInfo.setInstanceId(IdUtils.randomUuidUpper());
imageInfo.setObjectKey(fileName); imageInfo.setObjectKey(objectKey);
imageInfo.setImageName(fileName); imageInfo.setImageName(image.getOriginalFilename());
imageInfo.setContentType(cover.getContentType()); imageInfo.setContentType(image.getContentType());
imageInfo.setSize(cover.getSize()); imageInfo.setSize(image.getSize());
imageInfo.setUserId("9999"); imageInfo.setUserId("9999");
imageInfo.setUploadTime(LocalDateTime.now()); imageInfo.setUploadTime(LocalDateTime.now());
if (hashByFileHash != null && !hashByFileHash.isEmpty()) {
log.info("当前文件已存在不进行minio文件上传");
} else {
// 1. 上传到 MinIO
minioClient.putObject(PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectKey)
.stream(image.getInputStream(), image.getSize(), -1)
.contentType(image.getContentType())
.build());
}
fileHashMapper.insertFileHash(new FileHash(imageInfo.getInstanceId(), hash));
imageInfoMapper.insert(imageInfo); imageInfoMapper.insert(imageInfo);
return imageInfo.getInstanceId(); return imageInfo.getInstanceId();
@ -182,4 +206,13 @@ public class FileServiceImpl implements FileService {
.object(objectKey) .object(objectKey)
.build()); .build());
} }
@Override
public Map getImagesListByOwnerId(ImageInfoVo imageInfoVo) {
HashMap<String, String> map = new HashMap<>();
map.put("ownerId", imageInfoVo.getOwnerId());
Map resultMap = PageUtils.pageQuery(imageInfoVo.getCurrent(), imageInfoVo.getPageSize(), ImageInfoMapper.class, "selectListByOwnerId",
map, "list");
return resultMap;
}
} }

View File

@ -1,12 +1,14 @@
package com.timeline.file.vo; package com.timeline.file.vo;
import com.timeline.common.vo.CommonVo;
import lombok.Data; import lombok.Data;
@Data @Data
public class ImageInfoVo { public class ImageInfoVo extends CommonVo {
private String objectKey; private String objectKey;
private String imageName; private String imageName;
private String contentType; private String contentType;
private Long size; private Long size;
private String instanceId; private String instanceId;
private String ownerId;
} }

View File

@ -13,6 +13,9 @@ minio.bucketName=timeline-test
mybatis.mapper-locations=classpath:mapper/*.xml mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.timeline.file.entity mybatis.type-aliases-package=com.timeline.file.entity
mybatis.configuration.mapUnderscoreToCamelCase=true
server.port=30002 server.port=30002
spring.servlet.multipart.enabled=true spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-file-size=10MB

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.timeline.file.dao.FileHashMapper">
<insert id="insertFileHash" parameterType="com.timeline.file.entity.FileHash">
INSERT INTO file_hash (instance_id, hash_value, is_deleted)
VALUES (#{instanceId}, #{hashValue}, #{isDeleted})
</insert>
<select id="getFileHashByInstanceId" resultType="com.timeline.file.entity.FileHash">
SELECT * FROM file_hash WHERE instance_id = #{instanceId} AND is_deleted = 0
</select>
<select id="getFileHashByFileHash" resultType="com.timeline.file.entity.FileHash">
SELECT * FROM file_hash WHERE hash_value = #{hashValue} AND is_deleted = 0
</select>
<select id="getOtherFileHashByInstanceId" resultType="com.timeline.file.entity.FileHash">
SELECT * FROM file_hash WHERE instance_id != #{instanceId} AND is_deleted = 0 AND hash_value IN (SELECT hash_value FROM file_hash WHERE instance_id = #{instanceId} AND is_deleted = 0)
</select>
<delete id="deleteFileHash">
UPDATE file_hash SET is_deleted = 1 WHERE instance_id = #{instanceId}
</delete>
</mapper>

View File

@ -18,6 +18,22 @@
</delete> </delete>
<select id="selectObjectKeyById" resultType="string"> <select id="selectObjectKeyById" resultType="string">
SELECT object_key FROM image_info WHERE instance_id = #{instanceId} SELECT object_key FROM image_info WHERE instance_id = #{instanceId} AND is_deleted = 0
</select> </select>
<select id="selectListByOwnerId" resultType="com.timeline.file.entity.ImageInfo" parameterType="java.util.Map">
SELECT * FROM image_info WHERE user_id = #{ownerId} AND is_deleted = 0
</select>
<select id="selectByInstanceId" resultType="com.timeline.file.entity.ImageInfo">
SELECT * FROM image_info WHERE instance_id = #{instanceId} AND is_deleted = 0
</select>
<update id="update" parameterType="com.timeline.file.entity.ImageInfo">
UPDATE image_info SET object_key = #{objectKey},
user_id = #{userId},
update_time = #{updateTime},
is_deleted = #{isDeleted}
WHERE instance_id = #{instanceId}
</update>
</mapper> </mapper>

View File

@ -1,6 +1,6 @@
package com.timeline.story.controller; package com.timeline.story.controller;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;

View File

@ -1,6 +1,6 @@
package com.timeline.story.controller; package com.timeline.story.controller;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import com.timeline.story.entity.Story; import com.timeline.story.entity.Story;
import com.timeline.story.service.StoryService; import com.timeline.story.service.StoryService;
import com.timeline.story.vo.StoryVo; import com.timeline.story.vo.StoryVo;

View File

@ -1,13 +1,12 @@
package com.timeline.story.controller; package com.timeline.story.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import com.timeline.story.entity.StoryItem; import com.timeline.story.entity.StoryItem;
import com.timeline.story.service.StoryItemService; import com.timeline.story.service.StoryItemService;
import com.timeline.story.vo.StoryItemVo; import com.timeline.story.vo.StoryItemVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;

View File

@ -1,6 +1,6 @@
package com.timeline.story.dao; package com.timeline.story.dao;
import com.timeline.dto.CommonRelationDTO; import com.timeline.common.dto.CommonRelationDTO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;

View File

@ -18,4 +18,5 @@ public class Story {
private Integer isDelete; private Integer isDelete;
private String ownerId; private String ownerId;
private String status; private String status;
private String logo;
} }

View File

@ -1,20 +1,19 @@
package com.timeline.story.feign; package com.timeline.story.feign;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List; import java.util.List;
@FeignClient(name = "timeline.file", url = "${file.service.url}") @FeignClient(name = "timeline.file", url = "${file.service.url}")
public interface FileServiceClient { public interface FileServiceClient {
@PostMapping(value = "/upload-cover", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/upload-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
ResponseEntity<String> uploadCover(@RequestPart("cover") MultipartFile cover); ResponseEntity<String> uploadCover(@RequestPart("image") MultipartFile image);
@GetMapping("/download/cover/{coverKey}") @GetMapping("/download/cover/{coverKey}")
InputStreamResource downloadCover(@PathVariable String coverKey); InputStreamResource downloadCover(@PathVariable String coverKey);

View File

@ -1,18 +1,18 @@
package com.timeline.story.service.impl; package com.timeline.story.service.impl;
import com.timeline.constants.CommonConstants; import com.timeline.common.constants.CommonConstants;
import com.timeline.story.dao.CommonRelationMapper; import com.timeline.story.dao.CommonRelationMapper;
import com.timeline.dto.CommonRelationDTO; import com.timeline.common.dto.CommonRelationDTO;
import com.timeline.exception.CustomException; import com.timeline.common.exception.CustomException;
import com.timeline.response.ResponseEntity; import com.timeline.common.response.ResponseEntity;
import com.timeline.response.ResponseEnum; import com.timeline.common.response.ResponseEnum;
import com.timeline.story.dao.StoryItemMapper; import com.timeline.story.dao.StoryItemMapper;
import com.timeline.story.entity.StoryItem; import com.timeline.story.entity.StoryItem;
import com.timeline.story.feign.FileServiceClient; import com.timeline.story.feign.FileServiceClient;
import com.timeline.story.service.StoryItemService; import com.timeline.story.service.StoryItemService;
import com.timeline.story.vo.StoryItemVo; import com.timeline.story.vo.StoryItemVo;
import com.timeline.story.vo.StoryItemWithCoverVo; import com.timeline.story.vo.StoryItemWithCoverVo;
import com.timeline.utils.IdUtils; import com.timeline.common.utils.IdUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;

View File

@ -1,12 +1,12 @@
package com.timeline.story.service.impl; package com.timeline.story.service.impl;
import com.timeline.exception.CustomException; import com.timeline.common.exception.CustomException;
import com.timeline.response.ResponseEnum; import com.timeline.common.response.ResponseEnum;
import com.timeline.story.entity.Story; import com.timeline.story.entity.Story;
import com.timeline.story.dao.StoryMapper; import com.timeline.story.dao.StoryMapper;
import com.timeline.story.service.StoryService; import com.timeline.story.service.StoryService;
import com.timeline.story.vo.StoryVo; import com.timeline.story.vo.StoryVo;
import com.timeline.utils.IdUtils; import com.timeline.common.utils.IdUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -32,6 +32,7 @@ public class StoryServiceImpl implements StoryService {
story.setStatus(storyVo.getStatus()); story.setStatus(storyVo.getStatus());
story.setCreateTime(LocalDateTime.now()); story.setCreateTime(LocalDateTime.now());
story.setUpdateTime(LocalDateTime.now()); story.setUpdateTime(LocalDateTime.now());
story.setLogo(storyVo.getLogo());
story.setIsDelete(0); story.setIsDelete(0);
storyMapper.insert(story); storyMapper.insert(story);
} catch (Exception e) { } catch (Exception e) {
@ -52,6 +53,7 @@ public class StoryServiceImpl implements StoryService {
story.setDescription(storyVo.getDescription()); story.setDescription(storyVo.getDescription());
story.setStatus(storyVo.getStatus()); story.setStatus(storyVo.getStatus());
story.setUpdateTime(LocalDateTime.now()); story.setUpdateTime(LocalDateTime.now());
story.setLogo(storyVo.getLogo());
storyMapper.update(story); storyMapper.update(story);
} }

View File

@ -10,4 +10,5 @@ public class StoryVo {
private String title; private String title;
private String description; private String description;
private String status; private String status;
private String logo;
} }

View File

@ -5,8 +5,8 @@
<mapper namespace="com.timeline.story.dao.StoryMapper"> <mapper namespace="com.timeline.story.dao.StoryMapper">
<insert id="insert"> <insert id="insert">
INSERT INTO story (instance_id, title, description, owner_id, status) INSERT INTO story (instance_id, title, description, owner_id, status, logo)
VALUES (#{instanceId}, #{title}, #{description}, #{ownerId}, #{status}) VALUES (#{instanceId}, #{title}, #{description}, #{ownerId}, #{status}, #{logo})
</insert> </insert>
<update id="update"> <update id="update">
@ -15,7 +15,8 @@
description = #{description}, description = #{description},
update_id = #{updateId}, update_id = #{updateId},
status = #{status}, status = #{status},
update_time = NOW() update_time = NOW(),
logo = #{logo}
WHERE instance_id = #{instanceId} WHERE instance_id = #{instanceId}
</update> </update>