转转技术

回收系统架构演进实战:与Cursor结对扫清系统混沌

"最好的代码不是一次写对的,而是不断重构出来的。" —— Martin Fowler

前言:技术债务带来的效能瓶颈

我所在的团队负责一个多渠道回收业务系统,接入了十几个外部渠道。每个渠道都有自己独特的协议规范、业务流程和特殊要求。随着业务发展,系统逐渐的遇到了一系列问题:

  • 代码膨胀:单个策略类代码超过800行,包含大量if-else判断,可读性极差
  • 重复代码:不同渠道间有70%的代码逻辑相似,有很多都是逻辑类似,但参数名称、结构不一致,难以复用
  • 难以测试:业务逻辑与协议处理耦合,单元测试覆盖率不到30%,主要依赖人工测试
  • 维护困难:改一个渠道的逻辑,需要理解整个类的800行代码,调研成本极高

技术债务不仅影响代码质量,更直接冲击了团队的研发效能。我们在日常开发中遭遇了三个核心瓶颈:

① 新人上手困难,培养周期长

新同学加入团队后,需要2-3个月才能理解现有代码逻辑:

  1. 单个策略类800+行,找一个业务逻辑点要翻半天
  2. 协议处理、业务逻辑、数据操作混在一起,理不清头绪
  3. 没有清晰的架构文档,只能靠"师傅带徒弟"口口相传

② 需求承接效率低,开发成本高

每次接入新渠道,都是一次"复制粘贴+魔改"的过程:

  1. 复制一个相似渠道的策略类
  2. 对照需求文档,逐个方法修改参数、逻辑
  3. 开发写入个性化逻辑

③ 测试回归成本高,质量保障困难

代码耦合导致"牵一发而动全身":

  1. 修改一个渠道的逻辑,可能影响其他渠道,测试范围难以评估
  2. 单元测试覆盖率低(30%),主要靠人工回归测试
  3. 每次发版前,测试同学要回归受影响渠道的核心流程

此时我的脑子里出现了一个想法:为了早点下班~ 必须重构了!

一、 重构方案设计

针对上述的三大痛点,我们很快制定了初步的重构规划:

痛点
重构目标
预期效果
新人上手慢
架构清晰化、职责单一化
培养周期从3周降到1周,调研成本降低70%
需求效率低
通用逻辑可复用、新增渠道可配置化
新渠道接入成本降低70%
测试成本高
解耦分层、单元测试覆盖率≥80%
回归测试时间减半,Code Review时间降低80%

三大技术策略:

  1. 分层解耦:将协议层、业务层、数据层清晰分离,让新人快速定位代码
  2. 责任链模式:每个Handler职责单一,可独立开发、测试、复用
  3. 适配器模式:新渠道只需实现Adapter,组装Handler即可

目标有了,问题也来了:这是一个对接10余个活跃渠道的业务系统,系统复杂,且每日有数百万请求打进系统,如何在不影响线上业务的前提下进行重构?

渐进式落地,而非一步到位

出于对生产环境的敬畏之心,我们不敢"一刀切"地推倒重来。经过评估,我们选择了渐进式重构策略: 整体改进是一个持续的过程,目标清晰但落地需要时间。我们会逐步验证架构可行性,再慢慢进行迁移,确保每一步都是稳定可控的。

阶段规划:

阶段
时间
目标
第一阶段
1-2周
架构验证:完成1个渠道的规范化改造
第二阶段
1-2个月
核心迁移:完成50%渠道的迁移
第三阶段
3-6个月
全量迁移:剩余渠道全部迁移
第四阶段
持续优化
补充协议Handler、完善监控

关键原则

  1. 新老并行:新架构与旧代码并存,通过配置开关灰度切换

  2. 小步验证:每完成一个渠道,立即上线验证,确保稳定后再推进下一个

  3. 数据对比:新老架构同时执行,对比结果一致性,发现问题及时回滚

二、 基于PDCA思想驱动的人机协作模式

PDCA(Plan-Do-Check-Act)是戴明环的核心思想,特别适合用于:

  • 复杂系统的渐进式改进:将大目标拆解为小任务
  • 风险可控的迭代验证:每个小步骤都可验证
  • 知识沉淀与持续优化:从单次需求到全局改进

在与AI协作时,PDCA的价值更加明显:

PDCA阶段
人的职责
AI的职责
协作模式
Plan
架构设计、任务拆解、确定路径
提供设计建议、分析技术方案
人主导
,AI辅助
Do
编写核心逻辑、Review代码
生成模板代码、实现细节
结对编程
Check
验证业务逻辑、集成测试
生成单元测试、代码检查
人验证
,AI执行
Act
总结经验、沉淀规则
整理文档、提取模式
共同迭代

三、 实战案例:渠道统一Handler架构重构

3.1 Plan阶段:架构设计与任务拆解

3.1.1 原有架构的痛点分析

重构前,我们的代码结构是这样的:

@Service("kaChannelBusinessStrategy")publicclassKaChannelBusinessStrategyextendsBaseChannelBusinessStrategy{public EnquiryPriceResult getEnquiryPrice(...){// 1. 参数校验(50行)// 2. 解密验签(30行)// 3. 获取渠道配置(40行)// 4. 调用报价服务(60行)// 5. 计算佣金(50行)// 6. 组装响应(30行)// 7. 加密签名(20行)// ... 总计500+行    }public CreateOrderResult createRecycleOrder(...){// 同样的模式,又是几百行...    }}

问题总结:

  1. 职责不清:一个类既处理协议,又处理业务,还管数据
  2. 代码重复:AChannelBusinessStrategy、BChannelBusinessStrategy里有大量相似代码
  3. 难以扩展:新增一个渠道,需要复制粘贴800行代码
  4. 测试困难:依赖过多,mock成本高

3.1.2 新架构设计思路

本次我们的设计,是一个基于责任链模式的新架构:

Image

核心设计点:

  1. 分层解耦

  • Facade层:统一入口,参数校验、异常处理
  • Adapter层:渠道适配,协议转换
  • Handler层:责任链模式,每个Handler职责单一
  • 可复用的Handler

    DuplicationCheckHandler     → 幂等性检查(通用)ChannelConfigHandler        → 渠道配置获取(通用)ReportMappingHandler        → 报告映射(通用)OrderParamAssemblyHandler   → 参数组装(可定制)OrderCreationHandler        → 订单创建(通用)
  • 灵活的编排机制

    // A渠道:标准流程chain.addHandler(duplicationCheck)     .addHandler(channelConfig)     .addHandler(reportMapping)     .addHandler(orderParamAssembly)     .addHandler(orderCreation);// KA渠道:额外增加权限校验chain.addHandler(duplicationCheck)     .addHandler(kaAuthCheck)  // KA特有     .addHandler(channelConfig)     .addHandler(reportMapping)     .addHandler(orderParamAssembly)     .addHandler(orderCreation);
  • 3.1.3 任务拆解

    有了架构设计后,我将重构任务拆解为8个小任务:

    任务ID
    任务描述
    依赖关系
    T1
    定义责任链上下文数据结构
    -
    T2
    定义IChannelAdapter接口
    T1
    T3
    定义BusinessHandler接口
    T1
    T4
    实现5个通用Handler
    T3
    T5
    实现统一对外接口
    T2
    T6
    实现ChannelAdapter
    T2, T4
    T7
    编写单元测试
    T4-T6
    T8
    集成测试与灰度发布
    T7

    关键点:每个任务都是独立可验证的,这也为后续与Cursor协作奠定了基础。

    3.2 Do阶段:与Cursor协同编码

    3.2.1 建立清晰的上下文

    在开始编码前,我做了三件事:

    1、 用自然语言写了一个详细的架构说明文档

    AI更擅长理解结构化信息,为了提升Cursor的代码生成质量,我首先为Cursor量身定制了一份"产品需求文档"(PRD)。有了这份文档,Cursor代码生成的有效率大大提升,大部分时间,我只需要微调业务细节,就可以直接使用它生成的代码。

    包含:

    • 架构全景图
    • 核心设计模式(责任链、适配器、模板方法、策略)
    • 数据流转过程(Context如何在Handler间传递)
    • 扩展点说明(如何新增渠道、Handler、Action)

    2、 定义好接口和数据结构

    /** * 渠道上下文 */@Data@BuilderpublicclassChannelContext{private String requestId;        // 请求IDprivate String channelCode;      // 渠道编码private String action;           // 业务动作private Object bizData;          // 业务数据private Map<String, Object> handlerResults;  // Handler结果// ...}

    3、 编写第一个Handler作为示例

    我手写了一个DuplicationCheckHandler,作为Cursor的参考模板:

    @ComponentpublicclassDuplicationCheckBusinessHandlerextendsAbstractTypedBusinessHandler<CreateOrderBizData, Boolean> {@Overrideprotected Boolean handleTyped(CreateOrderBizData input, ChannelContext context){// 1. 根据outOrderNo查询订单        RecycleOrderVO existOrder = orderService.queryByOutOrderNo(input.getOutOrderNo());// 2. 如果订单已存在,返回true(跳过后续Handler)if (existOrder != null) {            log.info("订单已存在,跳过创建流程, outOrderNo={}", input.getOutOrderNo());            context.putHandlerResult("ORDER_EXISTS", existOrder);returntrue;        }returnfalse;    }@OverridepublicintgetOrder(){return100;  // 最先执行    }}

    完成了上述步骤后,我开始与Cursor结对编程。

    参考DuplicationCheckBusinessHandler,帮我实现ChannelConfigBusinessHandler,用于获取渠道配置。需要满足:

    1. 从ExternalChannelConfigService获取配置
    2. 从PricedSourceService获取报价源配置
    3. 将结果保存到context的handlerResults中
    4. order设置为200

    Cursor生成的代码:

    @ComponentpublicclassChannelConfigBusinessHandlerextendsAbstractTypedBusinessHandler<CreateOrderBizData, ExternalChannelConfig> {@Resourceprivate ExternalChannelConfigService configService;@Resourceprivate PricedSourceService pricedSourceService;@Overrideprotected ExternalChannelConfig handleTyped(CreateOrderBizData input, ChannelContext context){        String channelCode = context.getChannelCode();// 1. 获取渠道配置        ExternalChannelConfig channelConfig = configService.getByChannelCode(channelCode);if (channelConfig == null) {thrownew ChannelException("CHANNEL_NOT_FOUND", "渠道配置不存在: " + channelCode);        }// 2. 获取报价源配置        PricedSourceBO pricedSource = pricedSourceService.getPricedSource(            channelConfig.getOrgId(), input.getCategoryId());// 3. 保存到上下文        context.putHandlerResult("CHANNEL_CONFIG", channelConfig);        context.putHandlerResult("PRICED_SOURCE", pricedSource);        log.info("获取渠道配置成功, channelCode={}, orgId={}",             channelCode, channelConfig.getOrgId());return channelConfig;    }@OverridepublicintgetOrder(){return200;    }}

    可以看到,代码结构完全符合预期,异常处理到位,日志也很规范。

    3.2.3 快速迭代的秘诀

    在Do阶段,我总结出了几个与Cursor高效协作的技巧:

    技巧1:更加明确的提示

    不好的提示词:

    帮我实现所有的Handler

    好的提示词:

    先实现DuplicationCheckHandler,用于检查订单是否已存在

    技巧2:提供参考代码

    参考DuplicationCheckHandler的实现方式,生成ChannelConfigHandler

    技巧3:明确约束条件

    要求:1. 继承AbstractTypedBusinessHandler2. order设置为2003. 必须包含日志4. 异常使用ChannelException

    技巧4:及时Check

    每生成一个Handler,我都会:

    • 检查是否符合接口规范
    • 运行单元测试
    • 代码Review(看命名、异常处理、日志)

    3.3 Check阶段:代码审查与验证

    3.3.1 多层次的检查机制

    在Check阶段,我建立了三层验证机制:

    第一层:IDE即时检查

    • Cursor的智能提示会提醒语法错误
    • SonarLint插件实时检查代码质量

    第二层:单元测试

    我让Cursor帮我生成了单元测试:

    为DuplicationCheckHandler生成单元测试,需要覆盖:

    1. 订单不存在的场景
    2. 订单已存在的场景
    3. 查询异常的场景

    Cursor生成的测试:

    @RunWith(SpringRunner.class)@ContextConfiguration("classpath:scf-spring-test.xml")publicclassDuplicationCheckBusinessHandlerTest{@Resourceprivate DuplicationCheckBusinessHandler handler;@Resourceprivate IRecycleOrderService orderService;@TestpublicvoidtestHandle_OrderNotExists(){// 构建测试数据        CreateOrderBizData input = CreateOrderBizData.builder()                .outOrderNo("OUT_ORDER_001")                .channelCode("TEST_CHANNEL")                .productId(123456L)                .build();        ChannelContext context = ChannelContext.builder()                .requestId("REQ_001")                .channelCode("TEST_CHANNEL")                .action("CREATE_ORDER")                .bizData(input)                .build();// 执行        Boolean result = handler.handleTyped(input, context);// 验证        System.out.println("订单不存在场景测试结果: " + result);        System.out.println("Context中的ORDER_EXISTS: " + context.getHandlerResult("ORDER_EXISTS"));    }@TestpublicvoidtestHandle_OrderExists(){// 构建测试数据 - 订单已存在的情况        CreateOrderBizData input = CreateOrderBizData.builder()                .outOrderNo("OUT_ORDER_002")  // 这个订单号在数据库中已存在                .channelCode("TEST_CHANNEL")                .productId(123456L)                .build();        ChannelContext context = ChannelContext.builder()                .requestId("REQ_002")                .channelCode("TEST_CHANNEL")                .action("CREATE_ORDER")                .bizData(input)                .build();// 执行        Boolean result = handler.handleTyped(input, context);// 验证        RecycleOrderVO existOrder = (RecycleOrderVO) context.getHandlerResult("ORDER_EXISTS");        System.out.println("订单已存在场景测试结果: " + result);        System.out.println("已存在订单信息: " + JsonUtils.toJsonWithoutNull(existOrder));    }}

    第三层:集成测试

    我在测试环境跑了完整的下单流程,验证Handler链的协作:

    @RunWith(SpringRunner.class)@ContextConfiguration("classpath:scf-spring-test.xml")publicclassChannelUnifiedFacadeTest{@Resourceprivate IChannelUnifiedFacade channelUnifiedFacade;@Resourceprivate IRecycleOrderService orderService;@TestpublicvoidtestCreateOrder_FullProcess(){// 构建完整的渠道请求        CreateOrderBizData bizData = CreateOrderBizData.builder()                .outOrderNo("TEST_OUT_ORDER_" + System.currentTimeMillis())                .channelCode("KA")                .productId(123456L)                .categoryId(1L)                .skuId(1001L)                .userId(888888L)                .picList(Collections.singletonList(                    Pic.builder().picUrl("https://test.com/pic1.jpg").build()                ))                .build();        ChannelRequest<CreateOrderBizData> request = ChannelRequest.<CreateOrderBizData>builder()                .requestId("REQ_" + System.currentTimeMillis())                .channelCode("KA")                .action("CREATE_ORDER")                .bizData(bizData)                .build();// 执行完整流程        ChannelResponse<Long> response = channelUnifiedFacade.process(request);// 打印结果        System.out.println("=== 集成测试结果 ===");        System.out.println("请求成功: " + response.isSuccess());        System.out.println("订单ID: " + response.getData());        System.out.println("响应码: " + response.getCode());        System.out.println("响应信息: " + response.getMessage());// 验证订单确实创建成功if (response.getData() != null) {            RecycleOrderVO order = orderService.getById(response.getData());            System.out.println("订单详情: " + JsonUtils.toJsonWithoutNull(order));            System.out.println("订单状态: " + order.getOrderState());            System.out.println("外部订单号: " + order.getOutOrderNo());        }    }}

    3.4 Act:规则沉淀与持续优化

    3.4.1 沉淀提示词模板

    经过多次迭代,我总结出了一套可复用的提示词模板:

    模板1:生成Handler

    角色:你是一个Java后端工程师,熟悉责任链模式任务:实现一个BusinessHandler,用于[具体功能]要求:1. 继承AbstractTypedBusinessHandler<P, R>2. 输入类型是[P],输出类型是[R]3. order值设置为[ORDER_VALUE]4. 从context中获取[DEPENDENCY]5. 将结果保存到context.putHandlerResult("[RESULT_KEY]", result)6. 异常使用ChannelException,格式:new ChannelException(errorCode, message)7. 添加日志:处理开始、处理成功、处理失败参考代码:[粘贴参考Handler代码]

    模板2:生成Adapter

    角色:你是一个Java架构师,熟悉适配器模式和责任链模式任务:实现[渠道名]ChannelAdapter要求:1. 继承AbstractIChannelAdapter2. 注入以下Handler:[列出Handler列表]3. 根据action构建不同的Handler链:   - CREATE_ORDER: [Handler顺序]   - UPDATE_ORDER: [Handler顺序]4. 添加@Component注解,value为渠道code,用于自动注入5. 添加完善的日志参考代码:[粘贴AChannelAdapter代码]

    3.4.2 持续优化

    在重构过程中,我发现了一个可以提取的通用模式:参数校验。

    最初,每个Handler都自己做参数校验:

    // DuplicationCheckHandlerif (StringUtils.isBlank(input.getOutOrderNo())) {thrownew ChannelException("PARAM_ERROR", "外部订单号不能为空");}// ChannelConfigHandlerif (StringUtils.isBlank(input.getChannelCode())) {thrownew ChannelException("PARAM_ERROR", "渠道编码不能为空");}

    在Act阶段,我提取了一个通用的ParamValidationHandler:

    @ComponentpublicclassParamValidationBusinessHandlerextendsAbstractTypedBusinessHandler<Object, Void> {@Overrideprotected Void handleTyped(Object input, ChannelContext context){//.....returnnull;    }@OverridepublicintgetOrder(){return50;     }}

    现在,只需要在Handler链中加入这个Handler,就能统一处理参数校验了。

    最后的话

    这篇文章分享的是我们正在进行的探索,而非一个 "完美的成功案例" 。

    我们的重构之旅才走了一小步,还有很多坑要踩,很多问题要解决。但我们选择边做边分享,因为:

    真实的经验比完美的故事更有价值。

    愿你在AI时代,既能拥抱新工具提升效率,又能保持对代码质量的追求、对架构美感的坚持。

    关于作者,于天奇,侠客汇Java开发工程师。 想了解更多转转公司的业务实践,欢迎点击关注下方公众号

    想了解更多转转公司的业务实践,欢迎点击关注下方公众号: