diff --git a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java index fd32367..4882c7a 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java @@ -1,5 +1,7 @@ package com.flyfish.framework.annotations; +import org.springframework.core.annotation.AliasFor; + import java.lang.annotation.*; /** @@ -16,8 +18,22 @@ public @interface Property { * 显示标题 * @return 结果 */ + @AliasFor("title") + String value() default ""; + + /** + * 显示标题(别名) + * @return 结果 + */ + @AliasFor("value") String title() default ""; + /** + * 描述 + * @return 结果 + */ + String description() default ""; + /** * 被继承的,用于父类,自动拼接名称 * @return 结果 diff --git a/flyfish-data/src/main/java/com/flyfish/framework/domain/base/AuditDomain.java b/flyfish-data/src/main/java/com/flyfish/framework/domain/base/AuditDomain.java index ac18078..5e70ed4 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/domain/base/AuditDomain.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/domain/base/AuditDomain.java @@ -25,21 +25,21 @@ public abstract class AuditDomain extends Domain { * 创建日期 */ @CreatedDate - @Property(title = "创建日期") + @Property("创建日期") protected Date createTime; /** * 修改日期 */ @LastModifiedDate - @Property(title = "更新日期") + @Property("更新日期") protected Date modifyTime; /** * 创建者 */ @CreatedBy - @Property(title = "创建人") + @Property("创建人") protected String creator; /** @@ -51,7 +51,7 @@ public abstract class AuditDomain extends Domain { * 修改者 */ @LastModifiedBy - @Property(title = "更新人") + @Property("更新人") protected String modifier; /** diff --git a/flyfish-data/src/main/java/com/flyfish/framework/domain/base/TreeQo.java b/flyfish-data/src/main/java/com/flyfish/framework/domain/base/TreeQo.java new file mode 100644 index 0000000..92ab739 --- /dev/null +++ b/flyfish-data/src/main/java/com/flyfish/framework/domain/base/TreeQo.java @@ -0,0 +1,23 @@ +package com.flyfish.framework.domain.base; + +import com.flyfish.framework.builder.CriteriaBuilder; +import lombok.Getter; +import lombok.Setter; + +/** + * 属性菜单的qo + */ +@Getter +@Setter +public class TreeQo extends NameLikeQo { + + private Boolean leaf; + + private Integer level; + + @Override + public CriteriaBuilder criteriaBuilder() { + return super.criteriaBuilder() + .with("level", "leaf"); + } +} diff --git a/flyfish-data/src/main/java/com/flyfish/framework/domain/po/User.java b/flyfish-data/src/main/java/com/flyfish/framework/domain/po/User.java index b2684a2..26f7afb 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/domain/po/User.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/domain/po/User.java @@ -24,6 +24,7 @@ import java.util.List; public class User extends AuditDomain implements IUser { private static final long serialVersionUID = -960011918745179950L; + /** * 用户类型 */ diff --git a/flyfish-file/pom.xml b/flyfish-file/pom.xml new file mode 100644 index 0000000..3e9a350 --- /dev/null +++ b/flyfish-file/pom.xml @@ -0,0 +1,27 @@ + + + + flyfish-framework + com.flyfish.framework + 0.0.1-SNAPSHOT + + 4.0.0 + + flyfish-file + + + 8 + 8 + + + + + com.flyfish.framework + flyfish-web + ${project.version} + true + + + \ No newline at end of file diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/config/UploadConfiguration.java b/flyfish-file/src/main/java/com/flyfish/framework/file/config/UploadConfiguration.java new file mode 100644 index 0000000..debb853 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/config/UploadConfiguration.java @@ -0,0 +1,26 @@ +package com.flyfish.framework.file.config; + + +import com.flyfish.framework.file.utils.SystemUtils; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +@Configuration +@ConfigurationProperties(prefix = "upload") +@Getter +@Setter +public class UploadConfiguration { + + private String localPath = SystemUtils.isWindows() ? "C:/file-temp/" : "/usr/local/resources/"; + + public String path(List> generator) { + return generator.stream().map(Supplier::get) + .collect(Collectors.joining("/")); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentController.java b/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentController.java new file mode 100644 index 0000000..c99a176 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentController.java @@ -0,0 +1,14 @@ +package com.flyfish.framework.file.controller; + + +import com.flyfish.framework.controller.BaseController; +import com.flyfish.framework.file.domain.Attachment; +import com.flyfish.framework.file.domain.AttachmentQo; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/attachments") +public class AttachmentController extends BaseController { + +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentUploadController.java b/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentUploadController.java new file mode 100644 index 0000000..0f2af16 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/controller/AttachmentUploadController.java @@ -0,0 +1,64 @@ +package com.flyfish.framework.file.controller; + +import com.flyfish.framework.bean.Result; +import com.flyfish.framework.beans.annotations.RestMapping; +import com.flyfish.framework.file.config.UploadConfiguration; +import com.flyfish.framework.file.domain.Attachment; +import com.flyfish.framework.file.service.AttachmentService; +import com.flyfish.framework.file.utils.DownloadUtils; +import com.flyfish.framework.service.BaseService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +/** + * 附件上传相关 + * @author wangyu + */ +@RestMapping("/attachments") +public class AttachmentUploadController { + + @Resource + private UploadConfiguration configuration; + @Resource + private AttachmentService attachmentService; + + /** + * 上传媒体,支持多个同时上传 + * + * @param files 文件 + * @return 结果 + */ + @PostMapping("") + public Mono>> uploadAttachment(@RequestPart("file") Flux files) { + return files.flatMap(attachmentService::upload) + .reduce(Result.accept(new ArrayList<>()), ((listResult, attachment) -> { + listResult.getData().add(attachment); + return listResult; + })); + } + + @GetMapping("/**") + public Mono downloadStatic(ServerHttpRequest request, ServerHttpResponse response) { + String path = StringUtils.substringAfterLast(request.getURI().getPath(), "/attachments"); + return DownloadUtils.download(configuration.getLocalPath() + path, response); + } + + @GetMapping("/downloads/{id}") + public Mono downloadAttachment(@PathVariable String id, ServerHttpResponse response) { + return Mono.justOrEmpty(attachmentService.getById(id)) + .flatMap(attachment -> DownloadUtils.download(configuration.getLocalPath() + + attachment.getPath(), response)); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Attachment.java b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Attachment.java new file mode 100644 index 0000000..a0053bd --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Attachment.java @@ -0,0 +1,29 @@ +package com.flyfish.framework.file.domain; + +import com.flyfish.framework.domain.base.AuditDomain; +import lombok.*; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document(collection = "attachments") +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Attachment extends AuditDomain { + + /** + * 大小 + */ + private String size; + + /** + * 附件路径 + */ + private String path; + + /** + * 附件Url + */ + private String url; +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/domain/AttachmentQo.java b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/AttachmentQo.java new file mode 100644 index 0000000..acb083b --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/AttachmentQo.java @@ -0,0 +1,7 @@ +package com.flyfish.framework.file.domain; + + +import com.flyfish.framework.domain.base.NameLikeQo; + +public class AttachmentQo extends NameLikeQo { +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Media.java b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Media.java new file mode 100644 index 0000000..5d4b7c3 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/domain/Media.java @@ -0,0 +1,34 @@ +package com.flyfish.framework.file.domain; + +import com.flyfish.framework.file.enums.MediaType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Media { + + /** + * 媒体类型 + */ + private MediaType type; + + /** + * 媒体路径 + */ + private String url; + + /** + * 媒体大小 + */ + private String size; + + /** + * 缩略图 + */ + private String thumb; +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaType.java b/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaType.java new file mode 100644 index 0000000..14819de --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaType.java @@ -0,0 +1,29 @@ +package com.flyfish.framework.file.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@Getter +@AllArgsConstructor +public enum MediaType { + + UNKNOWN(Collections.emptyList(), "未知媒体"), + AUDIO(Arrays.asList("mp3", "wma", "amr"), "音频"), + VIDEO(Arrays.asList("mp4", "avi", "wmv", "mkv", "rmvb", "flv", "3gp", "m3u", "m3u8"), "视频"), + GRAPHICS(Arrays.asList("jpg", "jpeg", "bpm", "tiff", "png", "gif", "svg", "ico"), "图像"), + ARCHIVE(Arrays.asList("zip", "gz"), "压缩文档"); + + private final List suffix; + + private final String name; + + public static MediaType accept(String filename) { + String extend = StringUtils.substringAfterLast(filename, "."); + return Arrays.stream(values()).filter(value -> value.suffix.contains(extend)).findFirst().orElse(UNKNOWN); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaTypes.java b/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaTypes.java new file mode 100644 index 0000000..3855e9c --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/enums/MediaTypes.java @@ -0,0 +1,36 @@ +package com.flyfish.framework.file.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +@AllArgsConstructor +@Getter +public enum MediaTypes { + + OCT_STREAM(Collections.emptyList(), extend -> MediaType.APPLICATION_OCTET_STREAM), + IMAGE(Arrays.asList("jpg", "jpeg", "png", "gif"), extend -> { + return MediaType.valueOf("image/" + (extend.equalsIgnoreCase("jpg") ? "jpeg" : extend)); + }), + HLS(Arrays.asList("m3u8"), extend -> { + return new MediaType("application", "vnd.apple.mpegurl"); + }); + + + private List suffix; + + private Function mediaType; + + public static MediaType getMediaType(String filename) { + String extend = StringUtils.substringAfterLast(filename, "."); + return Arrays.stream(values()).filter(value -> value.suffix.contains(extend)) + .map(type -> type.getMediaType().apply(extend)) + .findFirst().orElse(MediaType.APPLICATION_OCTET_STREAM); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/repository/AttachmentRepository.java b/flyfish-file/src/main/java/com/flyfish/framework/file/repository/AttachmentRepository.java new file mode 100644 index 0000000..8fcbbcf --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/repository/AttachmentRepository.java @@ -0,0 +1,8 @@ +package com.flyfish.framework.file.repository; + + +import com.flyfish.framework.file.domain.Attachment; +import com.flyfish.framework.repository.DefaultRepository; + +public interface AttachmentRepository extends DefaultRepository { +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/service/AttachmentService.java b/flyfish-file/src/main/java/com/flyfish/framework/file/service/AttachmentService.java new file mode 100644 index 0000000..e0ebeb0 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/service/AttachmentService.java @@ -0,0 +1,38 @@ +package com.flyfish.framework.file.service; + + +import com.flyfish.framework.file.domain.Attachment; +import com.flyfish.framework.file.utils.FileSizeUtils; +import com.flyfish.framework.service.impl.BaseServiceImpl; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; + +@Service +public class AttachmentService extends BaseServiceImpl { + + private static String URL = "/api/attachments/"; + @Resource + private FileService fileService; + + /** + * 上传媒体文件 + * + * @param part 文件 + * @return 结果 + */ + public Mono upload(FilePart part) { + return fileService.saveLocal(part) + .map(path -> { + Attachment attachment = Attachment.builder() + .size(FileSizeUtils.size(part.headers().getContentLength())) + .path(path) + .url(URL + path) + .build(); + attachment.setName(part.filename()); + return create(attachment); + }); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/service/FileService.java b/flyfish-file/src/main/java/com/flyfish/framework/file/service/FileService.java new file mode 100644 index 0000000..ddbb8f9 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/service/FileService.java @@ -0,0 +1,57 @@ +package com.flyfish.framework.file.service; + + +import com.flyfish.framework.file.config.UploadConfiguration; +import com.flyfish.framework.file.utils.SystemUtils; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; + +/** + * 文件服务 + * + * @author wangyu + */ +@Service +public class FileService { + + @Resource + private UploadConfiguration uploadConfiguration; + + private List> generator = Arrays.asList( + SystemUtils::generateDatePath + ); + + /** + * 保存本地文件 + * + * @return 保存到本地,并返回相对路径 + */ + public Mono saveLocal(FilePart part) { + String localPath = uploadConfiguration.getLocalPath(); + String path = uploadConfiguration.path(generator) + "/" + part.filename(); + String fullPath = localPath + path; + SystemUtils.testPath(fullPath); + return part.transferTo(Paths.get(fullPath)) + .then(Mono.just(path)); + } + + /** + * 根据指定目录保存文件 + * + * @param part 文件 + * @param path 路径 + * @return 结果 + */ + public Mono saveLocalPath(FilePart part, String path) { + SystemUtils.testPath(path); + return part.transferTo(Paths.get(path)) + .then(Mono.just(path)); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/service/MediaService.java b/flyfish-file/src/main/java/com/flyfish/framework/file/service/MediaService.java new file mode 100644 index 0000000..349f94a --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/service/MediaService.java @@ -0,0 +1,96 @@ +package com.flyfish.framework.file.service; + + +import com.flyfish.framework.file.config.UploadConfiguration; +import com.flyfish.framework.file.domain.Media; +import com.flyfish.framework.file.enums.MediaType; +import com.flyfish.framework.file.utils.FileSizeUtils; +import com.flyfish.framework.file.utils.ZipUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import java.io.IOException; + +/** + * 媒体服务 + * + * @author wangyu + */ +@Service +@Slf4j +public class MediaService { + + private static String URL = "/api/media/"; + @Resource + private FileService fileService; + @Resource + private UploadConfiguration uploadConfiguration; + + /** + * 上传媒体文件 + * + * @param part 文件 + * @return 结果 + */ + public Mono upload(FilePart part) { + return fileService.saveLocal(part) + .map(path -> Media.builder() + .size(FileSizeUtils.size(part.headers().getContentLength())) + .type(MediaType.accept(part.filename())) + .url(URL + path) + .build() + ); + } + + /** + * 分析原媒体文件,尝试解压 + * + * @param part 原文件 + * @return 结果 + */ + public Mono slice(FilePart part) { + String localPath = uploadConfiguration.getLocalPath(); + return fileService.saveLocal(part) + .flatMap(path -> { + try { + return ZipUtils.unzip(localPath + path) + .stream().filter(file -> file.endsWith(".m3u8")) + .map(file -> file.replace(localPath, "")) + .findFirst() + .map(Mono::just) + .orElse(Mono.just(path)); + } catch (IOException e) { + log.error("很不幸,解压发生了错误:", e); + } + return Mono.just(path); + }) + .map(path -> Media.builder() + .size(FileSizeUtils.size(part.headers().getContentLength())) + .type(MediaType.accept(part.filename())) + .url(URL + path) + .build() + ); + } + + /** + * 上传到绝对路径 + * + * @param part 文件 + * @param absolutePath 绝对路径 + * @return 结果 + */ + public Mono upload(FilePart part, String absolutePath) { + Mono saver = StringUtils.isNotBlank(absolutePath) ? fileService.saveLocalPath(part, absolutePath) : + fileService.saveLocal(part); + return saver.map(path -> Media.builder() + .size(FileSizeUtils.size(part.headers().getContentLength())) + .type(MediaType.accept(part.filename())) + .url(URL + path) + .build() + ); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/utils/DownloadUtils.java b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/DownloadUtils.java new file mode 100644 index 0000000..5723627 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/DownloadUtils.java @@ -0,0 +1,26 @@ +package com.flyfish.framework.file.utils; + +import com.flyfish.framework.file.enums.MediaTypes; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.ContentDisposition; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ZeroCopyHttpOutputMessage; +import org.springframework.http.server.reactive.ServerHttpResponse; +import reactor.core.publisher.Mono; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public abstract class DownloadUtils { + + public static Mono download(String path, ServerHttpResponse response) { + ZeroCopyHttpOutputMessage zeroCopyResponse = (ZeroCopyHttpOutputMessage) response; + HttpHeaders headers = zeroCopyResponse.getHeaders(); + headers.setContentType(MediaTypes.getMediaType(path)); + headers.setContentDisposition(ContentDisposition.builder("attachment") + .filename(StringUtils.substringAfterLast(path, "/")) + .build()); + Path file = Paths.get(path); + return zeroCopyResponse.writeWith(file, 0, file.toFile().length()); + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileMD5Utils.java b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileMD5Utils.java new file mode 100644 index 0000000..ee5e73c --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileMD5Utils.java @@ -0,0 +1,123 @@ +/** + * FileMd5Util.java + * com.bibenet.cedp.utils + *

+ * **************************************************************************** + * Change Log + *

+ * 1. wangyu create me at 2016年7月16日上午9:48:14 for class DESC. + * 2. + * **************************************************************************** + * Copyright (c) 2016, www.bibenet.com All Rights Reserved.O(∩_∩)O + */ + +package com.flyfish.framework.file.utils; + + +import com.flyfish.framework.utils.Assert; + +import java.io.*; +import java.math.BigInteger; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + + +/** + * @author wangyu + * @name

FileMD5Util

+ * @desc 文件MD5校验工具 <1GB + */ +public class FileMD5Utils { + + /** + *

getMd5ByFile

+ * 获取文件的Md5 + * + * @param file 文件 + * @return md5 + */ + public static String getMd5ByFile(File file) { + + FileInputStream in = null; + try { + in = new FileInputStream(file); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return getMd5ByInputStream(in, file.length()); + } + + /** + *

getMd5ByInputStream

+ * 获取流的Md5 + * + * @param in 输入流 + * @param length 文件长度 + * @return md5 + */ + public static String getMd5ByInputStream(InputStream in, Long length) { + String value = null; + Assert.notNull(in, "计算md5的输入流为null!"); + if (in instanceof FileInputStream) { + try { + MappedByteBuffer byteBuffer = ((FileInputStream) in).getChannel().map(FileChannel.MapMode.READ_ONLY, 0, length); + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(byteBuffer); + BigInteger bi = new BigInteger(1, md5.digest()); + value = bi.toString(16); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + try { + value = getMd5ByASCIIStream(in, length); + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } finally { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return value; + } + + private static String getMd5ByASCIIStream(InputStream in, Long length) throws NoSuchAlgorithmException, IOException { + StringBuilder md5 = new StringBuilder(); + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] dataBytes = new byte[1024]; + + int nread; + while ((nread = in.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + byte[] mdbytes = md.digest(); + + // 转换二进制到HEX + for (byte mdByte : mdbytes) { + md5.append(Integer.toString((mdByte & 0xff) + 0x100, 16).substring(1)); + } + return md5.toString(); + } + + public static String getMd5ByPath(String path, Long length) { + Assert.notNull(path, "md5验证时文件路径为空!"); + try { + return getMd5ByInputStream(new FileInputStream(path), length); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return null; + } +} + diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileSizeUtils.java b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileSizeUtils.java new file mode 100644 index 0000000..e69a6a8 --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/FileSizeUtils.java @@ -0,0 +1,51 @@ +/** + * create by InteliJ Idea + *

+ * **************************************************************************** + * Change Log + *

+ * 1. wangyu create me at 2018年03月22日17:59 for class DESC. 2. + * **************************************************************************** + * Copyright (c) 2018, www.bibenet.com All Rights Reserved.O(∩_∩)O + */ +package com.flyfish.framework.file.utils; + +/** + * 文件大小工具类 + * + * @author wangyu + */ +public class FileSizeUtils { + + public static String size(Long size) { + if (null != size) { + //如果字节数少于1024,则直接以B为单位,否则先除于1024,后3位因太少无意义 + if (size < 1024) { + return size + "B"; + } else { + size = size / 1024; + } + //如果原字节数除于1024之后,少于1024,则可以直接以KB作为单位 + //因为还没有到达要使用另一个单位的时候 + //接下去以此类推 + if (size < 1024) { + return size + "KB"; + } else { + size = size / 1024; + } + if (size < 1024) { + //因为如果以MB为单位的话,要保留最后1位小数, + //因此,把此数乘以100之后再取余 + size = size * 100; + return size / 100 + "." + + size % 100 + "MB"; + } else { + //否则如果要以GB为单位的,先除于1024再作同样的处理 + size = size * 100 / 1024; + return size / 100 + "." + + size % 100 + "GB"; + } + } + return "0KB"; + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/utils/SystemUtils.java b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/SystemUtils.java new file mode 100644 index 0000000..fc2c11b --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/SystemUtils.java @@ -0,0 +1,95 @@ +package com.flyfish.framework.file.utils; + +import com.flyfish.framework.context.DateContext; +import com.flyfish.framework.utils.Assert; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.io.FileReader; +import java.io.LineNumberReader; +import java.util.Date; + +public abstract class SystemUtils { + + /** + * 判断是否是windows系统 + * + * @return 结果 + */ + public static boolean isWindows() { + String os = System.getProperty("os.name"); + return os.toLowerCase().startsWith("win"); + } + + public static String generateDatePath() { + return DateContext.formatDate(new Date(), "yyyy/MM/dd"); + } + + public static String generateDatePath(Date date) { + return DateContext.formatDate(date, "yyyy/MM/dd"); + } + + /** + * 根据当前位置生成按照指定步长的区间 + * + * @param pos 位置 + * @param step 步长 + * @return 结果 + */ + public static String range(long pos, int step) { + // 求余 + long left = pos % step; + // 求整数商 + long times = pos / step; + // 得出区间 + long min = times * step; + long max = (times + 1) * step; + // 返回 + return min + "-" + max; + } + + /** + * 根据基本路径生成时间戳(年/月/日)的目录接口,并创建文件 + * + * @param fullPath 基本路径 + * @param fileName 文件名 + * @return 结果 + */ + public static File createFile(String fullPath, String fileName) { + testPath(fullPath); + return new File(fullPath + fileName); + } + + /** + * 测试路径,并创建 + * + * @param fullPath 全路径 + */ + public static void testPath(String fullPath) { + if (fullPath.contains(".")) { + fullPath = getPath(fullPath); + } + File directory = new File(fullPath); + if (!directory.exists()) { + if (!directory.mkdirs()) { + Assert.isTrue(directory.exists(), "目录创建失败!"); + } + } + } + + + public static String getPath(String fullPath) { + return StringUtils.substringBeforeLast(fullPath, "/"); + } + + public static long getLineNumber(String fullPath) throws Exception { + try (FileReader in = new FileReader(fullPath); + LineNumberReader reader = new LineNumberReader(in)) { + long skipped = reader.skip(Long.MAX_VALUE); + if (skipped == 0) { + return 0; + } + return reader.getLineNumber(); + } + } +} diff --git a/flyfish-file/src/main/java/com/flyfish/framework/file/utils/ZipUtils.java b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/ZipUtils.java new file mode 100644 index 0000000..b6e668d --- /dev/null +++ b/flyfish-file/src/main/java/com/flyfish/framework/file/utils/ZipUtils.java @@ -0,0 +1,103 @@ +package com.flyfish.framework.file.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.StreamUtils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.zip.ZipFile; + +/** + * zip解压缩工具 + * + * @author wangyu + */ +@Slf4j +public class ZipUtils { + + /** + * 解压到当前目录 + * + * @param path 路径 + */ + public static List unzip(String path) throws IOException { + // 目录 + String directory = StringUtils.substringBeforeLast(path, "/"); + // 解压 + return unzip(path, directory); + } + + /** + * 解压文件到指定目录 + * 解压后的文件名,和之前一致 + * + * @param path zip文件目录 + * @param target 目标解压目录 + */ + public static List unzip(String path, String target) throws IOException { + // 解决中文文件夹乱码 + ZipFile zip = new ZipFile(path, Charset.defaultCharset()); + // 取得真实名字 + String name = StringUtils.substringBefore(StringUtils.substringAfterLast(path, "/"), "."); + // 父目录 + String parentPath = target + "/" + name; + Path pathFile = Paths.get(parentPath); + if (!Files.exists(pathFile)) { + Files.createDirectories(pathFile); + } + + // 循环zip内的文件 + List files = zip.stream().map(entry -> { + String entryName = entry.getName(); + String output; + try (InputStream in = zip.getInputStream(entry)) { + OutputStream out = prepare(output = parentPath + "/" + entryName); + // 拷贝所有流 + StreamUtils.copy(in, out); + return output; + } catch (IOException e) { + log.error("[zip]解压某个文件发生异常:{}", e.getMessage()); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + log.debug("解压了:{}", files); + return files; + } + +// public static void main(String[] args) throws IOException { +// unzip("/Users/wangyu/Documents/ceshi.m3u8"); +// } + + /** + * output + * 准备安全的输出环境 + * + * @param output 输出路径 + * @return 结果 + */ + private static FileOutputStream prepare(String output) throws IOException { + // 输出文件路径信息 + log.info("抽取{}", output); + // 判断路径是否存在,不存在则创建文件路径 + Path targetPath = Paths.get(StringUtils.substringBeforeLast(output, "/")); + if (!Files.exists(targetPath)) { + Files.createDirectories(targetPath); + } + // 判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压 + if (Files.isDirectory(Paths.get(output))) { + throw new IOException("[unzip]文件路径为目录,跳过写入"); + } + return new FileOutputStream(output); + } +} + diff --git a/flyfish-user/README.md b/flyfish-user/README.md new file mode 100644 index 0000000..856ede2 --- /dev/null +++ b/flyfish-user/README.md @@ -0,0 +1,3 @@ +# 用户模块 + +提供快速的用户集成能力,提供多种组合配置 \ No newline at end of file diff --git a/flyfish-user/pom.xml b/flyfish-user/pom.xml index 4a992c4..2d32b60 100644 --- a/flyfish-user/pom.xml +++ b/flyfish-user/pom.xml @@ -17,4 +17,17 @@ 8 + + + com.flyfish.framework + flyfish-data + ${project.version} + + + com.flyfish.framework + flyfish-web + ${project.version} + true + + diff --git a/flyfish-user/src/main/java/com/flyfish/framework/controller/DepartmentController.java b/flyfish-user/src/main/java/com/flyfish/framework/controller/DepartmentController.java new file mode 100644 index 0000000..a302239 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/controller/DepartmentController.java @@ -0,0 +1,15 @@ +package com.flyfish.framework.controller; + +import com.flyfish.framework.domain.DepartmentQo; +import com.flyfish.framework.domain.po.Department; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 部分或者校区controller + */ +@RestController +@RequestMapping("/departments") +public class DepartmentController extends BaseController { + +} \ No newline at end of file diff --git a/flyfish-user/src/main/java/com/flyfish/framework/controller/LoginController.java b/flyfish-user/src/main/java/com/flyfish/framework/controller/LoginController.java new file mode 100644 index 0000000..8fb0abd --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/controller/LoginController.java @@ -0,0 +1,15 @@ +package com.flyfish.framework.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 登录controller + * @author wangyu + */ +@RestController +@RequestMapping("/login") +public class LoginController { + + +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/controller/PermissionController.java b/flyfish-user/src/main/java/com/flyfish/framework/controller/PermissionController.java new file mode 100644 index 0000000..6de853b --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/controller/PermissionController.java @@ -0,0 +1,16 @@ +package com.flyfish.framework.controller; + +import com.flyfish.framework.domain.PermissionQo; +import com.flyfish.framework.domain.po.Permission; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 权限controller + * + * @author wybab + */ +@RestController +@RequestMapping("/permissions") +public class PermissionController extends BaseController { +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/controller/RoleController.java b/flyfish-user/src/main/java/com/flyfish/framework/controller/RoleController.java new file mode 100644 index 0000000..a563a04 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/controller/RoleController.java @@ -0,0 +1,17 @@ +package com.flyfish.framework.controller; + +import com.flyfish.framework.domain.RoleQo; +import com.flyfish.framework.domain.po.Role; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 角色controller + * + * @author wybab + */ +@RestController +@RequestMapping("/roles") +public class RoleController extends BaseController { + +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/controller/UserController.java b/flyfish-user/src/main/java/com/flyfish/framework/controller/UserController.java new file mode 100644 index 0000000..82751c7 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/controller/UserController.java @@ -0,0 +1,52 @@ +package com.flyfish.framework.controller; + +import com.flyfish.framework.bean.Result; +import com.flyfish.framework.configuration.annotations.CurrentUser; +import com.flyfish.framework.domain.UserPasswordDto; +import com.flyfish.framework.domain.UserQo; +import com.flyfish.framework.domain.base.IUser; +import com.flyfish.framework.domain.po.User; +import com.flyfish.framework.service.UserService; +import com.flyfish.framework.utils.Assert; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("/users") +public class UserController extends BaseController { + + /** + * 修改密码逻辑 + * + * @param passwordDto 密码dto + * @param user 用户 + * @return 结果 + */ + @PutMapping("/passwords") + public Result changePassword(@RequestBody UserPasswordDto passwordDto, @CurrentUser User user) { + // 检查原密码 + Assert.isTrue(user.getPassword().equals(passwordDto.getOldPassword()), "原密码不正确!"); + Assert.isTrue(!user.getPassword().equals(passwordDto.getPassword()), "新密码和旧密码一致,输入个新的吧!"); + userContext.setUser(user); + // 更新密码 + User updating = new User(); + updating.setId(user.getId()); + updating.setPassword(passwordDto.getPassword()); + service.updateSelectiveById(updating); + return Result.ok(); + } + + /** + * 获取当前用户 + * + * @return 结果 + */ + @GetMapping("/current") + public Mono> getCurrentUser() { + UserService userService = getService(); + return ReactiveSecurityContextHolder.getContext() + .map(context -> (IUser) context.getAuthentication().getPrincipal()) + .map(Result::ok); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/AdminUserDetails.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/AdminUserDetails.java new file mode 100644 index 0000000..90eb42c --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/AdminUserDetails.java @@ -0,0 +1,153 @@ +package com.flyfish.framework.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.flyfish.framework.domain.base.IUser; +import com.flyfish.framework.domain.po.Department; +import com.flyfish.framework.domain.po.Role; +import com.flyfish.framework.domain.po.User; +import com.flyfish.framework.enums.UserStatus; +import com.flyfish.framework.enums.UserType; +import com.flyfish.framework.utils.CopyUtils; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * 客户端用户详情 + * + * @author wangyu + */ +@Getter +@Setter +public class AdminUserDetails implements UserDetails, IUser { + + private static final long serialVersionUID = -2441854985340378429L; + + private static final List adminTypes = Arrays.asList(UserType.ADMIN, UserType.SUPER_ADMIN); + + + /** + * 判断是否是管理员 + * @param user 用户 + * @return 结果 + */ + public static boolean isAdmin(User user) { + return adminTypes.contains(user.getUserType()); + } + + /** + * 主键 + */ + protected String id; + /** + * 编号 + */ + protected String code; + /** + * 名称 + */ + protected String name; + + /** + * 用户类型 + */ + private UserType userType; + + /** + * 用户状态 + */ + private UserStatus userStatus; + + /** + * 冗余的电话号码 + */ + private String phone; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 是否启用 + */ + private Boolean enable; + + /** + * 能否登录移动端 + */ + private Boolean app; + + /** + * 有效期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date validDate; + + /** + * 可操作校区 + */ + private List departments; + + /** + * 所属角色 + */ + private List roles; + + /** + * 微信openId + */ + private String openId; + + /** + * 查询冗余,标记用户信息 + */ + private Object detail; + + @Override + @JsonIgnore + public Collection getAuthorities() { + return null; + } + + @Override + @JsonIgnore + public boolean isAccountNonExpired() { + return adminTypes.contains(getUserType()); + } + + @Override + @JsonIgnore + public boolean isAccountNonLocked() { + return isAccountNonExpired(); + } + + @Override + @JsonIgnore + public boolean isCredentialsNonExpired() { + return isAccountNonExpired(); + } + + @JsonIgnore + @Override + public boolean isEnabled() { + return BooleanUtils.isTrue(getEnable()); + } + + public User toUser() { + return CopyUtils.copyProps(this, new User()); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/DepartmentQo.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/DepartmentQo.java new file mode 100644 index 0000000..99eff82 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/DepartmentQo.java @@ -0,0 +1,27 @@ +package com.flyfish.framework.domain; + +import com.flyfish.framework.builder.CriteriaBuilder; +import com.flyfish.framework.domain.base.NameLikeQo; +import com.flyfish.framework.domain.po.Department; +import lombok.Getter; +import lombok.Setter; + +/** + * 部门(校区)查询实体 + * + * @author wybab + */ +@Getter +@Setter +public class DepartmentQo extends NameLikeQo { + + private Boolean leaf; + + private Integer level; + + @Override + public CriteriaBuilder criteriaBuilder() { + return super.criteriaBuilder() + .with("level", "leaf"); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/LoginDto.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/LoginDto.java new file mode 100644 index 0000000..5000644 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/LoginDto.java @@ -0,0 +1,16 @@ +package com.flyfish.framework.domain; + +import lombok.Data; + +/** + * 登录DTO + * + * @author wangyu + */ +@Data +public class LoginDto { + + private String username; + + private String password; +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/PermissionQo.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/PermissionQo.java new file mode 100644 index 0000000..467a261 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/PermissionQo.java @@ -0,0 +1,25 @@ +package com.flyfish.framework.domain; + + +import com.flyfish.framework.builder.CriteriaBuilder; +import com.flyfish.framework.domain.base.NameLikeQo; +import com.flyfish.framework.domain.po.Permission; +import lombok.Getter; +import lombok.Setter; + +/** + * 权限查询实体 + * + * @author wybab + */ +@Getter +@Setter +public class PermissionQo extends NameLikeQo { + + private Boolean admin; + + @Override + public CriteriaBuilder criteriaBuilder() { + return super.criteriaBuilder().with("admin"); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/RoleQo.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/RoleQo.java new file mode 100644 index 0000000..94ff3b1 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/RoleQo.java @@ -0,0 +1,26 @@ +package com.flyfish.framework.domain; + +import com.flyfish.framework.builder.CriteriaBuilder; +import com.flyfish.framework.domain.base.NameLikeQo; +import com.flyfish.framework.domain.po.Role; +import lombok.Getter; +import lombok.Setter; + +/** + * 角色查询实体 + * + * @author wybab + */ +@Getter +@Setter +public class RoleQo extends NameLikeQo { + + private Boolean admin; + + private Boolean system; + + @Override + public CriteriaBuilder criteriaBuilder() { + return super.criteriaBuilder().with("admin", "system"); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/UserPasswordDto.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/UserPasswordDto.java new file mode 100644 index 0000000..7d2604a --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/UserPasswordDto.java @@ -0,0 +1,17 @@ +package com.flyfish.framework.domain; + +import lombok.Getter; +import lombok.Setter; + +/** + * 修改密码dto + * @author wangyu + */ +@Getter +@Setter +public class UserPasswordDto { + + private String oldPassword; + + private String password; +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/domain/UserQo.java b/flyfish-user/src/main/java/com/flyfish/framework/domain/UserQo.java new file mode 100644 index 0000000..031703c --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/domain/UserQo.java @@ -0,0 +1,25 @@ +package com.flyfish.framework.domain; + +import com.flyfish.framework.builder.CriteriaBuilder; +import com.flyfish.framework.domain.base.NameLikeQo; +import com.flyfish.framework.domain.po.User; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UserQo extends NameLikeQo { + + private String userType; + + private String username; + + private String password; + + @Override + public CriteriaBuilder criteriaBuilder() { + return super.criteriaBuilder().with("userType", "username", "password"); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/repository/DepartmentRepository.java b/flyfish-user/src/main/java/com/flyfish/framework/repository/DepartmentRepository.java new file mode 100644 index 0000000..9894d2d --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/repository/DepartmentRepository.java @@ -0,0 +1,11 @@ +package com.flyfish.framework.repository; + +import com.flyfish.framework.domain.po.Department; + +/** + * 部门仓库 + * + * @author wybab + */ +public interface DepartmentRepository extends DefaultRepository { +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/repository/PermissionRepository.java b/flyfish-user/src/main/java/com/flyfish/framework/repository/PermissionRepository.java new file mode 100644 index 0000000..3b1d234 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/repository/PermissionRepository.java @@ -0,0 +1,12 @@ +package com.flyfish.framework.repository; + + +import com.flyfish.framework.domain.po.Permission; + +/** + * 权限仓库 + * + * @author wybab + */ +public interface PermissionRepository extends DefaultRepository { +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/repository/ReactiveUserRepository.java b/flyfish-user/src/main/java/com/flyfish/framework/repository/ReactiveUserRepository.java new file mode 100644 index 0000000..92780eb --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/repository/ReactiveUserRepository.java @@ -0,0 +1,14 @@ +package com.flyfish.framework.repository; + +import com.flyfish.framework.domain.po.User; +import reactor.core.publisher.Mono; + +/** + * 异步用户仓库 + * + * @author wybab + */ +public interface ReactiveUserRepository extends DefaultReactiveRepository { + + Mono findByUsername(String username); +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/repository/RoleRepository.java b/flyfish-user/src/main/java/com/flyfish/framework/repository/RoleRepository.java new file mode 100644 index 0000000..56a1367 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/repository/RoleRepository.java @@ -0,0 +1,11 @@ +package com.flyfish.framework.repository; + +import com.flyfish.framework.domain.po.Role; + +/** + * 角色仓库 + * + * @author wybab + */ +public interface RoleRepository extends DefaultRepository { +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/repository/UserRepository.java b/flyfish-user/src/main/java/com/flyfish/framework/repository/UserRepository.java new file mode 100644 index 0000000..74d1914 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/repository/UserRepository.java @@ -0,0 +1,30 @@ +package com.flyfish.framework.repository; + +import com.flyfish.framework.domain.po.User; + +import java.util.Optional; + +/** + * 用户repo + * + * @author wangyu + */ +public interface UserRepository extends DefaultRepository { + + /** + * 通过用户名密码查询用户 + * + * @param username 用户名 + * @param password 密码 + * @return 结果 + */ + Optional findByUsernameAndPassword(String username, String password); + + /** + * 通过用户名查询 + * + * @param username 用户名 + * @return 结果 + */ + Optional findByUsername(String username); +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/.gitkeep b/flyfish-user/src/main/java/com/flyfish/framework/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/DepartmentService.java b/flyfish-user/src/main/java/com/flyfish/framework/service/DepartmentService.java new file mode 100644 index 0000000..ec9de03 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/DepartmentService.java @@ -0,0 +1,10 @@ +package com.flyfish.framework.service; + +import com.flyfish.framework.domain.po.Department; +import com.flyfish.framework.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +@Service +public class DepartmentService extends BaseServiceImpl { + +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/MongoUserDetailsServiceImpl.java b/flyfish-user/src/main/java/com/flyfish/framework/service/MongoUserDetailsServiceImpl.java new file mode 100644 index 0000000..e35f5ad --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/MongoUserDetailsServiceImpl.java @@ -0,0 +1,151 @@ +package com.flyfish.framework.service; + +import com.flyfish.framework.domain.AdminUserDetails; +import com.flyfish.framework.domain.base.IUser; +import com.flyfish.framework.domain.po.User; +import com.flyfish.framework.enums.UserStatus; +import com.flyfish.framework.utils.Assert; +import com.flyfish.framework.utils.CopyUtils; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.security.authentication.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.web.server.context.ServerSecurityContextRepository; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * 基于mongo实现的用户详情 + * + * @author wangyu + */ +@RequiredArgsConstructor +@Service +public class MongoUserDetailsServiceImpl implements MongoUserDetailsService { + + // 存储用户校验规则的map + private static final Map, Supplier> checkMap; + + static { + checkMap = new HashMap<>(); + // 初始化用户校验规则 + checkMap.put(user -> null != user.getEnable() && !user.getEnable() || + user.getUserStatus() == UserStatus.DISABLED, () -> new DisabledException("用户被禁用")); + checkMap.put(user -> user.getUserStatus() == UserStatus.LOCKED, + () -> new LockedException("账户已经锁定!请联系管理员修改密码!")); + } + + private final UserService service; + private final ReactiveUserService userService; + + @Resource + private ServerSecurityContextRepository contextRepository; + + private ReactiveAuthenticationManager authenticationManager; + + @PostConstruct + private void init() { + UserDetailsRepositoryReactiveAuthenticationManager manager = + new UserDetailsRepositoryReactiveAuthenticationManager(this); + manager.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); + authenticationManager = manager; + } + + @Override + public Mono updatePassword(UserDetails userDetails, String s) { + return userService.updateById(((IUser) userDetails).toUser()).map(this::mapToUserDetails); + } + + @Override + public Mono findByUsername(String s) { + return userService.findByUsername(s) + .flatMap(this::validate) + .map(this::mapToUserDetails) + .switchIfEmpty(Mono.defer(() -> Mono.error(new UsernameNotFoundException("用户不存在!")))); + } + + private UserDetails mapToUserDetails(User user) { + AdminUserDetails userDetail = new AdminUserDetails(); + CopyUtils.copyProps(user, userDetail); + return userDetail; + } + + /** + * 校验 + * + * @param user 用户 + * @return 结果 + */ + private Mono validate(User user) { + return checkMap.entrySet().stream() + .filter(entry -> entry.getKey().apply(user)) + .findFirst() + .map(entry -> Mono.error(entry.getValue().get())) + .orElse(Mono.just(user)); + } + + /** + * 通过用户名密码直接认证 + * + * @param user 用户 + * @return 结果 + */ + @Override + public Mono authenticate(UserDetails user, ServerWebExchange exchange) { + return loadContext(user) + .flatMap(securityContext -> contextRepository.save(exchange, securityContext) + .subscriberContext(ReactiveSecurityContextHolder.withSecurityContext( + Mono.just(securityContext))) + .then(Mono.just(securityContext))) + .map(SecurityContext::getAuthentication) + .doOnNext(authentication -> { + if (authentication.getPrincipal() instanceof IUser) { + IUser userDetail = (IUser) authentication.getPrincipal(); + Assert.isTrue(BooleanUtils.isTrue(userDetail.getApp()), "用户没有移动端登录权限!"); + } + }); + } + + @Override + public Mono loadContext(UserDetails user) { + // 构建账号信息 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( + user.getUsername(), user.getPassword()); + // 登录先 + return authenticationManager.authenticate(token) + .map(authentication -> { + SecurityContextImpl securityContext = new SecurityContextImpl(); + securityContext.setAuthentication(authentication); + return securityContext; + }); + } + + /** + * 退出登录 + * + * @param exchange 数据请求 + * @return 结果 + */ + @Override + public Mono logout(ServerWebExchange exchange) { + return ReactiveSecurityContextHolder.getContext() + .switchIfEmpty(contextRepository.load(exchange)) + .flatMap(context -> contextRepository.save(exchange, context)); + + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/PermissionService.java b/flyfish-user/src/main/java/com/flyfish/framework/service/PermissionService.java new file mode 100644 index 0000000..5181222 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/PermissionService.java @@ -0,0 +1,11 @@ +package com.flyfish.framework.service; + + +import com.flyfish.framework.domain.po.Permission; +import com.flyfish.framework.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +@Service +public class PermissionService extends BaseServiceImpl { + +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/ReactiveUserService.java b/flyfish-user/src/main/java/com/flyfish/framework/service/ReactiveUserService.java new file mode 100644 index 0000000..0ae3814 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/ReactiveUserService.java @@ -0,0 +1,27 @@ +package com.flyfish.framework.service; + +import com.flyfish.framework.domain.po.User; +import com.flyfish.framework.repository.ReactiveUserRepository; +import com.flyfish.framework.service.impl.BaseReactiveServiceImpl; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +/** + * 异步用户service + * + * @author wangyu + */ +@Service +public class ReactiveUserService extends BaseReactiveServiceImpl { + + /** + * 获取用户数据 + * + * @param username 用户 + * @return 结果 + */ + public Mono findByUsername(String username) { + return ((ReactiveUserRepository) repository).findByUsername(username); + } + +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/RoleService.java b/flyfish-user/src/main/java/com/flyfish/framework/service/RoleService.java new file mode 100644 index 0000000..6d9b1dd --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/RoleService.java @@ -0,0 +1,43 @@ +package com.flyfish.framework.service; + + +import com.flyfish.framework.domain.PermissionQo; +import com.flyfish.framework.domain.po.Permission; +import com.flyfish.framework.domain.po.Role; +import com.flyfish.framework.enums.RoleType; +import com.flyfish.framework.service.impl.BaseServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class RoleService extends BaseServiceImpl { + + private final PermissionService permissionService; + + /** + * 如果是管理员,设置拥有所有权限 + * + * @param entity 实体 + * @return 结果 + */ + @Override + public Role create(Role entity) { + if (entity.isSystem()) { + entity.setPermissions(permissionService.getAll()); + } else if (BooleanUtils.isTrue(entity.getAdmin())) { + // 如果是管理员,拥有所有权限 + PermissionQo qo = new PermissionQo(); + qo.setAdmin(false); + List permissions = permissionService.getList(qo); + entity.setPermissions(permissions); + } + if (null == entity.getType()) { + entity.setType(RoleType.PC); + } + return super.create(entity); + } +} diff --git a/flyfish-user/src/main/java/com/flyfish/framework/service/UserService.java b/flyfish-user/src/main/java/com/flyfish/framework/service/UserService.java new file mode 100644 index 0000000..63d3258 --- /dev/null +++ b/flyfish-user/src/main/java/com/flyfish/framework/service/UserService.java @@ -0,0 +1,59 @@ +package com.flyfish.framework.service; + +import com.flyfish.framework.domain.UserQo; +import com.flyfish.framework.domain.base.Qo; +import com.flyfish.framework.domain.po.User; +import com.flyfish.framework.enums.UserStatus; +import com.flyfish.framework.enums.UserType; +import com.flyfish.framework.repository.UserRepository; +import com.flyfish.framework.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; +import java.util.Optional; + +@Service +public class UserService extends BaseServiceImpl { + + @Resource + private DepartmentService departmentService; + + /** + * 生成超级管理员 + */ + @PostConstruct + private void init() { + UserQo qo = new UserQo(); + qo.setUserType(UserType.SUPER_ADMIN.name()); + if (count(qo) == 0) { + User user = new User(); + user.setUsername("admin"); + user.setPassword("admin"); + user.setUserType(UserType.SUPER_ADMIN); + user.setEnable(true); + user.setApp(false); + user.setUserStatus(UserStatus.NORMAL); + user.setPhone("10000000000"); + user.setName("超级管理员"); + user.setCode("Administrator"); + createSelective(user); + } + } + + /** + * 获取用户数据 + * + * @param username 用户 + * @return 结果 + */ + public Optional findByUsername(String username) { + return ((UserRepository) repository).findByUsername(username); + } + + @Override + public List getList(Qo query) { + return super.getList(query); + } +} diff --git a/flyfish-web/src/main/java/com/flyfish/framework/controller/EnumController.java b/flyfish-web/src/main/java/com/flyfish/framework/controller/EnumController.java index dd913b1..f277e0b 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/controller/EnumController.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/controller/EnumController.java @@ -35,7 +35,7 @@ public class EnumController { Set> classSet = reflections.getSubTypesOf(NamedEnum.class); // 注入 classSet.stream().filter(clazz -> ClassUtils.isAssignable(clazz, Enum.class)).forEach(clazz -> { - String name = StringFormats.camel2Line(clazz.getSimpleName()); + String name = StringFormats.camel2Line(ClassUtils.getShortClassName(clazz)); List values = Arrays.stream(clazz.getEnumConstants()).reduce(new ArrayList<>(), (result, item) -> { result.add(new EnumValue(((Enum) item).name(), item.getName())); return result; diff --git a/pom.xml b/pom.xml index 938df8b..15276b1 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ flyfish-common flyfish-web flyfish-user + flyfish-file