opencode4-在已有项目中增加修改功能

本节目标

本节还是以开源项目RuoYi-Vue-Plus 为例,看看如何对已有项目添加和修改功能的一个完整流程。这个开源项目地址:gitee.com/dromara/Ruo...

  • 了解/new命令作用
  • 了解 /init 命令作用
  • 了解 AGENTS.md文件的重要性
  • 仅增加后端接口,不实现前端页面

先让RuoYi-Vue-Plus 跑起来

这个步骤,我们也利用 AI 帮我们做很多事情。比如:

  • 数据库的初始化工作
  • 配置修改工作

这里我选用的大模型是 claude sonnet 4.6 模型。为什么订阅 GitHub copilot ,就是因为它其中包含 Claude 这几个编程模型。非常好用!其中 sonnet 模型用于一般编程工作;而 opus 用于框架设计和复杂编程工作。不过这里是需要科学上网才能使用。这个要想用的话,需要自行解决。

数据库初始化

按照上节中提到的思路,让 opencode 通过官方文档进行学习。自动帮我们进行数据库初始化工作。

官方学习文档:plus-doc.dromara.org/#/_readme

  • 提示词

    bash 复制代码
    帮我在远程的 mysql 数据库上创建本项目所需的数据库。
    官方学习文档:https://plus-doc.dromara.org/#/_readme
    我的 mysql 数据库的相关信息如下:
    mysql服务器 ip:your mysql server ip
    端口:3306
    用户名:root
    密码:your password
    根据官网文档介绍,帮我创建所需的库和表吧。这里特别说明下,我仅仅需要ruoyi-vue-plus的基础库表。
  • 执行结果

    补充说明:

    其实我开始用的是一个opencode免费的模型MiniMax M2.5 Free模型。结果这个模型,告诉我无法直接操作远程的mysql数据库。它找不到对应的mysql客户端工具。但是用claude的模型它就能通过安装Node.js的mysql2包来连接mysql数据库。虽然免费的模型最后也能给我所有执行的脚本和完整步骤,但是依然没有"全自动"创建库表。所以,一个好的模型是灵魂。

配置文件修改

由于还是在这个上下文中沟通,所以不用再特别强调官方学习文档地址了。

  • 提示词

    yaml 复制代码
    帮我修改dev环境的配置文件,仅仅保留最基础的功能,其他功能都先关闭。
    修改数据库连接为刚才创建的库连接。
    redis相关信息如下:
    host: your redis host ip
    port: 6379
    password: your redis password
  • 执行结果

在 idea 中跑起来

这个 5.X 版本最低也要 JDK17 版本,我的环境是 JDK21 的。完全满足。启动最后打印的内容如下:

/new 命令

在 opencode 中,/new表示开始新的会话。这个命令会清空上下文记忆。也就是再次和模型对话时省 token 了。但是也失去了历史的上下文记忆了。特别适合在新功能开发;或者提交代码后的 bug 修复。总之是任务告一段落了。

和在UI 中点击新建会话一样:

我们这里用/new是表示,前期的准备告一段落了。在和大模型沟通的上下文中不用包含历史对话信息了。

/init 命令

这个命令会给项目做一个全面扫描,了解项目后生成一个 AGENTS.md文档。如果已经存在这个AGENTS.md文档,再次执行/init 命令时,该命令会尝试在其基础上进行补充。我们来执行这个命令,并且大概看下它生成的文档。记住哈,这一步很重要,新加功能和修改已有功能都基于此文档中定义的规则和约束进行的。

看下执行结果:

看起来还不错,只不过都是英文的。我们可以让 opencode 转为中文。当然为了避免这种情况,我们可以在全局规则中增加简体中文的交互规则。

AGENTS.md 文件

这个AGENTS.md文件,你可以理解成大模型的参考手册。是用于定义、配置和规范 AI Agent(智能体)的行为准则与技术细节的文档。这个文件可以是在项目下的,也可以在用户目录下的。一般在用户目录下的AGENTS.md就是全局的规则文件了。

生成全局 AGENTS.md文件

这步我们可以在 opencode 中直接让它来帮我们创建出来。

  • 提示词

    复制代码
    在全局规则中增加始终使用简体中文与我交流
  • 执行结果

  • 全局规则文件内容

    C:\Users\mayuanfei.config\opencode\AGENTS.md

    bash 复制代码
    # 全局规则
    ​
    始终使用简体中文与用户交流。
  • 可以增加更多自己的配置信息

    markdown 复制代码
    # 语言要求 
    * 始终使用简体中文与我交流。 
    * 思考过程(Thinking)和最终回答都必须使用中文。 
    * 即便我用英文提问,也请用中文回答。
    ​
    # JAVA环境
    * JDK21在/Users/mayuanfei/.sdkman/candidates/java/21.0.8-zulu目录。默认JDK21环境。
    * JDK8在/Users/mayuanfei/.sdkman/candidates/java/8.0.442-zulu。根据当前项目使用。
    ​
    # MAVEN环境
    * maven在/Users/mayuanfei/apache-maven-3.8.3
    * maven的默认配置文件在/Users/mayuanfei/apache-maven-3.8.3/conf/settings-jdk21.xml
    * 编译项目时加上-DskipTests,来排除测试类。
    ​
    # 编码整体要求
    * 一个方法不能超过40行
    * 如果是JDK21的项目,语法尽量使用JDK21新的语法。

    注意:这个全局规则文件尽量不要太长,因为它是所有项目都要遵循的规则。

