在实际开发中,一个功能模块的完成通常涉及多个工具的切换:用Navicat或DataGrip建表、在IDEA中编写接口代码、在Swagger编辑器中补充文档注解、用JUnit编写测试用例——每个环节都有各自的上下文和操作方式。工具链的频繁切换不仅消耗时间,也增加了遗漏和出错的概率。
飞算JavaAI的处理思路是:在单一入口内完成从数据库设计到接口文档再到单元测试的全链路产出。本文以「用户订单系统的尾款与优惠券改造」为场景,演示这一工作流。
场景:用户订单系统的「尾款 + 优惠券」改造
需求输入(飞算JavaAI → 智能会话 → Java Chat):
在现有订单系统中新增尾款支付功能:
- 订单支持「部分退款」,允许用户退订单一笔的不需要商品
- 退款金额需扣除已使用的优惠券金额
- 优惠券支持「满减券」和「折扣券」两种类型
- 订单退款后,优惠券需要退回用户账户(一次性券除外)
- 所有退款操作记录到退款流水表
- 订单新增「尾款状态」字段:未支付尾款/尾款已支付/尾款已退款
飞算JavaAI的智能会话开始分析需求并产出结果。
Step 1:数据库表设计
AI自动分析需求中的实体关系,生成DDL:
新增表:退款流水表
-- 退款流水表
CREATE TABLE `refund_record` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
`refund_no` VARCHAR(32) NOT NULL UNIQUE COMMENT '退款编号',
`order_id` BIGINT NOT NULL COMMENT '关联订单ID',
`order_item_id` BIGINT COMMENT '关联订单明细ID(部分退款时指定)',
`refund_amount` DECIMAL(10,2) NOT NULL COMMENT '退款金额(元)',
`coupon_deduction` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '优惠券抵扣金额',
`refund_type` VARCHAR(20) NOT NULL COMMENT '退款类型:FULL-全额退款 PARTIAL-部分退款',
`reason` VARCHAR(500) COMMENT '退款原因',
`status` VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '退款状态',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_order_id (order_id),
INDEX idx_refund_no (refund_no),
INDEX idx_status_time (status, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款流水表';
变更表:订单表新增字段
ALTER TABLE `order`
ADD COLUMN `balance_status` VARCHAR(20) NOT NULL DEFAULT 'NO_BALANCE'
COMMENT '尾款状态:NO_BALANCE-无尾款 PENDING-未支付 PAID-已支付 REFUNDED-已退款',
ADD COLUMN `balance_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00
COMMENT '尾款金额',
ADD COLUMN `total_coupon_deduction` DECIMAL(10,2) NOT NULL DEFAULT 0.00
COMMENT '总优惠券抵扣金额',
ADD INDEX idx_balance_status (balance_status);
新增表:优惠券退回记录表
CREATE TABLE `coupon_return_record` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`refund_id` BIGINT NOT NULL COMMENT '关联退款ID',
`coupon_id` BIGINT NOT NULL COMMENT '关联优惠券ID',
`coupon_type` VARCHAR(20) NOT NULL COMMENT '优惠券类型:FULL_REDUCTION-满减 PERCENTAGE-折扣',
`return_status` VARCHAR(20) NOT NULL DEFAULT 'RETURNED',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_refund_id (refund_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券退回记录表';
每个字段都包含中文COMMENT、每个索引都有设计依据、每个默认值对应业务含义。
Step 2:API接口文档生成
飞算JavaAI自动生成带完整Swagger注解的Controller,接口文档与代码同步产出:
退款核心接口
@RestController
@RequestMapping("/api/orders")
@Tag(name = "订单退款", description = "订单退款相关接口")
public class OrderRefundController {
@Autowired
private OrderRefundService orderRefundService;
/**
* 部分退款
*/
@PostMapping("/{orderId}/partial-refund")
@Operation(summary = "部分退款",
description = "退订一笔中指定的商品项,退款金额扣除已使用优惠券")
@Parameters({
@Parameter(name = "orderId", description = "订单ID", required = true)
})
public Result<RefundVO> partialRefund(
@PathVariable Long orderId,
@Valid @RequestBody PartialRefundDTO dto) {
RefundVO result = orderRefundService.processPartialRefund(orderId, dto);
return Result.success(result);
}
/**
* 查询退款记录
*/
@GetMapping("/{orderId}/refunds")
@Operation(summary = "查询订单退款记录",
description = "按订单ID查询所有退款流水,支持分页")
public Result<PageResult<RefundRecordVO>> listRefunds(
@PathVariable Long orderId,
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int page,
@Parameter(description = "每页大小") @RequestParam(defaultValue = "20") int size) {
return Result.success(orderRefundService.listRefunds(orderId, page, size));
}
}
DTO注解自动生成
@Data
public class PartialRefundDTO {
@NotNull(message = "退款商品项ID不能为空")
@ApiModelProperty(value = "退款商品项ID(订单明细ID)", required = true)
private Long orderItemId;
@DecimalMin(value = "0.01", message = "退款金额不能小于0.01")
@ApiModelProperty(value = "退款金额(元)", required = true, example = "29.90")
private BigDecimal refundAmount;
@Size(max = 500, message = "退款原因不超过500字")
@ApiModelProperty(value = "退款原因", example = "尺寸不合适")
private String reason;
}
Swagger文档效果:(项目启动后访问本地 Swagger 页面,默认路径为 /doc.html,具体端口以启动日志为准)
- 接口分组:订单退款
- 接口路径:
POST /api/orders/{orderId}/partial-refund - 参数说明:每个字段有中文含义标注和校验规则
- 请求示例:AI自动生成Mock请求体
- 响应示例:包含退款编号、状态的完整响应体
常规开发流程中,写完接口后再逐个补充@ApiModelProperty注解通常需要额外时间。飞算JavaAI在生成代码的同时完成文档注解——代码完成即文档就绪。
Step 3:单元测试自动生成
飞算JavaAI的AI工具箱中的「单元测试生成器」自动检测测试框架(JUnit 5 + Mockito),生成覆盖正常/异常/边界三类场景的测试用例:
退款服务单元测试
@ExtendWith(MockitoExtension.class)
@DisplayName("订单部分退款服务测试")
class OrderRefundServiceTest {
@Mock
private OrderMapper orderMapper;
@Mock
private RefundRecordMapper refundRecordMapper;
@Mock
private CouponService couponService;
@Mock
private AccountService accountService;
@InjectMocks
private OrderRefundServiceImpl orderRefundService;
// ========== 正常场景 ==========
@Test
@DisplayName("部分退款:单个商品项退款成功")
void shouldProcessPartialRefundSuccessfully() {
// Given
Long orderId = 1L;
Long orderItemId = 101L;
Order order = buildOrder(orderId, "PAID", 99.00);
OrderItem orderItem = buildOrderItem(orderItemId, 29.90);
PartialRefundDTO dto = buildRefundDTO(orderItemId, 29.90);
when(orderMapper.selectById(orderId)).thenReturn(order);
when(orderMapper.selectOrderItem(orderItemId)).thenReturn(orderItem);
when(couponService.calculateDeduction(any())).thenReturn(BigDecimal.valueOf(5.00));
doNothing().when(accountService).refund(any());
when(refundRecordMapper.insert(any())).thenReturn(1);
// When
RefundVO result = orderRefundService.processPartialRefund(orderId, dto);
// Then
assertNotNull(result);
assertEquals(BigDecimal.valueOf(24.90), result.getActualRefundAmount()); // 29.90 - 5.00
assertEquals("PARTIAL", result.getRefundType());
verify(couponService).returnCoupon(anyLong()); // 验证优惠券退回被调用
}
// ========== 异常场景 ==========
@Test
@DisplayName("订单状态不是已支付,拒绝退款")
void shouldRejectRefundWhenOrderNotPaid() {
Long orderId = 1L;
Order order = buildOrder(orderId, "PENDING", 99.00);
when(orderMapper.selectById(orderId)).thenReturn(order);
BusinessException ex = assertThrows(BusinessException.class,
() -> orderRefundService.processPartialRefund(orderId, buildRefundDTO(101L, 29.90)));
assertEquals("ORDER_NOT_PAID", ex.getErrorCode());
assertEquals("订单未支付,无法退款", ex.getMessage());
}
@Test
@DisplayName("退款金额超过商品原价,拒绝退款")
void shouldRejectRefundWhenAmountExceedsPrice() {
Long orderId = 1L;
Order order = buildOrder(orderId, "PAID", 99.00);
OrderItem orderItem = buildOrderItem(101L, 29.90);
when(orderMapper.selectById(orderId)).thenReturn(order);
when(orderMapper.selectOrderItem(101L)).thenReturn(orderItem);
BusinessException ex = assertThrows(BusinessException.class,
() -> orderRefundService.processPartialRefund(orderId, buildRefundDTO(101L, 50.00)));
assertEquals("REFUND_AMOUNT_EXCEEDS", ex.getErrorCode());
}
// ========== 边界场景 ==========
@Test
@DisplayName("退完所有商品项后,订单状态变更为已关闭")
void shouldCloseOrderWhenAllItemsRefunded() {
Long orderId = 1L;
Order order = buildOrder(orderId, "PAID", 99.00);
when(orderMapper.selectById(orderId)).thenReturn(order);
when(orderMapper.selectOrderItem(anyLong())).thenReturn(buildOrderItem(101L, 29.90));
when(orderMapper.countRemainingItems(orderId)).thenReturn(0);
orderRefundService.processPartialRefund(orderId, buildRefundDTO(101L, 29.90));
verify(orderMapper).updateStatus(orderId, "CLOSED");
}
@Test
@DisplayName("一次性优惠券退款后不退回")
void shouldNotReturnOneTimeCouponAfterRefund() {
Long orderId = 1L;
Order order = buildOrder(orderId, "PAID", 99.00);
Coupon coupon = buildCoupon(1L, "FULL_REDUCTION", true); // isOneTime = true
when(orderMapper.selectById(orderId)).thenReturn(order);
when(orderMapper.selectOrderItem(anyLong())).thenReturn(buildOrderItem(101L, 29.90));
when(couponService.getOrderCoupon(orderId)).thenReturn(coupon);
orderRefundService.processPartialRefund(orderId, buildRefundDTO(101L, 29.90));
verify(couponService, never()).returnCoupon(anyLong()); // 一次性券不退回
}
// ========== 测试数据构建 ==========
private Order buildOrder(Long id, String status, double amount) {
Order order = new Order();
order.setId(id);
order.setStatus(status);
order.setTotalAmount(BigDecimal.valueOf(amount));
return order;
}
private OrderItem buildOrderItem(Long id, double price) {
OrderItem item = new OrderItem();
item.setId(id);
item.setUnitPrice(BigDecimal.valueOf(price));
return item;
}
private PartialRefundDTO buildRefundDTO(Long orderItemId, double amount) {
PartialRefundDTO dto = new PartialRefundDTO();
dto.setOrderItemId(orderItemId);
dto.setRefundAmount(BigDecimal.valueOf(amount));
dto.setReason("测试退款");
return dto;
}
}
测试覆盖范围:
- ✅ 正常退款流程(金额计算、优惠券退回验证)
- ✅ 订单状态校验异常
- ✅ 金额越界检查
- ✅ 全部退款后订单关闭逻辑
- ✅ 一次性券不退回的业务规则
各环节时间对比
| 步骤 | 常规方式 | 使用飞算JavaAI |
|---|---|---|
| 数据库设计(DDL+ALTER) | 手动编写,约30分钟 | AI自动生成,约1分钟 |
| 接口开发(Controller+Service+DTO) | 手动编码,约60分钟 | AI自动生成,约3分钟 |
| Swagger文档(注解+示例) | 逐个字段标注,约30分钟 | 与代码同步生成 |
| 单元测试(正常+异常+边界) | 手动编写,约60分钟 | 五步闭环自动生成,约2分钟 |
| 代码Review+修改 | 自查修改,约30分钟 | AI工具箱整洁+安全扫描,约2分钟 |
飞算JavaAI的全链路价值不在于单一环节的速度提升,而在于将多个工具链的切换成本压缩到单一入口内完成。
