package cn.iocoder.yudao.module.archive.service.document;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
|
import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentCreateReqDTO;
|
import cn.iocoder.yudao.module.archive.controller.admin.document.vo.ArchiveDocumentPageReqVO;
|
import cn.iocoder.yudao.module.archive.controller.admin.document.vo.ArchiveDocumentSaveReqVO;
|
import cn.iocoder.yudao.module.archive.dal.dataobject.category.ArchiveCategoryDO;
|
import cn.iocoder.yudao.module.archive.dal.dataobject.document.ArchiveDocumentDO;
|
import cn.iocoder.yudao.module.archive.dal.mysql.borrow.ArchiveBorrowMapper;
|
import cn.iocoder.yudao.module.archive.dal.mysql.document.ArchiveDocumentMapper;
|
import cn.iocoder.yudao.module.archive.dal.redis.ArchiveNoRedisDAO;
|
import cn.iocoder.yudao.module.archive.enums.ArchiveBorrowStatusEnum;
|
import cn.iocoder.yudao.module.archive.enums.ArchiveCategoryStatusEnum;
|
import cn.iocoder.yudao.module.archive.enums.ArchiveDocumentStatusEnum;
|
import cn.iocoder.yudao.module.archive.enums.ArchiveSecretLevelEnum;
|
import cn.iocoder.yudao.module.archive.enums.ArchiveSourceTypeEnum;
|
import cn.iocoder.yudao.module.archive.service.category.ArchiveCategoryService;
|
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.time.LocalDate;
|
import java.util.List;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.module.archive.enums.ErrorCodeConstants.*;
|
|
/**
|
* 档案 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class ArchiveDocumentServiceImpl implements ArchiveDocumentService {
|
|
/** 敏感档案(保密级)查看权限点 */
|
public static final String SENSITIVE_PERMISSION = "archive:document:sensitive";
|
|
@Resource
|
private ArchiveDocumentMapper documentMapper;
|
|
@Resource
|
private ArchiveBorrowMapper borrowMapper;
|
|
@Resource
|
private ArchiveCategoryService categoryService;
|
|
@Resource
|
private ArchiveNoRedisDAO archiveNoRedisDAO;
|
|
@Resource
|
private StorageAttachmentApi storageAttachmentApi;
|
|
@Resource(name = "ss")
|
private SecurityFrameworkService securityFrameworkService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createDocument(ArchiveDocumentSaveReqVO createReqVO) {
|
// 1. 校验分类
|
ArchiveCategoryDO category = validateCategory(createReqVO.getCategoryId());
|
|
// 2. 组装档案(密级/载体/归档日期/归档人补默认值)
|
ArchiveDocumentDO document = BeanUtils.toBean(createReqVO, ArchiveDocumentDO.class);
|
document.setId(null);
|
document.setNo(archiveNoRedisDAO.generate(ArchiveNoRedisDAO.DOCUMENT_NO_PREFIX));
|
document.setSecretLevel(createReqVO.getSecretLevel() != null ? createReqVO.getSecretLevel()
|
: category.getDefaultSecretLevel());
|
document.setCarrierType(createReqVO.getCarrierType() != null ? createReqVO.getCarrierType() : 3);
|
document.setSourceType(ArchiveSourceTypeEnum.MANUAL.getType());
|
document.setArchiveDate(createReqVO.getArchiveDate() != null ? createReqVO.getArchiveDate() : LocalDate.now());
|
document.setArchivistUserId(getLoginUserId());
|
document.setStatus(ArchiveDocumentStatusEnum.IN_STOCK.getStatus());
|
documentMapper.insert(document);
|
|
// 3. 绑定附件
|
storageAttachmentApi.bindAttachments("file", DOCUMENT_RECORD_TYPE, document.getId(), createReqVO.getBlobIds());
|
return document.getId();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updateDocument(ArchiveDocumentSaveReqVO updateReqVO) {
|
// 1.1 校验档案存在
|
ArchiveDocumentDO oldDocument = validateDocumentExists(updateReqVO.getId());
|
// 1.2 借出中的档案不允许修改
|
if (ArchiveDocumentStatusEnum.BORROWED.getStatus().equals(oldDocument.getStatus())) {
|
throw exception(DOCUMENT_UPDATE_FAIL_BORROWED, oldDocument.getNo());
|
}
|
// 1.3 敏感档案需要有敏感档案权限才能修改
|
validateSecretLevelVisible(oldDocument);
|
// 1.4 校验分类
|
validateCategory(updateReqVO.getCategoryId());
|
|
// 2. 更新(密级为空时保留原值,避免被置空)
|
ArchiveDocumentDO updateObj = BeanUtils.toBean(updateReqVO, ArchiveDocumentDO.class);
|
if (updateObj.getSecretLevel() == null) {
|
updateObj.setSecretLevel(oldDocument.getSecretLevel());
|
}
|
documentMapper.updateById(updateObj);
|
|
// 3. 增量更新附件
|
storageAttachmentApi.updateAttachments("file", DOCUMENT_RECORD_TYPE, updateReqVO.getId(),
|
updateReqVO.getBlobIds());
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteDocument(Long id) {
|
// 1.1 校验档案存在
|
ArchiveDocumentDO document = validateDocumentExists(id);
|
// 1.2 借出中或存在未归还借阅单时不允许删除
|
boolean hasOpenBorrow = CollUtil.isNotEmpty(borrowMapper.selectListByDocumentIdAndStatusIn(id,
|
List.of(ArchiveBorrowStatusEnum.APPROVING.getStatus(), ArchiveBorrowStatusEnum.BORROWED.getStatus())));
|
if (ArchiveDocumentStatusEnum.BORROWED.getStatus().equals(document.getStatus()) || hasOpenBorrow) {
|
throw exception(DOCUMENT_DELETE_FAIL_STATUS, document.getNo(),
|
ArchiveDocumentStatusEnum.nameOf(document.getStatus()));
|
}
|
|
// 2. 删除档案与附件
|
documentMapper.deleteById(id);
|
storageAttachmentApi.deleteAttachmentsByRecord(DOCUMENT_RECORD_TYPE, id);
|
}
|
|
@Override
|
public ArchiveDocumentDO getDocument(Long id) {
|
ArchiveDocumentDO document = validateDocumentExists(id);
|
validateSecretLevelVisible(document);
|
return document;
|
}
|
|
@Override
|
public ArchiveDocumentDO getDocumentWithoutPermissionCheck(Long id) {
|
return documentMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<ArchiveDocumentDO> getDocumentPage(ArchiveDocumentPageReqVO reqVO) {
|
return documentMapper.selectPage(reqVO, !hasSensitivePermission());
|
}
|
|
@Override
|
public List<ArchiveDocumentDO> getDocumentList(ArchiveDocumentPageReqVO reqVO) {
|
return documentMapper.selectList(reqVO, !hasSensitivePermission());
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long archiveBySource(ArchiveDocumentCreateReqDTO reqDTO) {
|
// 1. 校验来源类型(只允许课题/会员/财务)
|
ArchiveSourceTypeEnum sourceType = ArchiveSourceTypeEnum.valueOf(reqDTO.getSourceType());
|
if (sourceType == null || sourceType == ArchiveSourceTypeEnum.MANUAL) {
|
throw exception(DOCUMENT_SOURCE_TYPE_UNSUPPORTED, reqDTO.getSourceType());
|
}
|
|
// 2. 重复归档校验:同一来源单据只允许生成一份档案
|
ArchiveDocumentDO exists = documentMapper.selectBySource(reqDTO.getSourceType(), reqDTO.getSourceId());
|
if (exists != null) {
|
throw exception(DOCUMENT_DUPLICATE_SOURCE,
|
StrUtil.blankToDefault(reqDTO.getSourceNo(), String.valueOf(reqDTO.getSourceId())),
|
exists.getNo());
|
}
|
|
// 3. 按分类编码定位档案分类
|
ArchiveCategoryDO category = categoryService.getCategoryByCode(reqDTO.getCategoryCode());
|
if (category == null) {
|
throw exception(DOCUMENT_SOURCE_CATEGORY_NOT_EXISTS, reqDTO.getCategoryCode());
|
}
|
|
// 4. 组装并落库档案(标题、密级为空时分别取来源标题、分类默认密级)
|
ArchiveDocumentDO document = new ArchiveDocumentDO();
|
document.setNo(archiveNoRedisDAO.generate(ArchiveNoRedisDAO.DOCUMENT_NO_PREFIX));
|
document.setTitle(StrUtil.blankToDefault(reqDTO.getTitle(), reqDTO.getSourceTitle()));
|
document.setCategoryId(category.getId());
|
document.setSecretLevel(reqDTO.getSecretLevel() != null ? reqDTO.getSecretLevel()
|
: category.getDefaultSecretLevel());
|
// 联动归档默认同时保留电子件与来源关联,载体取「电子+实体」,由档案管理员后续补充实体位置
|
document.setCarrierType(3);
|
document.setSourceType(reqDTO.getSourceType());
|
document.setSourceNo(reqDTO.getSourceNo());
|
document.setSourceId(reqDTO.getSourceId());
|
document.setSourceTitle(reqDTO.getSourceTitle());
|
document.setArchiveDate(reqDTO.getArchiveDate() != null ? reqDTO.getArchiveDate() : LocalDate.now());
|
document.setArchivistUserId(getLoginUserId());
|
document.setStatus(ArchiveDocumentStatusEnum.IN_STOCK.getStatus());
|
document.setSummary(reqDTO.getSummary());
|
document.setTags(reqDTO.getTags());
|
document.setRemark(reqDTO.getRemark());
|
documentMapper.insert(document);
|
|
// 5. 绑定来源单据的附件
|
storageAttachmentApi.bindAttachments("file", DOCUMENT_RECORD_TYPE, document.getId(), reqDTO.getBlobIds());
|
return document.getId();
|
}
|
|
@Override
|
public Boolean isArchived(Integer sourceType, Long sourceId) {
|
return documentMapper.selectBySource(sourceType, sourceId) != null;
|
}
|
|
@Override
|
public ArchiveDocumentDO getBySource(Integer sourceType, Long sourceId) {
|
if (sourceType == null || sourceId == null) {
|
return null;
|
}
|
return documentMapper.selectBySource(sourceType, sourceId);
|
}
|
|
@Override
|
public boolean updateDocumentStatusToBorrowed(Long documentId) {
|
int rows = documentMapper.update(null, new LambdaUpdateWrapper<ArchiveDocumentDO>()
|
.eq(ArchiveDocumentDO::getId, documentId)
|
.eq(ArchiveDocumentDO::getStatus, ArchiveDocumentStatusEnum.IN_STOCK.getStatus())
|
.set(ArchiveDocumentDO::getStatus, ArchiveDocumentStatusEnum.BORROWED.getStatus()));
|
return rows > 0;
|
}
|
|
@Override
|
public boolean updateDocumentStatusToInStock(Long documentId) {
|
int rows = documentMapper.update(null, new LambdaUpdateWrapper<ArchiveDocumentDO>()
|
.eq(ArchiveDocumentDO::getId, documentId)
|
.eq(ArchiveDocumentDO::getStatus, ArchiveDocumentStatusEnum.BORROWED.getStatus())
|
.set(ArchiveDocumentDO::getStatus, ArchiveDocumentStatusEnum.IN_STOCK.getStatus()));
|
return rows > 0;
|
}
|
|
// ==================== 校验逻辑 ====================
|
|
private ArchiveDocumentDO validateDocumentExists(Long id) {
|
ArchiveDocumentDO document = id != null ? documentMapper.selectById(id) : null;
|
if (document == null) {
|
throw exception(DOCUMENT_NOT_EXISTS);
|
}
|
return document;
|
}
|
|
/** 校验分类存在且启用 */
|
private ArchiveCategoryDO validateCategory(Long categoryId) {
|
ArchiveCategoryDO category = categoryService.getCategory(categoryId);
|
if (category == null) {
|
throw exception(DOCUMENT_CATEGORY_NOT_EXISTS);
|
}
|
if (ArchiveCategoryStatusEnum.DISABLE.getStatus().equals(category.getStatus())) {
|
throw exception(CATEGORY_DISABLED, category.getName());
|
}
|
return category;
|
}
|
|
/** 敏感档案可见性校验:保密级档案需要 {@code archive:document:sensitive} 权限 */
|
private void validateSecretLevelVisible(ArchiveDocumentDO document) {
|
if (ArchiveSecretLevelEnum.SECRET.getLevel().equals(document.getSecretLevel()) && !hasSensitivePermission()) {
|
throw exception(DOCUMENT_SENSITIVE_NO_PERMISSION, document.getTitle());
|
}
|
}
|
|
private boolean hasSensitivePermission() {
|
return securityFrameworkService.hasPermission(SENSITIVE_PERMISSION);
|
}
|
|
}
|