SpringBoot+Vue3 实现 OA 公文外来文与归档台账:外部收文、BPM办理、三类公文统一归档

SpringBoot+Vue3 实现 OA 公文外来文与归档台账:外部收文、BPM办理、三类公文统一归档

文档地址:http://ruoyioffice.com | 源码1:ruoyi-office-vben | 源码2:ruoyi-office | 源码3:ruoyi-office
在前两篇公文系列文章中,我们分别拆解了公文发文和内部收文。发文解决的是"企业内部正式文件如何产生",收文解决的是"内部发文如何分发到部门办理"。但真实企业办公里,还有一类更容易被忽视的公文:来自上级单位、监管部门、合作单位、外部机构的外来文件。它们不是系统内部发起,却必须被登记、批示、办理、留痕和归档。本文聚焦 RuoYi Office 的 外部收文 + 公文归档台账 设计,看看如何用 Spring Boot + Flowable + Vue3 把三类公文统一收口。

引言:外来文为什么不能只做"附件上传"?

很多企业第一次做 OA 公文模块时,会把外来文简化成一个"文件上传台账":填一下来文单位、标题、上传附件,然后结束。这个方案看似轻巧,但一旦进入真实管理场景,就会暴露出几个问题:

业务问题 简化方案的后果 系统应承担的能力
来文需要领导批示 附件上传后无法体现"谁批示、批示什么" 支持领导批示节点与意见留痕
来文需要承办部门处理 台账只能记录文件,不能追踪办理进展 支持承办办理、办理结果、办理期限
来文需要流程闭环 文件状态停留在"已上传",无法区分办理中和已办结 建立业务状态 + BPM 状态联动
来文最终要归档 外部收文和内部发文、收文分散在多个列表 建立统一归档台账
管理层要检索历史公文 只能按文件名查,缺少文号、密级、责任部门等维度 提供结构化检索和导出

所以,外部收文不应该只是"上传文件",而应该是一张具备流程属性的业务单据:

text 复制代码
外部单位来文 → 文秘登记 → 领导批示 → 承办部门办理 → 办结归档 → 统一台账检索

RuoYi Office 的设计重点正是在这里:外部收文独立于内部发文体系,但最终和发文、内部收文一起进入公文归档台账,形成完整的企业公文资产库。


一、业务场景:三类公文如何统一管理?

1.1 公文体系中的三类来源

在 RuoYi Office 中,公文归档台账聚合的不是单一表,而是三类来源:

类型 来源 典型场景 归档条件
发文 企业内部起草并审批签发 公司制度、会议纪要、任命通知、对外函件 processStatus = APPROVE
收文 内部发文按部门自动生成,或部门手工登记 主送部门签收、领导批示、承办办理 handleStatus = 3
外部收文 外部单位、监管机构、合作方来文 上级通知、监管文件、合作函件、纸质文件登记 processStatus = APPROVE

发文强调"生产正式文件",收文强调"内部传达办理",外部收文强调"外部文件进入企业后的处理闭环"。三者的业务动作不同,但最终都要落到同一个问题:哪些公文已经形成可追溯的正式记录?

1.2 外部收文的典型使用场景

外部收文在机关、国企、集团公司、园区企业中非常常见:

  • 上级单位下发通知,需要办公室登记后交由领导批示。
  • 监管部门发送检查整改文件,需要责任部门在期限内办理。
  • 合作单位发送合同函、联系函、会议纪要,需要内部留痕。
  • 纸质文件送达企业,需要扫描上传并纳入电子台账。
  • 历史公文需要按文号、标题、密级、责任部门进行检索和导出。

如果没有系统承接,这类文件通常散落在邮箱、微信群、纸质档案柜、个人电脑里。短期看还能靠人记,时间一长就会出现"找不到、说不清、追不回"的问题。

1.3 RuoYi Office 的解决思路

RuoYi Office 没有把外来文做成一个孤立表单,而是把它放进完整的公文体系里:

▲ 外部收文与归档流程:外部来文登记后进入 BPM 办理流程,流程通过后与发文、内部收文一起进入归档台账

