SpringBoot集成Activiti 7工作流引擎:从BPMN部署到审批流实战
SpringBoot 集成 Activiti 7 这个事我最近在一个审批流程项目里完整踩了一遍。项目不大但偏偏选了 Activiti 7 这个半新不旧的版本网上一搜全是 5.x 和 6.x 的老文章7 的坑只能自己一点点填。这篇文章就把我从选型到落地的全过程写清楚包括版本兼容、自动建表、BPMN 部署、任务流转还有那几个搜索引擎翻半天也翻不到答案的坑。适合正在做审批流、工单流、合同流转又刚好想在 SpringBoot 里集成工作流引擎的开发者参考。1. 项目背景与选型为什么是 Activiti 71.1 审批流到底要解决什么问题很多业务系统做到一定阶段就会冒出审批的需求请假要走流程、报销要走流程、合同要多人会签、工单要按角色流转。最开始大家可能用状态机硬写一个字段改来改去分支一多代码就炸了。比如请假时长不同要不同的审批人金额超过一定限制要加签这些逻辑如果用 if else 堆在 Service 里后续加一个审批节点就要动一坨代码测试压力巨大。工作流引擎解决的就是这个问题把流程定义从代码里抽出来放到 BPMN 文件里由引擎统一管理流程实例的推进、任务的分配、历史的记录。业务代码只关心发起流程查询待办审批通过/驳回这几个动作至于流程怎么走、走到哪一步、能不能走分支全部交给引擎。这么一来流程变更基本不需要改 Java 代码改完 BPMN 重新部署就行对开发和业务来说都是实打实的降负担。1.2 几个主流引擎的对比选型的时候我主要看了三个Activiti、Flowable、Camunda。这三个同源代码里很多 API 长得几乎一模一样但定位上已经分了家。Flowable 是从 Activiti 5 分支出来的社区活跃文档也比较全但它的版本升级节奏快SpringBoot 2 和 SpringBoot 3 的兼容性要小心确认。Camunda 在流程可视化、运维监控上做得最重平台化能力强适合大团队大平台但对一个中小型项目来说有点杀鸡用牛刀部署产物也偏重。Activiti 7 是 Activiti 团队在 6 之后重新梳理的项目结构把很多模块拆分重构了虽然资料少但如果你原本就是 Activiti 系的思路7 的 API 延续性还算友好。综合下来我这个项目不需要 Camunda 那么重的 Cockpit 监控界面Flowable 虽然稳但迁移成本没优势最终选了 Activiti 7。这里也建议读者别盲目抄作业要根据团队熟悉度和项目复杂度来定。1.3 Activiti 7 的变与不变既然是 7就要先搞清楚它跟老版本差在哪。最直观的变化是包名和模块结构老的 Activiti 5/6 里很多内部实现被重新组织7 的引擎更强调可嵌入、可配置。你在代码里常打交道的还是那几个 ServiceRepositoryService 管流程定义RuntimeService 管流程实例TaskService 管任务HistoryService 管历史。这一点跟 5/6 是一脉相承的老经验大部分能复用。但有一个很关键的变化7 对 IdentityService 的定位弱化了。老版本里有一张 ACT_ID_USER 之类的用户表可以直接往里面塞用户做人员管理。到了 7官方更希望你把用户体系留在业务系统里流程引擎只认用户 ID 字符串不负责认证和权限。这一点从一开始就要想清楚不要指望工作流引擎帮你管登录用户。2. 基础环境与依赖集成2.1 版本兼容性是第一道坎Activiti 7 对 SpringBoot 版本有明确要求。我这里用的是 SpringBoot 2.7.x 配 Activiti 7.1.0.M6这个组合实测比较稳。如果你拿 SpringBoot 3 去配 Activiti 7启动大概率会报各种 Javax 到 Jakarta 的迁移错误因为 Activiti 7 内部还在用 javax 命名空间适配 SpringBoot 3 是后面版本才做的事。所以版本选择上不要贪新先保证能跑起来。组合方案结论SpringBoot 2.3.x Activiti 7.1.0.M6兼容可运行SpringBoot 2.7.x Activiti 7.1.0.M6我实测的组合稳定SpringBoot 2.7.x Activiti 7.0.0.GA兼容但 7.1 修复了一些问题SpringBoot 3.x Activiti 7.x不推荐启动容易踩坑这里多说一句Maven 坐标要注意7 的 starter 还是activiti-spring-boot-starter但版本要写 7.1.0.M6 或 7.0.0.GA。别看到M就以为是测试版不能上生产Activiti 官方对 7 的里程碑版本维护得还可以很多项目实际就是跑在 M 版本上的。2.2 引入依赖与起步配置如果你用的是 Maven直接在 pom 里加这一段dependency groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter/artifactId version7.1.0.M6/version /dependency然后配置文件里加这几项我是放在 application.yml 里的spring: datasource: url: jdbc:mysql://localhost:3306/workflow?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver activiti: database-schema-update: true history-level: full db-history-used: true check-process-definitions: truedatabase-schema-update: true的意思是让引擎启动时自动检查并创建 Activiti 自己的表这对第一次跑通非常关键。history-level: full则是把历史数据完整记录包括流程变量、任务操作记录虽然占空间多一点但后续排查流程问题全靠它。check-process-definitions: true配合后面的classpath:/processes/目录SpringBoot 启动时会自动部署 BPMN 文件。注意一点数据库要预先建好Activiti 不会帮你创建 database只会创建表。我用的是 MySQL 8.0如果你的数据库版本太老比如 MySQL 5.5可能会有兼容问题建议至少 5.7 以上。2.3 数据源与 Activiti 表的初始化策略Activiti 7 默认会使用项目里的主数据源。如果你的 SpringBoot 项目只有一个数据源直接把spring.datasource配好就行引擎启动后会自动建表。它建的表大致分这几类通用表以 ACT_GE_ 开头流程定义和部署相关以 ACT_RE_ 开头运行时数据以 ACT_RU_ 开头历史数据以 ACT_HI_ 开头用户和组相关以 ACT_ID_ 开头。我刚跑起来的时候第一次启动日志里会看到一大堆建表语句这是正常的。第二次启动就不会重复建了因为 ACT_GE_PROPERTY 表里存了数据库版本信息。如果你是多数据源项目就要小心了。Activiti 的自动配置会尝试找到Primary的数据源来做引擎数据源如果业务系统有多个库最好给流程引擎单独配一个专用库不要让流程表和业务表混在同一个库里。混在同一个库的问题在于流程表里很多运行时数据需要频繁读写一旦业务量大会拖累业务查询性能而且后续迁移、备份都得连带着处理一堆流程表维护成本高。3. 流程定义与部署3.1 第一个 BPMN 文件请假流程Activiti 的流程定义是标准的 BPMN 2.0 文件后缀通常是.bpmn或者.bpmn20.xml。我第一个拿来做验证的是最简单的请假流程发起 → 主管审批 → 结束。这个文件放在src/main/resources/processes/目录下。?xml version1.0 encodingUTF-8? definitions iddefinitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:activitihttp://activiti.org/bpmn xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance targetNamespacehttp://workflow.example.com/process process idleaveProcess name请假流程 isExecutabletrue startEvent idstartEvent name开始/ userTask idleaderApprove name主管审批 activiti:assignee${applyUser}/ endEvent idendEvent name结束/ sequenceFlow idflow1 sourceRefstartEvent targetRefleaderApprove/ sequenceFlow idflow2 sourceRefleaderApprove targetRefendEvent/ /process /definitions这里我解释几个关键点。process标签的id是流程 key启动流程时要用到比如runtimeService.startProcessInstanceByKey(leaveProcess)就是根据这个 id 找到对应流程定义。userTask代表人工审批节点activiti:assignee${applyUser}表示这个任务分配给谁${applyUser}是一个表达式启动流程时通过流程变量传进来。也就是说谁发起请假主管审批任务就派给谁的直接主管。刚开始写 BPMN 文件最容易犯的错是忘记加xmlns:activiti这个命名空间。不加的话activiti:assignee会解析失败部署的时候直接报错。另一个坑是isExecutabletrue不能漏漏了启动流程时引擎会认为它不是可执行流程抛异常。3.2 部署方式与验证Activiti 7 在 SpringBoot 里部署流程定义有两种常见方式。第一种就是我最开始用的自动部署把 BPMN 文件丢到classpath:/processes/下启动时自动注册。这个方案适合开发阶段快速验证但有个隐患如果同一个流程 key 你改完 BPMN 又重启应用引擎会生成新的流程定义版本旧版本并不会被删掉。版本管理是 Activiti 的设计不算是 bug但你要明白新流程从新版本开始旧流程实例还在旧版本上跑这个规则。第二种是手动部署适合做流程管理后台功能比如页面上传 BPMN 文件然后调用部署接口Autowired private RepositoryService repositoryService; public void deployProcess(String bpmnPath) { Deployment deployment repositoryService.createDeployment() .addClasspathResource(bpmnPath) .name(请假流程) .deploy(); System.out.println(部署 ID deployment.getId()); }RepositoryService是流程定义和部署的管理入口。通过createDeployment()可以加载 classpath 里的 BPMN 文件也可以换成一个 InputStream 来加载上传的 XML 内容做在线部署解锁后台上传功能很方便。验证部署是否成功可以从数据库看 ACT_RE_PROCDEF 表里面的 KEY_ 列就是流程 keyVERSION_ 列是版本号。也可以用代码验证Autowired private RepositoryService repositoryService; public long countProcessDefinitions() { return repositoryService.createProcessDefinitionQuery() .processDefinitionKey(leaveProcess) .count(); }返回结果大于 0就说明部署成功了。3.3 流程定义的管理与版本版本这块多说一点。我第一次部署 leaveProcess 是 V1改了一次 BPMN 再次部署就变成 V2 了。ACT_RE_PROCDEF 表里两个版本都会在但默认启动流程用的都是最新版本。如果你在流程管理后台想指定版本部署可以在 query 里加上.processDefinitionVersion(1)然后把 deploymentId 找出来。这里有一个很隐蔽的坑如果业务系统里已经有几十个正在跑的旧流程实例你更新了流程定义后老实例还是按照部署时的旧版本 XML 继续跑直到结束。所以流程变更要么接受这种新流程走新规则的模式要么就要处理老实例的迁移比如人工干预或者流程跳转。这个在设计阶段要提前和业务方讲清楚否则上线后改一次流程被问为什么老的单子还是旧流程就解释不清楚了。4. 运行时流程操作从启动到审批4.1 发起流程实例与业务绑定流程定义部署好了接下来就是业务侧发起流程。这里有一个核心概念要理清流程定义像是模板流程实例才是具体的一单审批。每次发起请假引擎会创建一个新的流程实例相当于把模板实例化一份。发起流程的代码很简单但有几个细节值得注意Autowired private RuntimeService runtimeService; public String startLeaveProcess(String applyUserId, String businessKey, Double days) { MapString, Object variables new HashMap(); variables.put(applyUser, applyUserId); variables.put(days, days); ProcessInstance processInstance runtimeService.startProcessInstanceByKey( leaveProcess, businessKey, variables ); return processInstance.getId(); }startProcessInstanceByKey有三个参数流程 key、业务 key、流程变量。业务 key 非常建议传上去它是把流程实例和业务数据关联起来的关键。比如你的请假表单在业务表里主键是 BIZ-2024-001传到 businessKey 里之后随时可以通过runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(BIZ-2024-001).singleResult()反查流程实例这在做流程跟踪页面时非常有用。流程变量是流程引擎流转时的上下文数据。BPMN 里的条件表达式、任务分配表达式、网关判断用的都是这些变量。传变量的时候要注意类型匹配金额判断就要传数字别传字符串 5000否则比较大小会出事。4.2 待办查询与任务审批流程实例启动后引擎会自动推进到第一个 userTask也就是上面例子里的 leaderApprove。这个任务会出现在待办表里对应的数据库表是 ACT_RU_TASK。查询待办一般用 TaskServiceAutowired private TaskService taskService; public ListTask queryTodoTasks(String assignee) { return taskService.createTaskQuery() .taskAssignee(assignee) .orderByTaskCreateTime() .desc() .list(); }这里taskAssignee(assignee)是查询分配给指定人的任务。如果你做的是组任务比如有多个候选人都可以处理那么查询方式会变成taskCandidateUser这里先不展开后面扩展部分再说。审批动作其实就是完成任务public void completeTask(String taskId, boolean approved, String comment) { MapString, Object variables new HashMap(); variables.put(approved, approved); variables.put(comment, comment); taskService.complete(taskId, variables); }complete方法是引擎推进流程的核心。调用之后当前任务结束引擎会根据 BPMN 的 sequenceFlow 找到下一个节点。如果是 userTask就会生成新的待办如果是 endEvent流程实例就结束运行时表里的数据会被清理相关信息进入历史表。我在实际项目中踩过一个权限的坑单纯用taskService.complete(taskId, variables)是不校验操作人身份的拿到 taskId 就能完成任务。所以业务层必须先查一次任务归属确认当前登录用户确实是 assignee再调 complete否则就是越权审批。这种校验代码一定要写在 service 层别指望框架帮你做。4.3 排他网关与条件判断有了基础审批以后很快就会有分支需求。比如请假天数超过 3 天需要部门总监审批3 天以内主管审批就够了。BPMN 里实现这个分支最常用的就是排他网关Exclusive Gateway。exclusiveGateway idapprovalGateway name审批层级判断/ sequenceFlow idflowBranchHigh sourceRefapprovalGateway targetRefdirectorApprove conditionExpression xsi:typetFormalExpression ![CDATA[${days 3}]] /conditionExpression /sequenceFlow sequenceFlow idflowBranchLow sourceRefapprovalGateway targetRefendEvent conditionExpression xsi:typetFormalExpression ![CDATA[${days 3}]] /conditionExpression /sequenceFlow排他网关是多选一的逻辑引擎从上到下检查每一条出线的条件表达式遇到第一个为 true 的就走那条线。如果所有条件都不满足默认走没有条件的普通连线如果也没有普通连线流程就会抛出异常。写条件表达式的时候最容易翻车的是变量没传或者变量名拼错。比如启动流程时变量叫dayBPMN 里判断days那表达式结果是 false流程整个卡住不动还不给你明显报错只能去 ACT_RU_VARIABLE 表里一个一个对变量名。我建议在流程变量命名上定一个前缀规范比如所有业务参数统一叫form_开头至少能避免一半的拼写错误。4.4 流程历史与状态追踪流程跑起来之后业务方一定会在某个列表页想看到这个单子现在到哪一步了谁批的批没批。Activiti 的 HistoryService 就是干这个的。开启了history-level: full之后所有环节数据会落到历史表里。Autowired private HistoryService historyService; public ListHistoricTaskInstance getProcessHistory(String processInstanceId) { return historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .orderByHistoricTaskInstanceEndTime() .asc() .list(); }这个查询结果里包含每个节点的名称、办理人、开始时间、结束时间。如果任务还没处理endTime为空说明流程当前停在这个节点上。把列表渲染成时间线就是一个非常直观的审批进度图。另一个常用查询是判断流程是否还在运行public boolean isProcessRunning(String processInstanceId) { ProcessInstance processInstance runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); return processInstance ! null; }流程实例结束后ACT_RU_* 表里对应的记录会消失但 ACT_HI_* 表里会永久保留。所以判断流程是否结束不能查 RuntimeService要直接看历史表的 deleteTime。5. 常见问题与排查实录5.1 ACT 表没有自动创建这是我第一次启动遇到的头号问题。配置都写好了项目也起来了然后一调用流程接口就报Table act_ge_property doesnt exist。原因通常有三个第一spring.activiti.database-schema-update没设成 true 或者被其他配置覆盖了第二数据库连接指向的库不对你看错了库第三数据库账号权限不够建表语句被拒绝了。排查思路很简单看启动日志。正常的情况下启动日志里能看到多张 ACT_ 前缀的表格创建记录。如果一条都没有优先检查配置项是不是生效了。这里有个小技巧可以在启动类临时打印一下配置值确认Value(${spring.activiti.database-schema-update}) private String schemaUpdate; PostConstruct public void printConfig() { System.out.println(schemaUpdate schemaUpdate); }还有一类情况是 MySQL 配置了lower_case_table_names1所有表名都会变成小写这在 Linux 环境下比较多见。Activiti 本身对大小写不敏感但如果你有报表系统直接查 ACT_ 大写表名可能就会查不到。这种问题通常不是引擎的锅是周边系统对接时的表名大小写假设出了问题。5.2 流程文件没被部署BPMN 文件放在resources/processes/下启动时完全没反应流程定义数量一直为 0。这个问题的根源在于spring.activiti.check-process-definitions默认行为和你实际的 classpath 结构不一致。排查分三步先确认 BPMN 文件是否真的在编译后的 classes 目录里。有时候 IDE 的 build 配置把 resources 目录漏掉了文件根本没进 classpath。再确认文件名后缀Activiti 默认扫描的是.bpmn和.bpmn20.xml如果你命名为.xml它默认不识别除非你额外配置process-definition-location-prefix和process-definition-location-suffixes。最后确认 BPMN 文件本身是否合法可以用repositoryService.createDeployment().addClasspathResource(processes/leaveProcess.bpmn).deploy()手动部署一次如果手动能成功自动没反应就说明是扫描路径问题。我这里也建议不要在自动部署里依赖顺手看一下这种心态每次开发新流程都要写一个测试用例去断言repositoryService.createProcessDefinitionQuery().count()有值把这个验证自动化比盯着日志靠谱得多。5.3 与 MyBatis Plus 的冲突处理国内项目用 MyBatis Plus 的比例非常高而 Activiti 7 内部又自带了 MyBatis。如果项目里已经引入了mybatis-plus-boot-starter再引入 Activiti 的 starter启动时可能会出现奇怪的 mapper 扫描冲突、SqlSessionFactory 被覆盖等问题。我遇到的典型报错是某个org.apache.ibatis.binding.BindingException: Invalid bound statement。排查之后发现是 Activiti 内部的 mapper 被打包进了同一个 SqlSessionFactory但配置上没被正确识别。处理方式有两种看你的项目情况。如果业务系统没有复杂到必须 MyBatis Plus可以考虑不加 MyBatis Plus直接用JdbcTemplate或者只保留 Activiti 自带的 MyBatis。但大多数项目不可能为了工作流拆掉业务持久层所以更推荐第二种把 Activiti 的数据源和 MyBatis 配置隔离。Activiti 的 starter 对 MyBatis 的依赖是间接的如果确实冲突可以在依赖里排除掉 Activiti 自带的mybatis传递依赖再用排除后的方式把引擎数据源独立出来。不过这种方式要小心Activiti 引擎本身运行依赖 MyBatis排除后容易引发引擎内部找不到 mapper 的问题。如果你用的是 MyBatis Plus并且项目里有多数据源最简单的方案是引入 MyBatis Plus 官方提供的多数据源组件给 Activiti 的路由单独指定数据源同时明确 MyBatis Plus 的MapperScan扫描范围不要让引擎的 mapper 混进去。这样两个 MyBatis 就各管各的库互不干扰。5.4 多数据源场景下引擎连错库前面提过多数据源的问题这里单独展开。常见的多数据源实现有两种一种是 Spring 原生DataSource加Primary第二种是动态数据源框架。无论哪种Activiti 默认都会尝试拿容器里的主数据源来创建引擎数据源。如果你的主数据源是业务库而业务流程库是另一个启动时 Activiti 直接把流程表建到业务库里了后续流程数据全混在业务库里这就是连错库了。解决思路是给流程引擎单独设数据源。 Activiti 7 里可以通过自定义ProcessEngineConfiguration实现用代码创建一个独立的配置对象指定数据源和表策略Bean public ProcessEngineConfiguration processEngineConfiguration(DataSource activitiDataSource) { SpringProcessEngineConfiguration config new SpringProcessEngineConfiguration(); config.setDataSource(activitiDataSource); config.setDatabaseSchemaUpdate(ActivitiProperties.DATABASE_SCHEMA_UPDATE_TRUE); config.setHistoryLevel(HistoryLevel.FULL.getLevel()); config.setProcessEngineName(workflowEngine); return config; } Bean public ProcessEngine processEngine(ProcessEngineConfiguration processEngineConfiguration) { return processEngineConfiguration.buildProcessEngine(); }需要注意一旦手动创建了ProcessEngineConfiguration你就接管了 Activiti 的初始化逻辑SpringBoot 自动配置里的很多属性可能不再生效。你需要把database-schema-update、history-level这些参数都在 bean 里手动指定否则默认值可能不是你想要的。我建议这种方案只用于确实存在多数据源的项目单数据源项目就让 starter 自动配置千万别为了展示技术复杂度去手动接管引擎初始化。5.5 变量类型与 JSON 序列化问题流程变量传输时数类型和序列化值得留意。默认情况下Activiti 会把 Java 对象序列化后存到数据库的字节数组字段里。如果你在流程变量里塞了一个自定义对象而该对象没有实现Serializable启动流程时就会报序列化异常。更稳妥的做法是尽量用基本类型和 String、List、Map 这些能天然 JSON 化的结构。如果你一定要塞对象建议用 JSON 字符串传。比如把整个表单数据转成JSONObject.toString()存成一个 String 变量使用的时候再反序列化。这样流程表里存的东西人类可读排查问题也不用去解析二进制流。我在项目里就吃过这个亏。最开始图省事直接把一个表单对象塞进去了升级代码后对象里的字段变了导致老的历史流程实例在查询时反序列化失败直接抛异常。后来把所有变量改成基本类型加 JSON 字符串这个问题再没出现过。6. 实操心得与后续扩展6.1 几个常规文档不会写的经验跑通整个链路之后我总结了几条比较实用的经验不一定写进官方文档里但实际项目里非常重要。第一流程引擎初始化只能跑一次。如果你在一个 JVM 里同时启动多个 Spring 上下文比如测试环境同时起了多个配置文件就可能出现多个 ProcessEngine而它们各自可能维护不同的表数据。这个不是大毛病但容易造成为什么我改了数据库查询还是旧数据的困惑。第二业务表和流程表不要强耦合。Activiti 的表结构有自己的生命周期管理你不应该去直接修改 ACT_ 前缀的表结构更不要往里面加业务字段。业务数据留在自己的库表里通过 businessKey 关联这样才能保证引擎升级时你的业务代码不受影响。第三待办任务查询要控制范围。如果你做的是集团级系统所有人待办直接taskService.createTaskQuery().list()数据量上来后性能会很难看。一定要配合候选人、候选组、业务分类来查询不要让用户侧接口全表扫描。真到了大数据量可以考虑定时把运行时任务数据同步到 Elasticsearch但这个就属于大型架构方案了一般项目用不上。6.2 下一步可以扩展的方向如果你的项目跑通了基础的部署、启动、审批三个环节后面的路基本就顺畅了。比较值得扩展的方向有会签和或签也就是多实例任务用 BPMN 的multiInstanceLoopCharacteristics节点实现多人审批子流程把公共的审批逻辑抽出来复用驳回和转办这些可能需要自己维护节点的跳转逻辑Activiti 7 提供了runtimeService.createChangeActivityStateBuilder()接口可以实现流程实例的任意节点跳转。我个人做下来最深的体会是工作流引擎的学习曲线其实不在 API 上而在 BPMN 建模思维上。API 就那几个 Service翻一遍文档就能写但怎么把一个业务场景抽象成节点和连线怎么用网关表达复杂的流转条件这才是真正需要花时间琢磨的地方。建议你先拿一个小流程练手跑通了再往复杂里加别一上来就想做个万能审批平台那样大概率会被复杂的 BPMN 文件困住。如果你现在做的就是 SpringBoot 集成 Activiti 7 的项目我的建议是第一版把部署、启动、审批、历史查询这四件事做得足够扎实就足够应付大多数审批场景了。后面再加多人会签、驳回、跳转这些高级能力的时候你会发现基础打好了一切都好说。

相关新闻

三明治定理视角下的模型泛化:为什么越精确越脆弱?

三明治定理视角下的模型泛化:为什么越精确越脆弱?

整天泡在模型训练里的朋友,应该都遇到过这种怪事:一个模型的验证集准确率漂亮得吓人,训练损失一路压到小数点后三位,结果一上真实环境就明显“虚”。更诡异的是,你把模型调得越精确、拟合得越狠,它反而越容…

2026/10/13 20:25:49 阅读更多 →
从搜索式提问到外部大脑:程序员如何正确使用AI编程助手

从搜索式提问到外部大脑:程序员如何正确使用AI编程助手

把同一个对话模型又当成百度在用,大概是现在程序员浪费算力最隐蔽的方式。不是它不好用,是你还没到“用对”的那一层。两年前我也是这样:遇到问题,开个新会话,把报错信息粘贴进去,复制一段代码,…

2026/10/13 20:25:49 阅读更多 →
二手房房价预测实战:Python数据清洗与SHAP可解释建模

二手房房价预测实战:Python数据清洗与SHAP可解释建模

简介:本资源是一套面向计算机专业本科生及数据科学初学者的二手房房价分析与预测实战项目,聚焦真实业务场景下的数据清洗、探索性分析与机器学习建模全流程。压缩包共17个文件,含12个CSV格式原始与处理后数据集、3个Jupyter Notebook&#xf…

