用最强的ChatGPT o1-preview模型生成极轻量级流程引擎,直接运行,可扩展多场景

349 阅读13分钟

1. 背景与结论

自从ChatGPT o1-preview放出来,在网上时不时看到介绍说这模型会思考,武力值爆棚。于是就试试。

结论武力值的确是爆棚,起码理解设计意图,写代码,快速定位并解决问题的能力,远超一般程序员,起码超过我身边90%以上的中高级程序员。之所以限定中高级程序员,因为再往上还有架构能力我还无法测试验证,无法给出答案。

这个流程引擎适合处理支付业务,驱动完成类似下面这样的配置:

whenOrderState(CommonOrderState.INIT) // 初始条件:主单状态INIT
.onEvent(CommonEvent.CREATE) // 触发事件:创建
.transitionOrderStateTo(CommonOrderState.PROCESS) // 推进到:支付中
.request(CommonOperation.PAY) // 操作:外发银行
   .when("subOrder.currentState == SubOrderState.S") // 银行返回成功推进主单成功
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
   .when("subOrder.currentState == SubOrderState.F") // 银行返回失败推进主单失败
      .transitionOrderStateTo(CommonOrderState.FAIL)
   .when("subOrder.currentState == SubOrderState.U && subOrder.webForm != null") // 推进发消息
      .notifyNode();

在知名的测试Mock框架Mockito中,我们这样使用:

when(mockedList.get(0)).thenReturn("first element");

在java的流式代码中,我们这样使用:

names.stream().filter(name -> name.startsWith("A"))
.collect(Collectors.toList());

在jQuery中,我们这样使用(很多年前我也写JQuery):

$('#element').css('color', 'red').show() 
.click(function() { alert('Clicked!'); });

所有以上这些都有一个共同的特点:链式调用

链式调用有个好处:非常接近自然语言,容易理解。如果扩展一下,专门针对支付领域定制几个专用的语法,既可减少出错可能性,又能提高效率。

为什么不使用像Activiti、jBPM、liteflow这样成熟的流程引擎框架?原因在后面有详细说明。

2. 效果

核心代码99%都是ChatGPT生成,基本没有动。运行报错就把错误信息扔给GPT,都是秒定位,秒修复。

如果需要生成的源代码,请参考“结束语”部分。

支付成功测试:

支付失败测试:

自动生成的JAVA代码:

自动生成的测试用例:

其中一个测试方法示例:

3. 提示词

提供一篇提示词,可直接用于ChatGPT生成所有流程引擎代码,生成内容包括:可直接运行的核心代码,测试代码,常用配置等。生成的代码规范性和可读性都非常高。

这篇文章最有价值的就是这篇提示词。我先做了N轮对话,在生成了完整的可运行代码并验证通过后,再让ChatGPT根据我和它的交互,由它生成一份提示词给我,再做了几次调整后,发起多轮新的独立对话(没有使用以前的上下文)验证基本可行。

需要留意一点:AI每次生成的都不一样,所以仍然可能需要调整,如何调整参考后面的调优说明。

完整提示词如下,有点长,基本就是用自然语言写一篇设计。

=============提示词开始=============

提示:
角色:你是一位支付领域的java技术专家,同时精通java,架构设计知识,领域驱动设计知识,设计模式知识,代码简洁之道知识,DSL,支付业务,与外部银行渠道的交互流程。
任务:请帮助我根据以下详细要求,生成一个流程引擎项目的完整Java代码。

任务概述:

  • 目标: 实现一个可以处理链式请求和操作的流程引擎,例如在进行支付之前先刷新令牌(Token)。
  • 功能: 引擎应支持通过配置状态、事件、操作和转换来定义流程。它应通过构建处理节点的链表并按顺序执行,来处理包括链式在内的操作。
  • 原子操作:SubOrderSendHandlerSubOrderCallbackHandler 这样的操作是原子性的,不应处理链式操作。链式应在更高层次(特别是在 FlowEngineServiceImpl 中)管理。
  • 可扩展性: 设计应允许未来的扩展,例如添加新的原子操作如发送通知等。
  • 测试: 提供测试类来验证流程引擎的功能,确保所有代码正确执行。
  • 可读性: 代码可读性强,所有代码要有合适的包名,类注释,属性注释,方法注释,关键代码的注释。所有注释使用中文。
  • 完整性: 代码完整,可直接运行。