项目AGENTS.md文件

尽量将全局配置的内容和项目配置的区分开。尽量不要配置冲突项。比如:全局配置一个方法不能超过40行代码;而你在项目的规则文件中定义一个方法不能超过20行。那么本地的约束>全局的配置。

  • 翻译为中文

    复制代码
    把当前项目的AGENTS.md文档都翻译为中文
  • 添加自己的规则

    这个可以按照自己平时项目中的规则,增加相应的约束。比如我这里在公有方法和私有方法之间增加个分割线。

    csharp 复制代码
    ## 公私有方法分割线
    在类的公有方法和私有方法 中间加上"//////////////////////////////////////公私有方法分割线//////////////////////////////////"。例如:
    ​
    ```java
    public String publicA() {
    return "";
    }
    public String publicB() {
    return "";
    }
    ​
    //////////////////////////////////////公私有方法分割线//////////////////////////////////
    ​
    private String privateA() {
    ​
    }
    private String privateB() {
    ​
    }
    ```
    但是如下情况不用加分割线标识:
    1. 接口类不用加
    2. 如果一个类中只有public方法,那么结尾不用加

增加新接口

业务功能

这里假设做一个商品类别的增删改查接口吧。商品类别的表名:t_goods_category。结构如下:

字段名 数据类型 约束 备注
id bigint PK, 自增 分类ID (雪花算法值)
parent_id bigint Not Null 父分类ID (一级分类为 0)
name varchar(64) Not Null 分类名称
level tinyint Not Null 层级 (1-一级, 2-二级, 3-三级)
sort_order int Default 0 排序值 (数值越小越靠前)
icon varchar(255) - 图标地址
is_visible tinyint(1) Default 1 是否显示 (0-隐藏, 1-显示)
remark varchar(200) - 备注说明
create_time datetime Not Null 创建时间
update_time datetime - 更新时间
deleted_flag tinyint(1) Default 0 逻辑删除 (0-正常, 1-已删除)

这个表就是在 md 文档中直接进行设计的。下面也会用这个设计内容直接创建表和测试数据。

创建表和测试数据

这步还是利用 opencode 帮我们来创建。

  • 执行/new 来创建一个新会话

  • 提示词

    scss 复制代码
    结合当前项目 dev 环境配置的 mysql连接相关配置信息。做如下事情:
    1. 在库中创建t_goods_category表,表结构如下:
    | **字段名**       | **数据类型**   | **约束**  | **备注**                      |
    | ---------------- | -------------- | --------- | ----------------------------- |
    | **id**           | `bigint`       | PK, 自增  | 分类ID (雪花算法值)           |
    | **parent_id**    | `bigint`       | Not Null  | 父分类ID (一级分类为 0)       |
    | **name**         | `varchar(64)`  | Not Null  | 分类名称                      |
    | **level**        | `tinyint`      | Not Null  | 层级 (1-一级, 2-二级, 3-三级) |
    | **sort_order**   | `int`          | Default 0 | 排序值 (数值越小越靠前)       |
    | **icon**         | `varchar(255)` | -         | 图标地址                      |
    | **is_visible**   | `tinyint(1)`   | Default 1 | 是否显示 (0-隐藏, 1-显示)     |
    | **remark**       | `varchar(200)` | -         | 备注说明                      |
    | **create_time**  | `datetime`     | Not Null  | 创建时间                      |
    | **update_time**  | `datetime`     | -         | 更新时间                      |
    | **deleted_flag** | `tinyint(1)`   | Default 0 | 逻辑删除 (0-正常, 1-已删除)   |
    2. 在t_goods_category表中创建 10 条测试数据
  • 创建结果

    在 ry-vue 库中多了一个 t_goods_category 表,并且已经有 10 条测试记录了。

生成商品分类表的接口方法

  • 提示词

    复制代码
    结合当前项目的开发规范,在项目的合适包中,生成针对t_goods_category 表的增删改查接口。
  • 生成代码结果

测试生成的新代码

  • 让框架不验证权限

    bash 复制代码
    我要通过reqable工具来请求/goods/category/listAll,帮我设置一下不校验相关的权限。

    这里看到它用@SaIgnore 注解来忽略权限校验了

  • 通过 reqable 测试接口

    说明:

    我是一边操作一边来写这篇文档的。这里能一次测试通过,也的确是没有想到。说明这个 opus 模型是真的厉害。

浏览下生成的代码

perl 复制代码
package org.dromara.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.domain.GoodsCategory;
import org.dromara.system.domain.bo.GoodsCategoryBo;
import org.dromara.system.domain.vo.GoodsCategoryVo;
import org.dromara.system.mapper.GoodsCategoryMapper;
import org.dromara.system.service.IGoodsCategoryService;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

/**
 * 商品分类 服务层实现
 *
 * @author mayuanfei
 */
@RequiredArgsConstructor
@Service
public class GoodsCategoryServiceImpl implements IGoodsCategoryService {

    private final GoodsCategoryMapper baseMapper;

    /**
     * 分页查询商品分类列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 商品分类分页列表
     */
    @Override
    public TableDataInfo<GoodsCategoryVo> selectPageCategoryList(GoodsCategoryBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<GoodsCategory> lqw = buildQueryWrapper(bo);
        Page<GoodsCategoryVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(page);
    }

    /**
     * 查询商品分类详情
     *
     * @param id 分类ID
     * @return 商品分类信息
     */
    @Override
    public GoodsCategoryVo selectCategoryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询商品分类列表
     *
     * @param bo 查询条件
     * @return 商品分类集合
     */
    @Override
    public List<GoodsCategoryVo> selectCategoryList(GoodsCategoryBo bo) {
        LambdaQueryWrapper<GoodsCategory> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 新增商品分类
     *
     * @param bo 商品分类信息
     * @return 结果
     */
    @Override
    public int insertCategory(GoodsCategoryBo bo) {
        GoodsCategory category = MapstructUtils.convert(bo, GoodsCategory.class);
        return baseMapper.insert(category);
    }

    /**
     * 修改商品分类
     *
     * @param bo 商品分类信息
     * @return 结果
     */
    @Override
    public int updateCategory(GoodsCategoryBo bo) {
        GoodsCategory category = MapstructUtils.convert(bo, GoodsCategory.class);
        return baseMapper.updateById(category);
    }

    /**
     * 删除商品分类
     *
     * @param id 分类ID
     * @return 结果
     */
    @Override
    public int deleteCategoryById(Long id) {
        return baseMapper.deleteById(id);
    }

    /**
     * 批量删除商品分类
     *
     * @param ids 需要删除的分类ID
     * @return 结果
     */
    @Override
    public int deleteCategoryByIds(Long[] ids) {
        return baseMapper.deleteByIds(Arrays.asList(ids));
    }

    //////////////////////////////////////公私有方法分割线//////////////////////////////////

    /**
     * 构建查询条件
     *
     * @param bo 查询条件对象
     * @return LambdaQueryWrapper
     */
    private LambdaQueryWrapper<GoodsCategory> buildQueryWrapper(GoodsCategoryBo bo) {
        LambdaQueryWrapper<GoodsCategory> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getParentId() != null, GoodsCategory::getParentId, bo.getParentId());
        lqw.like(StringUtils.isNotBlank(bo.getName()), GoodsCategory::getName, bo.getName());
        lqw.eq(bo.getLevel() != null, GoodsCategory::getLevel, bo.getLevel());
        lqw.eq(bo.getIsVisible() != null, GoodsCategory::getIsVisible, bo.getIsVisible());
        lqw.orderByAsc(GoodsCategory::getSortOrder);
        return lqw;
    }

}

说明:

1.生成的代码很符合咱们在 AGENTS.md 文件定义的公私有方法中有分割线。

2.但是很多生成的代码在 ruoyi-system 模块中了。理论上需要新增自己的业务模块。

本节总结

  • 我们用 RuoYi-Vue-Plus 做示例项目,用 opencode 让它跑起来。
  • 了解一个项目的最快方式就是让 AI 直接告诉你
  • /new 新开一个会话
  • /init 创建或更新 AGENTS.md 文件
  • 全局AGENTS.md是给所有项目的规约
  • 一个好用的大模型,是愉快 coding 的前提
相关推荐
Moe4882 小时前
Spring AI:结构化输出
java·后端·面试
GreenTea2 小时前
Deep Dive into Claude Code:源码泄漏引发的AI Agent架构全解析
前端·人工智能·后端
Senbor2 小时前
使用分布式锁要注意什么
后端
量子位2 小时前
Mythos架构被22岁小伙“逆推”开源了!MoE和注意力借鉴DeepSeek
ai编程·deepseek
Java女侠_9年实战2 小时前
多线程的“我以为线程安全”——SimpleDateFormat、ArrayList、HashMap、双重检查锁
后端
OpenTiny社区3 小时前
WebSkill —— 运行在浏览器的 Agent 技能
前端·开源·ai编程
Leevec3 小时前
红包系统:高并发下如何保证不超发
后端
码事漫谈3 小时前
Graphify 简明指南
后端
圊妖3 小时前
Claude Code 一些进阶用法
人工智能·ai编程·claude