*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.*/public class UmsMemberServiceImpl implements UmsMemberService {private static final Logger LOGGER = LoggerFactory.getLogger(UmsMemberServiceImpl.class);private PasswordEncoder passwordEncoder;private JwtTokenUtil jwtTokenUtil;private UmsMemberMapper memberMapper;private UmsMemberLevelMapper memberLevelMapper;private UmsMemberCacheService memberCacheService;private String redisKeyAuthCode;private Long authCodeExpire;/*** 根据用户名查询用户**/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;}public UmsMember getById(Long id) {return memberMapper.selectByPrimaryKey(id);}/*** 用户注册**/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);}/*** 生成验证码**/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();}/*** 修改密码**/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());}public UmsMember getCurrentMember() {Authentication auth = SecurityContextHolder.getContext().getAuthentication();MemberDetails memberDetails = (MemberDetails) auth.getPrincipal();return memberDetails.getUmsMember();}public void updateIntegration(Long id, Integer integration) {UmsMember record = new UmsMember();record.setId(id);record.setIntegration(integration);memberMapper.updateByPrimaryKeySelective(record);memberCacheService.delMember(id);}/*** 加载用户信息**/public UserDetails loadUserByUsername(String username) {UmsMember member = getByUsername(username);if (member != null) {return new MemberDetails(member);}throw new UsernameNotFoundException("用户不存在");}/*** 用户登录**/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;}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 配置规则后,评审的"精准蜕变"
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中的规定做到精准匹配。针对如下
针对技术框架,规则明确 Spring Boot、MyBatis 版本兼容及使用规范,评审时直接校验代码对框架的遵循度,像依赖是否适配 Spring Boot 2.7.x ,MyBatis 是否混用其他 ORM 框架。
代码风格遵循开发手册,类名、方法名、常量命名规范,Javadoc 注释要求,都让编辑器自动对齐标准,代码评审无需反复强调基础规范,把精力留给复杂逻辑评审。
(二)测试质量强管控
测试与质量规则要求每个 Service 单元测试覆盖率≥80% ,新增功能有集成测试。智能编辑器评审时,不仅看功能代码,还校验测试用例覆盖。如对UmsMemberServiceImpl,检查到了login、register等关键方法的测试用例缺失,可以督促补全。历史代码也能通过规则回溯测试短板,推动整体质量提升。
结合规则(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代码:
("修改会员信息")(value = "/update", method = RequestMethod.POST)public CommonResult update( String username,String password,String telephone,String authCode) {memberService.update(username, password, telephone, authCode);return CommonResult.success(null,"会员信息修改成功");}
生成Service代码:
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 {private UmsMemberMapper memberMapper;private UmsMemberCacheService memberCacheService;private PasswordEncoder passwordEncoder;private UmsMemberServiceImpl memberService;void setUp() {MockitoAnnotations.openMocks(this);}void update_WithInvalidAuthCodeLength_ShouldThrowException() {// ArrangeString username = "testUser";String password = "newPassword";String telephone = "13800138000";String invalidAuthCode = "12345"; // 5位验证码// Act & AssertassertThrows(IllegalArgumentException.class, () -> {memberService.update(username, password, telephone, invalidAuthCode);});}void update_WithInvalidAuthCode_ShouldThrowException() {// ArrangeString username = "testUser";String password = "newPassword";String telephone = "13800138000";String authCode = "123456";when(memberCacheService.getAuthCode(telephone)).thenReturn("654321");// Act & AssertassertThrows(IllegalArgumentException.class, () -> {memberService.update(username, password, telephone, authCode);});}void update_WithNonExistingUser_ShouldThrowException() {// ArrangeString 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 & AssertassertThrows(IllegalArgumentException.class, () -> {memberService.update(username, password, telephone, authCode);});}void update_WithValidParameters_ShouldUpdateMember() {// ArrangeString 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");// ActmemberService.update(username, password, telephone, authCode);// Assertverify(memberMapper).updateByPrimaryKeySelective(member);verify(memberCacheService).delMember(member.getId());assertEquals(telephone, member.getPhone());assertEquals("encodedPassword", member.getPassword());}void update_WithoutPassword_ShouldNotUpdatePassword() {// ArrangeString 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));// ActmemberService.update(username, null, telephone, authCode);// Assertverify(memberMapper).updateByPrimaryKeySelective(member);assertEquals("oldPassword", member.getPassword());}}
可以看到,智能体生成的代码严格遵守了rules中"- 所有公共方法必须有 **Javadoc 注释**。",而且打印日志的时候没有直接打印出用户名,而是拿用户名去反查用户id,遵守rules中的"- 日志中严禁输出用户隐私信息(如手机号、身份证号、邮箱等)。",并且遵守规范生成了对应的单测
结语
若由个人独立实现"修改会员信息"功能,从梳理需求,到编写代码,再到自测验证,通常需要 1~2 小时(过程中需花费大量时间回忆 / 查阅命名规范、安全编码细节、日志使用要求等,还要手动编写验证码校验、通过用户名查用户 ID 以避免日志泄露隐私等逻辑)。而借助结合规则的智能编程助手,只需提供接口信息、规则要求及简单提示,助手就能快速生成符合所有规范的 Controller 与 Service 代码和测试用例,仅需 5~10 分钟即可完成基础代码产出,后续只需少量调整。
若个人进行代码评审,通常需要 30~45 分钟(尤其是面对历史代码或复杂类时,需投入更多精力确认每一项规范的契合度)。而智能编程助手结合规则评审时,能快速扫描代码,依据预设规则(技术框架约束、安全合规硬检查、代码风格自动校验等),直接列出违规行和具体规范,仅需 3~5 分钟就能输出结构化评审结果。
在代码评审上,它精准识别问题,减轻高阶开发负担,助力初中级开发者成长,还能治理历史代码;在代码生成上,它严格遵循规范,产出优质代码,减少开发者重复编码工作量,降低出错概率。无论是评审还是生成,都让开发流程更高效、代码质量更可靠,为团队在快速交付与高质量产出之间,搭建起坚实的桥梁,推动软件开发迈向更智能、更规范的新阶段。
往期内容 点击直达
点击“阅读原文”,进入iFlyCode官网





