crud-development

SKILL.md

CRUD 开发指南

通用模板。如果项目有专属技能(如 leniu-crud),优先使用。

核心规范

三层架构

职责 命名示例
Controller 接收请求、参数校验、路由分发 OrderController
Service 业务逻辑、事务管理 OrderService / OrderServiceImpl
Mapper 数据访问(ORM 映射) OrderMapper

标准包结构

[你的包名]/
├── controller/        # 控制器
├── service/
│   └── impl/          # 服务实现
├── mapper/            # 数据访问层
├── entity/            # 实体类
├── dto/               # 请求参数对象
├── vo/                # 响应视图对象
├── enums/             # 枚举常量
└── config/            # 模块配置

代码示例

1. Entity(实体类)

package [你的包名].entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("t_order")
public class Order {

    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    private String orderNo;

    private Integer status;

    private Long amount;

    @TableField(fill = FieldFill.INSERT)
    private String createBy;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableLogic(value = "0", delval = "1")
    private Integer deleted;
}

2. DTO(请求参数)

package [你的包名].dto;

import jakarta.validation.constraints.*;
import lombok.Data;

@Data
public class OrderCreateDTO {

    @NotBlank(message = "订单号不能为空")
    private String orderNo;

    @NotNull(message = "金额不能为空")
    @Min(value = 1, message = "金额必须大于0")
    private Long amount;
}
package [你的包名].dto;

import lombok.Data;

@Data
public class OrderQueryDTO {

    private String orderNo;

    private Integer status;

    private Integer pageNum = 1;

    private Integer pageSize = 10;
}

3. VO(响应对象)

package [你的包名].vo;

import lombok.Data;
import java.time.LocalDateTime;

@Data
public class OrderVO {

    private Long id;

    private String orderNo;

    private Integer status;

    private Long amount;

    private LocalDateTime createTime;
}

4. Mapper

package [你的包名].mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import [你的包名].entity.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

5. Service

package [你的包名].service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import [你的包名].entity.Order;
import [你的包名].dto.OrderCreateDTO;
import [你的包名].dto.OrderQueryDTO;
import [你的包名].vo.OrderVO;

public interface IOrderService extends IService<Order> {

    Long createOrder(OrderCreateDTO dto);

    void updateOrder(Long id, OrderCreateDTO dto);

    OrderVO getOrderDetail(Long id);

    Page<OrderVO> pageQuery(OrderQueryDTO query);
}
package [你的包名].service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import [你的包名].dto.OrderCreateDTO;
import [你的包名].dto.OrderQueryDTO;
import [你的包名].entity.Order;
import [你的包名].mapper.OrderMapper;
import [你的包名].service.IOrderService;
import [你的包名].vo.OrderVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createOrder(OrderCreateDTO dto) {
        Order order = new Order();
        // 使用 [你的对象转换工具] 或手动赋值
        order.setOrderNo(dto.getOrderNo());
        order.setAmount(dto.getAmount());
        order.setStatus(0);
        this.save(order);
        return order.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(Long id, OrderCreateDTO dto) {
        Order order = this.getById(id);
        if (order == null) {
            throw new [你的业务异常类]("订单不存在");
        }
        order.setOrderNo(dto.getOrderNo());
        order.setAmount(dto.getAmount());
        this.updateById(order);
    }

    @Override
    public OrderVO getOrderDetail(Long id) {
        Order order = this.getById(id);
        if (order == null) {
            throw new [你的业务异常类]("订单不存在");
        }
        OrderVO vo = new OrderVO();
        // 使用 [你的对象转换工具] 或手动赋值
        vo.setId(order.getId());
        vo.setOrderNo(order.getOrderNo());
        vo.setStatus(order.getStatus());
        vo.setAmount(order.getAmount());
        vo.setCreateTime(order.getCreateTime());
        return vo;
    }

    @Override
    public Page<OrderVO> pageQuery(OrderQueryDTO query) {
        Page<Order> page = new Page<>(query.getPageNum(), query.getPageSize());
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<Order>()
                .like(StringUtils.hasText(query.getOrderNo()), Order::getOrderNo, query.getOrderNo())
                .eq(query.getStatus() != null, Order::getStatus, query.getStatus())
                .orderByDesc(Order::getCreateTime);
        Page<Order> result = this.page(page, wrapper);

        // 转换为 VO
        Page<OrderVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
        voPage.setRecords(result.getRecords().stream().map(order -> {
            OrderVO vo = new OrderVO();
            vo.setId(order.getId());
            vo.setOrderNo(order.getOrderNo());
            vo.setStatus(order.getStatus());
            vo.setAmount(order.getAmount());
            vo.setCreateTime(order.getCreateTime());
            return vo;
        }).toList());
        return voPage;
    }
}

6. Controller

package [你的包名].controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import [你的包名].dto.OrderCreateDTO;
import [你的包名].dto.OrderQueryDTO;
import [你的包名].service.IOrderService;
import [你的包名].vo.OrderVO;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
public class OrderController {

    private final IOrderService orderService;

    @PostMapping
    public ResponseEntity<Long> create(@Valid @RequestBody OrderCreateDTO dto) {
        return ResponseEntity.ok(orderService.createOrder(dto));
    }

    @PutMapping("/{id}")
    public ResponseEntity<Void> update(@PathVariable Long id,
                                       @Valid @RequestBody OrderCreateDTO dto) {
        orderService.updateOrder(id, dto);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/{id}")
    public ResponseEntity<OrderVO> detail(@PathVariable Long id) {
        return ResponseEntity.ok(orderService.getOrderDetail(id));
    }

    @GetMapping
    public ResponseEntity<Page<OrderVO>> page(OrderQueryDTO query) {
        return ResponseEntity.ok(orderService.pageQuery(query));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        orderService.removeById(id);
        return ResponseEntity.ok().build();
    }
}

常见错误

错误 正确做法
Controller 里写业务逻辑 业务逻辑放 Service 层
直接返回 Entity 给前端 使用 VO 封装响应数据
用 Map 传递业务数据 使用 DTO/VO 强类型对象
忘记加 @Transactional 写操作方法加事务注解
逻辑删除字段值搞反 确认项目约定(通用:0=正常, 1=删除)
分页参数未设默认值 DTO 中给 pageNum/pageSize 默认值
Service 之间循环依赖 提取公共 Service 或使用事件机制解耦
Weekly Installs
4
GitHub Stars
8
First Seen
7 days ago
Installed on
gemini-cli4
github-copilot4
codex4
kimi-cli4
cursor4
amp4