详细要求:

  1. 流程引擎采用DSL设计思路。比如java的流式处理,或者Mockitor的测试条件处理等。

  2. 流程引擎脚本涉及的关键字:

    • whenOrderState:初始条件,主订单状态要满足给定条件。比如:whenOrderState(CommonOrderState.INIT)。
    • onEvent:触发事件。比如:onEvent(CommonEvent.CREATE)。
    • transitionOrderStateTo:推进主订单状态。比如:transitionOrderStateTo(CommonOrderState.PROCESS)。
    • request:请求操作。比如:request(CommonOperation.PAY)。
    • when:判断request返回的数据,比如:when("subOrder.currentState == SubOrderState.S")。
    • notify:发出订单消息给其它域。比如:notify()。
    • then:进入子流程。比如:when("subOrder.currentState == SubOrderState.S")
      .then(xxx)。
    • subFlow:子流程标识。比如subFlow().request(xxx)。
  3. 流程引擎脚本配置:

    • 要求所有流程脚本不要修改。
    • 发起支付:whenOrderState(CommonOrderState.INIT)
      .onEvent(CommonEvent.CREATE)
      .transitionOrderStateTo(CommonOrderState.PROCESS)
      .request(CommonOperation.PAY)
      .when("subOrder.currentState == SubOrderState.S")
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
      .when("subOrder.currentState == SubOrderState.F")
      .transitionOrderStateTo(CommonOrderState.FAIL)
      .when("subOrder.currentState == SubOrderState.U && subOrder.webForm != null")
      .notify();
    • 发起刷新TOKEN后支付:super.whenOrderState(CommonOrderState.INIT)
      .onEvent(CommonEvent.CREATE)
      .transitionOrderStateTo(CommonOrderState.PROCESS)
      .request(CommonOperation.REFRESH_TOKEN)
      .when("subOrder.currentState == SubOrderState.F")
      .transitionOrderStateTo(CommonOrderState.FAIL)
      .when("subOrder.currentState == SubOrderState.S")
      .then(
      subFlow()
      .request(CommonOperation.PAY)
      .when("subOrder.currentState == SubOrderState.S")
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
      .when("subOrder.currentState == SubOrderState.F")
      .transitionOrderStateTo(CommonOrderState.FAIL)
      );
    • 发起支付查询:super.whenOrderState(CommonOrderState.PROCESS)
      .onEvent(CommonEvent.QUERY)
      .request(CommonOperation.QUERY)
      .when("subOrder.currentState == SubOrderState.S")
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
      .when("subOrder.currentState == SubOrderState.F")
      .transitionOrderStateTo(CommonOrderState.FAIL)
      .when("subOrder.currentState == SubOrderState.U && subOrder.webForm != null")
      .notify();
    • 渠道发起支付回调通知:super.whenOrderState(CommonOrderState.PROCESS)
      .onEvent(CommonEvent.CALLBACK)
      .request(CommonOperation.PARSE)
      .when("subOrder.currentState == SubOrderState.S")
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
      .when("subOrder.currentState == SubOrderState.F")
      .transitionOrderStateTo(CommonOrderState.FAIL)
      .when("subOrder.currentState == SubOrderState.U && subOrder.webForm != null")
      .notify();
    • 要注意脚本的层级。比如上面的 .request(CommonOperation.PAY)后面的.when("subOrder.currentState == SubOrderState.S").transitionOrderStateTo(CommonOrderState.SUCCESS)和.when("subOrder.currentState == SubOrderState.F").transitionOrderStateTo(CommonOrderState.FAIL),两者是平级的,不能生成F为S的子结点。
  1. groovy脚本支持:
    • 需要支持配置中的groovy脚本。比如.when("subOrder.currentState == SubOrderState.F")中"subOrder.currentState == SubOrderState.F"就是groovy脚本,当脚本结果为真,就执行后面的操作。
    • 需要在groovy脚本引擎上下文中导入必要的类,包括但不限于 SubOrderState等类,确保脚本不会出错。
  1. 包、类、导入和代码规范:
    • 将代码组织到适当的包中,包结构:
      • 包名为 com.yinmo.flowengine,包含以下子包:
        • config:用于存放流程配置类。
        • context:用于存放流程上下文类。
        • enums:用于存放枚举类型。
        • handler:用于存放处理器类。
        • model:用于存放模型类。
        • service:用于存放服务类。有子包impl存放实现类。
        • test:用于存放测试类。
    • 在每个类中包含所有必要的import语句。
    • 使用标准注解,如 @Service@Component@Slf4j,以及适当的自定义注解。如果是PO类,加上@Builder@Data,@ToString,@AllArgsConstructor,@NoArgsConstructor等。使用lombok取代所有通用的get/set方法。
    • 注释要求:所有文件要求有类注释,方法注释,属性注释,关键逻辑的注释。类注释要求有作者:隐墨星辰
    • 代码风格参考Springboot框架。
    • 所有类的属性名和方法不能一样。比如不能定义一个属性isCallback,有一个方法也是isCallback();
    • 除了测试类外,其它正式代码不能有魔法值。
  1. 模型:
    • Order 模型,属性包括:String orderId, FlowState previousState, FlowState currentState, Money transactionAmount, List subOrders, String webForm等,增加一个方法transitionToState(FlowState)。
    • SubOrder 模型,属性包含String subOrderId, String parentOrderId,SubOrderState previousState, SubOrderState currentState, Money transactionAmount, String channelResponseCode, String channelResponseMessage, String standardResponseCode, String standardResponseMessage, String webForm, String sendToChannelContext, String receiveFromChannelContext等,增加一个方法transitionToState(SubOrderState)。
    • Money 模型。属性:金额、币种。方法:加、减、乘、除等操作。
  1. 枚举:
    • 所有枚举全部继承于interface,方便扩展。所有枚举要有description字段,description的内容使用中文描述。
    • 定义interface:FlowState。
    • 定义interface:FlowEvent。
    • 定义interface:FlowOperation。包含方法 String name()String getMethod()boolean isCallback()。isCallback()用于表示是否是一个外部渠道回调操作,回调是直接解析,不需要发给渠道。getMethod()获取方法名,会发给网关,网关根据这个参数组装发给外部渠道的参数。
    • 定义:CommonOrderState继承FlowState,有INIT, PROCESS, SUCCESS, FAIL。
    • 定义:SubOrderState继承FlowState,有I(Init), U(Unkown), S(Success), F(Fail)。
    • 定义:CommonOperation继承FlowOperation,有PAY("pay", false), PAY_QUERY("payQuery", false), REFUND("refund", false), REFRESH_TOKEN("refreshToken", false), PARSE("parse", true)等,需要补充完整其它操作。PAY("pay", false)为例:说明是一个支付操作,“pay"会发给网关,网关根据这个参数组装发给外部渠道的参数,“false"表示是否是一个回调。
    • 定义:CommonEvent继承FlowEvent,有CREATE(订单创建事件,驱动系统向渠道发起支付退款等操作), QUERY(订单查询事件,驱动系统向渠道发起查询操作), CALLBACK(外部渠道主动回调通知事件,驱动系统直接解析网关接收到的报文)。
    • 定义节点处理器类型枚举:HandlerNodeType,不需要继承接口。有:TRANSITION(推进主订单状态变更),REQUEST(向外部渠道发送请求,或解析外部渠道回调报文),NOTIFY(发送消息通知其他域)
  1. FlowContext 和GatewayCallbackContext:
    • FlowContext 类,包含channelNameordereventconfigNamegatewayCallbackContextsubOrder 等属性。
    • GatewayCallbackContext 类应包含 subOrderIdstatecallbackMessagechannelResponseCodechannelResponseMessagestandardResponseCodestandardResponseMessage 等属性。
    • 确保上下文在处理器和服务之间适当传递。
  1. AbstractConfig 和 配置实现类:
    • 定义接口:FlowConfig
    • 实现一个 AbstractConfig 类,实现FlowConfig,允许使用构建器模式来定义流程。
    • AbstractConfig 维护一个 Map<FlowState, Map<FlowEvent, HandlerNode>> 来存储配置。AbstractConfig定义abstract initConfig()让子类实现,在@PostConstruct init()调用initConfig进行初始化,子类在initConfig()配置脚本。
    • 提供方法来定义状态、事件、转换、请求和通知。
    • 所有流程配置类继续自AbstractConfig。所有流程配置类提供String name()方法,返回当前的类简要名称用于标识流程名称。示例:getClass().getSimpleName()。
    • 定义子类: CommonPayConfigRefreshTokenPayConfigCommonPayQueryConfigCommonPayCallbackConfig ,全部继承自 AbstractConfig。配置内容见前面的“流程引擎脚本配置”描述,不要修改。
  1. 处理节点:
    • 创建一个 HandlerNodeType 枚举,值包括 TRANSITION(推进主订单状态变更)、REQUEST (向外部渠道发送请求,或解释外部渠道主动回调通知的报文)和 NOTIFY(发送消息通知其它域)。
    • 定义一个 HandlerNode 类,表示流程中的一个节点,包含节点类型、下一个状态、操作、回调处理器和子节点列表等字段。
    • 确保 HandlerNode 能通过其子节点来完成链式操作。
  1. 处理器和服务:
    • 创建一个 SubOrderHandler 类,根据操作委托给 SubOrderSendHandlerSubOrderCallbackHandler
    • SubOrderSendHandlerSubOrderCallbackHandler 只处理原子操作,不处理链式操作。
    • SubOrderHandler:负责子订单业务。
    • SubOrderSendHandler: 创建子订单,保存到数据库,组装网关报文,发送给网关,由网关发送给外部渠道,解析渠道返回的报文,更新子订单,保存子订单最新数据到数据库。把子订单放到FlowContext中。
    • SubOrderCallbackHandler:创建子订单,解析渠道主动回调通知返回的报文,更新子订单,保存子订单最新数据到数据库。把子订单放到FlowContext中。
    • 定义HandlerNode用于节点处理。
    • 定义FlowEngineService 和“FlowEngineServiceImpl”,有方法execute(FlowContext)。
    • FlowEngineServiceImpl 类通过执行从配置生成的处理节点列表来处理流程。
    • FlowEngineServiceImplexecute() 方法应迭代处理节点,并根据需要处理链式操作,通过递归处理子节点来实现。
    • FlowEngineServiceImpl不需要带参构造函数。增加属性@Autowired List configs, Map<String, Config> configMap; 提供一个@PostConstruct init()方法用于初始化,在init()方法中,把自动装配的List configs转存到configMap,在execute方法中直接使用configMap.get(context.getConfigName())获取配置。初始化完成后,使用fastjson格式化打印configMap的内容。
  1. 日志:
    • 在需要日志的类中使用 @Slf4j
    • 以下情况需要打印日志:关键处理,状态变更,只写了空方法还没有具体实现的方法。
  1. 测试:
    • 编写测试类: CommonPayTestCommonPayQueyTestCommonPayCallbackConfig ,RefreshTokenPayTest。
    • 所有测试类使用Mockitor进行mock子订单的返回值。数据校验使用断言。
    • 所有测试类需要有三个方法:testSubOrderSuccess(), testSubOrderSuccessFail(),testSubOrderUnkown()。分别是子订单返回成功,返回失败,返回未知。
    • 使用注解的形式使用Mock。
  1. 代码质量:
    • 确保所有类都有完整的包名和导入语句。
    • 方法和属性应有清晰简洁的注释,解释其用途。
    • 代码应组织良好、可读,并遵循Java最佳实践。
    • 整个代码库应编译并运行无误。

