手写通用状态机,终结订单状态流转的if-else噩梦
接手过几个订单系统的同学应该都有同感状态流转这块代码刚开始写if-else还挺顺手的等业务跑起来、需求迭代几个月之后就会变成一个到处打补丁的“面条锅”。今天我用订单场景做例子从零手写一个通用状态机把状态、事件、动作、转移这些概念一次讲透并且给出能直接落到项目里的代码骨架。这篇文章适合被状态判断折磨过的后端开发也适合刚接触状态机、想搞懂原理的初学者。1. 为什么业务代码里需要状态机1.1 订单状态流转的“脏乱差”现状先还原一下没有状态机时,订单系统长什么样。典型的订单起码有四个状态待支付、已支付、已发货、已完成再加上取消、退款之类的分支排列组合一下就非常可观。我见过最典型的问题场景是这样的只要某个地方需要“支付成功更新订单状态”开发同学就复制一段逻辑取消订单的业务分散在支付接口、超时任务、用户后台三个入口每个入口都要各自判断当前状态能不能取消判断条件稍微写差一点就会出现“已发货的订单被取消了”这种线上事故。代码写到这个阶段痛点会非常集中状态校验逻辑散落在各处每加一个入口就要重新校一遍漏一处就是bug。调状态的操作散落在service里想梳理“哪些操作会导致订单变成已完成”完全靠人肉搜索。非法流转的兜底主要靠开发自觉靠评审会口头提醒没有任何强制约束。新人接手代码只能在几百行if-else里慢慢猜业务规则上手成本很高。说白了问题不在于“需求有多复杂”而在于状态流转这件事没有一个统一的管理模型。每个业务方各写各的重复代码和逻辑遗漏几乎必然出现。1.2 状态机到底解决了什么问题状态机做的事情看起来很简单把所有合法的状态流转收拢到一张表里每次状态变更都必须经过这张表非法流转直接拒绝根本不给你执行动作的机会。还是以订单为例。有了状态机之后业务方不再需要自己写“if (当前状态 待支付) 才能支付”这种代码而是直接触发一个“支付事件”状态机自己判断当前状态待支付加上支付事件合不合法。合法就执行对应的动作、把状态推进到目标状态不合法就抛异常业务代码在入口处统一拦截。这种集中管理带来的实际收益有三个流转规则的可读性大幅提升。一张配置表就能说清楚订单从生到死的所有合法路径产品经理也能看得懂。重复校验逻辑被收口。取消订单不管从哪个入口进来都走同一个状态机判定不会出现入口之间标准不一致。非法路径被系统和测试双层盯死。代码层面的校验是第一层状态机配置是第二层逻辑遗漏的概率大大降低。这里要强调一下状态机不是炫技它的本质是“用一张确定性规则表替换散落的if-else分支”把状态管理的复杂度从“人肉维护”降级为“配置维护”。2. 状态机的核心概念与设计思路2.1 四个核心要素状态、事件、动作、转移在动手写代码之前先把概念理清。状态机的四个核心要素分别是状态、事件、动作、转移理解清楚这四个词后面所有代码都是围绕它们展开的。状态State业务进行到某个稳定阶段时对象所处的情况。订单的待支付、已支付、已发货都是状态。它描述的是“现在是什么阶段”。事件Event驱动状态发生变化的消息比如用户点了“支付”按钮、后台执行了“发货”操作。事件描述的是“发生了什么”。动作Action状态发生变化时需要执行的业务逻辑。比如订单从待支付变成已支付可能要执行扣减库存、发送通知、写支付流水这一串业务动作。转移Transition把状态、事件、动作串起来的规则。它回答了“在什么状态下、遇到什么事件、做什么动作、最终落到哪个状态”这个问题。可以拿红绿灯当类比红灯是状态倒计时结束是事件红灯变绿的过程中行人放行、车辆起步是动作从红灯变成绿灯这个整体描述就是转移。订单业务其实就是把这张表画出来然后用一个通用引擎去执行。2.2 从表驱动到通用模型如何抽象一份“状态机配置”写状态机的第一步永远是建模而建模最实用的方式就是先画一张状态流转表。以订单为例按“源状态 事件 - 目标状态 动作”的格式整理我随手画一份核心的源状态事件目标状态动作待支付支付成功已支付扣库存、发通知、写流水待支付取消已取消释放优惠券、回补库存已支付发货已发货生成物流单、推送物流信息已支付取消已取消申请退款、回补库存已发货确认收货已完成结算确认、发送评价邀请这张表画完之后状态机的通用模型就呼之欲出了。核心数据结构可以用一个双层Map来表示MapS, MapE, TransitionS, E第一层key是当前状态S。第二层key是触发事件E。最终value是转移规则对象Transition里面封装了目标状态和要执行的动作。选择嵌套Map而不是List主要原因是查找复杂度。状态机每触发一次事件就要根据“当前状态 事件”找到对应转移规则嵌套Map能做到O(1)级别的查找而List线性扫描在状态和事件数量多了之后会明显变慢。订单场景虽然状态数不多但作为通用模型这个设计必须一开始就做对。2.3 方案选型自研轻量级还是引入开源框架有了模型之后接下来的问题就是到底自己写一套还是引入现成的框架。我给出的建议是订单这种业务量级自研一套轻量级状态机的性价比是最高的。我把两种方案的取舍列一个对比维度自研轻量级引入开源状态机框架学习成本代码量小半天能看完需要学习配置方式、扩展API灵活性完全按业务定制受框架抽象约束功能覆盖只做状态流转核心够用提供持久化、监听器等高级能力依赖复杂度无额外依赖引入大量间接依赖排障难度代码在自己手里好排查出了深层次问题要啃框架源码很多团队一上来就引入开源框架结果发现光是为了跑通Hello World就要理解一大套概念最后项目里用得并不深反而徒增维护成本。我个人的经验是如果业务的状态数少于几十个、流转逻辑不超过几十条自研一个几十行的通用状态机完全够用。等哪天真出现状态数量爆炸、流转规则复杂到没法用一张表描述那再考虑换成框架也不迟。做这个决策的核心判断标准只有一个状态流转逻辑的复杂度。不要因为“开源框架功能多”就选它你的业务可能连百分之十的功能都没用到。3. 手写一个通用状态机核心实现3.1 定义核心模型类状态、事件、转移规则明确了模型之后代码就顺理成章了。先定义状态和事件的类型这里用泛型来处理这样订单、物流、售后等不同业务都能复用同一套状态机引擎。// 状态机转移规则从from状态经过event事件执行actions动作到达to状态 public class TransitionS, E { private final S from; private final E event; private final S to; private final ListActionS, E actions; public Transition(S from, E event, S to, ListActionS, E actions) { this.from from; this.event event; this.to to; this.actions actions; } public S getTo() { return to; } public ListActionS, E getActions() { return actions; } }再定义一个动作接口。动作要尽量设计成“可访问上下文”的形式因为状态转移时业务动作往往需要拿到订单号、用户ID、金额这些数据。这里我直接用Context把整个状态机的输入包装起来动作接口内部可以从Context里取所需数据。public interface ActionS, E { void execute(S from, S to, E event, StateContextS, E context); }Context对象对外看起来是“黑盒”泛型类型由具体业务自己定义。订单场景里Context里面放的是订单数据、操作人等业务信息。泛型设计的目的就是让这个状态机不仅可以处理订单以后接物流、售后、优惠券的时候只需要替换类型参数即可无需改动引擎代码。3.2 核心容器状态机引擎的骨架代码接下来是状态机最核心的容器类。它负责维护“当前状态 事件 - 转移规则”的双层映射并对外提供事件触发入口。public class StateMachineS, E { // 核心存储源状态 - (事件 - 转移规则) private final MapS, MapE, TransitionS, E transitionMap new HashMap(); // 注册状态转移规则 public void register(TransitionS, E transition) { S from transition.getFromState(); E event transition.getEvent(); transitionMap.computeIfAbsent(from, k - new HashMap()) .put(event, transition); } // 触发事件执行状态流转 public S fire(S currentState, E event, StateContextS, E context) { MapE, TransitionS, E transitionMapForState transitionMap.get(currentState); if (transitionMapForState null) { throw new IllegalStateException(状态不存在: currentState); } TransitionS, E transition transitionMapForState.get(event); if (transition null) { throw new IllegalStateException(状态 [ currentState ] 不支持事件 [ event ]); } // 执行动作链 for (ActionS, E action : transition.getActions()) { action.execute(currentState, transition.getTo(), event, context); } // 返回目标状态 return transition.getTo(); } }这段代码是整个状态机引擎的灵魂但有一个细节需要特别注意Transition里我并没有存from和event。注册时用from作为第一层key用event作为第二层key其实from和event已经被映射表的位置语义隐含了再存一遍属于冗余。真正需要塞进对象里的是to和actions。设计成“位置即语义”的结构还有个好处后续做校验可以很方便地遍历transitionMap检查是否出现冲突注册。3.3 用Builder模式搭配置让注册过程可读直接在业务代码里一行一行调用register不是不行但状态多了之后可读性会变差。我习惯提供一个Builder风格的配置入口让每一行代码读起来就像在描述那条表驱动记录。StateMachineOrderState, OrderEvent machine new StateMachine(); machine.register(new Transition( OrderState.WAIT_PAY, OrderEvent.PAY_SUCCESS, OrderState.PAID, Arrays.asList( new StockAction(), new PayNotifyAction(), new PayFlowAction() ) ));machine.register(new Transition( OrderState.WAIT_PAY, OrderEvent.CANCEL, OrderState.CANCELED, Arrays.asList( new CouponReleaseAction(), new StockBackAction() ) ));每一条注册代码就是状态流转表里的一行配置和表完全一一对应。如果后续想做到“配置化”把这张表放到数据库或配置中心那只需要写一个加载器读配置生成Transition对象再注册到机器里就行引擎代码一行都不用改。这是“通用状态机”设计里最值钱的地方——业务规则和引擎逻辑彻底解耦。3.4 把状态机接入订单服务完整代码演示核心引擎写完之后接入业务就非常简单了。以订单服务为例一个典型的支付成功入口可能是这样的Service public class OrderService { // 状态机按订单类型注入Spring管理单例即可 private final StateMachineOrderState, OrderEvent orderStateMachine; public OrderService(StateMachineOrderState, OrderEvent orderStateMachine) { this.orderStateMachine orderStateMachine; } Transactional(rollbackFor Exception.class) public void paySuccess(Long orderId) { Order order orderMapper.selectById(orderId); // 组装业务上下文 OrderContext context new OrderContext(order); // 触发状态机流转非法状态直接抛异常事务回滚 OrderState newState orderStateMachine.fire( order.getStatus(), OrderEvent.PAY_SUCCESS, context ); // 状态机返回的新状态更新到数据库 order.setStatus(newState); orderMapper.updateById(order); } }这段代码里面有两个很关键的地方值得展开讲。第一个是状态机的线程安全特性它启动后只有注册表数据注册过程结束后不再修改所以完全可以作为一个Spring单例被多线程共享。第二个是状态机只负责“算出新状态”具体怎么把新状态写回数据库是业务自己的事。这样设计的好处是状态机不需要关心持久化中间状态逻辑纯粹、也好测试。4. 订单场景的完整落地从配置到实战4.1 订单状态与事件定义有了通用引擎接下来就是把它应用到订单业务上。首先定义订单的状态枚举和事件枚举。这两个枚举建议用Java的enum既能做类型约束又能天然配合泛型参数使用。public enum OrderState { WAIT_PAY, // 待支付 PAID, // 已支付 SHIPPED, // 已发货 COMPLETED, // 已完成 CANCELED // 已取消 } public enum OrderEvent { PAY_SUCCESS, // 支付成功 SHIP, // 发货 CONFIRM, // 确认收货 CANCEL // 取消订单 }这里有个很容易踩的坑很多团队喜欢把状态定义成字符串比如1表示待支付、2表示已支付。字符串的可读性极差而且写错一个字符不会被编译器发现只能等运行时报错。用枚举之后状态机的泛型推演、IDE的自动补全、编译期的类型检查全部都能享受到。4.2 动作实现与扩展点设计状态机的动作部分是业务逻辑最丰富的区域。以“支付成功”这个事件为例会依次执行扣减库存、发送通知、写支付流水三个动作。public class StockAction implements ActionOrderState, OrderEvent { Override public void execute(OrderState from, OrderState to, OrderEvent event, StateContextOrderState, OrderEvent context) { OrderContext ctx (OrderContext) context; System.out.println(扣减库存: 订单号 ctx.getOrder().getId()); } }public class PayNotifyAction implements ActionOrderState, OrderEvent { Override public void execute(OrderState from, OrderState to, OrderEvent event, StateContextOrderState, OrderEvent context) { System.out.println(发送支付成功通知); } }在实际项目里动作之间如果存在依赖关系就需要通过“动作链”来编排。这里我建议动作类保持职责单一不要在一个动作里同时干“扣库存发通知写流水”三件事。动作拆得足够细后续调整顺序、复用、单测都会方便很多。另外如果状态机需要支持前后置处理比如某个动作执行前先做参数校验、执行后统一记录审计日志可以再扩展两个钩子beforeAction在整条动作链执行前触发。afterAction在整条动作链执行后触发。实现的思路是在fire方法里预留两个可覆写的钩子方法默认空实现具体业务按需覆盖。这样审计、埋点、监控这类横切逻辑就不用污染每个业务动作。4.3 幂等与并发状态机最容易被问住的点状态机最难的部分往往不是引擎本身而是把它放到并发环境里还能正确工作。我直接说结论状态机只解决“流转合法性”的问题不解决“并发安全性”的问题。这两个问题经常被混为一谈导致实际落地时出事故。看一个典型并发场景用户在前端同时点了多次支付回调或者超时任务和用户取消操作同时在处理同一笔订单。两个线程分别读取到订单的当前状态都是待支付都通过了内存里的状态校验都调用了stateMachine.fire这时候就会产生同一个订单被处理两次的严重后果。要解决这个问题最优雅的做法是把状态校验下沉到数据库层用带条件的更新语句来保证原子性UPDATE orders SET status #{newStatus} WHERE id #{orderId} AND status #{expectStatus}如果更新影响行数为1说明更新成功为0说明中间态已经被其他线程改掉了当前操作应该被拒绝重试或者丢弃。这个方案在业内通常叫CAS更新和乐观锁思路是一致的它能保证“状态变更”这一步在任何并发级别下都不会被超卖或错卖。在编写时需要把CAS更新放在业务动作执行之前还是之后我的经验是先CAS更新状态再执行业务动作。因为状态一旦抢到了说明当前线程拿到了这份数据的“处理权”其他线程的状态校验会失败不会再进来。只要CAS成功后续执行业务动作就是安全的如果业务动作失败整个事务回滚状态也会跟着回滚。事务边界也要讲清楚状态更新、库存扣减、流水写入这三步必须放在同一个数据库事务里。状态机框架本身不管理事务事务应该由最外层调用方比如Transactional注解标记的Service方法统一控制。任何一步抛出异常全部回滚这样就不会出现“库存扣了但状态还是待支付”的数据不一致。5. 常见问题与排查技巧实录5.1 状态非法转移被拒后事务要不要回滚这个问题的答案取决于业务的执行顺序。如果状态机的校验发生在业务动作之前那非法转移被拒时此前还没有执行任何业务逻辑自然不需要也不是事务提交的问题此时事务正常回滚即可。但有一种反模式需要注意先把业务动作执行了最后再通过状态机校验。这种顺序一旦动作执行成功、状态校验失败事务回滚会把动作也回滚掉表面上看起来没问题但动作的副作用如果是发短信、调外部接口这类不受本事务控制的操作就会造成“数据库回滚了短信已经发出去了”的惨案。我的建议是状态机校验永远前置。在校验通过之后才允许开始执行任何有副作用的业务动作。面对外部调用这种无法回滚的操作尽量放在所有本地数据库操作完成之后、事务提交的边缘侧执行也可以引入可靠消息机制来保证一致性。5.2 并发重复提交导致状态校验失效怎么排查这种问题的典型症状是线上偶尔出现订单被重复发货、重复退款但代码逻辑看下来明明每个入口都有状态判断。排查路径可以这样走第一步确认状态机是否加了并发保护。如果只是在内存里用if (currentState ...)再updateById那肯定有竞态必须改成CAS方式更新。第二步检查数据库更新语句有没有status #{expectStatus}这个条件。很多代码改成CAS更新之后写SQL时忘了加这个条件效果等同于没有保护。第三步检查动作链上是否有网络IO或者长期等待。事务持有时间越长并发冲突概率越高要把耗时操作尽量移出事务。5.3 扩展点太多动作链代码变乱怎么办状态机用一段时间后动作清单会越拉越长。我见过一个订单支付成功的事件上挂了十几二十个动作从核心的库存、流水、通知到后面的优惠券、积分、推荐奖励全都往一条动作链上堆。代码看起来还能跑但出问题时你很难快速定位是哪个动作抛的异常。面对这种复杂度我会做两步整理。第一步是给动作分层核心动作库存、状态、流水和外围动作通知、积分、奖励分开外围动作通过消息队列异步消费这样既拖垮了主链路又能保证核心流程的稳定性。第二步是引入事件发布机制状态机流转完成后发布一个“订单已支付”领域事件让各个业务方订阅自己关心的部分各干各的互不干扰。这其实就是从直链动作向事件驱动架构演进的过程也是状态机落地成熟的标志之一。5.4 状态机问题速查表问题原因解决方案状态不存在异常注册表里漏注册了某个状态检查状态枚举和注册清单的覆盖是否一致状态不支持事件异常流转规则缺失或者业务本就不允许对照状态流转表确认路径是否合法重复支付/重复发货并发调用导致内存校验失效数据库CAS更新或分布式锁状态变了但业务动作没执行动作抛异常被吞掉检查动作链异常处理逻辑避免catch后不抛回滚后状态还是不对状态更新和业务动作不在一个事务统一事务边界状态更新和动作必须同事务写到这里我再分享一点个人体会。状态机这种设计模式看起来技术含量不算高但它的核心价值恰恰在于“把复杂问题变得可列举、可检查”。我见过太多团队在状态机引擎代码上花了很多时间又是持久化又是可视化最后死在过度设计上。倒不如先把一张完整的状态流转表画出来写一个几十行的轻量引擎让业务跑起来等真的撑不住了再考虑上更强的能力。状态机最值得投入精力的地方不是引擎本身而是那张表和那条动作链的边界设计这两样东西想清楚了代码怎么写都是顺的。

