1. 项目背景与核心价值
去年参与了一个企业级专利管理系统的开发,用SpringBoot实现了从专利申请到维护的全流程数字化。这个系统最让我自豪的是成功将平均专利审批周期从45天压缩到18天,以下是整个项目的技术复盘。
专利管理在科技型企业中是个高频刚需场景。传统纸质流程存在三大痛点:审批链路不透明、文档版本混乱、统计维度单一。我们设计的系统围绕这三个痛点展开,采用SpringBoot+MyBatis-plus技术栈,前后端分离架构,实现了以下核心能力:
- 多级审批工作流引擎(部门初审→法务复核→管理层终审)
- 文档版本树形管理(支持差异对比和版本回滚)
- 多维数据看板(按部门/类型/进度等多角度统计)
关键设计原则:所有审批节点必须保留操作留痕,文档修改需触发版本快照,这是后续可能涉及法律纠纷时的关键证据链。
2. 技术架构解析
2.1 整体架构设计
系统采用经典三层架构,但针对专利业务做了特殊优化:
code复制[前端Vue] ←HTTP→ [SpringBoot REST API] ←→ [MySQL 8.0]
↑
[Redis缓存] ↓
[ElasticSearch]
创新点在于双写机制:审批流程状态变更同时写入MySQL和Redis,利用Redis的Pub/Sub功能实时推送到前端。这样既保证数据持久化,又实现了审批动态的秒级感知。
2.2 核心组件选型
-
工作流引擎:选用Activiti 7.x而非Flowable,主要考虑因素:
- 更友好的中国区文档支持
- 与SpringBoot的自动配置兼容性更好
- 历史任务查询性能优化(专利审批常需要追溯3年内的记录)
-
文档管理:集成MinIO对象存储而非直接使用本地文件系统,实现:
- 自动生成文档水印(申请人+时间戳)
- 版本快照存储空间自动回收(保留最近5个版本)
- 文档预览服务(通过LibreOffice在线转换)
-
权限控制:采用RBAC+ABAC混合模型:
- 基础功能用Spring Security实现角色权限
- 敏感操作(如驳回申请)额外增加属性校验(需满足:操作人职级≥申请人职级)
3. 关键实现细节
3.1 审批流程设计
专利审批包含7个状态节点,通过状态机模式实现:
java复制public enum PatentStatus {
DRAFT, // 草稿
PENDING_REVIEW, // 待初审
LEGAL_REVIEW, // 法务复核
FINAL_APPROVAL, // 终审
REJECTED, // 已驳回
GRANTED, // 已授权
ARCHIVED // 已归档
}
状态转换通过策略模式实现,典型代码结构:
java复制public interface StateTransitionHandler {
void handle(Patent patent, String operator);
}
@Service
@RequiredArgsConstructor
public class DraftToReviewHandler implements StateTransitionHandler {
private final PatentRepository repository;
private final DocumentService documentService;
@Override
@Transactional
public void handle(Patent patent, String operator) {
// 校验文档完整性
if(!documentService.validateCompleteness(patent.getDocId())){
throw new IllegalStateException("缺少必要附件");
}
patent.setStatus(PENDING_REVIEW);
patent.setSubmitter(operator);
patent.setSubmitTime(LocalDateTime.now());
repository.save(patent);
}
}
3.2 文档版本控制
采用组合模式实现文档树形结构:
java复制public class DocumentVersion {
private String versionId;
private String parentVersionId;
private String minioPath;
private String md5;
private LocalDateTime createTime;
private String changeLog;
}
版本对比算法核心逻辑:
java复制public List<DiffItem> compareVersions(String baseVersion, String targetVersion) {
String baseText = extractText(baseVersion);
String targetText = extractText(targetVersion);
return DiffUtils.diff(
Arrays.asList(baseText.split("\n")),
Arrays.asList(targetText.split("\n"))
).stream()
.filter(diff -> !diff.getOperation().equals(Operation.EQUAL))
.collect(Collectors.toList());
}
4. 性能优化实践
4.1 审批历史查询优化
专利审批历史查询有三个特点:高频、需分页、时间跨度大。原始方案使用JOIN查询导致性能瓶颈:
sql复制/* 问题SQL */
SELECT * FROM patent_process
JOIN user ON process.operator = user.id
WHERE patent_id = ?
ORDER BY operate_time DESC
LIMIT ?, ?
优化方案:
- 建立联合索引:(patent_id, operate_time)
- 采用冗余存储:审批时同步存储操作人姓名
- 引入二级缓存:Redis存储最近3个月的审批记录
优化后查询耗时从1200ms降至80ms。
4.2 大数据量导出
专利统计报表导出涉及复杂关联查询,我们采用分段加载+内存压缩技术:
java复制public void exportPatentReport(HttpServletResponse response) {
try(OutputStream out = response.getOutputStream();
ZipOutputStream zipOut = new ZipOutputStream(out)) {
// 分页查询避免OOM
int pageSize = 1000;
for(int i=0; ;i++) {
Page<Patent> page = repository.findByCondition(condition, PageRequest.of(i, pageSize));
if(page.isEmpty()) break;
// 每个分片一个CSV文件
zipOut.putNextEntry(new ZipEntry("part_" + i + ".csv"));
writeCsv(page.getContent(), zipOut);
zipOut.closeEntry();
}
}
}
5. 踩坑记录与解决方案
5.1 并发提交冲突
早期版本出现多个部门同时提交专利导致数据覆盖问题。解决方案:
- 数据库增加version字段实现乐观锁
- 前端采用防重复提交令牌(Token)
java复制@PostMapping("/submit")
public Result submitPatent(@RequestBody PatentDTO dto,
@RequestHeader("X-Submit-Token") String token) {
if(!tokenService.validateToken(token)){
return Result.fail("请勿重复提交");
}
// ...业务逻辑
}
5.2 文档预览服务崩溃
LibreOffice进程在并发高时会崩溃,最终方案:
- 引入Docker容器池管理Office进程
- 增加健康检查机制
- 设置超时熔断(超过30秒自动降级为下载原始文件)
yaml复制# docker-compose配置片段
services:
libreoffice:
image: onlyoffice/documentserver
deploy:
replicas: 3
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080"]
interval: 30s
timeout: 10s
retries: 3
6. 部署与监控方案
采用Prometheus+Grafana监控关键指标:
- 审批流程平均耗时
- 文档转换成功率
- 并发用户数阈值预警
关键PromQL示例:
code复制# 审批延迟监控
rate(patent_process_duration_seconds_sum[5m])
/
rate(patent_process_duration_seconds_count[5m])
日志收集采用ELK栈,特别注意审计日志的完整保留:
java复制@Aspect
@Component
@Slf4j
public class AuditLogAspect {
@AfterReturning("execution(* com..service..*(..)) && @annotation(auditLog)")
public void after(AuditLog auditLog) {
log.info("操作审计|{}|{}|{}",
auditLog.value(),
SecurityUtils.getCurrentUser(),
LocalDateTime.now());
}
}
7. 扩展性设计
系统预留了三个重要扩展点:
-
区块链存证:通过SPI接口设计,可快速接入司法区块链
java复制public interface BlockchainService { String storeEvidence(byte[] data); } -
多租户支持:通过TenantContext维护租户标识
java复制public class TenantFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res) { String tenantId = ((HttpServletRequest)req).getHeader("X-Tenant-ID"); TenantContext.setCurrentTenant(tenantId); } } -
规则引擎:使用Drools实现可配置的审批规则
drl复制rule "高级专利自动加速" when $p : Patent(techLevel >= 3, applicant.dept == "R&D") then modify($p) { setPriority(1) }; end
项目源码已做企业级脱敏处理,保留了核心架构实现。建议运行前先初始化测试数据:
sql复制INSERT INTO sys_role VALUES
(1,'ROLE_ADMIN','系统管理员'),
(2,'ROLE_DEPT_HEAD','部门负责人'),
(3,'ROLE_LAWYER','法务专员');