提交 3c565a73 作者: obcy

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/com/zzsn/knowbase/service/KbKnowledgeProjectService.java
#	src/main/java/com/zzsn/knowbase/service/impl/KbKnowledgeProjectServiceImpl.java
......@@ -160,6 +160,18 @@
<artifactId>htmlcleaner</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.hashids</groupId>
<artifactId>hashids</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
<build>
......
package com.zzsn.knowbase.constant;
import java.time.Duration;
/**
* @author: zhangcx
* @date: 2019/8/7 17:23
*/
public class DocumentConstants {
public static final String HTTP_SCHEME = "http";
/**
* 支持的文档类型
*/
public static final String[] FILE_TYPE_SUPPORT_VIEW = {"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"};
/**
* 不支持编辑的类型
*/
public static final String[] FILE_TYPE_UNSUPPORT_EDIT = {"pdf"};
/**
* 文档文件下载接口地址
*/
public static final String OFFICE_API_DOC_FILE = "%s/download%s";
/**
* 文档信息获取地址
*/
public static final String OFFICE_API_DOC = "%s/api/doc/%s";
/**
* 编辑回调地址
*/
public static final String OFFICE_API_CALLBACK = "%s/callback";
/**
* 预览地址
*/
public static final String OFFICE_VIEWER = "%s/viewer/%s";
/**
* 编辑地址
*/
public static final String OFFICE_EDITOR = "%s/editor/%s";
/**
* 文档redis缓存前缀 格式化
*/
public static final String DOCUMENT_REDIS_KEY_PREFIX_FORMAT = "onlyoffice:document:%s";
/**
* 缓存过期时间: 1天
*/
public static final Duration CACHE_DURATION = Duration.ofDays(1);
public static final String HASH_KEY = "lezhixing";
}
......@@ -16,7 +16,7 @@
*
*/
package com.zzsn.knowbase.enums;
package com.zzsn.knowbase.constant;
public enum DocumentType {
word,
......
package com.zzsn.knowbase.constant;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
/**
* 错误码枚举
* 约定 code 为 0 表示操作成功,
* 1 或 2 等正数表示软件错误,
* -1, -2 等负数表示系统错误.
* @author: zhangcx
* @date: 2019/8/12 13:18
*/
public enum ErrorCodeEnum {
SUCCESS("0", "success"),
DOC_FILE_NOT_EXISTS("1001", "目标文档不存在"),
DOC_FILE_EMPTY("1002", "目标文档是目录或空文件"),
DOC_FILE_UNREADABLE("1003", "目标文档不可读"),
DOC_FILE_OVERSIZE("1004", "目标文档大小超过限制"),
DOC_FILE_TYPE_UNSUPPORTED("1005", "目标文档格式不正确"),
DOC_FILE_MD5_ERROR("1006", "目标文档md5校验失败"),
DOC_FILE_MIME_ERROR("1007", "目标文档MIME检查失败"),
DOC_FILE_NO_EXTENSION("1008", "文件路径不包含扩展名"),
DOC_FILE_EXTENSION_NOT_MATCH("1009", "文件路径和名称后缀不匹配"),
DOC_FILE_KEY_ERROR("1010", "目标文档key计算失败"),
DOC_CACHE_ERROR("1101", "文档信息缓存失败"),
DOC_CACHE_NOT_EXISTS("1102", "从缓存中获取文档信息失败"),
UNSUPPORTED_REQUEST_METHOD("1201", "不支持的请求类型"),
SYSTEM_UNKNOWN_ERROR("-1", "系统繁忙,请稍后再试...");
private String code;
private String msg;
ErrorCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
@Override
public String toString() {
return "ErrorCodeEnum{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
'}';
}
public boolean isSuccessful() {
return this.code == ErrorCodeEnum.SUCCESS.getCode();
}
public boolean isFailed() {
return !isSuccessful();
}
public static void main(String[] args) {
System.out.println("| 代码 | 描述 |");
System.out.println("| ---- | ---- |");
Arrays.stream(ErrorCodeEnum.values()).forEach((ce) -> {
System.out.println("| " + StringUtils.rightPad(ce.getCode(), 4) + " | " + ce.getMsg() + " |");
});
}
}
package com.zzsn.knowbase.controller;
import com.zzsn.knowbase.service.ILocalFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Version 1.0
* @Author: ZhangJingKun
* @Date: 2024/1/10 10:18
* @Content:
*/
@RestController
@RequestMapping("/api/file")
public class KbFileController {
@Autowired
private ILocalFileService localFileService;
/**
* 下载文档接口
* @param fileName
* @param filePath
* @param response
*/
@GetMapping("/download")
public void download(String fileName, String filePath, HttpServletResponse response) {
localFileService.download(fileName, filePath, response);
}
/**
* 编辑文档接口
* @param fileName
* @param filePath
* @param userName
* @param model
* @return
*/
@GetMapping("/edit")
public String editDocFile(String fileName, String filePath, String userName, Model model) {
return localFileService.editDocFile(fileName,filePath,userName,model);
}
/**
* 编辑文档时的回调接口
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/callback")
public void saveDocumentFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
//处理编辑回调逻辑
localFileService.callBack(request, response);
}
}
......@@ -25,10 +25,8 @@ import org.springframework.web.util.WebUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Description: 知识
......@@ -153,7 +151,7 @@ public class KnowledgeController {
public Result<?> intelligentQa(IntelligentQaParam intelligentQaParam) throws IOException {
JSONObject params = new JSONObject();
params.put("question", intelligentQaParam.getQuestion());
params.put("knowledge_base_id", Arrays.asList(intelligentQaParam.getKnowledgeProjectIds().split(",")));
params.put("knowledge_base_id", intelligentQaParam.getKbKnowledgeIds().split(","));
String result = HttpUtil.doPost(intelligentQaUrl, params, 120000);
if (!result.isEmpty()) {
JSONObject jsonObject = JSON.parseObject(result);
......
package com.zzsn.knowbase.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* onlyoffice定义的文档对象
* @author: zhangcx
* @date: 2019/8/7 16:30
*/
@ApiModel("文档实体")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Document implements Serializable {
/** 【必需】文件唯一标识 */
@ApiModelProperty(value = "文档 key", example="xYz123")
private String key;
/** 【必需】文档名称 */
@ApiModelProperty(value = "文档标题", example="test.doc")
private String title;
/** 【必需】文档后缀 */
@ApiModelProperty(value = "文档类型", example="doc")
private String fileType;
/** mimeType 应该先校验文件是否可以打开(非api必须字段) */
//private String mimeType;
/** 文件实体在服务器硬盘存储位置 */
@ApiModelProperty(value = "文档物理存储位置", example="/temp/test.doc")
private String storage;
/** 【必需】文件实体下载地址 */
@ApiModelProperty(value = "文档获取url", example="http://192.168.0.58:20053/api/file/xYz123")
private String url;
/** 打开文件预览/编辑的链接 */
//private String refrence;
/** 文档打开方式 {@link OpenModeEnum} */
//private String mode;
}
package com.zzsn.knowbase.entity;
import lombok.Builder;
import lombok.Data;
/**
* 文档编辑时参数 实体
* @author: zhangcx
* @date: 2019/8/26 14:50
*/
@Data
@Builder
public class DocumentEditParam {
/** 当前打开编辑页面的用户信息 */
private UserBean user;
/** onlyoffice在编辑时请求的回调地址,必选项 */
private String callbackUrl;
@Data
@Builder
public static class UserBean {
/** 用户id */
private String id;
/** 用户姓名 */
private String name;
}
}
......@@ -99,6 +99,10 @@ public class Knowledge implements Serializable {
*/
private String knowledgeProjectId;
/**
* 知识库id
*/
private String kbKnowledgeId;
/**
* 类型
*/
private String typeId;
......
package com.zzsn.knowbase.service;
import com.zzsn.knowbase.entity.Document;
import com.zzsn.knowbase.entity.DocumentEditParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 文档业务 接口
* @author: zhangcx
* @date: 2019/8/7 16:30
*/
public interface DocumentService {
/**
* 构建文档对象
* @param filePath
* @param fileName
* @return documentKey 文档key
*/
String buildDocument(String filePath, String fileName);
/**
* 从缓从中获取文档信息
* @param documentKey
* @return
*/
Document getDocument(String documentKey);
/**
* 下载文档实体文件
* @param documentKey
* @param request
* @param response
* @throws IOException
*/
void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException;
/**
* 构建文档编辑参数 对象
* @param userId
* @param userName
* @return
*/
DocumentEditParam buildDocumentEditParam(String userId, String userName, String fileName);
/**
* 编辑后保存文档实体文件
* @param documentKey
* @param downloadUrl
* @throws IOException
*/
boolean saveDocumentFile(String documentKey, String downloadUrl) throws IOException;
/**
* 获取服务暴露的host(包含端口)
* @return
*/
Object getServerHost();
/**
* 文档是否支持编辑
* @param document
* @return
*/
boolean canEdit(Document document);
}
......@@ -4,8 +4,12 @@ import com.zzsn.knowbase.entity.KnowFile;
import com.zzsn.knowbase.vo.Result;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
......@@ -33,11 +37,32 @@ public interface ILocalFileService {
/**
* 文件下载
* @param
* @param
* @return
*/
//ResponseEntity<Resource> download(String fileName, String filePath);
void download(String fileName, String filePath, HttpServletResponse response);
/**
* 编辑文档
* @param fileName
* @param filePath
* @param userName
* @param model
* @return
*/
ResponseEntity<Resource> download(String fileName, String filePath);
String editDocFile(String fileName, String filePath, String userName, Model model);
/**
* 编辑文档时的回调接口
* @param request
* @param response
* @throws IOException
*/
void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
......@@ -4,8 +4,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.zzsn.knowbase.entity.KbKnowledgeProject;
import com.zzsn.knowbase.vo.Result;
import javax.servlet.http.HttpServletRequest;
/**
* <p>
* 服务类
......@@ -30,4 +28,12 @@ public interface KbKnowledgeProjectService extends IService<KbKnowledgeProject>
Result<?> del(String id);
Result<?> lists(String sign, String projectId, String category, HttpServletRequest httpServletRequest);
Result<?> lists(String sign, String projectId, String category);
/**
* 通过id获取子节点列表
* @param id id
* @return
*/
List<KbKnowledgeProject> getTreeListBy(String id);
}
package com.zzsn.knowbase.service.impl;
import com.alibaba.fastjson.JSON;
import com.zzsn.knowbase.constant.DocumentConstants;
import com.zzsn.knowbase.constant.ErrorCodeEnum;
import com.zzsn.knowbase.entity.Document;
import com.zzsn.knowbase.entity.DocumentEditParam;
import com.zzsn.knowbase.service.DocumentService;
import com.zzsn.knowbase.util.DocumentException;
import com.zzsn.knowbase.util.file.Md5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hashids.Hashids;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* 文档相关业务方法
*
* @author: zhangcx
* @date: @date: 2019/8/7 16:30
*/
@Slf4j
@Service
public class DocumentServiceImpl implements DocumentService {
@Autowired
Environment environment;
@Value("${document.server.host}")
private String serverHost ;
/**
* 大小限制,默认10M
*/
@Value("${document.file-size.limit:10485760}")
private Long docFileSizeLimit;
@Value("${files.docservice.url.site}")
private String documentServerHost;
@Value("${files.docservice.url.api}")
private String documentServerApiJs;
// @Autowired
//private DocumentCacheService cacheService;
@Override
public String buildDocument(String filePath, String fileName) {
if (StringUtils.isBlank(filePath)) {
throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
}
filePath = FilenameUtils.normalize(filePath);
String fileType = StringUtils.lowerCase(FilenameUtils.getExtension(filePath));
if (StringUtils.isBlank(fileType)) {
throw new DocumentException(ErrorCodeEnum.DOC_FILE_NO_EXTENSION);
}
// 如果指定了文件名,则需要校验和实体文件格式是否一致
if (StringUtils.isNotBlank(fileName) && !fileType.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) {
throw new DocumentException(ErrorCodeEnum.DOC_FILE_EXTENSION_NOT_MATCH);
}
File docFile = new File(filePath);
// 校验文件实体
preFileCheck(docFile);
fileName = StringUtils.isNotBlank(fileName) ? fileName : docFile.getName();
String fileKey = this.fileKey(docFile, fileName);
Document document = Document.builder()
.fileType(fileType)
.title(fileName)
.storage(filePath)
.build();
boolean cached = false;
// try {
// cached = cacheService.put(fileKey, document);
// } catch (Exception e) {
// log.error("$$$ 缓存失败~~", e);
// }
// if (!cached) {
// throw new DocumentException(ErrorCodeEnum.DOC_CACHE_ERROR);
// }
document.setKey(fileKey);
return JSON.toJSONString(document);
}
@Override
public Document getDocument(String documentKey) {
Document doc = null;
try {
doc = JSON.parseObject(documentKey,Document.class);
} catch (Exception e) {
log.error("$$$ 获取缓存失败~~", e);
}
if (doc == null) {
throw new DocumentException(ErrorCodeEnum.DOC_CACHE_NOT_EXISTS);
}
// 从缓存中取出后,再绑定非必需缓存字段(节省缓存大小)
// doc.setKey(documentKey);
doc.setUrl(fileUrl(doc.getTitle()));
if (log.isDebugEnabled()) {
log.info(doc.toString());
}
return doc;
}
/**
* 计算文件key值: 文件md5值+路径的短md5值+名称的短md5值
* @param docFile
* @param name 生成协作时文档的docuemnt.key的值
* @return
*/
public String fileKey(File docFile, String name) {
String docFileMd5 = Md5Utils.getFileMd5(docFile);
if (StringUtils.isBlank(docFileMd5)) {
log.error("$$$ 构建文件信息失败!计算文件 md5 失败!");
throw new DocumentException(ErrorCodeEnum.DOC_FILE_MD5_ERROR);
}
String pathShortMd5 = Md5Utils.md5(docFile.getAbsolutePath());
String nameShortMd5 = Md5Utils.md5(name);
Hashids hashids = new Hashids(DocumentConstants.HASH_KEY);
// (将路径字符串短md5值 + 名称字符串短md5值) ==> 再转成短id形式 ==> 作为文档的key(暂且认为是不会重复的)
String key = hashids.encodeHex(String.format("%s%s%s", docFileMd5,pathShortMd5, nameShortMd5));
if (StringUtils.isBlank(key)) {
throw new DocumentException(ErrorCodeEnum.DOC_FILE_KEY_ERROR);
}
return key;
}
/**
* 文件key值
* @param fileType
* @param docCrc32
* @return
*/
private String fileKey(String fileType, String docCrc32) {
return String.format("%s_%s", fileType, docCrc32);
}
/**
* 文件url地址
* @param
* @return
*/
private String fileUrl(String filename) {
return String.format(DocumentConstants.OFFICE_API_DOC_FILE, getServerHost(), "?name="+filename);
// return "http://192.168.0.58:20053/download?name="+filename;
}
/**
* 根据文档信息下载文档文件
* @param documentKey
* @param request
* @param response
* @throws IOException
*/
@Override
public void downloadDocumentFile(String documentKey, HttpServletRequest request, HttpServletResponse response) throws IOException {
Document doc = this.getDocument(documentKey);
File file = new File(doc.getStorage());
try (InputStream reader = new FileInputStream(file);
OutputStream out = response.getOutputStream()) {
byte[] buf = new byte[(int) FileUtils.ONE_KB];
int len = 0;
//response.setContentType(mimeType(file));
while ((len = reader.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.flush();
} catch (Exception e) {
log.error("下载失败!读取文件[" + doc.getStorage() + "]报错~~", e);
}
}
@Override
public DocumentEditParam buildDocumentEditParam(String userId, String userName,String fileName) {
return DocumentEditParam.builder()
.callbackUrl(callbackUrl(fileName))
.user(DocumentEditParam.UserBean.builder()
.id(userId)
.name(userName)
.build())
.build();
}
private String callbackUrl(String fileName) {
String format = String.format(DocumentConstants.OFFICE_API_CALLBACK, getServerHost());
format=format+"?fileName="+fileName;
return format;
}
/**
* 上传文档实体文件
* @param documentKey
* @param downloadUrl
* @throws IOException
*/
@Override
public boolean saveDocumentFile(String documentKey, String downloadUrl) {
if (log.isInfoEnabled()) {
log.info(downloadUrl);
}
// TODO 默认覆盖源文件,如果调用者指定,则存到临时目录?
boolean isCover = true;
Document doc = this.getDocument(documentKey);
String saveFilePath = doc.getStorage();
// if (!isCover) {
// String baseDir = environment.getProperty("java.io.tmpdir");
// saveFilePath = String.format("%s/office-api/%s/%s.%s", baseDir, documentKey, System.currentTimeMillis(), doc.getFileType());
// }
File saveFile = new File(saveFilePath);
boolean success = false;
try {
FileUtils.copyURLToFile(new URL(downloadUrl), saveFile);
if (saveFile.exists() && saveFile.length() > 0) {
success = true;
}
} catch (IOException e) {
log.error("$$$ 保存文档失败!", e);
}
return success;
//TODO 编辑成功后,应该删除之前的编辑状态缓存
}
@Override
public Object getServerHost() {
if (StringUtils.startsWith(serverHost, DocumentConstants.HTTP_SCHEME)) {
return serverHost;
}
return String.format("http://%s", serverHost);
}
@Override
public boolean canEdit(Document document) {
if (ArrayUtils.contains(DocumentConstants.FILE_TYPE_UNSUPPORT_EDIT, document.getFileType())) {
return false;
}
return true;
}
/**
* 获取文档信息api地址
* @param docId
* @return
*/
private String docInfoUrl(String docId) {
return String.format(DocumentConstants.OFFICE_API_DOC, getServerHost(), docId);
}
/**
* 获取文件的 mimetype
* @param file
* @deprecated
* @return
*/
@Deprecated
private String mimeType(File file) {
try {
return Files.probeContentType(Paths.get(file.toURI()));
} catch (IOException e) {
log.error("$$$ 获取文件mimeType错误!", e);
}
return null;
}
/**
* 先校验文档文件
* @param docFile
* @return
*/
private void preFileCheck(File docFile) {
if (log.isDebugEnabled()) {
log.debug("### 开始校验文档:[{}]", docFile.getAbsolutePath());
}
if (docFile == null || !docFile.exists()) {
log.error("$$$ 目标文档不存在,无法打开!");
throw new DocumentException(ErrorCodeEnum.DOC_FILE_NOT_EXISTS);
}
if (docFile.isDirectory() || docFile.length() <= 0) {
log.error("$$$ 目标文档[{}]是目录或空文件,无法打开!", docFile.getAbsolutePath());
throw new DocumentException(ErrorCodeEnum.DOC_FILE_EMPTY);
}
if (!docFile.canRead()) {
log.error("$$$ 目标文档[{}]不可读,无法打开!", docFile.getAbsolutePath());
throw new DocumentException(ErrorCodeEnum.DOC_FILE_UNREADABLE);
}
if (docFile.length() > docFileSizeLimit) {
log.error("$$$ 目标文档大小超过限制({}B > {}B),无法打开!", docFile.length(), docFileSizeLimit);
throw new DocumentException(ErrorCodeEnum.DOC_FILE_OVERSIZE);
}
String ext = StringUtils.lowerCase(FilenameUtils.getExtension(docFile.getName()));
if (!ArrayUtils.contains(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ext)) {
log.error("$$$ 目标文档格式[{}]不正确,无法打开!(只支持:{})",
ext, StringUtils.join(DocumentConstants.FILE_TYPE_SUPPORT_VIEW, ","));
throw new DocumentException(ErrorCodeEnum.DOC_FILE_TYPE_UNSUPPORTED);
}
}
}
......@@ -5,22 +5,18 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzsn.knowbase.entity.KbKnowledgeProject;
import com.zzsn.knowbase.mapper.KbKnowledgeProjectMapper;
import com.zzsn.knowbase.service.KbAuthorizedUserService;
import com.zzsn.knowbase.service.KbAuthuserKnowledgeprojectMapService;
import com.zzsn.knowbase.service.KbKnowledgeProjectService;
import com.zzsn.knowbase.vo.Result;
import com.zzsn.knowbase.vo.UserInfoVo;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
......@@ -35,12 +31,6 @@ import java.util.List;
@Service
public class KbKnowledgeProjectServiceImpl extends ServiceImpl<KbKnowledgeProjectMapper, KbKnowledgeProject> implements KbKnowledgeProjectService {
@Autowired
KbAuthorizedUserService kbAuthorizedUserService;
@Autowired
KbKnowledgeProjectMapper kbKnowledgeProjectMapper;
@Autowired
KbAuthuserKnowledgeprojectMapService authuserKnowledgeprojectMapService;
@Override
@Transactional
public Result<?> add(KbKnowledgeProject knowledgeProject) {
......@@ -119,7 +109,6 @@ public class KbKnowledgeProjectServiceImpl extends ServiceImpl<KbKnowledgeProjec
}
super.updateBatchById(list);
}
super.updateById(knowledgeProject);
return Result.OK();
}
......@@ -157,7 +146,6 @@ public class KbKnowledgeProjectServiceImpl extends ServiceImpl<KbKnowledgeProjec
.eq(KbKnowledgeProject::getPid,projectId)
.eq(StrUtil.isNotBlank(category),KbKnowledgeProject::getCategory,category));
}
return Result.OK(kbKnowledgeProjects);
}
......
......@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zzsn.knowbase.constant.Constants;
import com.zzsn.knowbase.entity.Content;
import com.zzsn.knowbase.entity.KbKnowledgeProject;
import com.zzsn.knowbase.entity.KnowFile;
import com.zzsn.knowbase.entity.Knowledge;
import com.zzsn.knowbase.enums.KnowTypeEnum;
......@@ -15,6 +16,7 @@ import com.zzsn.knowbase.kafka.message.KnowledgeMessage;
import com.zzsn.knowbase.kafka.producer.ProduceInfo;
import com.zzsn.knowbase.service.IKnowledgeService;
import com.zzsn.knowbase.service.ILocalFileService;
import com.zzsn.knowbase.service.KbKnowledgeProjectService;
import com.zzsn.knowbase.util.*;
import com.zzsn.knowbase.vo.KnowledgeParam;
import com.zzsn.knowbase.vo.KnowledgeVO;
......@@ -62,6 +64,8 @@ class KnowledgeServiceImpl implements IKnowledgeService {
@Autowired
private CodeGenerateUtil codeGenerateUtil;
@Autowired
private KbKnowledgeProjectService knowledgeProjectService;
@Autowired
private ProduceInfo produceInfo;
@Value("${python.searchUrl:}")
private String searchUrl;
......@@ -162,8 +166,11 @@ class KnowledgeServiceImpl implements IKnowledgeService {
@Override
public IPage<KnowledgeVO> queryPageList(KnowledgeParam knowledgeParam, Integer pageNo, Integer pageSize, String column, String order) {
List<KbKnowledgeProject> treeList = knowledgeProjectService.getTreeListBy(knowledgeParam.getKnowledgeProjectId());
if(treeList.isEmpty()){
return null;
}
List<Long> catList = treeList.stream().map(KbKnowledgeProject::getId).collect(Collectors.toList());
SearchRequest searchRequest = new SearchRequest(Constants.ES_DATA_FOR_KNOWLEDGE);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//设置分页参数
......@@ -254,9 +261,11 @@ class KnowledgeServiceImpl implements IKnowledgeService {
if (StringUtils.isNotEmpty(knowledgeParam.getId())) {
boolQuery.must(QueryBuilders.termQuery("id", knowledgeParam.getId()));
}
if (!catList.isEmpty()) {
boolQuery.must(QueryBuilders.termsQuery("knowledgeProjectId", catList));
}
if (null != knowledgeParam.getVerifyStatus()) {
boolQuery.must(QueryBuilders.termQuery("verifyStatus", knowledgeParam.getVerifyStatus()));
}
//时间过滤筛选
......@@ -295,10 +304,14 @@ class KnowledgeServiceImpl implements IKnowledgeService {
@Override
public IPage<KnowledgeVO> listFromPython(KnowledgeParam knowledgeParam, Integer pageNo, Integer pageSize, String column, String order) {
Object data = redisUtil.get(genKey(knowledgeParam));
Object data = redisUtil.get(genKey(knowledgeParam));
// if (org.springframework.util.StringUtils.isEmpty(data)) {
if (true) {
List<KbKnowledgeProject> treeList = knowledgeProjectService.getTreeListBy(knowledgeParam.getKnowledgeProjectId());
if(treeList.isEmpty()){
return null;
}
StringBuilder typeNames = new StringBuilder();
if(null != knowledgeParam.getTypeIds()){
for (String type : knowledgeParam.getTypeIds().split(",")) {
......@@ -314,7 +327,7 @@ class KnowledgeServiceImpl implements IKnowledgeService {
params.put("verifierName", knowledgeParam.getVerifierName());
params.put("verifyStatus", knowledgeParam.getVerifyStatus());
params.put("verifyStartTime", knowledgeParam.getVerifyStartTime());
params.put("knowledge_base_id", knowledgeParam.getKnowledgeProjectId());
params.put("knowledge_base_id", treeList);
params.put("verifyEndTime", knowledgeParam.getVerifyEndTime());
params.put("score_threshold", 600);//score
params.put("vector_search_top_k", 100);//number
......
package com.zzsn.knowbase.service.impl;
import com.zzsn.knowbase.constant.Constants;
import com.zzsn.knowbase.entity.Document;
import com.zzsn.knowbase.entity.KnowFile;
import com.zzsn.knowbase.service.DocumentService;
import com.zzsn.knowbase.service.ILocalFileService;
import com.zzsn.knowbase.util.CodeGenerateUtil;
import com.zzsn.knowbase.util.file.FileUtility;
import com.zzsn.knowbase.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
/**
* @Version 1.0
......@@ -36,7 +44,13 @@ import java.util.UUID;
public class LocalFileServiceImpl implements ILocalFileService {
@Autowired
private FileUtility fileUtility;
private FileUtility fileUtility;//文件工具类
@Autowired
private CodeGenerateUtil codeGenerateUtil; //生成唯一id
@Autowired
private DocumentService documentService;
@Value("${files.storage}")
String filesStorage;
......@@ -44,8 +58,9 @@ public class LocalFileServiceImpl implements ILocalFileService {
@Override
public Result<KnowFile> upload(MultipartFile file) {
try {
String fullFileName = file.getOriginalFilename(); // get file name
String fileExtension = fileUtility.getFileExtension(fullFileName); // get file extension
String fileName = file.getOriginalFilename(); // 获取文件名称
String fileExtension = fileUtility.getFileExtension(fileName); // 获取文件扩展名
String fileType = fileUtility.getFileType(fileName); //获取文件类型
long fileSize = file.getSize(); // get file size
// check if the file size exceeds the maximum file size or is less than 0
......@@ -59,21 +74,17 @@ public class LocalFileServiceImpl implements ILocalFileService {
return result;
}
String fileName = file.getOriginalFilename();
String fileSuffix = getFileSuffix(fileName);
String uid = UUID.randomUUID().toString();
String filePath = getFilePath() + uid + "." + fileSuffix;
byte[] bytes = file.getBytes(); // get file in bytes
String fileId = codeGenerateUtil.geneIdNo(Constants.FINANCE, 8);
String filePath = getFilePath() + fileId + fileExtension;
//byte[] bytes = file.getBytes(); // get file in bytes
//Files.write(Paths.get(filePath), bytes);
file.transferTo(new File(filePath));
file.transferTo(new File(filesStorage + filePath));
KnowFile knowFile = new KnowFile();
knowFile.setFileId(uid);
knowFile.setFileId(fileId);
knowFile.setFileName(fileName);
knowFile.setFilePath(filePath);
knowFile.setFileType(fileSuffix);
knowFile.setFileType(fileType);
knowFile.setFileSize(fileSize);
Result result = Result.OK(knowFile);
return result; // create user metadata and return it
......@@ -96,11 +107,163 @@ public class LocalFileServiceImpl implements ILocalFileService {
return list;
}
@Override
public void download(String fileName, String filePath, HttpServletResponse response) {
// path是指想要下载的文件的路径
File file = new File(filesStorage + filePath);
try {
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
//edit
@Override
public String editDocFile(String fileName, String filePath, String userName, Model model) {
String path = filePath;
Document document = documentService.getDocument(documentService.buildDocument(path, fileName));
model.addAttribute("document", document);
// 如果该格式不支持编辑,则返回预览页面
if (!documentService.canEdit(document)) {
return "/demo";
}
model.addAttribute("documentEditParam", documentService.buildDocumentEditParam(userName, userName,fileName));
return "/editor";
}
//编辑文档时回调接口
@Override
public void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException{
PrintWriter writer = null;
JSONObject jsonObj = null;
System.out.println("===saveeditedfile------------");
try {
writer = response.getWriter();
Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
String body = scanner.hasNext() ? scanner.next() : "";
jsonObj = (JSONObject) new JSONParser().parse(body);
System.out.println(jsonObj);
System.out.println("===saveeditedfile:" + jsonObj.get("status"));
/*
0-找不到具有密钥标识符的文档,
1-文档正在编辑,
2-文档已准备好保存,
3-发生文档保存错误,
4-文档已关闭,没有任何更改,
6-文档正在编辑,但当前文档状态已保存,
7-强制保存文档时发生错误。
*/
if ((long) jsonObj.get("status") == 2) {
callBackSaveDocument(jsonObj,request, response);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* status = 1,我们给onlyoffice的服务返回{"error":"0"}的信息,这样onlyoffice会认为回调接口是没问题的,这样就可以在线编辑文档了,否则的话会弹出窗口说明
* 在线编辑还没有关闭,前端有人下载文档时,强制保存最新内容 当status 是6时说明有人在编辑时下载文档
* */
System.out.println(jsonObj.get("status"));
if ((long) jsonObj.get("status") == 6) {
//处理当文档正在编辑为关闭时,下载文档
if (((String)jsonObj.get("userdata")).equals("sample userdata")){
callBackSaveDocument(jsonObj,request, response);
}
System.out.println("====保存失败:");
writer.write("{\"error\":1}");
} else {
//执行删除编辑时下载保存的文件:
deleteTempFile(request.getParameter("fileName"));
writer.write("{\"error\":0}");
}
}
/**
* 编辑以后保存文件
* @param jsonObj
* @param request
* @param response
* @throws IOException
*/
public void callBackSaveDocument(JSONObject jsonObj, HttpServletRequest request, HttpServletResponse response) throws IOException {
/*
* 当我们关闭编辑窗口后,十秒钟左右onlyoffice会将它存储的我们的编辑后的文件,,此时status = 2,通过request发给我们,我们需要做的就是接收到文件然后回写该文件。
* */
/*
* 定义要与文档存储服务保存的编辑文档的链接。当状态值仅等于2或3时,存在链路。
* */
String downloadUri = (String) jsonObj.get("url");
System.out.println("====文档编辑完成,现在开始保存编辑后的文档,其下载地址为:" + downloadUri);
//解析得出文件名
//String fileName = downloadUri.substring(downloadUri.lastIndexOf('/')+1);
String fileName = request.getParameter("fileName");
System.out.println("====下载的文件名:" + fileName);
URL url = new URL(downloadUri);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
//更换为实际的路径F:\DataOfHongQuanzheng\java\eclipse-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Java Example\\app_data\192.168.56.1\
//File savedFile = new File("F:\\DataOfHongQuanzheng\\onlyoffice_data\\app_data\\"+fileName);
File savedFile = new File(filesStorage + fileName);
if (null!=((String) jsonObj.get("userdata"))&&((String) jsonObj.get("userdata")).equals("sample userdata")) {
savedFile = new File(filesStorage + "v1" + fileName);
}
try (FileOutputStream out = new FileOutputStream(savedFile)) {
int read;
final byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
}
connection.disconnect();
}
public void deleteTempFile(String fileName) {
//因为临时存储的文件都添加了v1前缀所以删除文件时需要在文件名测前边加一个v1
File file = new File(filesStorage + "v1" + fileName);
if (file.exists()) {
file.delete();
}
}
/**
* 文件下载
*/
/*
@Override
public ResponseEntity<Resource> download(String fileName, String filePath) {
Path path = Paths.get(filePath);
......@@ -118,15 +281,20 @@ public class LocalFileServiceImpl implements ILocalFileService {
throw new RuntimeException("文件读取失败");
}
}
*/
//获取文件夹路径
//生成文件夹路径
private String getFilePath(){
LocalDate currentDate = LocalDate.now();
//System.out.println("当前日期: " + currentDate);
String filePath = filesStorage + currentDate + "/";
String current = currentDate.toString().replace("-", "");
String filePath = current + "/";
//判断文件夹是否存在,不存在创建
Path directory = Paths.get(filePath);
Path directory = Paths.get(filesStorage + filePath);
if (!Files.exists(directory)) {
try {
Files.createDirectories(directory);
......@@ -138,14 +306,5 @@ public class LocalFileServiceImpl implements ILocalFileService {
}
return filePath;
}
//获取文件后缀
private String getFileSuffix(String fileName){
int lastIndexOfDot = fileName.lastIndexOf('.');
String fileExtension = "";
if (lastIndexOfDot != -1) {
fileExtension = fileName.substring(lastIndexOfDot + 1);
}
return fileExtension;
}
}
package com.zzsn.knowbase.util;
import com.zzsn.knowbase.constant.ErrorCodeEnum;
/**
* 文档异常封装类
* @author zhangcx
*/
public final class DocumentException extends RuntimeException {
private ErrorCodeEnum errorCode;
public DocumentException(ErrorCodeEnum errorCode) {
super(errorCode.getMsg());
this.errorCode = errorCode;
}
public DocumentException(ErrorCodeEnum errorCode, Throwable t) {
super(errorCode.getMsg(), t);
this.errorCode = errorCode;
}
public ErrorCodeEnum getErrorCode() {
return errorCode;
}
}
......@@ -19,7 +19,7 @@
package com.zzsn.knowbase.util.file;
import com.zzsn.knowbase.constant.Constants;
import com.zzsn.knowbase.enums.DocumentType;
import com.zzsn.knowbase.constant.DocumentType;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
......@@ -123,6 +123,15 @@ public class DefaultFileUtility implements FileUtility {
String fileExt = fileName.substring(fileName.lastIndexOf("."));
return fileExt.toLowerCase();
}
//get file type from URL
public String getFileType(final String url) {
String fileName = getFileName(url);
if (fileName == null) {
return null;
}
String fileExt = fileName.substring(fileName.lastIndexOf("."));
return fileExt.toLowerCase();
}
// get an editor internal extension
public String getInternalExtension(final DocumentType type) {
......
......@@ -18,7 +18,7 @@
package com.zzsn.knowbase.util.file;
import com.zzsn.knowbase.enums.DocumentType;
import com.zzsn.knowbase.constant.DocumentType;
import java.nio.file.Path;
import java.util.List;
......@@ -29,6 +29,7 @@ public interface FileUtility {
String getFileName(String url); // get file name from its URL
String getFileNameWithoutExtension(String url); // get file name without extension
String getFileExtension(String url); // get file extension from URL
String getFileType(String url); // get file type from URL
String getInternalExtension(DocumentType type); // get an editor internal extension
List<String> getFileExts(); // get all the supported file extensions
List<String> getFillExts(); // get file extensions that can be filled
......
package com.zzsn.knowbase.util.file;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Md5 工具类
* need commons-codec-1.6.jar +
* @author zhangcx
* @date 2019-7-23
*/
@Slf4j
public class Md5Utils {
private static MessageDigest MD5 = null;
static {
try {
MD5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ne) {
ne.printStackTrace();
}
}
public static String getFileMd5(String filePath) {
if (StringUtils.isBlank(filePath)) {
return null;
}
return getFileMd5(new File(filePath));
}
/**
* 对一个文件获取md5值
*/
public static String getFileMd5(File file) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[8192];
int length;
while ((length = fileInputStream.read(buffer)) != -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
} catch (IOException e) {
log.error("$$$ 获取文件md5失败!", e);
return null;
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
log.error("关闭文件流出错!", e);
}
}
}
/**
* 计算字符串的md5值
* @param target 字符串
* @return md5 value
*/
public static String md5(final String target) {
return DigestUtils.md5Hex(target);
}
}
......@@ -14,9 +14,6 @@ public class IntelligentQaParam {
* 问题
*/
private String question;
/**
* 知识库ids
*/
private String knowledgeProjectIds;
private String kbKnowledgeIds;
}
......@@ -77,5 +77,6 @@ public class KnowledgeParam {
* 审核结束时间
*/
private String verifyEndTime;
private String kbKnowledgeId;
}
......@@ -26,6 +26,7 @@ public class KnowledgeVO {
private String verifyTime;
private String publishDate;
private String createTime;
private String kbKnowledgeId;
private String type;
private String verifierName;
private Integer score;
......
......@@ -50,9 +50,18 @@ know:
checkuserurl: http://127.0.0.1:9988/sys/checkToken
getusersurl: http://127.0.0.1:9988/sys/user/thirdparty
document:
server:
host: http://gq55rd.natappfree.cc
files:
storage: E:/aaa/
storage: D:/know/
docservice:
url:
site: http://114.116.116.241:80/
converter: ConvertService.ashx
command: coauthoring/CommandService.ashx
api: web-apps/apps/api/documents/api.js
preloader: web-apps/apps/api/documents/cache-scripts.html
fillforms-docs: .docx|.oform
viewed-docs: .djvu|.oxps|.pdf|.xps
edited-docs: .csv|.docm|.docx|.docxf|.dotm|.dotx|.epub|.fb2|.html|.odp|.ods|.odt|.otp|.ots|.ott|.potm|.potx|.ppsm|.ppsx|.pptm|.pptx|.rtf|.txt|.xlsm|.xlsx|.xltm|.xltx
......
......@@ -22,10 +22,10 @@ class KnowBaseApplicationTests {
@Test
void contextLoads() throws IOException {
ResponseEntity<Resource> re = localFileService.download("abc.docx", "E:/aaa/abc.docx");
File file = re.getBody().getFile();
System.out.println(re);
// ResponseEntity<Resource> re = localFileService.download("abc.docx", "E:/aaa/abc.docx");
// File file = re.getBody().getFile();
//
// System.out.println(re);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论