相关新闻

制造业数智化升级路径:从设备联网到数据驱动的实战指南

制造业数智化升级路径:从设备联网到数据驱动的实战指南

先说明一点:我这些年接触过不少做智能制造、产线数字化的项目,很多企业砸了几百万上了系统,最后变成一堆漂亮的报表和永远没人看的看板。问题通常不是出在技术上,而是出在“路径”上——大家把数智化升级当成一个IT项目在推&#…

2026/10/13 4:22:04 阅读更多 →
DEA数据包络分析:美赛效率评估模型选型与Python实战指南

DEA数据包络分析:美赛效率评估模型选型与Python实战指南

2026年美赛(MCM/ICM)备赛周期刚开始,数据包络分析(DEA)就已经在各路经验帖里刷屏了。效率评估类问题几乎是竞赛里的常客:宏观一点能考区域发展质量、产业升级效果,微观一点能考医院运营、银行网…

2026/10/13 4:22:04 阅读更多 →
前端组件库调试痛点看 yalc:用快照与显式推送替代 npm link

前端组件库调试痛点看 yalc:用快照与显式推送替代 npm link

如果你维护过跨业务项目复用的前端组件库,大概率经历过这种尴尬:在某业务项目里发现了组件的样式问题,改完组件库代码要重新构建、再手动把产物拷贝进目标项目的node_modules里反复验证,来回几次就烦了。我一开始也是这么干的&…

