feat:增加图片上传功能,增加多种消息类型

This commit is contained in:
zhongzb
2023-06-28 23:25:53 +08:00
parent 892b658d38
commit c889a9fdc6
35 changed files with 946 additions and 49 deletions

View File

@@ -112,9 +112,11 @@
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.1</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<!-- Used for unit testing -->
<dependency>
<groupId>junit</groupId>

View File

@@ -0,0 +1,31 @@
package com.abin.mallchat.common.chat.domain.entity.msg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* Description: 语音消息入参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-04
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FileMsgDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("大小(字节)")
private Long size;
@ApiModelProperty("下载地址")
private String url;
@ApiModelProperty("文件名(带后缀)")
private String fileName;
}

View File

@@ -0,0 +1,41 @@
package com.abin.mallchat.common.chat.domain.entity.msg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Description: 图片消息入参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-04
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ImgMsgDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("大小(字节)")
@NotNull
private Long size;
@ApiModelProperty("宽度(像素)")
@NotNull
private Integer width;
@ApiModelProperty("高度(像素)")
@NotNull
private Integer height;
@ApiModelProperty("下载地址")
@NotBlank
private String url;
}

View File

@@ -28,4 +28,12 @@ public class MessageExtra implements Serializable {
private MsgRecall recall;
//艾特的uid
private List<Long> atUidList;
//文件消息
private FileMsgDTO fileMsg;
//图片消息
private ImgMsgDTO imgMsgDTO;
//语音消息
private SoundMsgDTO soundMsgDTO;
//文件消息
private VideoMsgDTO videoMsgDTO;
}

View File

@@ -0,0 +1,30 @@
package com.abin.mallchat.common.chat.domain.entity.msg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* Description: 语音消息入参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-04
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SoundMsgDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("大小(字节)")
private Long size;
@ApiModelProperty("时长(秒)")
private Integer second;
@ApiModelProperty("下载地址")
private String url;
}

View File

@@ -0,0 +1,46 @@
package com.abin.mallchat.common.chat.domain.entity.msg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* Description: 视频消息入参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-06-04
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class VideoMsgDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("大小(字节)")
private Long size;
@ApiModelProperty("下载地址")
private String url;
@ApiModelProperty("缩略图宽度(像素)")
@NotNull
private Integer thumbWidth = BigDecimal.ROUND_HALF_DOWN;
@ApiModelProperty("缩略图高度(像素)")
@NotNull
private Integer thumbHeight;
@ApiModelProperty("缩略图大小(字节)")
private Long thumbSize;
@ApiModelProperty("缩略图下载地址")
@NotBlank
private String thumbUrl;
}

View File

@@ -16,8 +16,12 @@ import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
public enum MessageTypeEnum {
TEXT(1, "正常"),
TEXT(1, "正常消息"),
RECALL(2, "撤回消息"),
IMG(3, "图片"),
FILE(4, "文件"),
SOUND(5, "语音"),
VIDEO(6, "视频"),
;
private final Integer type;

View File

@@ -0,0 +1,36 @@
package com.abin.mallchat.common.common.config;
import com.abin.mallchat.common.common.utils.oss.MinIOTemplate;
import com.abin.mallchat.common.common.utils.oss.OssProperties;
import io.minio.MinioClient;
import lombok.SneakyThrows;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({MinioClient.class})
@EnableConfigurationProperties(OssProperties.class)
@ConditionalOnExpression("${oss.enabled}")
@ConditionalOnProperty(value = "oss.type", havingValue = "minio")
public class MinIOConfiguration {
@Bean
@SneakyThrows
@ConditionalOnMissingBean(MinioClient.class)
public MinioClient minioClient(OssProperties ossProperties) {
return MinioClient.builder()
.endpoint(ossProperties.getEndpoint())
.credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey())
.build();
}
@Bean
@ConditionalOnBean({MinioClient.class})
@ConditionalOnMissingBean(MinIOTemplate.class)
public MinIOTemplate minioTemplate(MinioClient minioClient, OssProperties ossProperties) {
return new MinIOTemplate(minioClient, ossProperties);
}
}

View File

@@ -9,6 +9,8 @@ import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@@ -28,7 +30,9 @@ public class LockService {
try {
return supplier.get();//执行锁内的代码逻辑
} finally {
lock.unlock();
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
@@ -37,6 +41,10 @@ public class LockService {
return executeWithLockThrows(key, waitTime, unit, supplier::get);
}
public <T> T executeWithLock(String key, Supplier<T> supplier) {
return executeWithLock(key, -1, TimeUnit.MILLISECONDS, supplier);
}
@FunctionalInterface
public interface SupplierThrow<T> {
@@ -47,4 +55,15 @@ public class LockService {
*/
T get() throws Throwable;
}
public static void main(String[] args) {
List<String> sensitiveList = Arrays.asList("abcd", "abcbba", "adabca");
String text = "abcdefg";
for (String s : sensitiveList) {
boolean hit = text.contains(s);
System.out.println(hit);
}
}
}

View File

@@ -1,7 +1,7 @@
package com.abin.mallchat.common.common.utils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.io.BufferedReader;
import java.io.IOException;

View File

@@ -0,0 +1,163 @@
package com.abin.mallchat.common.common.utils.oss;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import com.abin.mallchat.common.common.utils.oss.domain.OssReq;
import com.abin.mallchat.common.common.utils.oss.domain.OssResp;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Slf4j
@AllArgsConstructor
public class MinIOTemplate {
/**
* MinIO 客户端
*/
MinioClient minioClient;
/**
* MinIO 配置类
*/
OssProperties ossProperties;
/**
* 查询所有存储桶
*
* @return Bucket 集合
*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**
* 桶是否存在
*
* @param bucketName 桶名
* @return 是否存在
*/
@SneakyThrows
public boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 创建存储桶
*
* @param bucketName 桶名
*/
@SneakyThrows
public void makeBucket(String bucketName) {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 删除一个空桶 如果存储桶存在对象不为空时,删除会报错。
*
* @param bucketName 桶名
*/
@SneakyThrows
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 返回临时带签名、过期时间一天、Get请求方式的访问URL
*/
@SneakyThrows
public OssResp getPreSignedObjectUrl(OssReq req) {
String absolutePath = req.isAutoPath() ? generateAutoPath(req) : req.getFilePath() + StrUtil.SLASH + req.getFileName();
String url = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(ossProperties.getBucketName())
.object(absolutePath)
.expiry(60 * 60 * 24)
.build());
return OssResp.builder()
.uploadUrl(url)
.downloadUrl(getDownloadUrl(ossProperties.getBucketName(), absolutePath))
.build();
}
private String getDownloadUrl(String bucket, String pathFile) {
return ossProperties.getEndpoint() + StrUtil.SLASH + bucket + pathFile;
}
/**
* GetObject接口用于获取某个文件Object。此操作需要对此Object具有读权限。
*
* @param bucketName 桶名
* @param ossFilePath Oss文件路径
*/
@SneakyThrows
public InputStream getObject(String bucketName, String ossFilePath) {
return minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(ossFilePath).build());
}
/**
* 查询桶的对象信息
*
* @param bucketName 桶名
* @param recursive 是否递归查询
* @return
*/
@SneakyThrows
public Iterable<Result<Item>> listObjects(String bucketName, boolean recursive) {
return minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).recursive(recursive).build());
}
/**
* 生成随机文件名,防止重复
*
* @return
*/
public String generateAutoPath(OssReq req) {
String uid = Optional.ofNullable(req.getUid()).map(String::valueOf).orElse("000000");
cn.hutool.core.lang.UUID uuid = cn.hutool.core.lang.UUID.fastUUID();
String suffix = FileNameUtil.getSuffix(req.getFileName());
String year = DateUtil.format(new Date(), DatePattern.NORM_YEAR_PATTERN);
return req.getFilePath() + StrUtil.SLASH + year + StrUtil.SLASH + uid + StrUtil.SLASH + uuid + StrUtil.DOT + suffix;
}
/**
* 获取带签名的临时上传元数据对象前端可获取后直接上传到Minio
*
* @param bucketName
* @param fileName
* @return
*/
@SneakyThrows
public Map<String, String> getPreSignedPostFormData(String bucketName, String fileName) {
// 为存储桶创建一个上传策略过期时间为7天
PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7));
// 设置一个参数key值为上传对象的名称
policy.addEqualsCondition("key", fileName);
// 添加Content-Type以"image/"开头,表示只能上传照片
policy.addStartsWithCondition("Content-Type", "image/");
// 设置上传文件的大小 64kiB to 10MiB.
policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024);
return minioClient.getPresignedPostFormData(policy);
}
}

View File

@@ -0,0 +1,17 @@
package com.abin.mallchat.common.common.utils.oss;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class OssFile {
/**
* OSS 存储时文件路径
*/
String ossFilePath;
/**
* 原始文件名
*/
String originalFileName;
}

View File

@@ -0,0 +1,39 @@
package com.abin.mallchat.common.common.utils.oss;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
/**
* 是否开启
*/
Boolean enabled;
/**
* 存储对象服务器类型
*/
OssType type;
/**
* OSS 访问端点,集群时需提供统一入口
*/
String endpoint;
/**
* 用户名
*/
String accessKey;
/**
* 密码
*/
String secretKey;
/**
* 存储桶
*/
String bucketName;
}

View File

@@ -0,0 +1,39 @@
package com.abin.mallchat.common.common.utils.oss;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum OssType {
/**
* Minio 对象存储
*/
MINIO("minio", 1),
/**
* 华为 OBS
*/
OBS("obs", 2),
/**
* 腾讯 COS
*/
COS("tencent", 3),
/**
* 阿里巴巴 SSO
*/
ALIBABA("alibaba", 4),
;
/**
* 名称
*/
final String name;
/**
* 类型
*/
final int type;
}

View File

@@ -0,0 +1,29 @@
package com.abin.mallchat.common.common.utils.oss.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Description: 上传url请求入参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-03-23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OssReq {
@ApiModelProperty(value = "文件存储路径")
private String filePath;
@ApiModelProperty(value = "文件名")
private String fileName;
@ApiModelProperty(value = "请求的uid")
private Long uid;
@ApiModelProperty(value = "自动生成地址")
@Builder.Default
private boolean autoPath = true;
}

View File

@@ -0,0 +1,26 @@
package com.abin.mallchat.common.common.utils.oss.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Description: 上传url请求出参
* Author: <a href="https://github.com/zongzibinbin">abin</a>
* Date: 2023-03-23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OssResp {
@ApiModelProperty(value = "上传的临时url")
private String uploadUrl;
@ApiModelProperty(value = "成功后能够下载的url")
private String downloadUrl;
}

View File

@@ -5,10 +5,10 @@ import com.abin.mallchat.common.sensitive.dao.SensitiveWordDao;
import com.abin.mallchat.common.sensitive.domain.SensitiveWord;
import com.abin.mallchat.common.sensitive.service.ISensitiveWordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import java.util.List;

View File

@@ -18,4 +18,11 @@ mallchat.wx.secret=380bfc1c9147fdsf4sf07
# 接口配置里的Token值
mallchat.wx.token=sdfsf
# 接口配置里的EncodingAESKey值
mallchat.wx.aesKey=sha1
mallchat.wx.aesKey=sha1
##################OSS配置##################
oss.enabled=true
oss.type=minio
oss.endpoint=http://localhost:9000
oss.access-key=BEZ213
oss.secret-key=Ii4vCMIXuFfds1EZ8e7RXI2342342kV
oss.bucketName=default

View File

@@ -18,4 +18,11 @@ mallchat.wx.secret=380bfc1c9147fdsf4sf07
# 接口配置里的Token值
mallchat.wx.token=sdfsf
# 接口配置里的EncodingAESKey值
mallchat.wx.aesKey=sha1
mallchat.wx.aesKey=sha1
##################OSS配置##################
oss.enabled=true
oss.type=minio
oss.endpoint=http://localhost:9000
oss.access-key=BEZ213
oss.secret-key=Ii4vCMIXuFe241dsfEZ8e7RXI2342342kV
oss.bucketName=default