核心思路可以概括为三句话:

  1. 外部收文是流程单据:登记不是终点,提交后进入 BPM 流程。
  2. 办理状态由流程驱动:待登记、办理中、已办结不是人工随便改,而是由流程状态回调统一维护。
  3. 归档台账是查询层聚合:不强行把三类公文塞进一张大表,而是在归档服务中统一投影、排序和分页。

二、系统设计:外部收文与归档台账的边界

2.1 模块分工

这次拆解涉及两个核心后端服务和三组前端页面:

层级 文件/页面 职责
后端服务 OfficeDocOutsideServiceImpl.java 外部收文保存、提交、删除、附件维护、流程状态回调
后端服务 OfficeDocArchiveServiceImpl.java 聚合发文、收文、外部收文,输出统一归档列表
PC 列表页 views/oa/officedoc/outside/list 外部收文查询、新建、导出、删除
PC 详情页 views/oa/officedoc/outside/info 外部收文登记、保存、提交、撤回、上传正式公文和附件
PC 台账页 views/oa/officedoc/archive/list 三类公文统一检索、查看详情、PDF 预览、导出

这套边界很清晰:外部收文负责"产生并办理一条来文",归档台账负责"把已经完成的公文汇总出来"。

2.2 为什么归档不直接建一张汇总表?

公文归档可以有两种实现方案:

方案 做法 优点 风险
物理归档表 流程结束时复制一份数据到 archive 查询快,结构统一 数据冗余,回写一致性复杂
查询层聚合 查询时分别查三类来源并合并 无冗余,来源数据始终真实 大数据量下需要优化分页

当前 RuoYi Office 采用的是第二种:查询层聚合

原因很务实:公文归档台账本质上是一个管理查询视图,数据源仍然是发文、收文、外部收文各自的业务表。这样既避免了"业务表改了,归档表没同步"的一致性问题,也能保留每类公文的原始详情入口。

2.3 状态设计:BPM 状态与办理状态分层

外部收文有两个容易混淆的状态:

状态字段 含义 面向对象 示例
processStatus BPM 流程实例状态 流程引擎、审批中心 未开始、运行中、通过、驳回、撤回
handleStatus 外部收文业务办理状态 业务人员、列表台账 待登记、办理中、已办结

真实实现中,外部收文办理状态有三个核心值:

状态码 常量 含义 触发场景
0 HANDLE_STATUS_PENDING 待登记 新建未提交、驳回、取消、撤回、未开始
2 HANDLE_STATUS_PROCESSING 办理中 提交 BPM 或流程运行中
3 HANDLE_STATUS_COMPLETED 已办结 BPM 审批通过

这种分层设计的好处是:BPM 可以保持通用状态,业务页面可以展示更贴近用户语言的"办理状态"。


三、流程设计:从登记到归档的闭环

3.1 外部收文流程节点

外部收文的流程和内部收文类似,重点不是"签发",而是"批示与办理":

text 复制代码
文秘登记 → 领导批示 → 承办部门办理 → 办结确认 → 流程结束
节点 角色 核心操作 数据落点
文秘登记 办公室/综合部 填写来文单位、文号、标题、密级、收文日期,上传附件 基础字段、附件
领导批示 部门/公司领导 填写批示意见,明确处理方向 leaderOpinion
承办部门办理 责任部门/主办人 填写办理结果,补充处理情况 handleResult
办结确认 文秘/流程节点配置角色 确认办理完成 BPM 审批通过

外部收文没有内部发文的"套红模板"和"生成正式 PDF"动作,因为它的正式文件通常来自外部单位,系统只需要保存原文件或扫描件,并记录企业内部办理过程。

3.2 状态流转

外部收文的状态流转可以这样理解:

text 复制代码
待登记(0)
  ├─ 提交 BPM → 办理中(2)
  ├─ 流程通过 → 已办结(3)
  └─ 驳回/取消/撤回/未开始 → 待登记(0)

注意这里有一个关键点:提交时直接把业务状态置为"办理中"。这意味着只要单据进入 BPM,就不再是普通草稿,而是一条正在办理的外来文。

3.3 节点级保存

前端详情页在审批模式下会识别当前节点名称:

  • 当前节点是"领导批示"时,允许编辑 leaderOpinion
  • 当前节点是"承办部门办理"时,允许编辑 handleResult
  • 审批通过前调用保存接口,避免批示或办理结果只停留在前端表单。

这延续了 RuoYi Office 公文收文模块的节点级业务定制思路:流程节点不只是"同意/拒绝"的按钮,还可以承载业务字段的编辑权限。


四、PC 端功能实现:外部收文列表、详情、归档台账

4.1 外部收文列表:面向登记与办理跟踪

外部收文列表页位于 views/oa/officedoc/outside/list,核心能力包括:

▲ 外部收文列表:支持按公文标题、来文单位、流程状态查询,展示办理状态和流程状态

列表页的字段设计很贴近文秘日常工作:

字段 说明
单据编号 系统生成的外部收文业务编号
公文标题 外部来文的标题
来文单位 文件来源单位,如上级单位、合作方、监管部门
来文字号 外部文件自带文号
密级/紧急程度 便于优先级管理
收文部门 文件进入企业后的责任部门
办理状态 待登记、办理中、已办结
流程状态 BPM 流程状态

列表页的删除逻辑也遵循 BPM 单据规范:只有处于允许删除的流程状态时,删除按钮才可用;运行中的流程单据不可直接删除。

4.2 外部收文详情:登记、提交、上传正式文件

外部收文详情页位于 views/oa/officedoc/outside/info,它不是简单表单,而是一个流程表单页面:

区域 功能 说明
基础信息 来文单位、来文字号、公文标题、密级、紧急程度、收文日期 登记外来文的结构化元数据
办理信息 收文部门、主办人、领导批示、办理结果、办理期限 支撑领导批示和承办办理
正式公文 上传 PDF、Word、扫描件 保存外部来文原件
附件列表 上传补充材料 保存过程材料、相关附件
操作按钮 保存、提交、撤回、删除 与 BPM 状态联动

详情页初始化新单据时,会自动带入当前用户的公司、部门、创建人、默认密级、紧急程度和收文日期:

typescript 复制代码
const now = new Date();
formData.value = {
  creator: userStore.userInfo?.id,
  companyId: userStore.userInfo?.companyId || 0,
  companyName: userStore.userInfo?.companyName || '',
  deptId: userStore.userInfo?.deptId || 0,
  deptName: userStore.userInfo?.deptName || '',
  processStatus: BpmProcessInstanceStatus.NOT_START,
  secretLevel: 0,
  urgencyLevel: 0,
  handleStatus: 0,
  sourceOrg: '',
  title: '',
  receiveDate: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`,
  docFileUrl: '',
  attachments: [],
};

这种默认值设计的价值在于减少文秘录入成本:系统知道当前用户属于哪个公司、哪个部门,没必要让用户每次重复填写。

4.3 归档台账:三类公文统一入口

归档台账页面位于 views/oa/officedoc/archive/list,它面向的是管理查询而不是单据办理:

▲ 公文归档台账:统一展示发文、收文、外部收文,支持按归档类型、标题、文号、密级、归档时间查询

归档台账的前端设计有三个细节:

设计点 实现方式 用户价值
归档类型区分 发文蓝色、收文绿色、外部收文橙色标签 一眼识别来源
详情跳转 根据 archiveType 跳转到发文、收文、外部收文详情页 保留原始业务上下文
PDF 预览 docFileUrl 时展示"预览"按钮,用弹窗 iframe 打开 不离开台账即可查看正式文件

前端跳转逻辑非常直接:

typescript 复制代码
function handleViewDetail(row: OfficeDocArchiveApi.OfficeDocArchive) {
  const pathMap: Record<string, string> = {
    send: '/oa/officedoc/send-info',
    receive: '/oa/officedoc/receive-info',
    outside: '/oa/officedoc/outside-info',
  };
  const path = pathMap[row.archiveType || ''];
  if (path) {
    router.push({ path, query: { id: row.id, viewType: 'done' } });
  }
}

这说明归档台账不是复制出来的"静态档案",而是一个统一入口。用户在台账中看到的是归档视图,点击进去仍然能回到原始业务单据。


五、后端核心代码:提交、回调、聚合

5.1 外部收文提交:保存单据并发起 BPM

外部收文提交的关键动作有四步:

  1. 如果没有单号,生成业务单据编号。
  2. 保存外部收文主表,并将 processStatus 设为运行中。
  3. handleStatus 设为办理中。
  4. 提交 BPM 流程实例,回写 processInstanceId,保存附件。
java 复制代码
@Override
public Long submitOfficeDocOutside(OfficeDocOutsideSaveReqVO saveReqVO) {
    if (StringUtils.isBlank(saveReqVO.getBillCode())) {
        saveReqVO.setBillCode(BillCodeUtils.generateBillCode(
                SystemEnum.OA, OaBillTypeEnum.OA_OFFICE_DOC_OUTSIDE));
    }
    OfficeDocOutsideDO docOutside = BeanUtils.toBean(saveReqVO, OfficeDocOutsideDO.class)
            .setProcessStatus(BpmTaskStatusEnum.RUNNING.getStatus())
            .setHandleStatus(HANDLE_STATUS_PROCESSING);
    officeDocOutsideMapper.insertOrUpdate(docOutside);

    Map<String, Object> variables = BpmProcessVariableUtils.buildBillVariables(saveReqVO);
    Long submitUserId = StringUtils.isNotBlank(saveReqVO.getCreator())
            ? Long.valueOf(saveReqVO.getCreator()) : SecurityFrameworkUtils.getLoginUserId();
    String processInstanceId = processInstanceApi.submitProcessInstance(submitUserId,
            new BpmProcessInstanceCreateReqDTO()
                    .setProcessDefinitionKey(OaBillTypeEnum.OA_OFFICE_DOC_OUTSIDE.getProcessDefinitionKey())
                    .setVariables(variables).setBusinessKey(String.valueOf(docOutside.getId()))
    ).getCheckedData();
    officeDocOutsideMapper.updateById(new OfficeDocOutsideDO().setId(docOutside.getId())
            .setProcessInstanceId(processInstanceId));

    if (saveReqVO.getAttachments() != null) {
        attachmentService.saveAttachmentList(OaBillTypeEnum.OA_OFFICE_DOC_OUTSIDE.getTypeCode(),
                docOutside.getId(), saveReqVO.getAttachments());
    }
    return docOutside.getId();
}

这段代码体现了 RuoYi Office 流程单据的通用模式:业务表先落库,BPM 以业务表 ID 作为 businessKey,流程实例启动后再回写到业务表。

5.2 流程状态回调:BPM 驱动办理状态

外部收文实现了 FlowBillService<OaBillTypeEnum>,因此流程状态变化时会回调业务服务。核心逻辑如下:

java 复制代码
@Override
public void updateProcessStatus(String businessKey, Integer status) {
    Long id = Long.parseLong(businessKey);
    log.info("[updateProcessStatus] 外部收文,id: {}, status: {}", id, status);
    validateExists(id);

    OfficeDocOutsideDO updateObj = new OfficeDocOutsideDO();
    updateObj.setId(id);
    updateObj.setProcessStatus(status);
    if (BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(status)) {
        updateObj.setHandleStatus(HANDLE_STATUS_COMPLETED);
    } else if (BpmProcessInstanceStatusEnum.RUNNING.getStatus().equals(status)) {
        updateObj.setHandleStatus(HANDLE_STATUS_PROCESSING);
    } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus().equals(status)
            || BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(status)
            || BpmProcessInstanceStatusEnum.WITHDRAW.getStatus().equals(status)
            || BpmProcessInstanceStatusEnum.NOT_START.getStatus().equals(status)) {
        updateObj.setHandleStatus(HANDLE_STATUS_PENDING);
    }
    officeDocOutsideMapper.updateById(updateObj);
}

这段代码是外部收文状态机的核心。它没有让前端直接传"已办结",而是把办理状态收敛到后端流程回调里。这样可以避免用户手动篡改状态,也能确保审批中心和业务列表的状态一致。

5.3 归档聚合查询:三类来源合并后分页

归档服务的入口是 getArchivePage。它根据查询条件决定是否查询发文、收文、外部收文,然后把结果合并、按归档时间倒序排序,并在内存中分页:

java 复制代码
@Override
public PageResult<OfficeDocArchiveRespVO> getArchivePage(OfficeDocArchivePageReqVO reqVO) {
    String archiveType = reqVO.getArchiveType();

    List<OfficeDocArchiveRespVO> allRecords = new ArrayList<>();

    if (archiveType == null || TYPE_SEND.equals(archiveType)) {
        allRecords.addAll(querySendArchives(reqVO));
    }
    if (archiveType == null || TYPE_RECEIVE.equals(archiveType)) {
        allRecords.addAll(queryReceiveArchives(reqVO));
    }
    if (archiveType == null || TYPE_OUTSIDE.equals(archiveType)) {
        allRecords.addAll(queryOutsideArchives(reqVO));
    }

    allRecords.sort(Comparator.comparing(
            OfficeDocArchiveRespVO::getArchiveTime,
            Comparator.nullsLast(Comparator.reverseOrder())));

    int pageNo = reqVO.getPageNo() != null ? reqVO.getPageNo() : 1;
    int pageSize = reqVO.getPageSize() != null ? reqVO.getPageSize() : 20;
    int start = (pageNo - 1) * pageSize;
    int end = Math.min(start + pageSize, allRecords.size());
    List<OfficeDocArchiveRespVO> pageList = start < allRecords.size()
            ? allRecords.subList(start, end) : new ArrayList<>();
    return new PageResult<>(pageList, (long) allRecords.size());
}

这就是"统一归档视图"的核心:不是让三类公文改变自己的业务模型,而是把它们投影成统一的 OfficeDocArchiveRespVO

5.4 外部收文归档条件

外部收文进入归档台账的条件是 processStatus = APPROVE。归档投影时,会把外部收文字段映射成统一字段:

java 复制代码
private List<OfficeDocArchiveRespVO> queryOutsideArchives(OfficeDocArchivePageReqVO reqVO) {
    LambdaQueryWrapperX<OfficeDocOutsideDO> wrapper = new LambdaQueryWrapperX<OfficeDocOutsideDO>()
            .eq(OfficeDocOutsideDO::getProcessStatus, BpmProcessInstanceStatusEnum.APPROVE.getStatus())
            .eqIfPresent(OfficeDocOutsideDO::getCompanyId, reqVO.getCompanyId())
            .likeIfPresent(OfficeDocOutsideDO::getTitle, reqVO.getTitle())
            .likeIfPresent(OfficeDocOutsideDO::getDocNumber, reqVO.getDocNumber())
            .eqIfPresent(OfficeDocOutsideDO::getSecretLevel, reqVO.getSecretLevel())
            .betweenIfPresent(OfficeDocOutsideDO::getUpdateTime, reqVO.getArchiveTime());

    return officeDocOutsideMapper.selectList(wrapper).stream().map(out -> {
        OfficeDocArchiveRespVO vo = new OfficeDocArchiveRespVO();
        vo.setId(out.getId());
        vo.setArchiveType(TYPE_OUTSIDE);
        vo.setArchiveTypeName("外部收文");
        vo.setBillCode(out.getBillCode());
        vo.setDocNumber(out.getDocNumber());
        vo.setTitle(out.getTitle());
        vo.setDocDate(out.getReceiveDate());
        vo.setDeptName(out.getReceiveDeptName());
        vo.setHandlerName(out.getHandlerName());
        vo.setArchiveTime(out.getUpdateTime());
        return vo;
    }).toList();
}

注意这里外部收文的 docDate 对应的是 receiveDatedeptName 对应的是 receiveDeptNamehandlerName 对应的是 handlerName。统一字段名不代表丢失业务语义,而是在归档视图中把不同来源转换成可比较、可展示的共同语言。


六、数据结构:外部收文与归档视图

6.1 外部收文主表字段

外部收文主表对应 OfficeDocOutsideDO,表名为 oa_office_doc_outside。核心字段可以分为五组:

字段 类型含义 说明
id 主键 外部收文记录 ID
billCode 单据编号 系统生成,作为业务单号
processInstanceId 流程实例 ID 关联 BPM 流程实例
processStatus 流程状态 BPM 状态
handleStatus 办理状态 待登记、办理中、已办结
字段 类型含义 说明
sourceOrg 来文单位 外部文件来源单位
docNumber 来文字号 外部公文自带文号
title 公文标题 外部来文标题
secretLevel 密级 公开、内部、秘密等
urgencyLevel 紧急程度 普通、紧急等
receiveDate 收文日期 企业收到文件的日期
字段 类型含义 说明
receiveDeptId / receiveDeptName 收文部门 负责接收和办理的部门
handlerId / handlerName 主办人 具体承办人
leaderOpinion 领导批示 领导处理意见
handleResult 办理结果 承办部门处理结果
handleDeadline 办理期限 用于督办和时效管理
字段 类型含义 说明
content 内容摘要 文件主要内容
docFileUrl 正式公文地址 外部文件 PDF、Word 或扫描件
companyId / companyName 公司信息 多组织隔离与展示
deptId / deptName 创建部门 登记人所在部门
remark 备注 补充说明

6.2 归档响应视图

归档台账使用 OfficeDocArchiveRespVO 作为统一返回结构:

字段 统一含义 发文映射 收文映射 外部收文映射
id 原始记录 ID 发文 ID 收文 ID 外部收文 ID
archiveType 归档类型 send receive outside
archiveTypeName 类型名称 发文 收文 外部收文
billCode 单据编号 发文单号 收文单号 外部收文单号
docNumber 文号 发文字号 来文字号 来文字号
title 公文标题 发文标题 收文标题 外部来文标题
docDate 公文日期 发文日期 收文日期 收文日期
deptName 责任部门 发文部门 收文部门 收文部门
handlerName 经办人 签发人 主办人 主办人
archiveTime 归档时间 更新时间 更新时间 更新时间

这张视图表不是数据库表,而是"查询返回模型"。它的价值在于把不同来源的字段压平到一套列表结构,前端不需要关心每类公文背后的表结构差异。

6.3 附件设计

外部收文附件没有直接塞进主表,而是通过通用附件服务维护:

附件类型 存储方式 说明
正式公文 docFileUrl 适合单个主文件,如 PDF、Word、扫描件
补充附件 AttachmentService 适合多个佐证材料、过程附件

这种设计区分了"主文件"和"附件"。主文件用于台账预览和正式文件展示,附件用于保存过程材料。


七、RuoyiOffice 创新设计

7.1 业务状态不等于流程状态

很多系统会直接把 BPM 状态展示给业务用户,比如"运行中、已通过、已驳回"。这对开发者很清楚,但对文秘和业务部门不够友好。

RuoYi Office 做了一层业务状态映射:

BPM 状态 外部收文办理状态 用户理解
未开始、驳回、取消、撤回 待登记 还没有形成有效办理流程
运行中 办理中 已进入内部处理
通过 已办结 文件处理完成,可归档

这样列表页既能展示流程状态,也能展示办理状态,既满足审批系统的准确性,也满足业务用户的可读性。

7.2 归档台账不是"复制数据",而是"统一视图"

传统 OA 往往会在归档时复制一份数据,久而久之就出现两份数据不一致的问题。RuoYi Office 当前采用查询层聚合,保证归档台账看到的是业务源数据的最新状态。

这对中小企业很友好:表结构更简单,维护成本更低,数据链路更容易理解。

7.3 三类公文保留各自详情入口

归档台账中点击详情,不是进入一个抽象的"归档详情页",而是根据类型跳回原始单据:

归档类型 详情入口
发文 /oa/officedoc/send-info
收文 /oa/officedoc/receive-info
外部收文 /oa/officedoc/outside-info

这意味着归档台账既是统一检索入口,又不牺牲各类公文自己的业务上下文。

7.4 PC 端与 BPM 审批中心共用表单

外部收文详情页既可以从业务列表打开,也可以在 BPM 审批中心作为流程表单打开。通过 props.isApprovalprops.nodeKeyNameviewType 等参数,页面可以自动判断当前是编辑、查看还是审批场景。

这套设计让业务页面和审批页面复用同一份 Vue 组件,避免维护两套表单。

7.5 附件与流程生命周期联动

外部收文删除时,不仅删除主表,还会清理流程实例和附件:

  • 如果存在 processInstanceId,尝试删除 BPM 流程实例。
  • 调用附件服务删除业务附件。
  • 删除外部收文主记录。

这避免了"单据删了,流程还在;附件没人引用,却占用存储"的数据垃圾问题。


八、技术亮点总结

设计要点 实现方式 业务价值
外部收文流程化 提交时创建 BPM 流程实例 外来文不止登记,还能办理闭环
状态分层 processStatus + handleStatus 兼顾流程准确性和业务可读性
状态回调 FlowBillService.updateProcessStatus BPM 状态变化自动同步业务状态
附件管理 AttachmentService + docFileUrl 区分正式文件和补充材料
归档聚合 发文、收文、外部收文分别查询后合并 保留源数据,不额外复制
统一响应 VO OfficeDocArchiveRespVO 前端列表无需感知多表差异
类型化跳转 archiveType 映射详情路径 台账可回溯原始单据
PDF 预览 docFileUrl + iframe 弹窗 台账页面快速查看正式文件
多组织过滤 companyIddeptId 条件 支持集团/多部门数据隔离
导出能力 列表导出 Excel 满足线下档案移交和统计

九、快速体验路径

如果你想在系统中完整体验外部收文与归档台账,可以按下面的顺序操作:

  1. 进入 OA 办公 → 公文管理 → 外部收文
  2. 点击"新增外部收文",填写来文单位、来文字号、公文标题、收文日期。
  3. 上传正式公文或附件,点击保存,确认草稿数据正常。
  4. 点击提交,外部收文进入 BPM 办理流程,办理状态变为"办理中"。
  5. 在审批中心完成领导批示和承办办理。
  6. 流程审批通过后,外部收文办理状态变为"已办结"。
  7. 进入 OA 办公 → 公文管理 → 公文归档台账
  8. 选择归档类型"外部收文",查看刚才办结的记录,也可以切换"全部"查看发文、收文、外部收文混合台账。

源码模块对应关系如下:

体验功能 前端路径 后端服务
外部收文列表 views/oa/officedoc/outside/list OfficeDocOutsideServiceImpl
外部收文详情 views/oa/officedoc/outside/info OfficeDocOutsideServiceImpl
公文归档台账 views/oa/officedoc/archive/list OfficeDocArchiveServiceImpl

结语

外部收文看起来只是公文管理的一个补充模块,但它其实补齐了企业公文体系中非常关键的一环:不是所有文件都从企业内部发起,但所有进入企业的重要文件都应该被登记、办理和归档。

RuoYi Office 的实现没有把外来文做成孤立附件库,而是通过 BPM 流程、办理状态机、附件服务和统一归档台账,把外部来文纳入企业公文闭环。发文、收文、外部收文三类来源最终汇聚到同一个归档视图中,既保留各自业务差异,又形成统一查询入口。

这也是 RuoYi Office 在 OA 模块设计中的一贯思路:业务可以复杂,但系统边界要清晰;流程可以灵活,但数据状态要可控。OA · HRM · CRM · ERP,一站打通,最终服务的都是同一个目标:让企业的每一类业务都能形成可追踪、可复盘、可沉淀的数字化资产。


RuoYi Office ------ 一个平台,管好整个企业

在线演示:http://ruoyioffice.com/web/(账号 admin / admin123)

技术咨询:添加 17156169080,备注「RuoYi Office」

如果觉得不错,请给个 Star 支持一下。

复制代码
相关推荐
IT邦德2 小时前
26ai OGG 微服务高可用部署及切换
微服务·云原生·架构
callJJ12 小时前
Spring Data Redis 两种编程模型详解:同步 vs 响应式
java·spring boot·redis·python·spring
海兰12 小时前
【第27篇】Micrometer + Zipkin
人工智能·spring boot·alibaba·spring ai
phltxy13 小时前
Spring Cloud 分布式服务部署实战:从 0 到 1 实现微服务上线
spring·spring cloud·微服务
海兰14 小时前
【第28篇】可观测性实战:LangFuse 方案详解
人工智能·spring boot·alibaba·spring ai
RuoyiOffice15 小时前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
spring boot·后端·vue·anti-design-vue·ruoyioffice·假期·人力
xmjd msup15 小时前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
9523616 小时前
SpringBoot统一功能处理
java·spring boot·后端
rleS IONS16 小时前
SpringBoot中自定义Starter
java·spring boot·后端