当前位置:首页>java>AI编程实践:Vibe Coding之规范驱动开发(SDD:Specification Driven Development)

AI编程实践:Vibe Coding之规范驱动开发(SDD:Specification Driven Development)

  • 2026-02-05 00:58:53
AI编程实践:Vibe Coding之规范驱动开发(SDD:Specification Driven Development)
原文:AI编码实践:从Vibe Coding到SDD
更多相关内容:
程序员必看|让AI编程100%可控!从1到N使用OpenSpec规范驱动开发完整项目实战指南!
免费开源工具-claude-code-router:让你丝滑纵享各种Claude Code模型
本文系统回顾了淘特导购团队在AI编码实践中的演进历程,从初期的代码智能补全Agent Coding再到引入Rules约束最终探索SDD(Specification Driven Development,规格驱动开发)——以自然语言规格(spec.md)为唯一真理源,驱动代码、测试、文档自动生成,实现设计先行、可测试性内建与文档永不过期。实践中发现SDD理念先进但落地门槛高、工具链不成熟、历史代码集成难,因此团队当前采用融合策略:以轻量级技术方案模板为输入 + Rules严格约束 + Agent Coding高效实现 + AI自动汇总架构文档,形成兼顾规范性、效率与可维护性的AI辅助编程最佳实践。
背景

1.1 业务背景

生成式AI技术的范式突破正驱动智能开发工具进入超线性演进阶段,主流代码生成工具的迭代周期已从季度级压缩至周级,智能体架构创新推动开发效能持续提升。
淘特导购系统承载着商品推荐、会场投放、活动营销等多样化的业务场景,技术团队面临着需求迭代频繁、代码腐化及团队协作度高的问题,如何提升开发效率、保证代码质量、降低维护成本成为我们面临的重要挑战。正是在这样的背景下,我们开始尝试将AI技术融入到日常开发流程中,探索从传统编码到AI辅助编程的转变之路。

1.2 AI编程工具的引入

2024年初,团队开始探索AI编程工具,希望通过AI提升开发效率和代码质量。最初接触的是Aone Copilot(阿里内部AI工具)的代码智能补全功能,后来逐步尝试Agentic Coding、Rules约束、SDD(Specification Driven Development)等多种AI编程模式。本文将详细记录我们的探索历程、实践经验以及对AI编程未来的思考。
代码智能补全与单方法改写

2.1 初识AI编程

场景1:代码自动补全
// 开发者输入:public List<ItemCardVO> buildItemCards(List<ContentEntity> entities) {    List<ItemCardVO> result = new ArrayList<>();    // AI自动补全以下代码    for (ContentEntity entity : entities) {        ItemCardVO itemCard = new ItemCardVO();        itemCard.setItemId(entity.getItemId());        itemCard.setItemTitle(entity.getTitle());        itemCard.setItemImg(entity.getPicUrl());        result.add(itemCard);    }    return result;}
场景2:单方法重构
// 原始代码(冗长难读)public String getDiscountText(Long finalPrice, Long nnPrice) {    if (finalPrice == null || nnPrice == null) {        return "";    }    if (finalPrice <= nnPrice) {        return "";    }    Long discount = finalPrice - nnPrice;    if (discount <= 0) {        return "";    }    String discountYuan = String.valueOf(discount / 100.0);    return discountYuan + "元";}// AI重构后(简洁优雅)public String getDiscountText(Long finalPrice, Long nnPrice) {    if (finalPrice == null || nnPrice == null || finalPrice <= nnPrice) {        return "";    }    Money discount = Money.ofFen(finalPrice).subtract(Money.ofFen(nnPrice));    if (discount.getCent() <= 0) {        return "";    }    return String.format("%s元", discount.getYuan());}

2.2 初步收益

效率提升:
  • 去年某次商详项目中,代码补全在对象构建、模型转换中减少70-80%的键盘输入。
  • 单方法重构速度提升50%。
体验优化:
  • 减少了查找API文档的时间
  • 避免了拼写错误和语法错误
  • 让开发者更专注于业务逻辑

2.3 遇到的问题

然而,这个阶段也暴露出一些问题:
  1. 局限于局部优化:只能帮助完成单个方法或代码片段,无法理解整体业务逻辑
  2. 缺乏上下文理解:不了解项目的架构规范和代码风格
  3. 无法应对复杂需求:对于跨多个类、多个模块的需求无能为力