注意: 请提供所有类的完整代码,包括包声明、导入语句、类定义、方法,以及任何必要的辅助类或接口。确保代码是自包含的,不依赖于标准Java库和常用框架(如Spring)之外的外部依赖,如果有,需要给出maven配置,比如groovy,junit等。

示例结构:

  • com.yinmo.flowengine.enums
  • com.yinmo.flowengine.handler
  • com.yinmo.flowengine.config
  • com.yinmo.flowengine.context
  • com.yinmo.flowengine.model
  • com.yinmo.flowengine.service
  • com.yinmo.flowengine.test

指示:

  • 参考使用上述结构和要求,生成流程引擎项目的完整Java代码。
  • 确保所有类都完整实现了必要的逻辑,import正确,并正确交互。
  • 按照指定添加注释、注解和日志。
  • 提供测试类,包含适当的测试方法以验证流程。

=============提示词结束=============

4. 调优

因为AI每次生成的东西都或多或少有一些不一样,所以需要调优,直到满足自己的设计意图为止

好在调优不需要自己写代码,直接把想做的,想优化的,或者出错信息给ChatGPT,基本都是秒定位,秒解决。

还会给出思考过程,比如:

5. 为何重复造流程引擎的轮子

一是我乐意,可以学点新东西。二是这个轮子核心代码只有几百行,极其轻量级。三是在支付系统中,流程编排随处可见,比如调用外部的渠道进行支付。而市场上的流程引擎因为考虑通用性,配置都较为复杂。像Activiti、jBPM、liteflow都试用过。

Activiti和jBPM:配置文件非常繁琐。

liteflow:配置文件很简单,但是配置上只知道节点的流转,核心业务逻辑在代码里面,比如什么条件下推进到成功。

下面是一个支付流程编排图:

想要这样的配置

whenOrderState(CommonOrderState.INIT) // 初始条件:主单状态INIT
.onEvent(CommonEvent.CREATE) // 触发事件:创建
.transitionOrderStateTo(CommonOrderState.PROCESS) // 推进到:支付中
.request(CommonOperation.PAY) // 操作:外发银行
   .when("subOrder.currentState == SubOrderState.S") // 银行返回成功推进主单成功
      .transitionOrderStateTo(CommonOrderState.SUCCESS)
   .when("subOrder.currentState == SubOrderState.F") // 银行返回失败推进主单失败
      .transitionOrderStateTo(CommonOrderState.FAIL)
   .when("subOrder.currentState == SubOrderState.U && subOrder.webForm != null") // 推进发消息
      .notifyNode();

通过这个配置,知道初始状态,触发条件,如何推进。

Activiti和jBPM类似,配置差不多长这样:

liteflow大概长这样:

配置简单,但是需要配合代码才知道怎么做了推进。无法在配置上看清楚,如何怎么推进到成功。

6. 结束语

如果要我说说感想,我只能说,AI的发展是“日新月异”。

我不知道AI后面会不会让我丢了工作,起码目前为止,我们需要好好利用AI来提高工作效率,多一些工作产出,倒是更现实一点。

最后想重复提一句,ChatGPT的o1-preview真是强到离谱,文章中涉及的代码99%都是AI生成的,我基本只描述需求,ChatGPT生成代码,我拿到代码去跑一下,跑不过就把错误信息抛给GPT,如果跑得过但是觉得设计不好,也再提需求。基本都是秒解决,还给出详细分析。

如果对GPT生成的源代码有兴趣,请留言或私信我。

这是《图解支付系统设计与实现》专栏系列文章中的番外第(2)篇。 欢迎关注公众号“ 隐墨星辰 ”,和我一起深入解码支付系统设计与实现的方方面面。