微信扫码
添加专属顾问
我要投稿
淘特导购团队揭秘AI编码实践:从智能补全到规格驱动开发的演进之路,探索高效编程新范式。核心内容: 1. AI编程工具演进历程:从代码补全到SDD(规格驱动开发) 2. 实践中的挑战与解决方案:融合轻量级模板+Rules约束+Agent Coding 3. 对未来AI编程的思考:如何平衡规范性、效率与可维护性
阿里妹导读
本文系统回顾了淘特导购团队在AI编码实践中的演进历程,从初期的代码智能补全到Agent Coding再到引入Rules约束,最终探索SDD(Specification Driven Development,规格驱动开发)——以自然语言规格(spec.md)为唯一真理源,驱动代码、测试、文档自动生成,实现设计先行、可测试性内建与文档永不过期。实践中发现SDD理念先进但落地门槛高、工具链不成熟、历史代码集成难,因此团队当前采用融合策略:以轻量级技术方案模板为输入 + Rules严格约束 + Agent Coding高效实现 + AI自动汇总架构文档,形成兼顾规范性、效率与可维护性的AI辅助编程最佳实践。
一、背景
1.1 业务背景
1.2 AI编程工具的引入
二、代码智能补全与单方法改写
2.1 初识AI编程
// 开发者输入: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;}
// 原始代码(冗长难读)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 初步收益
2.3 遇到的问题
三、Agentic Coding的探索与挑战
3.1 Agentic Coding的尝试
需求:实现NN页面红包模块背景:- 需要展示用户可用的红包列表- 红包按门槛从小到大排序实现要求:1. 创建数据服务类 NnRedPacketDataService,查询用户红包2. 创建模块VO NnRedPacketVO,包含红包列表、总金额等字段3. 创建模块构建器 NnRedPacketModuleBuilder,组装数据技术细节:- 数据服务需要实现 DataService<List<FundQueryDTO>> 接口- 数据服务实现类需要依赖FpProvider,并执行红包查询,参数请参考已有实现- 模块构建器需要继承 BaseModuleBuilder<NnRedPacketVO>- 使用 @Component 注解标记为Spring Bean- 遵循项目代码规范请生成完整的代码。
// 1. 数据服务public class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {private FpProvider fpProvider;public List<FundQueryDTO> execute(InvocationContext context, JSONObject req) {// 从配置获取红包池IDString 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. 模块VOpublic class NnRedPacketVO {private List<RedPacketItem> redPacketList;private String totalAmount;private String expandText;}// 3. 模块构建器public class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {public String getName() {return "nnRedPacket";}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;}// 构建VOreturn NnRedPacketVO.builder().redPacketList(buildRedPacketList(funds)).totalAmount(calculateTotalAmount(funds)).build();}private List<RedPacketItem> buildRedPacketList(List<FundQueryDTO> funds) {return funds.stream().map(this::toRedPacketItem).collect(Collectors.toList());}// ... 其他方法}
3.2 显著的效率提升
3.3 快速暴露的问题
// 第一次生成(简洁风格)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), 2, RoundingMode.HALF_UP);String yuanStr = yuan.stripTrailingZeros().toPlainString();return "省" + yuanStr + "元";}
3.4 原因分析
四、Rules约束 - 建立AI的"项目规范"
4.1 引入Rules文件
.aone_copilot/├── rules/│ ├── code-style.aonerule # 代码风格规范│ ├── project-structure.aonerule # 项目结构规范│ └── features.aonerule # 功能实现规范└── tech/├── xx秒杀-技术方案.md # 具体需求的技术方案└── xx红包模块-技术方案.md
4.2 Rules文件内容示例
代码风格规范## 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> 接口- 使用 注解- execute方法的第一个参数是 InvocationContext- execute方法的第二个参数是 JSONObject businessReq示例:```javapublic class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {public List<FundQueryDTO> execute(InvocationContext context, JSONObject businessReq) {// 实现逻辑}}```## 模块构建器- 必须继承 BaseModuleBuilder- 使用 注解- 实现 getName()、doBuild()、bottomTransform() 三个方法- 通过 ContextUtils.getBizResult() 获取数据服务结果示例:```public class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {public String getName() {return "nnRedPacket";}protected NnRedPacketVO doBuild(InvocationContext context) {List<FundQueryDTO> funds = ContextUtils.<List<FundQueryDTO>>getBizResult(NnRedPacketDataService.class.getSimpleName(),context,500).orElse(Collections.emptyList());// 构建逻辑}}```
4.3 技术方案模板
# 业务定义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 显著改善的效果
4.5 依然存在的问题
五、SDD探索 - 规格驱动开发
5.1 SDD的引入
5.2 Speckit执行流程
├── .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
## 核心原则### 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和代码审查必须验证是否符合这些原则。
# 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(因为门槛超限,红包被过滤)......
public class NnRedPacketDataService implements DataService<List<FundQueryDTO>> {private FpProvider fpProvider;public List<FundQueryDTO> execute(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();}// ... 其他方法}
public class NnRedPacketModuleBuilder extends BaseModuleBuilder<NnRedPacketVO> {public String getName() {return "nnRedPacket";}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<FundQueryDTO> sortFunds(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());}// ... 其他方法}
public class NnRedPacketModuleBuilderTest {private InvocationContext context;private NnRedPacketDataService dataService;private NnRedPacketModuleBuilder builder;/*** 测试用例 TC-1: 正常流程 - 有可用红包*/public void testBuild_WithAvailableFunds_ShouldReturnVO() {// 准备数据List<FundQueryDTO> mockFunds = Arrays.asList(createFund(1L, 500L, 1500L), // 5元,门槛15元createFund(2L, 1000L, 2500L) // 10元,门槛25元);// Mockwhen(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: 边界条件 - 红包门槛超限*/public void testBuild_ThresholdExceeded_ShouldReturnNull() {// 准备数据:门槛25元,超过配置的20元List<FundQueryDTO> mockFunds = Arrays.asList(createFund(1L, 500L, 2500L));// 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*/public void testBuild_ProviderReturnsNull_ShouldReturnNull() {// Mockwhen(dataService.execute(any(), any())).thenReturn(null);mockBizResult(context, NnRedPacketDataService.class.getSimpleName(), null);// 执行NnRedPacketVO result = builder.doBuild(context);// 验证(按规格预期结果)assertNull(result);}/*** 测试用例 TC-4: 异常场景 - 用户未登录*/public void testBuild_UserNotLogin_ShouldReturnNull() {// Mock:userId = 0when(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.4 SDD的问题与挑战
5.5 SDD适用场景分析
六、当前最佳实践 - Rules + Agentic Coding + AI文档汇总
6.1 融合各阶段优势
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 |
6.3 AI文档汇总机制
完成需求开发 → 提交AI:"将本次代码逻辑汇总到汇总文档" → AI分析代码 → AI更新文档我刚完成了NN红包模块的开发,请分析以下代码:- NnRedPacketDataService.java- NnRedPacketModuleBuilder.java- NnRedPacketVO.java然后将其业务逻辑汇总到"NN业务整体架构与逻辑文档.md"中,确保:1. 描述模块的核心功能和业务价值2. 说明数据流转过程3. 列出关键的业务规则和判断逻辑4. 保持与现有文档的风格一致
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吊钩层模块[类似结构]# 四、数据服务层[各个数据服务的功能和依赖]# 五、关键流程[重要的业务流程时序图]# 六、配置说明[配置项说明]# 七、扩展点[未来可能的扩展方向]
## 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() != 2) return 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": "展开查看更多" // 展开文案}
七、思考总结
最后
八、团队介绍
本文作者式遂,来自淘天集团-淘特用户技术团队。团队主要负责淘宝行业&淘特C端链路的研发工作,包含:搜索推荐、互动游戏、导购、交易等基础服务及创新业务。当下我们积极拥抱AI时代,探索智能化在研发提效和业务场景中的无限可能。技术不只是工具,更是为用户创造价值的力量。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-01-23
Anthropic 内部技术与非技术团队如何通过 Claude Code 实现工作流深度转型
2026-01-23
刚刚!Cursor 2.4 发布:Subagents 让 AI 晋升“项目经理”,还能秒出 UI 设计图!
2026-01-23
为什么Codex似乎更强,Claude Code却更流行?
2026-01-22
刚刚,Anthropic 发布 Claude「宪法」:一份写给 AI 的人生指南
2026-01-22
Skills 为何成为 Agent 工程化的关键拼图?
2026-01-22
AI4Data 领域中应该如何工程化的实现“定向幻觉”?
2026-01-22
把 Claude Skills 包起来!Claude Code 很强,但别让它毁了你的本地环境
2026-01-22
红杉xbench 最新报告:104 项日常任务,Agent 可处理 60+%
2025-10-26
2026-01-10
2025-11-19
2025-11-13
2025-11-03
2026-01-01
2025-11-12
2025-12-09
2025-11-21
2025-11-15
2026-01-23
2026-01-22
2026-01-22
2026-01-21
2026-01-21
2026-01-12
2026-01-12
2026-01-11