2026/10/13 4:22:04 阅读更多 →

最新新闻

CentOS手动编译FFmpeg:集成libx264、libfdk-aac与openssl实战指南

CentOS手动编译FFmpeg:集成libx264、libfdk-aac与openssl实战指南

1. 项目概述:为什么在CentOS上手动编译FFmpeg远比yum install重要在某高校多媒体实验室做视频转码系统维护时,我第一次被“默认FFmpeg”坑得不轻——用yum install ffmpeg装完,跑个H.264编码就报错“Unknown encoder libx264”,AA…

2026/10/13 5:08:23 阅读更多 →
Shellcode中动态获取Windows API函数地址的核心方法

Shellcode中动态获取Windows API函数地址的核心方法

1. 项目概述:为什么“获取函数地址”是Shellcode绕不开的生死关你写完一段精巧的汇编逻辑,寄存器状态清零、栈帧干净利落,甚至把payload压缩到了200字节以内——结果一执行就弹出“访问冲突”,或者干脆静默退出。这不是代码写错了…

2026/10/13 5:08:23 阅读更多 →
Bustub数据库内核实现:缓冲池、B+树与查询执行引擎实战解析

Bustub数据库内核实现:缓冲池、B+树与查询执行引擎实战解析

简介:这是基于CMU-15445课程Bustub数据库系统的个人实现源码包,面向数据库内核学习者、系统程序员及准备求职的在校学生,用于理解DBMS底层机制并沉淀项目经验。压缩包共1195个文件、33.89MB,以C/C源码(h/cpp/cc&#x…

2026/10/13 5:08:23 阅读更多 →
AI编程助手运行机制改造:钩子、自定义工具与上下文注入实战

AI编程助手运行机制改造:钩子、自定义工具与上下文注入实战

1. 从“用工具”到“改工具”:AI编程助手正在发生什么变化过去两年,AI编程助手从“能补全一行代码”进化到“能独立完成一个模块”,速度之快让很多一线开发者都有点跟不上节奏。但真正让我觉得有意思的,不是模型本身又涨了多少分&…

2026/10/13 5:08:23 阅读更多 →
PDF可视化导入UEditor:从版式到流式的降级转换实践

PDF可视化导入UEditor:从版式到流式的降级转换实践

做后台管理系统的时候,最容易被产品经理和业务人员提的一个需求就是:把PDF里的内容直接搬进UEditor,最好还能保持样子,改完再导出来。这需求说简单也简单,说麻烦是真麻烦——PDF是版式文档,UEditor是流式富…

2026/10/13 5:08:23 阅读更多 →
Go unsafe包深度解析:指针操作、内存布局与零拷贝实践

Go unsafe包深度解析:指针操作、内存布局与零拷贝实践

在Go社区里,unsafe包一直是个自带争议的话题。有人把它当成“禁区里的核按钮”,有人把它当成“性能优化的万能钥匙”。我自己的态度是:它更像一把手术刀——用对了能精准切到病灶,用错了就得自己承担后果。这篇文章我会从指针操作…

2026/10/13 5:07:23 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →