Merge remote-tracking branch 'origin/feature/REQ-3540' into feature/REQ-3540

# Conflicts:
#	doc/doc-server/src/main/java/cn/axzo/nanopart/doc/wps/wpsedit/WpsEditManager.java
This commit is contained in:
xudawei 2025-03-21 18:11:06 +08:00
commit ada74334f7
12 changed files with 59 additions and 63 deletions

View File

@ -11,4 +11,9 @@ import lombok.Setter;
@Getter
public class DirectoryAttributes {
/**
* mock字段, 暂时避免序列化错误
*/
private boolean placeholder;
}

View File

@ -19,26 +19,6 @@ public interface IndexNodeScope {
return TREE_ROOT_NODE_CODE;
}
default boolean isChildrenNameDuplicatable() {
switch (context()) {
case FILE_TEMPLATE:
case TEMPLATE_DATABASE:
return false;
default:
return scope().getChildNameDuplicatable();
}
}
default boolean isLimitChildrenCount() {
switch (context()) {
case FILE_TEMPLATE:
case TEMPLATE_DATABASE:
return true;
default:
return scope().getLimitChildrenCount();
}
}
String TREE_ROOT_NODE_CODE = "";
Long TREE_ROOT_NODE_ID = 0L;

View File

@ -5,7 +5,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import cn.axzo.nanopart.doc.api.enums.FileFormat;
import lombok.Getter;
import lombok.Setter;
@ -21,7 +20,6 @@ public class DocProps {
private String createFileOssFileKeyWord = "ent-workspace-empty-docx";
private String createFileFileKeyExcel = "ent-workspace-empty-xlsx";
private String createFileFileKeyPpt = "ent-workspace-empty-pptx";
private int indexNodeMaxChildrenSize = 200;
private int indexNodeMaxDepth = 10;
private int indexNodeMaxCopyFileSize = 1000;
@ -35,14 +33,4 @@ public class DocProps {
private String defaultIconWord = "https://axzo-obs-public.obs.cn-north-4.myhuaweicloud.com:443/doc/doc/word.png";
private String defaultIconPdf = "https://axzo-obs-public.obs.cn-north-4.myhuaweicloud.com:443/doc/doc/pdf.png";
public String createFileOssFileKey(FileFormat format) {
if (format == FileFormat.WORD)
return createFileOssFileKeyWord;
if (format == FileFormat.EXCEL)
return createFileFileKeyExcel;
if (format == FileFormat.PDF)
return createFileFileKeyPpt;
return "";
}
}

View File

@ -45,7 +45,7 @@ import cn.axzo.nanopart.doc.entity.IndexNode;
import cn.axzo.nanopart.doc.entity.TemplateDatabase;
import cn.axzo.nanopart.doc.file.index.IndexManager;
import cn.axzo.nanopart.doc.file.index.IndexQueryService;
import cn.axzo.nanopart.doc.file.index.copy.CopiedOssFiles;
import cn.axzo.nanopart.doc.file.index.copy.OssFilesCopy;
import cn.axzo.nanopart.doc.file.index.copy.SetScopeCopyFileVisitor;
import cn.axzo.nanopart.doc.file.index.domain.IndexNodes;
import cn.axzo.nanopart.doc.file.templatedb.TemplateDatabaseQueryService;
@ -56,6 +56,8 @@ import cn.azxo.framework.common.model.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
/**
* @author yanglin
*/
@ -118,19 +120,19 @@ public class FileDatabaseService {
asyncUtils.getOrTimeout(future, "激活");
}
private Future<?> asyncCopySubtree(FileDatabase db, IndexNode templateRoot, Runnable nullablePostProcessor) {
private Future<?> asyncCopySubtree(FileDatabase db, IndexNode templateRoot, @Nullable Runnable postProcessor) {
return indexManager.async(() -> {
// don't inline in transaction
CopiedOssFiles copiedOssFiles = indexManager.copySubtreeOssFiles(templateRoot);
OssFilesCopy filesCopy = indexManager.copySubtreeOssFiles(templateRoot);
transaction.executeWithoutResult(unused -> {
FileDatabase reload = fileDatabaseDao.getForUpdateOrThrow(db.getCode());
if (reload.isActivated())
return;
indexManager.copySubTree(templateRoot, null, new SetScopeCopyFileVisitor(copiedOssFiles, db));
indexManager.copySubtree(templateRoot, null, new SetScopeCopyFileVisitor(filesCopy, db));
fileDatabaseDao.updateState(db.getCode(), FileDatabaseState.ACTIVATED);
fileDatabaseDao.setPurchaseTime(db.getCode(), new Date());
if (nullablePostProcessor != null)
nullablePostProcessor.run();
if (postProcessor != null)
postProcessor.run();
});
});
}

View File

@ -25,9 +25,9 @@ import cn.axzo.nanopart.doc.dao.DocLogDao;
import cn.axzo.nanopart.doc.dao.IndexNodeDao;
import cn.axzo.nanopart.doc.entity.IndexNode;
import cn.axzo.nanopart.doc.file.index.copy.ConnectNodeVisitor;
import cn.axzo.nanopart.doc.file.index.copy.CopiedOssFiles;
import cn.axzo.nanopart.doc.file.index.copy.CopyFileVisitor;
import cn.axzo.nanopart.doc.file.index.copy.CopyNodeVisitor;
import cn.axzo.nanopart.doc.file.index.copy.OssFilesCopy;
import cn.axzo.nanopart.doc.file.index.domain.NameUsedException;
import cn.axzo.nanopart.doc.file.mq.FileBroadcaster;
import cn.axzo.nanopart.doc.integration.OssClient;
@ -70,7 +70,9 @@ public class IndexManager {
// check without lock
ensureChildNameNotUsed(create, IndexNodeType.FILE, false);
String fullFileName = String.format("%s.%s", create.name(), format.createFileExtension());
String ossFileKey = ossClient.copy(docProps.createFileOssFileKey(format), fullFileName);
String emptyOssFileKey = format == FileFormat.WORD ? docProps.getCreateFileOssFileKeyWord()
: docProps.getCreateFileFileKeyExcel();
String ossFileKey = ossClient.copy(emptyOssFileKey, fullFileName);
return OssFile.create(format, format.createFileExtension(), 0, ossFileKey);
}
@ -123,7 +125,7 @@ public class IndexManager {
}
private void ensureChildNameNotUsed(NodeCreate create, IndexNodeType nodeType, boolean withLock) {
if (create.nodeScope().isChildrenNameDuplicatable())
if (indexSupport.isChildrenNameDuplicatable(create))
return;
if (withLock)
indexSupport.lockParentAndReleaseOnCommit(create);
@ -136,7 +138,7 @@ public class IndexManager {
if (indexNode.getName().equals(newName))
return;
docLogDao.log("indexNode:rename", code, "newName", newName);
if (!indexNode.isChildrenNameDuplicatable()) {
if (!indexSupport.isChildrenNameDuplicatable(indexNode)) {
indexSupport.lockParentAndReleaseOnCommit(indexNode);
indexSupport.ensureChildNameNotUsed(indexNode, indexNode.getNodeType(), newName);
}
@ -157,10 +159,10 @@ public class IndexManager {
return subtree;
}
public CopiedOssFiles copySubtreeOssFiles(IndexNode srcNode) {
public OssFilesCopy copySubtreeOssFiles(IndexNode srcNode) {
BizAssertions.assertFalse(TransactionSynchronizationManager.isActualTransactionActive(), "不能在事务中使用");
List<String> ossFileKeys = indexNodeDao.collectValidSubtreeFileOssKeys(srcNode);
return new CopiedOssFiles(ossClient.copy(ossFileKeys));
return new OssFilesCopy(ossClient.copy(ossFileKeys));
}
/**
@ -177,7 +179,7 @@ public class IndexManager {
return transaction.execute(unused -> {
docLogDao.log("indexNode:asyncCopy", srcCode, "srcCode", srcCode, "destParentCode", destParentCode);
//noinspection SpringTransactionalMethodCallsInspection
return copySubTree(srcNode, destParentNode, copyNodeVisitor);
return copySubtree(srcNode, destParentNode, copyNodeVisitor);
});
});
}
@ -209,7 +211,7 @@ public class IndexManager {
}
@BizTransactional
public IndexNode copySubTree(IndexNode src, @Nullable IndexNode destParent, CopyNodeVisitor copyNodeVisitor) {
public IndexNode copySubtree(IndexNode src, @Nullable IndexNode destParent, CopyNodeVisitor copyNodeVisitor) {
List<IndexNode> subtreeNodes = indexSupport.collectValidSubtreeAsValueRoot(src);
RootNode<IndexNode> srcRoot = TreeUtils.transform(subtreeNodes, IndexNode.class, true);
srcRoot.walkDown(copyNodeVisitor);
@ -226,7 +228,7 @@ public class IndexManager {
indexNodeDao.connectNodes(TreeUtils.collectValues(connectRoot));
IndexNode rootNode = connectRoot.getChildren().get(0).tryGetValue();
// resolve the name issue
if (!rootNode.isChildrenNameDuplicatable())
if (!indexSupport.isChildrenNameDuplicatable(rootNode))
indexSupport.incrNameIfDuplicate(rootNode);
// get the coped root with full props
return indexNodeDao.findOrNull(rootNode.getCode());

View File

@ -96,7 +96,7 @@ public class IndexSupport {
BizAssertions.assertTrue(
indexNodeDao.validChildrenCount(parent.getCode()) < docProps.getIndexNodeMaxChildrenSize(),
"创建失败, 子节点数量不能超过 {}", docProps.getIndexNodeMaxChildrenSize());
if (!parent.isRoot() && parent.isLimitChildrenCount())
if (!parent.isRoot() && isLimitChildrenCount(child))
BizAssertions.assertTrue(parent.path().depth() < docProps.getIndexNodeMaxDepth(), //
"节点深度超过限制{}, 无法再创建新节点", docProps.getIndexNodeMaxDepth());
}
@ -164,6 +164,28 @@ public class IndexSupport {
.list();
}
boolean isChildrenNameDuplicatable(IndexNodeParentScope parentScope) {
IndexNodeScope nodeScope = parentScope.nodeScope();
switch (nodeScope.context()) {
case FILE_TEMPLATE:
case TEMPLATE_DATABASE:
return false;
default:
return nodeScope.scope().getChildNameDuplicatable();
}
}
boolean isLimitChildrenCount(IndexNodeParentScope parentScope) {
IndexNodeScope nodeScope = parentScope.nodeScope();
switch (nodeScope.context()) {
case FILE_TEMPLATE:
case TEMPLATE_DATABASE:
return true;
default:
return nodeScope.scope().getLimitChildrenCount();
}
}
<T> Future<T> submit(Callable<T> callable) {
return executor.submit(() -> {
try {

View File

@ -46,7 +46,7 @@ public class ConnectNodeVisitor extends ValueNodeRecursiveVisitor<IndexNode> {
continue;
}
// IMPORTANT: append REVERSED parent path but not parent id, because:
// 1. We're walking the tree up
// 1. We're walking up the tree
// 2. The dest parent node may not be the tree root
if (destParentNode != null)
path = path.append(destParentNode.path().reverse());

View File

@ -13,14 +13,14 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class CopyFileVisitor extends CopyNodeVisitor {
private final CopiedOssFiles copiedOssFiles;
private final OssFilesCopy ossFilesCopy;
@Override
public WalkingDecision visit(ValueNode<IndexNode> node) {
IndexNode copy = node.getValue();
if (copy.isFile()) {
FileAttributes fileAttributes = copy.getOrCreateFileAttributes();
String newOssFileKey = copiedOssFiles.getCopyOssFileKey(fileAttributes.getOssFileKey());
String newOssFileKey = ossFilesCopy.getCopyOssFileKey(fileAttributes.getOssFileKey());
fileAttributes.setOssFileKey(newOssFileKey);
}
return super.visit(node);

View File

@ -9,7 +9,7 @@ import lombok.RequiredArgsConstructor;
* @author yanglin
*/
@RequiredArgsConstructor
public class CopiedOssFiles {
public class OssFilesCopy {
private final Map<String, String> old2newOssFileKey;

View File

@ -13,8 +13,8 @@ public class SetScopeCopyFileVisitor extends CopyFileVisitor {
private final IndexNodeScope nodeScope;
public SetScopeCopyFileVisitor(CopiedOssFiles copiedOssFiles, IndexNodeScope nodeScope) {
super(copiedOssFiles);
public SetScopeCopyFileVisitor(OssFilesCopy ossFilesCopy, IndexNodeScope nodeScope) {
super(ossFilesCopy);
this.nodeScope = nodeScope;
}

View File

@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
import com.alibaba.excel.util.StringUtils;
import cn.axzo.nanopart.doc.api.domain.FileAttributes;
import cn.axzo.nanopart.doc.api.wps.request.WpsEditUploadAddressRequest;
import cn.axzo.nanopart.doc.api.wps.request.WpsEditUploadCompleteRequest;
import cn.axzo.nanopart.doc.api.wps.request.WpsEditUploadPrepareRequest;
@ -20,16 +21,15 @@ import cn.axzo.nanopart.doc.api.wps.response.WpsEditUploadCompleteResponse;
import cn.axzo.nanopart.doc.api.wps.response.WpsEditUploadPrepareResponse;
import cn.axzo.nanopart.doc.config.DocProps;
import cn.axzo.nanopart.doc.dao.IndexNodeDao;
import cn.axzo.nanopart.doc.dao.TemplateDatabaseDao;
import cn.axzo.nanopart.doc.entity.IndexNode;
import cn.axzo.nanopart.doc.file.index.IndexManager;
import cn.axzo.nanopart.doc.file.mq.FileBroadcaster;
import cn.axzo.nanopart.doc.integration.DocOssGateway;
import cn.axzo.nanopart.doc.utils.BizTransactional;
import cn.axzo.oss.http.model.ApiSignUrlUploadRequest;
import cn.axzo.oss.http.model.ApiSignUrlUploadResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
/**
* 文档编辑
@ -45,7 +45,6 @@ import org.springframework.transaction.annotation.Transactional;
public class WpsEditManager {
private final IndexManager indexManager;
private final TemplateDatabaseDao templateDatabaseDao;
private final IndexNodeDao indexNodeDao;
private final DocOssGateway docOssGateway;
private final FileBroadcaster fileBroadcaster;
@ -110,7 +109,7 @@ public class WpsEditManager {
* 3 上传完成后回调通知上传结果
* 说明WebOffice 在将新版本文件上传到指定地址后将会回调通知接入方
*/
@Transactional
@BizTransactional
public WpsEditUploadCompleteResponse uploadComplete(WpsEditUploadCompleteRequest request) {
log.info("wpsEditManager-uploadComplete-params,request:{}", request);
//加锁
@ -140,9 +139,7 @@ public class WpsEditManager {
}
fileAttributes.setVersion(fileAttributes.fetchVersion() + 1);
indexNodeDao.updateAttributes(node);
indexNodeDao.updateFileSize(request.getDocCode(), request.getSize());
fileBroadcaster.fireFileSizeChanged(request.getDocCode());
indexManager.updateFileSize(node, request.getSize());
}