Agentic Coding的探索与挑战
3.1 Agentic Coding的尝试
带着上面的问题,我们开始尝试Agentic Coding模式,通过编写详细的提示词(Prompt),让AI一次性实现整个功能。
AI工具:Aone Copilot,模型:Qwen3-coder。
典型的Prompt结构:
需求:实现NN页面红包模块背景:- 需要展示用户可用的红包列表- 红包按门槛从小到大排序实现要求:1. 创建数据服务类 NnRedPacketDataService,查询用户红包2. 创建模块VO NnRedPacketVO,包含红包列表、总金额等字段3. 创建模块构建器 NnRedPacketModuleBuilder,组装数据技术细节:- 数据服务需要实现 DataService<List<FundQueryDTO>> 接口- 数据服务实现类需要依赖FpProvider,并执行红包查询,参数请参考已有实现- 模块构建器需要继承 BaseModuleBuilder<NnRedPacketVO>- 使用 @Component 注解标记为Spring Bean- 遵循项目代码规范请生成完整的代码。
AI生成的代码示例:
// 1. 数据服务@Componentpublic class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {    @Autowired    private FpProvider fpProvider;    @Override    public List<FundQueryDTOexecute(InvocationContext context, JSONObject req) {        // 从配置获取红包池ID        String poolIds = req.getString("nnRedPacketPoolIds");        List<Long> fundPoolIds = Arrays.stream(poolIds.split(","))            .map(Long::parseLong)            .collect(Collectors.toList());        // 查询用户红包        return fpProvider.queryUserFundBuyPoolId(            context,             fundPoolIds,             customRuleId,             securityCode        );    }}// 2. 模块VO@Data@Builderpublic class NnRedPacketVO {    private List<RedPacketItem> redPacketList;    private String totalAmount;    private String expandText;}// 3. 模块构建器@Componentpublic class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {    @Override    public String getName() {        return "nnRedPacket";    }    @Override    protected NnRedPacketVO doBuild(InvocationContext context) {        // 获取红包数据        List<FundQueryDTO> funds = ContextUtils            .<List<FundQueryDTO>>getBizResult(                NnRedPacketDataService.class.getSimpleName(),                 context,                 500            )            .orElse(Collections.emptyList());        if (CollectionUtils.isEmpty(funds)) {            return null;        }        // 构建VO        return NnRedPacketVO.builder()            .redPacketList(buildRedPacketList(funds))            .totalAmount(calculateTotalAmount(funds))            .build();    }    private List<RedPacketItembuildRedPacketList(List<FundQueryDTO> funds) {        return funds.stream()            .map(this::toRedPacketItem)            .collect(Collectors.toList());    }    // ... 其他方法}
3.2 显著的效率提升
Agentic Coding实现了开发效率的显著优化,通过提示词驱动的自动代码生成替代了传统手动编写,虽然Prompt设计需要额外时间,但综合效率提升效果明显。
3.3 快速暴露的问题
然而,在后续的需求迭代中,我们发现了严重的问题:
问题1:代码延续性差
现象: 同样的NN业务,第二次让AI实现时,生成的代码风格完全不同
// 第一次生成(简洁风格)private String buildDiscountText(Money discount) {    return String.format("省%s元", discount.getYuan());}// 第二次生成(冗长风格)  private String buildDiscountText(Money discount) {    BigDecimal yuan = BigDecimal.valueOf(discount.getCent())        .divide(BigDecimal.valueOf(100), 2RoundingMode.HALF_UP);    String yuanStr = yuan.stripTrailingZeros().toPlainString();    return "省" + yuanStr + "元";}
影响: 同一个项目内,类似功能的实现方式五花八门,维护成本高
问题2:代码风格不一致
现象: AI不了解项目的代码规范,导致生成的代码风格和存量代码不一致。
问题3:团队协同性差
现象: 不同开发者写的Prompt差异大,生成的代码质量参差不齐
  • 新手写的Prompt过于简单,AI生成的代码质量差
  • 老手写的Prompt详细但冗长,难以复用
  • 缺乏统一的Prompt模板和最佳实践
3.4 原因分析
这些问题的根本原因在于:AI缺乏项目特定的上下文和约束
  1. 没有项目规范AI不知道项目的代码风格、架构模式、命名规范
  2. 没有领域知识AI不了解淘特导购业务的特定术语和设计模式
  3. 没有历史经验每次都是"零基础"生成代码,无法从历史代码中学习
这让我们意识到,需要给AI建立"项目规范"和"领域知识"。
Rules约束 - 建立AI的"项目规范"
4.1 引入Rules文件
我们开始尝试用Rules文件来约束AI的行为,将项目规范、架构模式、领域知识固化下来。
Rules文件体系:
.aone_copilot/├── rules/│   ├── code-style.aonerule           # 代码风格规范│   ├── project-structure.aonerule    # 项目结构规范│   └── features.aonerule              # 功能实现规范└── tech/    ├── xx秒杀-技术方案.md      # 具体需求的技术方案    └── xx红包模块-技术方案.md
4.2 Rules文件内容示例
代码风格规范(code-style.aonerule)
# 代码风格规范## Java代码规范- 类名使用大驼峰命名法(PascalCase)- 方法名和变量名使用小驼峰命名法(camelCase)- 常量使用全大写,单词间用下划线分隔(CONSTANT_CASE)## 空值判断- 集合判空统一使用:CollectionUtils.isEmpty() 或 isNotEmpty()- 字符串判空统一使用:StringUtils.isBlank() 或 isNotBlank()- 对象判空统一使用:Objects.isNull() 或 Objects.nonNull()## 日志规范- 使用 LogUtil 工具类记录日志- 错误日志格式:LogUtil.error("类名, 方法名, 错误描述, 关键参数={}", param, exception)## 注解使用- Service类使用 @Component 注解- 数据服务实现 DataService<T> 接口- 模块构建器继承 BaseModuleBuilder<T>
项目结构规范
# 项目结构规范## 包结构com.alibaba.aladdin.app/├── module/              # 模块构建器│   ├── nn/             # NN业务模块│   ├── seckill/        # 秒杀业务模块│   └── common/         # 通用模块├── domain/             # 领域对象│   ├── module/         # 模块VO(继承ModuleObject)│   └── [业务名]/       # 业务领域对象(BO、DTO)├── dataservice/impl/   # 数据服务实现└── provider/           # 外部服务提供者## 命名规范- 数据服务:[业务名]DataService(如 NnRedPacketDataService- 模块构建器:[业务名]ModuleBuilder(如 NnFeedsModuleBuilder- 模块VO:[业务名]VO(如 NnRedPacketVO)- 业务BO:[业务名]BO(如 NnRoundFeatureBO)
功能实现规范
# 功能实现规范## 数据服务层- 必须实现 DataService<T> 接口- 使用 @Component 注解- execute方法的第一个参数是 InvocationContext- execute方法的第二个参数是 JSONObject businessReq示例:```java@Componentpublic class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {    @Override    public List<FundQueryDTO> execute(InvocationContext context, JSONObject businessReq) {        // 实现逻辑    }}```## 模块构建器- 必须继承 BaseModuleBuilder- 使用 @Component 注解- 实现 getName()doBuild()bottomTransform() 三个方法- 通过 ContextUtils.getBizResult() 获取数据服务结果示例:```@Componentpublic class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {    @Override    public String getName() {        return "nnRedPacket";    }    @Override    protected NnRedPacketVO doBuild(InvocationContext context) {        List<FundQueryDTO> funds = ContextUtils            .<List<FundQueryDTO>>getBizResult(                NnRedPacketDataService.class.getSimpleName(),                context,                500            )            .orElse(Collections.emptyList());        // 构建逻辑    }}```
4.3 技术方案模板
除了Rules文件,我们还为每个需求创建技术方案文档,明确定义需要生成的代码:
技术方案示例(NN红包模块-技术方案.md):
## 业务定义NN红包模块用于展示用户在NN业务场景下可用的红包列表。## 业务领域对象无(复用 FundQueryDTO)## 模块领域对象| 对象含义 | 实现方案 | 属性及类型 ||---------|---------|-----------|| NN红包模块VO | 新增 | 1. redPacketList:List<RedPacketItem> - 红包列表<br>2. totalAmount:String - 总金额<br>3. expandText:String - 展开文案 |## 数据服务层| 数据服务定义 | 实现方案 | execute ||------------|---------|---------|| NN红包查询服务 | 新增 | 1. 从配置获取红包池ID列表<br>2. 调用FpProvider查询用户红包<br>3. 过滤可用红包(状态=2,未过期)<br>4. 返回红包列表 |## 模块构建器| 模块构建器定义 | 实现方案 | doBuild逻辑 ||--------------|---------|-------------|| NN红包模块构建器 | 新增 | 1. 获取红包数据<br>2. 过滤门槛>20元的红包<br>3. 按门槛从小到大排序<br>4. 构建VO |
4.4 显著改善的效果
引入Rules文件后,我们看到了明显的改善:
代码一致性:
  • 所有生成的代码都遵循统一的命名规范
  • 项目结构清晰,模块划分明确
  • 代码风格保持一致
开发效率:
  • 技术方案填写时间从2小时降低到20分钟
  • 代码实现时间从1天降低到2小时(需要人工收尾)
团队协作:
  • 技术方案成为团队共同语言
  • Code Review效率提升50%
  • 新人上手时间从1周降低到2天
4.5 依然存在的问题
虽然Rules带来了显著改善,但仍存在一些问题:
  1. 需求理解不够深入:AI仍然是基于技术方案"翻译"成代码,对业务语义理解有限
  2. 测试质量参差不齐:虽然能生成单测,但测试用例的通过率和覆盖度仍需人工把关
  3. 文档滞后:代码变更后,文档更新容易遗漏
  4. 依赖关系管理:对于复杂的模块依赖关系,AI处理不够优雅
这些问题让我们思考:能否找到一种方式,让AI能更加规范和延续的coding?
SDD探索 - 规格驱动开发
5.1 SDD的引入
近期,我们开始初步尝试SDD(Specification Driven Development,规格驱动开发),使用了Spec Kit工具链。
SDD的核心理念:
规格是唯一真理源(Single Source of Truth)
  • 所有的代码、测试、文档都从规格生成
  • 规格即文档,文档永不过期
设计先于实现
  • 先用自然语言描述"做什么"(规格)
  • 再让AI生成"怎么做"(代码)
可测试性内建
  • 规格中明确定义测试用例
  • 自动生成完整的单元测试
5.2 Speckit执行流程
5.2.1 环境准备
我们主要使用了两种工具:
  1. iflow + qwen3 coder plus + spec kit
  2. qwen + qwen3 coder plus + spec kit
文件体系:
├── .specify/│   ├── memory/│   │   └── constitution.md│   ├── scripts/│   └── templates/├── specs/│   └── 001-nn-redpacket-module/│       ├── checklists/│       │   └── requirements.md│       ├── contracts/│       │   └── api-contract.md│       ├── data-model.md│       ├── plan.md│       ├── quickstart.md│       ├── research.md│       └── spec.md└── req/    └── nn-redpacket.md
5.2.2 speckit.constitution—制定整个项目的原则
这一步会生成项目全局的宪章,constitution.md
以下是部分节选:
## 核心原则### I. 模块化服务架构所有服务必须遵循模块化设计原则,具有明确的关注点分离和定义良好的接口。每个模块应具有单一职责并可独立部署。模块必须以松耦合和高内聚的方式设计,以增强可维护性和可扩展性,遵循最小依赖原则。### II. 阿里巴巴开发标准所有代码必须遵循阿里巴巴Java开发指南(基于阿里巴巴Java编码规范)。这包括命名约定、异常处理实践、日志标准、安全最佳实践和性能优化模式。代码必须遵守样式一致性要求,以保持代码库的统一性。### III. 质量保证实践全面测试是强制性的:对所有业务逻辑进行单元测试,对服务交互进行集成测试,对API兼容性进行合同测试。代码覆盖率必须保持在80%以上,特别关注关键业务路径。代码质量工具必须集成到CI/CD管道中以执行标准,遵循阿里巴巴开发规范以确保质量和可靠性。### IV. 模块设计原则遵循单一职责原则,每个模块都有一个明确的目的。模块必须以松耦合和高内聚的方式设计,遵循关注点分离原则。模块边界应与业务能力和领域上下文对齐。所有模块都遵循最小依赖原则,仅导入必要的依赖项以减少系统复杂性。### V. 项目架构设计原则本项目采用分层架构设计,通过模块化组织代码,支持淘特投放业务的各种场景需求。架构层次包括:1. **接入层**:处理请求接入和协议转换2. **解决方案层**:业务解决方案的统一入口3. **子解决方案层**:细粒度的业务处理能力4. **模块构建层**:按业务功能划分的模块构建器5. **数据服务层**:负责各种业务数据的获取、处理和封装6. **外部服务层**:负责调用外部服务并进行模型转换7. **领域模型层**:定义核心业务对象和数据传输对象8. **基础设施层**:包含基础组件和框架封装9. **通用模块层**:公共组件和工具类### VI. 依赖管理遵循最小依赖原则:每个模块应只拥有其实际需要的依赖项。避免模块之间的循环依赖。使用依赖注入实现松耦合。定期审核和更新依赖项以最小化安全漏洞。这确保了可维护和高效的代码结构。### VII. 代码风格一致性在整个项目中保持一致的代码风格,使用标准化的格式化规则。所有代码在合并前必须通过静态分析检查。一致地遵循设计模式,并对与标准实践的任何偏差提供清晰的证明。这确保了统一的代码规范和样式,符合项目标准。### VIII. Speckit中文本地化所有speckit相关文件、文档和配置都应使用中文,以支持本地开发团队。`.specify/``specs/`目录中的文件和相关speckit构件必须使用中文,以便本地开发人员更好地理解和维护,同时应为可能服务国际市场面向用户的组件保留国际化支持。## 安全和合规要求所有代码必须符合阿里巴巴的安全标准,并在部署前进行强制性安全审查。必须为所有暴露的端点实现适当的身份验证和授权。敏感数据必须根据内部合规要求进行处理。必须扫描依赖项中的安全漏洞。## 开发工作流程1. 所有代码更改必须遵循标准的阿里巴巴开发工作流程:功能分支、代码审查、自动化测试和CI/CD管道验证。拉取请求必须通过所有测试并获得指定审阅者的批准后才能合并。除非明确批准进行具有迁移计划的破坏性更改,否则所有更改必须向后兼容。每次更改都必须遵循模块设计原则并保持代码风格一致性。2. 所有操作不要创建新分支,而是在当前分支下进行3. 代码生成必须遵循code-generation-prompt.aonerule文件## 治理本宪法凌驾于所有其他开发实践之上,必须在存储库中的所有工作中遵循。对本宪法的任何修改都需要正式文档、团队批准和迁移计划。所有PR和代码审查必须验证是否符合这些原则。
5.2.3 speckit.specify—编写规格说明
这一步会新建一个分支,创建spec.md、requirements.md等文件,这里反复修改了多次,主要还是后续的执行不理想的返工。
NN红包模块规格说明(spec.md):
# NN红包模块规格说明## 功能概述NN红包模块用于在NN频道页面展示用户可用的红包列表,帮助用户了解可以使用的优惠。## 功能需求### FR-1: 红包数据获取**描述:** 系统应该能够查询用户在当前NN业务场景下可用的红包**前置条件:**- 用户已登录- 配置了红包池ID(fundPoolIds)- 配置了规则id(customRuleId)- 配置了securityCode**输入:**- userId:用户ID- fundPoolIds:红包池ID列表- customRuleId:自定义规则ID- securityCode:安全码**处理逻辑:**1. 调用FpProvider.queryUserFundBuyPoolId()查询红包2. 过滤条件:   - 红包状态(payStatus)= 2(可使用)   - 红包未过期(当前时间在startTime和endTime之间)   - 红包门槛 <= 配置的amountThreshold(默认20元)**输出:**- 返回符合条件的红包列表**异常处理:**- 如果FpProvider调用失败,返回空列表- 如果用户未登录,返回空列表.........**处理逻辑:**1. 如果红包列表为空,不展示模块(返回null)2. 构建NnRedPacketVO:   - redPacketList:转换每个红包为RedPacketItem   - totalAmount:计算所有红包金额总和   - expandText:从配置获取展开文案**输出:** NnRedPacketVO## 非功能需求### NFR-1: 性能要求- 红包查询超时时间:500ms- 如果超时,返回空列表,不影响页面其他模块### NFR-2: 可扩展性- 支持配置不同的红包池ID- 支持配置不同的门槛限制## 测试用例### TC-1: 正常流程 - 有可用红包**前置条件:**- 用户ID:123456- 配置的红包池ID:[1001, 1002]- FpProvider返回2个可用红包:  - 红包1:金额5元,门槛15元  - 红包2:金额10元,门槛25元**执行步骤:**1. 调用 doBuild(context)**预期结果:**- 返回的VO不为null- redPacketList包含2个红包- totalAmount = "15.00"(5 + 10)- 红包按门槛排序(红包1在前)### TC-2: 边界条件 - 红包门槛超限**前置条件:**- FpProvider返回1个红包,门槛25元- 配置的amountThreshold = 20元(2000分)**预期结果:**- 返回null(因为门槛超限,红包被过滤)......
5.2.4 speckit.plan—制定实施计划
这一步会生成data-model.md、api.md等几个文件,最重要的是plan.md
5.2.5 speckit.tasks—任务分解
生成task.md
5.2.6 speckit.implement—实现代码
数据服务实现
@Componentpublic class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {    @Autowired    private FpProvider fpProvider;    @Override    public List<FundQueryDTOexecute(InvocationContext context, JSONObject businessReq) {        long userId = context.getUserId();        if (userId <= 0) {            return Collections.emptyList();        }        // 获取配置        String poolIdsStr = businessReq.getString("nnRedPacketPoolIds");        List<Long> fundPoolIds = parsePoolIds(poolIdsStr);        Long customRuleId = businessReq.getLong("customRuleId");        String securityCode = businessReq.getString("securityCode");        // 查询红包        List<FundQueryDTO> funds = fpProvider.queryUserFundBuyPoolId(            context, fundPoolIds, customRuleId, securityCode        );        if (CollectionUtils.isEmpty(funds)) {            return Collections.emptyList();        }        // 过滤可用红包(按规格FR-1的过滤条件)        Long amountThreshold = businessReq.getLong("amountThreshold", 2000L);        return funds.stream()            .filter(fund -> fund.getPayStatus() == 2)  // 可使用状态            .filter(fund -> isNotExpired(fund))         // 未过期            .filter(fund -> fund.getAmountThreshold() <= amountThreshold)  // 门槛限制            .collect(Collectors.toList());    }    private boolean isNotExpired(FundQueryDTO fund) {        long now = System.currentTimeMillis();        return fund.getStartTime() <= now && now <= fund.getEndTime();    }    // ... 其他方法}
模块构建器实现
@Componentpublic class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {    @Override    public String getName() {        return "nnRedPacket";    }    @Override    protected NnRedPacketVO doBuild(InvocationContext context) {        // 获取红包数据(FR-1)        List<FundQueryDTO> funds = ContextUtils            .<List<FundQueryDTO>>getBizResult(                NnRedPacketDataService.class.getSimpleName(),                context,                500            )            .orElse(Collections.emptyList());        // 如果无可用红包,不展示模块(FR-3)        if (CollectionUtils.isEmpty(funds)) {            return null;        }        // 红包排序(FR-2)        List<FundQueryDTO> sortedFunds = sortFunds(funds, context);        // 构建VO(FR-3)        return NnRedPacketVO.builder()            .redPacketList(buildRedPacketList(sortedFunds))            .totalAmount(calculateTotalAmount(sortedFunds))            .expandText(getExpandText(context))            .build();    }    /**     * 红包排序:NN专属红包优先,其次按门槛从小到大     * 对应规格 FR-2     */    private List<FundQueryDTOsortFunds(List<FundQueryDTO> funds, InvocationContext context) {        JSONObject extraParams = getExtraParams(context);        String nnPoolIdsStr = extraParams.getString("nnRedPacketPoolIds");        Set<Long> nnPoolIds = parsePoolIds(nnPoolIdsStr).stream()            .collect(Collectors.toSet());        return funds.stream()            .sorted(Comparator                .comparing((FundQueryDTO fund) ->                     nnPoolIds.contains(fund.getFromFundId()) ? 0 : 1)                .thenComparing(FundQueryDTO::getAmountThreshold))            .collect(Collectors.toList());    }    // ... 其他方法}
完整的单元测试
@RunWith(MockitoJUnitRunner.class)public class NnRedPacketModuleBuilderTest {    @Mock    private InvocationContext context;    @Mock    private NnRedPacketDataService dataService;    @InjectMocks    private NnRedPacketModuleBuilder builder;    /**     * 测试用例 TC-1: 正常流程 - 有可用红包     */    @Test    public void testBuild_WithAvailableFunds_ShouldReturnVO() {        // 准备数据        List<FundQueryDTO> mockFunds = Arrays.asList(            createFund(1L500L1500L),   // 5元,门槛15元            createFund(2L1000L2500L)   // 10元,门槛25元        );        // Mock        when(dataService.execute(any(), any())).thenReturn(mockFunds);        mockContext(context, "nnRedPacketPoolIds""1,2");        mockBizResult(context, NnRedPacketDataService.class.getSimpleName(), mockFunds);        // 执行        NnRedPacketVO result = builder.doBuild(context);        // 验证(按规格预期结果)        assertNotNull(result);        assertEquals(2, result.getRedPacketList().size());        assertEquals("15.00", result.getTotalAmount());        // 验证排序:门槛低的在前        assertEquals(1500L, result.getRedPacketList().get(0).getAmountThreshold());    }    /**     * 测试用例 TC-2: 边界条件 - 红包门槛超限     */    @Test    public void testBuild_ThresholdExceeded_ShouldReturnNull() {        // 准备数据:门槛25元,超过配置的20元        List<FundQueryDTO> mockFunds = Arrays.asList(            createFund(1L500L2500L)        );        // Mock(配置amountThreshold=2000,即20元)        JSONObject businessReq = new JSONObject();        businessReq.put("amountThreshold"2000L);        when(dataService.execute(any(), eq(businessReq))).thenReturn(Collections.emptyList());        mockBizResult(context, NnRedPacketDataService.class.getSimpleName(), Collections.emptyList());        // 执行        NnRedPacketVO result = builder.doBuild(context);        // 验证(按规格预期结果)        assertNull(result);    }    /**     * 测试用例 TC-3: 异常场景 - FpProvider返回null     */    @Test    public void testBuild_ProviderReturnsNull_ShouldReturnNull() {        // Mock        when(dataService.execute(any(), any())).thenReturn(null);        mockBizResult(context, NnRedPacketDataService.class.getSimpleName(), null);        // 执行        NnRedPacketVO result = builder.doBuild(context);        // 验证(按规格预期结果)        assertNull(result);    }    /**     * 测试用例 TC-4: 异常场景 - 用户未登录     */    @Test    public void testBuild_UserNotLogin_ShouldReturnNull() {        // Mock:userId = 0        when(context.getUserId()).thenReturn(0L);        when(dataService.execute(any(), any())).thenReturn(Collections.emptyList());        mockBizResult(context, NnRedPacketDataService.class.getSimpleName(), Collections.emptyList());        // 执行        NnRedPacketVO result = builder.doBuild(context);        // 验证(按规格预期结果)        assertNull(result);    }    // 辅助方法    private FundQueryDTO createFund(Long id, Long amount, Long threshold) {        FundQueryDTO fund = new FundQueryDTO();        fund.setFundId(id);        fund.setAvailableAmount(amount);        fund.setAmountThreshold(threshold);        fund.setPayStatus(2);  // 可使用        fund.setStartTime(System.currentTimeMillis() - 3600000);  // 1小时前开始        fund.setEndTime(System.currentTimeMillis() + 3600000);    // 1小时后结束        return fund;    }}
5.3 SDD带来的改进
5.3.1 一致性显著提升
代码层面:
  • 所有代码都严格遵循规格说明,消除了理解偏差
  • 不同开发者实现相同规格,代码风格完全一致
  • 代码变更时,必须先更新规格,保证文档与代码同步
业务层面:
  • 产品、开发、测试对需求的理解高度一致
  • 减少了需求理解偏差导致的返工
5.3.2 可测试性大幅提升
测试覆盖:
  • 自动生成的测试用例覆盖了所有正常和异常流程
  • 测试用例与规格说明一一对应,确保完整性
  • 边界条件和异常场景都有明确的测试用例
测试质量:
  • Mock方式规范统一,符合项目最佳实践
  • 断言准确全面,不会遗漏关键验证点
  • 测试代码可读性好,易于维护
5.3.3 可维护性显著改善
文档永不过期:
  • 规格说明就是最准确的文档
  • 任何变更都先更新规格,再同步代码
  • 新人通过阅读规格说明就能快速理解功能
变更影响分析:
  • 修改规格时,清晰知道影响哪些代码模块
  • 依赖关系在规格中明确定义
  • 重构时可以基于规格验证正确性
代码可读性:
  • 代码结构清晰,层次分明
  • 注释完整准确,与规格保持一致
  • 命名规范统一,易于理解
5.3.4 团队协作效率提升
  • 新人通过阅读规格说明快速上手
  • 跨团队协作时,规格成为统一语言
  • 历史需求回溯更容易,规格即完整记录
5.4 SDD的问题与挑战
虽然SDD带来了价值,但在实践中也遇到了一些明显的问题:
问题1:规格编写门槛高
现象: 编写高质量的规格说明需要较强的抽象能力和文档编写能力
  • 新手往往写不好规格,过于技术化或过于模糊
  • 规格模板虽然有,但如何填写仍需要经验
  • 不合格的规格对后面的代码实现影响
影响: 对于简单需求,写规格的时间甚至超过直接写代码
问题2:Spec Kit工具链不成熟
遇到的具体问题:
  1. 规格解析不准确
    AI有时无法正确理解规格中的复杂逻辑
    需要用非常精确的语言描述,稍有歧义就可能理解错误
  2. 代码生成质量不稳定
    相同的规格,不同时间生成的代码质量差异大
    有时生成的代码过于冗长,有时又过于简化
  3. 增量更新困难
    规格修改后,很难做到只更新变化的部分
    往往需要重新生成整个文件,导致手工修改的部分丢失
问题3:与现有代码库集成困难
现象: 我们的代码库已经有大量历史代码,SDD更适合从零开始的新项目
  • 历史代码缺乏规格说明,无法纳入SDD体系
  • 新老代码风格混杂,维护成本反而增加
  • 团队一部分人用SDD,一部分人用传统方式,协作困难
问题4:学习成本高
数据:
  • 写出合格的第一份规格说明,平均需要3-5次迭代
  • 老员工接受度较低,认为"还不如直接写代码快"
5.5 SDD适用场景分析
经过3个月的实践,我们总结出SDD的适用场景:
适合使用SDD:
  • ✅ 全新的项目或模块
  • ✅ 核心业务逻辑,需要长期维护
  • ✅ 复杂度高,需要详细设计的功能
  • ✅ 多人协作的大型需求
  • ✅ 对质量要求极高的场景
不适合使用SDD:
  • ❌ 简单的工具函数或配置修改
  • ❌ 快速验证的实验性功能
  • ❌ 一次性的临时需求
  • ❌ 对现有代码的小修改
当前最佳实践 - 
Rules + Agentic Coding + AI文档汇总
6.1 融合各阶段优势
核心思路:
  1. 用Rules约束AI
  2. 用技术方案指导实现
  3. 用Agentic Coding快速迭代
  4. 用AI汇总文档保持同步
6.2 技术方案模板优化
我们优化了技术方案模板,更加轻量级:
# [需求名称]-技术方案## 业务定义[简要描述业务背景和目标,1-2句话]## 业务领域对象[如果需要新增/修改BO或DTO,在此说明]## 模块领域对象[需要新增/修改的VO对象]| 对象含义 | 实现方案 | 属性及类型 ||---------|---------|-----------|| [对象名] | 新增/修改 | 1. 字段1:类型 - 说明<br>2. 字段2:类型 - 说明 |## 数据服务层[需要新增/修改的数据服务]| 数据服务定义 | 实现方案 | execute逻辑 ||------------|---------|-----------|| [服务名] | 新增/复用 | 1. 步骤1<br>2. 步骤2 |## 模块构建器[需要新增/修改的模块构建器]| 模块构建器定义 | 实现方案 | doBuild逻辑 ||--------------|---------|-------------|| [构建器名] | 新增/修改 | 1. 获取数据<br>2. 处理逻辑<br>3. 构建VO |
特点:
  • 比SDD规格更轻量,编写时间从2小时降低到30分钟
  • 比纯Agentic Coding更规范,有明确的结构约束
  • 聚焦于"做什么",而非"怎么做"(实现细节交给AI)
6.3 AI文档汇总机制
即:让AI自动维护"整体架构与业务逻辑文档"
6.3.1 文档汇总流程
完成需求开发 → 提交AI"将本次代码逻辑汇总到汇总文档" → AI分析代码 → AI更新文档
Prompt示例:
我刚完成了NN红包模块的开发,请分析以下代码:- NnRedPacketDataService.java- NnRedPacketModuleBuilder.java- NnRedPacketVO.java然后将其业务逻辑汇总到"NN业务整体架构与逻辑文档.md"中,确保:1. 描述模块的核心功能和业务价值2. 说明数据流转过程3. 列出关键的业务规则和判断逻辑4. 保持与现有文档的风格一致
6.3.2 架构文档结构
# NN业务整体架构与逻辑文档## 一、业务概述[业务背景、目标、核心价值]## 二、整体架构### 2.1 技术架构[分层架构图、技术栈]### 2.2 模块组成[各个模块的功能和关系]## 三、核心模块详解### 3.1 NN Feeds模块#### 3.1.1 功能说明[模块的核心功能]#### 3.1.2 数据流转[数据从哪里来,经过哪些处理,最终输出什么]#### 3.1.3 关键逻辑[重要的业务规则、计算逻辑、判断条件]#### 3.1.4 代码位置[对应的类和方法]### 3.2 NN红包模块[类似结构]### 3.3 NN吊钩层模块[类似结构]## 四、数据服务层[各个数据服务的功能和依赖]## 五、关键流程[重要的业务流程时序图]## 六、配置说明[配置项说明]## 七、扩展点[未来可能的扩展方向]
6.3.3 AI汇总效果示例
AI生成的文档片段:
### 3.2 NN红包模块#### 3.2.1 功能说明NN红包模块用于在NN频道页面展示用户可用的红包列表,帮助用户了解可以使用的优惠,提升转化率。#### 3.2.2 数据流转用户请求 → NnRedPacketDataService          → 调用FpProvider查询用户红包         → 过滤可用红包(状态=2,未过期,门槛<=20元)         → 返回红包列表         ↓NnRedPacketModuleBuilder         → 获取红包数据         → 红包排序(NN专属优先,门槛从小到大)         → 构建NnRedPacketVO         → 返回前端
#### 3.2.3 关键逻辑**1. 红包过滤规则**```java// 只展示满足以下条件的红包:// 1. 状态为可使用(payStatus = 2)// 2. 未过期(当前时间在startTime和endTime之间)// 3. 门槛 <= 配置的amountThreshold(默认20元)if(fund.getPayStatus() != 2return false;if(fund.getStartTime() > now || now > fund.getEndTime()) return false;if(fund.getAmountThreshold() > amountThreshold) return false;**2. 红包排序规则**```java// 排序规则:// 1. NN专属红包(配置的nnRedPacketPoolIds)优先// 2. 其次按门槛从小到大排序funds.stream()    .sorted(Comparator        .comparing(fund -> nnPoolIds.contains(fund.getFromFundId()) ? 0 1)        .thenComparing(FundQueryDTO::getAmountThreshold))**3. 总金额计算**```java// 计算所有可用红包的金额总和String totalAmount = funds.stream()    .map(FundQueryDTO::getAvailableAmount)    .reduce(0L, Long::sum)    .map(PriceUtils::getPriceFenToYuan)    .orElse("0.00");#### 3.2.4 代码位置- 数据服务:`NnRedPacketDataService`  - 位置:`dataservice/impl/nn/NnRedPacketDataService.java`  - 核心方法:`execute(InvocationContext, JSONObject)`- 模块构建器:`NnRedPacketModuleBuilder`  - 位置:`module/nn/NnRedPacketModuleBuilder.java`  - 核心方法:`doBuild(InvocationContext)`、`sortFunds(List<FundQueryDTO>)`- 模块VO:`NnRedPacketVO`  - 位置:`domain/module/nn/NnRedPacketVO.java`#### 3.2.5 配置项```json{  "nnRedPacketPoolIds""1001,1002,1003",  // NN专属红包池ID  "amountThreshold"2000,                  // 红包门槛上限(分)  "expandText""展开查看更多"              // 展开文案}
思考总结
在淘特导购业务的AIcoding实践中,我们经历了从简单代码补全到Agentic Coding,再到基于规则和SDD的编程模式的演进过程。每个阶段都有其价值和局限性:
  1. 初期探索让我们认识到AI在编码辅助方面的潜力,但也暴露了缺乏规范指导的问题;
  2. Agentic Coding提升了功能实现的完整性,但可延续性和一致性仍有不足;
  3. 基于规则的模式有效解决了代码规范和架构一致性问题,成为当前的主要实践方式;
  4. SDD尝试虽然在理念上很有价值,但在实际应用中还需要进一步完善。
虽然在SDD编程方面遇到了一些挑战,但我们认为AI规范化编程是未来发展的方向。团队中的同学正在持续探索和优化:
  1. 完善工具链:改进Spec Kit等工具,提升自动化能力
  2. 优化流程整合:更好地将SDD模式与现有开发流程结合
  3. 降低学习成本:通过培训和实践案例帮助团队成员适应新模式
  4. 持续改进规则:根据实践经验不断完善规则定义
我们相信,通过持续的探索和实践,一定能找到更适合团队的AI辅助编程模式,进一步提升开发效率和代码质量。
  最后
本文大部分内容由AI根据代码及配置生成。
团队介绍

本文作者式遂,来自淘天集团-淘特用户技术团队。团队主要负责淘宝行业&淘特C端链路的研发工作,包含:搜索推荐、互动游戏、导购、交易等基础服务及创新业务。当下我们积极拥抱AI时代,探索智能化在研发提效和业务场景中的无限可能。技术不只是工具,更是为用户创造价值的力量。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 20:00:09 HTTP/2.0 GET : https://f.mffb.com.cn/a/472550.html
  2. 运行时间 : 0.152085s [ 吞吐率:6.58req/s ] 内存消耗:4,682.33kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=e7b82b3b8b87d932900eae910473af62
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000679s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000672s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.004305s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.002658s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000777s ]
  6. SELECT * FROM `set` [ RunTime:0.004194s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000782s ]
  8. SELECT * FROM `article` WHERE `id` = 472550 LIMIT 1 [ RunTime:0.005533s ]
  9. UPDATE `article` SET `lasttime` = 1770465609 WHERE `id` = 472550 [ RunTime:0.004311s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.002466s ]
  11. SELECT * FROM `article` WHERE `id` < 472550 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.003667s ]
  12. SELECT * FROM `article` WHERE `id` > 472550 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003248s ]
  13. SELECT * FROM `article` WHERE `id` < 472550 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.027365s ]
  14. SELECT * FROM `article` WHERE `id` < 472550 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.007142s ]
  15. SELECT * FROM `article` WHERE `id` < 472550 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.010015s ]
0.153761s