大数跨境
0
0

【iFlyCode-Rules】智能体 + 规则配置(rules):释放开发效能,重塑代码评审和代码生成

【iFlyCode-Rules】智能体 + 规则配置(rules):释放开发效能,重塑代码评审和代码生成 星火飞码 iFlyCode
2025-11-28
3
导读:让团队高质量产出、快速交付
图片
*本期内容源于开发伙伴真实案例投稿,也非常欢迎更多小伙伴,添加下方官方企微投稿,被选中即有机会获得最高2000元实物奖品!(GoPro运动相机、科大讯飞无线耳机、腰部按摩仪、iFlyCode周边大礼包奖品任选)
图片

*iFlyCode官方企微

— 以下为投稿正文 —

在软件开发流程里,代码评审是保障质量的关键环节,却常让高阶开发陷入繁琐,初中级开发者也因经验不足,容易留下隐患。当智能体遇上自定义规则(rules),这场代码评审的变革,正为团队效能与代码质量带来质的飞跃。

图片


背景介绍

1. 工具介绍

关键词:iFlyCode Rules规则

iFlyCode版本号:V3.5.1002

IDE版本号:IntelliJ IDEA 2024.2.0.1

模型:DeepSeek-V3-0324

对话模式:智能体

2. 任务需求

2.1 针对如下的代码,进行评审,评审出不符合规范的代码

代码详情:

package com.macro.mall.portal.service;import cn.hutool.core.util.StrUtil;import com.macro.mall.common.exception.Asserts;import com.macro.mall.mapper.UmsMemberLevelMapper;import com.macro.mall.mapper.UmsMemberMapper;import com.macro.mall.model.UmsMember;import com.macro.mall.model.UmsMemberExample;import com.macro.mall.model.UmsMemberLevel;import com.macro.mall.model.UmsMemberLevelExample;import com.macro.mall.portal.domain.MemberDetails;import com.macro.mall.security.util.JwtTokenUtil;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import java.util.Date;import java.util.List;import java.util.Random;/** * 会员管理Service实现类 * Created by macro on 2018/8/3. */@Servicepublic class UmsMemberServiceImpl implements UmsMemberService {    private static final Logger LOGGER = LoggerFactory.getLogger(UmsMemberServiceImpl.class);    @Autowired    private PasswordEncoder passwordEncoder;    @Autowired    private JwtTokenUtil jwtTokenUtil;    @Autowired    private UmsMemberMapper memberMapper;    @Autowired    private UmsMemberLevelMapper memberLevelMapper;    @Autowired    private UmsMemberCacheService memberCacheService;    @Value("${redis.key.authCode}")    private String redisKeyAuthCode;    @Value("${redis.expire.authCode}")    private Long authCodeExpire;    /**     * 根据用户名查询用户     *     */    @Override    public UmsMember getByUsername(String username) {        UmsMember member = memberCacheService.getMember(username);        if (member != null) {            return member;        }        UmsMemberExample example = new UmsMemberExample();        example.createCriteria().andUsernameEqualTo(username);        List<UmsMember> memberList = memberMapper.selectByExample(example);        if (!CollectionUtils.isEmpty(memberList)) {            member = memberList.get(0);            memberCacheService.setMember(member);            return member;        }        return null;    }    @Override    public UmsMember getById(Long id) {        return memberMapper.selectByPrimaryKey(id);    }    /**     * 用户注册     *      */    @Override    public void register(String username, String password, String telephone, String authCode) {        if (!verifyAuthCode(authCode, telephone)) {            Asserts.fail("验证码错误");        }        UmsMemberExample example = new UmsMemberExample();        example.createCriteria().andUsernameEqualTo(username);        example.or(example.createCriteria().andPhoneEqualTo(telephone));        List<UmsMember> existMembers = memberMapper.selectByExample(example);        if (!CollectionUtils.isEmpty(existMembers)) {            Asserts.fail("用户名或手机号已被占用");        }        // 创建新用户        UmsMember umsMember = new UmsMember();        umsMember.setUsername(username);        umsMember.setPhone(telephone);        umsMember.setPassword(passwordEncoder.encode(password));        umsMember.setCreateTime(new Date());        umsMember.setStatus(1);        // 获取默认会员等级        UmsMemberLevelExample levelExample = new UmsMemberLevelExample();        levelExample.createCriteria().andDefaultStatusEqualTo(1);        List<UmsMemberLevel> memberLevelList = memberLevelMapper.selectByExample(levelExample);        if (!CollectionUtils.isEmpty(memberLevelList)) {            umsMember.setMemberLevelId(memberLevelList.get(0).getId());        }        //        memberMapper.insert(umsMember);        umsMember.setPassword(null);    }    /**     * 生成验证码     *     */    @Override    public String generateAuthCode(String telephone) {        StringBuilder sb = new StringBuilder();        Random random = new Random();        for (int i = 0; i < 6; i++) {            sb.append(random.nextInt(10));        }        memberCacheService.setAuthCode(telephone, sb.toString());        return sb.toString();    }    /**     * 修改密码     *     */    @Override    public void updatePassword(String telephone, String password, String authCode) {        UmsMemberExample example = new UmsMemberExample();        example.createCriteria().andPhoneEqualTo(telephone);        List<UmsMember> memberList = memberMapper.selectByExample(example);        if (CollectionUtils.isEmpty(memberList)) {            Asserts.fail("该账号不存在");        }        if (!verifyAuthCode(authCode, telephone)) {            Asserts.fail("验证码错误");        }        UmsMember umsMember = memberList.get(0);        umsMember.setPassword(passwordEncoder.encode(password));        memberMapper.updateByPrimaryKeySelective(umsMember);        memberCacheService.delMember(umsMember.getId());    }    @Override    public UmsMember getCurrentMember() {        Authentication auth = SecurityContextHolder.getContext().getAuthentication();        MemberDetails memberDetails = (MemberDetails) auth.getPrincipal();        return memberDetails.getUmsMember();    }    @Override    public void updateIntegration(Long id, Integer integration) {        UmsMember record = new UmsMember();        record.setId(id);        record.setIntegration(integration);        memberMapper.updateByPrimaryKeySelective(record);        memberCacheService.delMember(id);    }    /**     * 加载用户信息     *     */    @Override    public UserDetails loadUserByUsername(String username) {        UmsMember member = getByUsername(username);        if (member != null) {            return new MemberDetails(member);        }        throw new UsernameNotFoundException("用户不存在");    }    /**     * 用户登录     *     */    @Override    public String login(String username, String password) {        String token = null;        try {            UserDetails userDetails = loadUserByUsername(username);            if (!passwordEncoder.matches(password, userDetails.getPassword())) {                // 日志输出用户名,存在信息泄露风险                LOGGER.warn("用户{}登录失败,密码错误", username);                throw new BadCredentialsException("密码不正确");            }            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(                    userDetails, null, userDetails.getAuthorities());            SecurityContextHolder.getContext().setAuthentication(authentication);            token = jwtTokenUtil.generateToken(userDetails);        } catch (AuthenticationException e) {            System.out.println("登录失败" + e);        }        return token;    }    @Override    public String refreshToken(String token) {        return jwtTokenUtil.refreshHeadToken(token);    }    //    private boolean verifyAuthCode(String authCode, String telephone) {        if (StrUtil.isEmpty(authCode)) {            return false;        }        String realAuthCode = memberCacheService.getAuthCode(telephone);        return authCode.equals(realAuthCode);    }}

2.2 基于代码规范,按照需求文档生成代码

需求文档:

我的要求是根据username去修改用户的密码,手机;需要校验传入的验证码是否正确;接口信息如下:## 修改会员信息**接口地址**:`/sso/update`**请求方式**:`POST`**请求数据类型**:`application/x-www-form-urlencoded`**响应数据类型**:`*/*`**接口描述**:**请求参数**:| 参数名称 | 参数说明 | 类型 | schema || -------- | -------- | ----- |----- | |username|用户名|String|||password|密码|String|||telephone|返回数据|String|||authCode|验证码|String||**请求示例**:{        "code": 200,        "message""成功",        "data": null}**响应状态**:| 状态码 | 说明 | schema || -------- | -------- | ----- | |200|OK|CommonResult||**响应参数**:| 参数名称 | 参数说明 | 类型 | schema || -------- | -------- | ----- |----- | |code|返回码|long|long||message|返回消息|string|||data|返回数据|T|任意类型|**响应示例**:```javascript{        "code": 200,        "message""成功",        "data": null}```
图片


代码评审:无规则时,评审的"艰难处境"

1.1 未配置project_rules.md时,评审结果如下

请你对以上代码输出结构化评审意见,要列出违规代码所在的行数和违反的具体规范

可以看到,未配置规则前,智能编辑器对UmsMemberServiceImpl.java的评审,虽能发现安全风险、日志规范等问题,但零散且缺乏针对性,且未评审出如下点

  1. 日志输出敏感信息

  2. 代码规范/注释规范依赖模型通用能力,无法自动固定样式输出

  3. 单测覆盖率要求

1.2 配置规则后,评审的"精准蜕变"

1.2.1 如何配置 rules

rules详情展示

---description:globs: truealwaysApply: true---## 1. 技术框架- 使用 **Java 8** 作为开发语言。- 使用 **Spring Boot 2.7.x** 作为Spring Boot框架。- 数据持久层统一使用 **mybatis 3.5.10**,禁止混用其他 ORM 框架。- 接口遵循 **RESTful API 规范**,返回统一的响应格式(code、message、data)。- 数据库使用 **MySQL 5.7**- 统一依赖管理采用 **Maven**- Spring Boot 版本兼容性:确保依赖项与 Spring Boot 版本兼容。---## 2. 业务逻辑规范- 所有业务逻辑必须封装在 **Service 层**,Controller 不得写复杂逻辑。- 公共逻辑提取到 `common` 模块或工具类,避免重复代码。- 异常必须抛出自定义异常类,并通过全局异常处理器统一处理。---## 3. 代码风格- 严格遵循 **Java 开发手册**(简化版即可)。- 类、接口使用**帕斯卡命名**(例如,UserController、ProductService)。- 变量、方法名使用 **驼峰命名法**- 常量使用大写+下划线(例如 MAX_RETRIES、DEFAULT_TIMEOUT)。- Controller 命名以 `*Controller` 结尾,Service 以 `*Service` 结尾,Mapper 以 `*Mapper` 结尾。- DTO/VO 与 Entity 严格分离,不得在 Controller 中直接返回 Entity。- 所有公共方法必须有 **Javadoc 注释**- 严禁使用 System.out.println 进行日志记录:要使用合适的日志记录框架。---## 4. 安全与合规- 禁止在代码中硬编码密码、密钥等敏感信息,统一存放在配置中心。- SQL 必须使用 **参数绑定**,禁止拼接 SQL 防止注入。- 日志中严禁输出用户隐私信息(如手机号、身份证号、邮箱等)。- 金额计算使用 BigDecimal,禁止 float / double。---## 5. 测试与质量- 每个 Service 必须有单元测试,覆盖率不得低于 **80%**- 所有新增功能必须有对应的集成测试。- 严格遵循 **代码 Review 流程**,不符合规则的代码不得合并。---

1.2.2 引用rules后审核代码

(一)贴合项目架构,评审更贴合要求

引入涵盖技术框架、业务逻辑、代码风格等规则后,智能编辑器输出更加清晰和精准,可以把不符合规范的代码和rules中的规定做到精准匹配。针对如下

  1. 针对技术框架,规则明确 Spring Boot、MyBatis 版本兼容及使用规范,评审时直接校验代码对框架的遵循度,像依赖是否适配 Spring Boot 2.7.x ,MyBatis 是否混用其他 ORM 框架。

  2. 代码风格遵循开发手册,类名、方法名、常量命名规范,Javadoc 注释要求,都让编辑器自动对齐标准,代码评审无需反复强调基础规范,把精力留给复杂逻辑评审。

(二)测试质量强管控

测试与质量规则要求每个 Service 单元测试覆盖率≥80% ,新增功能有集成测试。智能编辑器评审时,不仅看功能代码,还校验测试用例覆盖。如对UmsMemberServiceImpl,检查到了loginregister等关键方法的测试用例缺失,可以督促补全。历史代码也能通过规则回溯测试短板,推动整体质量提升。

图片


结合规则(rules)开发需求代码

(一)需求描述

## 修改会员信息**接口地址**:`/sso/update`**请求方式**:`POST`**请求数据类型**:`application/x-www-form-urlencoded`**响应数据类型**:`*/*`**接口描述**:**请求参数**:| 参数名称 | 参数说明 | 类型 | schema || -------- | -------- | ----- |----- | |username|用户名|String|||password|密码|String|||telephone|返回数据|String|||authCode|验证码|String||**请求示例**:{        "code": 200,        "message""成功",        "data": null}**响应状态**:| 状态码 | 说明 | schema || -------- | -------- | ----- | |200|OK|CommonResult||**响应参数**:| 参数名称 | 参数说明 | 类型 | schema || -------- | -------- | ----- |----- | |code|返回码|long|long||message|返回消息|string|||data|返回数据|T|任意类型|**响应示例**:```javascript{        "code": 200,        "message""成功",        "data": null}```

提示词如下:

根据上面接口信息,完成"修改会员信息"的开发,可以去参考同一个类中"会员注册"完成开发,需要校验验证码长度为6,需要打印一些日志,方便报错定位,请遵守相关rules代码规则

(二)过程参考

*点击查看大图

(三)代码

生成Controller代码:

@ApiOperation("修改会员信息")@RequestMapping(value = "/update", method = RequestMethod.POST)@ResponseBodypublic CommonResult update(@RequestParam String username,                           @RequestParam String password,                           @RequestParam String telephone,                           @RequestParam String authCode) {    memberService.update(username, password, telephone, authCode);    return CommonResult.success(null,"会员信息修改成功");}

生成Service代码:

@Override    public void update(String username, String password, String telephone, String authCode) {        // 验证码长度校验        if (StrUtil.isEmpty(authCode) || authCode.length() != 6) {            LOGGER.warn("验证码长度不正确,用户ID: {}"getUserIdForLog(username));            Asserts.fail("验证码必须为6位");        }        // 验证码校验        if (!verifyAuthCode(authCode, telephone)) {            LOGGER.warn("验证码校验失败,用户ID: {}"getUserIdForLog(username));            Asserts.fail("验证码错误");        }        // 查询会员信息        UmsMember member = getByUsername(username);        if (member == null) {            LOGGER.warn("用户不存在,用户名: {}", username);            Asserts.fail("用户不存在");        }        // 更新会员信息        member.setPhone(telephone);        member.setPassword(passwordEncoder.encode(password));        memberMapper.updateByPrimaryKeySelective(member);        memberCacheService.delMember(member.getId());        LOGGER.info("会员信息更新成功,用户ID: {}", member.getId());    }

生成Mapper代码:

<update id="updateByPrimaryKeySelective" parameterType="com.macro.mall.model.UmsMember">  update ums_member  <set>    <if test="memberLevelId != null">      member_level_id = #{memberLevelId,jdbcType=BIGINT},    </if>    <if test="username != null">      username = #{username,jdbcType=VARCHAR},    </if>    <if test="password != null">      password = #{password,jdbcType=VARCHAR},    </if>    <if test="nickname != null">      nickname = #{nickname,jdbcType=VARCHAR},    </if>    <if test="phone != null">      phone = #{phone,jdbcType=VARCHAR},    </if>    <if test="status != null">      status = #{status,jdbcType=INTEGER},    </if>    <if test="createTime != null">      create_time = #{createTime,jdbcType=TIMESTAMP},    </if>    <if test="icon != null">      icon = #{icon,jdbcType=VARCHAR},    </if>    <if test="gender != null">      gender = #{gender,jdbcType=INTEGER},    </if>    <if test="birthday != null">      birthday = #{birthday,jdbcType=DATE},    </if>    <if test="city != null">      city = #{city,jdbcType=VARCHAR},    </if>    <if test="job != null">      job = #{job,jdbcType=VARCHAR},    </if>    <if test="personalizedSignature != null">      personalized_signature = #{personalizedSignature,jdbcType=VARCHAR},    </if>    <if test="sourceType != null">      source_type = #{sourceType,jdbcType=INTEGER},    </if>    <if test="integration != null">      integration = #{integration,jdbcType=INTEGER},    </if>    <if test="growth != null">      growth = #{growth,jdbcType=INTEGER},    </if>    <if test="luckeyCount != null">      luckey_count = #{luckeyCount,jdbcType=INTEGER},    </if>    <if test="historyIntegration != null">      history_integration = #{historyIntegration,jdbcType=INTEGER},    </if>  </set>  where id = #{id,jdbcType=BIGINT}</update>
package com.macro.mall.portal.service.impl;import com.macro.mall.common.exception.Asserts;import com.macro.mall.mapper.UmsMemberMapper;import com.macro.mall.model.UmsMember;import com.macro.mall.portal.service.UmsMemberCacheService;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.MockitoAnnotations;import org.springframework.security.crypto.password.PasswordEncoder;import java.util.ArrayList;import java.util.List;import static org.junit.jupiter.api.Assertions.*;import static org.mockito.ArgumentMatchers.any;import static org.mockito.Mockito.*;class UmsMemberServiceImplTest {    @Mock    private UmsMemberMapper memberMapper;    @Mock    private UmsMemberCacheService memberCacheService;    @Mock    private PasswordEncoder passwordEncoder;    @InjectMocks    private UmsMemberServiceImpl memberService;    @BeforeEach    void setUp() {        MockitoAnnotations.openMocks(this);    }    @Test    void update_WithInvalidAuthCodeLength_ShouldThrowException() {        // Arrange        String username = "testUser";        String password = "newPassword";        String telephone = "13800138000";        String invalidAuthCode = "12345"// 5位验证码        // Act & Assert        assertThrows(IllegalArgumentException.class, () -> {            memberService.update(username, password, telephone, invalidAuthCode);        });    }    @Test    void update_WithInvalidAuthCode_ShouldThrowException() {        // Arrange        String username = "testUser";        String password = "newPassword";        String telephone = "13800138000";        String authCode = "123456";        when(memberCacheService.getAuthCode(telephone)).thenReturn("654321");        // Act & Assert        assertThrows(IllegalArgumentException.class, () -> {            memberService.update(username, password, telephone, authCode);        });    }    @Test    void update_WithNonExistingUser_ShouldThrowException() {        // Arrange        String username = "nonExistingUser";        String password = "newPassword";        String telephone = "13800138000";        String authCode = "123456";        when(memberCacheService.getAuthCode(telephone)).thenReturn(authCode);        when(memberMapper.selectByExample(any())).thenReturn(new ArrayList<>());        // Act & Assert        assertThrows(IllegalArgumentException.class, () -> {            memberService.update(username, password, telephone, authCode);        });    }    @Test    void update_WithValidParameters_ShouldUpdateMember() {        // Arrange        String username = "testUser";        String password = "newPassword";        String telephone = "13800138000";        String authCode = "123456";        UmsMember member = new UmsMember();        member.setUsername(username);        member.setPhone("oldPhone");        member.setPassword("oldPassword");        when(memberCacheService.getAuthCode(telephone)).thenReturn(authCode);        when(memberMapper.selectByExample(any())).thenReturn(List.of(member));        when(passwordEncoder.encode(password)).thenReturn("encodedPassword");        // Act        memberService.update(username, password, telephone, authCode);        // Assert        verify(memberMapper).updateByPrimaryKeySelective(member);        verify(memberCacheService).delMember(member.getId());        assertEquals(telephone, member.getPhone());        assertEquals("encodedPassword", member.getPassword());    }    @Test    void update_WithoutPassword_ShouldNotUpdatePassword() {        // Arrange        String username = "testUser";        String telephone = "13800138000";        String authCode = "123456";        UmsMember member = new UmsMember();        member.setUsername(username);        member.setPhone("oldPhone");        member.setPassword("oldPassword");        when(memberCacheService.getAuthCode(telephone)).thenReturn(authCode);        when(memberMapper.selectByExample(any())).thenReturn(List.of(member));        // Act        memberService.update(username, null, telephone, authCode);        // Assert        verify(memberMapper).updateByPrimaryKeySelective(member);        assertEquals("oldPassword", member.getPassword());    }}

可以看到,智能体生成的代码严格遵守了rules中"- 所有公共方法必须有 **Javadoc 注释**。",而且打印日志的时候没有直接打印出用户名,而是拿用户名去反查用户id,遵守rules中的"- 日志中严禁输出用户隐私信息(如手机号、身份证号、邮箱等)。",并且遵守规范生成了对应的单测

图片


结语

若由个人独立实现"修改会员信息"功能,从梳理需求,到编写代码,再到自测验证,通常需要 1~2 小时(过程中需花费大量时间回忆 / 查阅命名规范、安全编码细节、日志使用要求等,还要手动编写验证码校验、通过用户名查用户 ID 以避免日志泄露隐私等逻辑)。而借助结合规则的智能编程助手,只需提供接口信息、规则要求及简单提示,助手就能快速生成符合所有规范的 Controller 与 Service 代码和测试用例,仅需 5~10 分钟即可完成基础代码产出,后续只需少量调整

若个人进行代码评审,通常需要 30~45 分钟(尤其是面对历史代码或复杂类时,需投入更多精力确认每一项规范的契合度)。而智能编程助手结合规则评审时,能快速扫描代码,依据预设规则(技术框架约束、安全合规硬检查、代码风格自动校验等),直接列出违规行和具体规范,仅需 3~5 分钟就能输出结构化评审结果

在代码评审上,它精准识别问题,减轻高阶开发负担,助力初中级开发者成长,还能治理历史代码;在代码生成上,它严格遵循规范,产出优质代码,减少开发者重复编码工作量,降低出错概率。无论是评审还是生成,都让开发流程更高效、代码质量更可靠,为团队在快速交付与高质量产出之间,搭建起坚实的桥梁,推动软件开发迈向更智能、更规范的新阶段。

— END —
图片

往期内容 点击直达

图片
图片


图片

图片点击“阅读原文”,进入iFlyCode官网

【声明】内容源于网络
0
0
星火飞码 iFlyCode
星火飞码iFlyCode是由科大讯飞自主研发的智能编程助手,它基于先进的星火认知大模型,旨在降低开发门槛,提升软件开发效率,让“编程更轻松,创意更自由”。
内容 87
粉丝 0
星火飞码 iFlyCode 星火飞码iFlyCode是由科大讯飞自主研发的智能编程助手,它基于先进的星火认知大模型,旨在降低开发门槛,提升软件开发效率,让“编程更轻松,创意更自由”。
总阅读21
粉丝0
内容87