2026/10/13 20:25:49 阅读更多 →

最新新闻

深度学习损失函数选型实战:从MSE到Focal Loss的原理与踩坑

深度学习损失函数选型实战:从MSE到Focal Loss的原理与踩坑

损失函数这玩意儿,说起来是深度学习里最基础的组件之一,但恰恰是很多同学在实战里翻车最频繁的地方。我带项目的时候经常看到有人模型结构抄得挺像样,学习率调度也配了一堆,可训练出来的结果就是不对,Loss 曲线跟心电图…

2026/10/14 6:38:29 阅读更多 →
微信小程序聊天室:WebSocket实时通信与图片消息实现

微信小程序聊天室:WebSocket实时通信与图片消息实现

上个月接了个需求,要做微信小程序聊天室,支持文字消息和图片消息,图片要能直接发到聊天流里。团队里一位同事提议先上HTTP轮询,2秒拉一次,省事。我说这个方案在简单场景下能跑,但聊天室里撑不住多少用户。后…

2026/10/14 6:38:29 阅读更多 →
SQLite FTS5+BM25实现上下文感知搜索(context-mode)

SQLite FTS5+BM25实现上下文感知搜索(context-mode)

1. “context-mode”到底是什么?别被术语唬住,它本质是让工具真正“听懂你话”的底层机制 “context-mode”这个词最近在开发者社区、低代码平台和本地AI工具链里高频出现,但翻遍官方文档,几乎找不到一句清晰定义。我最初看到它时…

2026/10/14 6:38:29 阅读更多 →
QGIS 加载天地图 403 权限类型错误(301013):User-Agent 分流与三种解法

QGIS 加载天地图 403 权限类型错误(301013):User-Agent 分流与三种解法

场景QGIS 里挂天地图底图,画面全空。F12 或者 QGIS 的网络日志里刷一片 403,响应体是:{"code":301013,"msg":"权限类型错误"}天地图控制台里 key 明明状态正常、配额也没用完,换一个浏览器端 key 试…

2026/10/14 6:38:29 阅读更多 →
极地环境计算设备可靠性测试:低温启动与温循实战指南

极地环境计算设备可靠性测试:低温启动与温循实战指南

把服务器从20℃的调试间一路运到零下四十多度的极地站点,开机那一刻,谁都不敢保证屏幕上是正常的登录界面还是黑屏。我在嵌入式测试这一行摸爬滚打了十几年,实验室里跑高低温箱和真正面对“跨越温差的可靠性挑战”,是完全两种体验…

2026/10/14 6:38:29 阅读更多 →
Simulink Bode图频域分析:线性化与稳定裕度实战

Simulink Bode图频域分析:线性化与稳定裕度实战

简介:这份资源面向学习自动控制原理、从事控制系统频域分析的初学者与工程人员,针对传统用MATLAB M语言绘制Bode图耗时费力、不便直观理解频域指标的问题,讲解如何借助Simulink可视化工具完成开环与闭环Bode图的绘制。资源包内含1个PDF文档&a…

2026/10/14 6:37:29 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →