一、项目需求

搭建一个用户管理系统的后端 API:

┌──────────────────────────────────────────────────┐
│               用户管理 RESTful API                │
├────────┬──────────┬─────────────────────────────┤
│ 接口    │  方法    │           说明                │
├────────┼──────────┼─────────────────────────────┤
│/users  │ GET      │ 获取用户列表(分页)            │
│/users  │ POST     │ 创建用户                      │
│/users/{id}│ GET   │ 获取单个用户                   │
│/users/{id}│ PUT   │ 更新用户                      │
│/users/{id}│ DELETE│ 删除用户                      │
└────────┴──────────┴─────────────────────────────┘

技术栈:

  • Spring Boot 3.2 + Java 17
  • MyBatis-Plus(简化数据库操作)
  • MySQL 8.0
  • Lombok

二、Cursor 新建项目

2.1 创建 Spring Boot 项目

在 Cursor 中打开终端,输入:

# 方式一:用 Spring Initializr(推荐在 Cursor Terminal 中操作)
# 访问 https://start.spring.io/ 下载项目,然后在 Cursor 中打开

# 方式二:或者直接让 Cursor 生成
# 打开 Cursor 的 Composer (Ctrl+I / Cmd+I),输入:

Cursor Prompt:

帮我创建一个 Spring Boot 3.2 项目,要求:
1. Java 17
2. 依赖:spring-boot-starter-web, mybatis-plus-spring-boot3-starter, mysql-connector-j, lombok
3. groupId: com.example, artifactId: user-management
4. 在 application.yml 中配置 MySQL 数据源(用户名root,密码123456,数据库user_db)
5. 创建数据库建表SQL:users表(id, username, email, phone, create_time, update_time, is_deleted逻辑删除)

Cursor 会自动帮你生成项目结构和配置文件。

2.2 项目结构

生成后项目结构应该是:

user-management/
├── src/main/java/com/example/usermanagement/
│   ├── UserManagementApplication.java
│   ├── entity/
│   │   └── User.java
│   ├── mapper/
│   │   └── UserMapper.java
│   ├── service/
│   │   ├── UserService.java
│   │   └── impl/
│   │       └── UserServiceImpl.java
│   ├── controller/
│   │   └── UserController.java
│   ├── dto/
│   │   ├── UserCreateDTO.java
│   │   ├── UserUpdateDTO.java
│   │   └── UserVO.java
│   ├── common/
│   │   ├── Result.java
│   │   └── PageResult.java
│   └── config/
│       └── MybatisPlusConfig.java
├── src/main/resources/
│   ├── application.yml
│   └── db/
│       └── schema.sql
└── pom.xml

三、Cursor 对话实录:搭框架

3.1 让 Cursor 生成实体类和 Mapper

Prompt(在 Cursor Chat 中输入):

基于上面的 users 表结构,帮我生成以下文件:

1. User.java (entity) - 使用 MyBatis-Plus 注解 @TableName, @TableId, @TableField
2. UserMapper.java - 继承 BaseMapper<User>
3. MybatisPlusConfig.java - 配置分页插件和逻辑删除

注意:
- 使用 @TableLogic 实现逻辑删除
- id 使用雪花算法 @TableId(type = IdType.ASSIGN_ID)
- create_time 和 update_time 使用 @TableField(fill = FieldFill.INSERT/UPDATE)
- 代码风格要简洁,用 Lombok

3.2 Cursor 生成的实体类

package com.example.usermanagement.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName("users")
public class User {

    /** 主键ID(雪花算法) */
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    /** 用户名 */
    private String username;

    /** 邮箱 */
    private String email;

    /** 手机号 */
    private String phone;

    /** 创建时间(自动填充) */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /** 更新时间(自动填充) */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /** 逻辑删除(0=正常,1=已删除) */
    @TableLogic
    private Integer isDeleted;
}

四、Cursor 对话实录:写业务代码

4.1 Service 层

Prompt:

帮我生成 UserService 和 UserServiceImpl:

要求:
1. 分页查询用户列表(支持按用户名模糊搜索)
2. 根据 ID 获取用户详情
3. 创建用户(校验用户名不能重复,邮箱格式校验)
4. 更新用户(只更新非空字段)
5. 删除用户(逻辑删除)

返回值统一用 Result<T> 包装,分页用 PageResult<T>
DTO 和 VO 要和 Entity 分开
// Service 接口
public interface UserService {
    /** 分页查询 */
    PageResult<UserVO> listUsers(int pageNum, int pageSize, String username);
    /** 获取详情 */
    UserVO getUserById(Long id);
    /** 创建用户 */
    Long createUser(UserCreateDTO dto);
    /** 更新用户 */
    void updateUser(Long id, UserUpdateDTO dto);
    /** 删除用户 */
    void deleteUser(Long id);
}
// Service 实现
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;

    @Override
    public PageResult<UserVO> listUsers(int pageNum, int pageSize, String username) {
        Page<User> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(username != null && !username.isBlank(), User::getUsername, username);
        wrapper.orderByDesc(User::getCreateTime);

        userMapper.selectPage(page, wrapper);

        List<UserVO> voList = page.getRecords().stream()
                .map(this::toVO)
                .collect(Collectors.toList());

        return new PageResult<>(voList, page.getTotal());
    }

    @Override
    public UserVO getUserById(Long id) {
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        return toVO(user);
    }

    @Override
    @Transactional
    public Long createUser(UserCreateDTO dto) {
        // 校验用户名唯一
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, dto.getUsername());
        if (userMapper.selectCount(wrapper) > 0) {
            throw new RuntimeException("用户名已存在");
        }

        User user = new User();
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        user.setPhone(dto.getPhone());
        userMapper.insert(user);
        return user.getId();
    }

    @Override
    @Transactional
    public void updateUser(Long id, UserUpdateDTO dto) {
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 只更新非空字段
        if (dto.getUsername() != null) user.setUsername(dto.getUsername());
        if (dto.getEmail() != null) user.setEmail(dto.getEmail());
        if (dto.getPhone() != null) user.setPhone(dto.getPhone());

        userMapper.updateById(user);
    }

    @Override
    public void deleteUser(Long id) {
        userMapper.deleteById(id); // 逻辑删除
    }

    private UserVO toVO(User user) {
        return UserVO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .email(user.getEmail())
                .phone(user.getPhone())
                .createTime(user.getCreateTime())
                .build();
    }
}

4.2 Controller 层

Prompt:

帮我生成 UserController,遵循 RESTful 风格:
- GET    /api/users?page=1&size=10&username=xxx
- POST   /api/users
- GET    /api/users/{id}
- PUT    /api/users/{id}
- DELETE /api/users/{id}

用 @Valid 做参数校验,统一异常处理
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Tag(name = "用户管理")
public class UserController {

    private final UserService userService;

    @GetMapping
    @Operation(summary = "分页查询用户列表")
    public Result<PageResult<UserVO>> listUsers(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(required = false) String username) {
        return Result.success(userService.listUsers(page, size, username));
    }

    @GetMapping("/{id}")
    @Operation(summary = "获取用户详情")
    public Result<UserVO> getUser(@PathVariable Long id) {
        return Result.success(userService.getUserById(id));
    }

    @PostMapping
    @Operation(summary = "创建用户")
    public Result<Long> createUser(@Valid @RequestBody UserCreateDTO dto) {
        return Result.success(userService.createUser(dto));
    }

    @PutMapping("/{id}")
    @Operation(summary = "更新用户")
    public Result<Void> updateUser(@PathVariable Long id,
                                     @Valid @RequestBody UserUpdateDTO dto) {
        userService.updateUser(id, dto);
        return Result.success();
    }

    @DeleteMapping("/{id}")
    @Operation(summary = "删除用户")
    public Result<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return Result.success();
    }
}

五、Cursor 对话实录:写测试

Prompt:

帮我写 UserController 的集成测试类,使用 @SpringBootTest + MockMvc:
1. 测试创建用户(正常 + 用户名重复)
2. 测试分页查询
3. 测试获取用户详情(存在 + 不存在)
4. 测试更新用户
5. 测试删除用户
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void createUser_success() throws Exception {
        UserCreateDTO dto = new UserCreateDTO();
        dto.setUsername("testuser");
        dto.setEmail("test@example.com");

        mockMvc.perform(post("/api/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(dto)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data").isNumber());
    }
}

六、接口测试

用 Apifox / Postman / cURL 测试:

# 1. 创建用户
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"username":"张三","email":"zhangsan@example.com","phone":"13800138000"}'

# 2. 查询用户列表
curl http://localhost:8080/api/users?page=1\&size=10

# 3. 获取用户详情
curl http://localhost:8080/api/users/1

# 4. 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email":"newemail@example.com"}'

# 5. 删除用户
curl -X DELETE http://localhost:8080/api/users/1

七、Cursor 使用技巧总结

7.1 Prompt 编写技巧

┌─────────────────────────────────────────────────────────┐
│  ✅ 好的 Prompt                           │  ❌ 差的 Prompt            │
├───────────────────────────────┬──────────────────────────┤
│ "用 MyBatis-Plus 写,雪花算法ID" │ "写一个用户管理"           │
│ "返回 Result<T> 包装"           │ "给我代码"                │
│ "校验用户名唯一,邮箱格式"       │ "加上校验"                │
│ "只更新非空字段"                │ "写 update 方法"          │
│ "分页查询,按创建时间倒序"       │ "查询所有用户"            │
└───────────────────────────────┴──────────────────────────┘

7.2 Cursor Debug 技巧

当代码有 bug 时,不要直接让 Cursor “帮我修”。正确做法:

Step 1: 先自己看报错信息,理解错误原因
Step 2: 在 Cursor Chat 中粘贴报错日志
Step 3: 告诉 Cursor 你已经做了什么排查
Step 4: 让 Cursor 给出修复建议,而不是直接给完整代码

示例 Prompt:
"用户创建时报错 DuplicateKeyException,我已经检查了数据库确实有唯一索引。
但我的代码里有校验用户名唯一性,为什么还会报这个错?
我的校验逻辑是:[粘贴代码]。帮我看看问题在哪。"

7.3 效率对比

┌─────────────────────────────────────────────────────┐
│         传统开发 vs Cursor 开发(本项目管理API)        │
├──────────────┬──────────────┬───────────────────────┤
│     步骤      │  传统方式     │  Cursor 辅助            │
├──────────────┼──────────────┼───────────────────────┤
│ 创建项目      │  10分钟       │  2分钟(自动生成)        │
│ 写实体类      │  15分钟       │  1分钟(Prompt生成)     │
│ 写 Mapper     │  5分钟        │  30秒                   │
│ 写 Service    │  40分钟       │  5分钟(Prompt+微调)    │
│ 写 Controller │  20分钟       │  3分钟                  │
│ 写 DTO/VO     │  15分钟       │  2分钟                  │
│ 写异常处理    │  15分钟       │  3分钟                  │
│ 写测试        │  30分钟       │  5分钟                  │
│ 调试Bug      │  30分钟       │  10分钟                 │
├──────────────┼──────────────┼───────────────────────┤
│ 总计          │  ~3小时       │  ~30分钟               │
└──────────────┴──────────────┴───────────────────────┘

八、踩坑记录

踩坑1:MyBatis-Plus 分页不生效

忘记配置分页插件,分页查询返回的是全部数据。

// ❌ 忘记加这个配置
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

踩坑2:逻辑删除和查询冲突

配置了 @TableLogic 后,selectById 会自动加 is_deleted=0 条件。如果需要查包含已删除的数据,需要手写 SQL。

踩坑3:Cursor 生成的 import 可能缺失

Cursor 有时不会自动添加所有 import,特别是自定义的类。编译报错后让 Cursor “帮我修复 import” 即可。


🎉 总结:Cursor 最大的价值不是"帮你写代码",而是把你的想法快速变成可运行的代码,让你把精力放在架构设计和业务逻辑上。用对 Prompt,效率可以提升 5-10 倍。

如果觉得有帮助,点赞 + 收藏支持一下!有问题欢迎评论区讨论 💬

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