AI真正帮我干好的事儿


记录我用AI工具实际解决的问题,记录尝试的过程,每个案例都是真实经历。

一、开发调试

作为开发人员,日常工作中最多的问题就是排查和解决Bug。AI工具在这方面的帮助超出我的预期。

1.1 解决BUG

案例1:Jar冲突导致Word导出失败

问题描述:执行Word模板填充功能时,使用EasyPoi库导出Word文档失败,抛出 NoSuchMethodError异常。

错误日志

1
2
3
Exception in thread "main" java.lang.NoSuchMethodError: org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth.getW()Ljava/math/BigInteger;
at cn.afterturn.easypoi.util.PoiWordStyleUtil.copyCellAndSetValue(PoiWordStyleUtil.java:34)
at cn.afterturn.easypoi.word.parse.excel.ExcelMapParse.parseNextRowAndAddRow(ExcelMapParse.java:131)

提示词:程序启动报错 [Copy 10+] , 请修复。

AI分析错误日志,定位是Apache POI版本冲突问题,查看pom.xml中相关依赖版本,锁定 openxmlformats库版本不兼容,排除冲突依赖,统一定义版本

当天发了个朋友圈:

1
2
3
4
5
6
7
一个jar 冲突 的 bug  自己研究一小时,没解决。

同样的提示词
opencode(GML) 半小时没解决,还反复在哪试错,耗我token。
codex(GPT) 3分钟解决,一次命中。

看了他们的思维链,冷静的可怕。有时候得反思向Agent学习了,真是教会了徒弟饿死了师傅。



1.2 代码审查

案例:Review代码指出问题

场景:开发完成代码后,让AI进行代码审查,发现潜在问题。

提示词:review vxlink-bill-server 项目下的 feature-workingday 分支代码

<think>现在我已经获取了完整的代码差异,可以进行代码审查了。让我分析一下这个分支的变更内容。
</think>

现在我来对代码进行详细的 review:


📋 Code Review: feature-workingday 分支

一、概述

该分支主要实现了工作日服务功能,包括:

  • 新增 WorkDayService 接口及其实现
  • 新增 Redis 缓存枚举 RedisKeyPrefixEnum
  • 提供获取当月/下月第一个工作日的功能
  • 使用 Redis 缓存优化查询性能

二、变更文件清单

文件 变更类型 说明
RedisKeyPrefixEnum.java 新增 Redis 缓存 Key 前缀枚举
WorkDayService.java 新增 工作日服务接口
WorkDayServiceImpl.java 新增 工作日服务实现

三、代码质量分析

✅ 做得好的地方

  1. 接口设计清晰 - WorkDayService 接口职责单一,只提供两个方法
  2. 使用缓存优化 - 实现了 Redis 缓存逻辑,减少 RPC 调用
  3. 使用函数式编程 - 使用 Supplier 封装查询逻辑,代码简洁
  4. 日志记录完善 - 有适当的 debug/info 日志

⚠️ 需要改进的问题

1. 缓存逻辑存在 Bug

1
2
3
// 第 55-56 行
String cachedValue = null != redisTemplate.opsForValue().get(key)
? redisTemplate.opsForValue().get(key).toString() : null;

问题:对同一个 key 调用了两次 opsForValue().get(key),应该只调用一次然后判断结果。

1
2
3
// 建议修改为:
Object cachedObj = redisTemplate.opsForValue().get(key);
String cachedValue = cachedObj != null ? cachedObj.toString() : null;

2. 日期处理逻辑有误

1
2
// getCurrentMonthFirstWorkDay() 方法
Date nextWorkDay = calendarRpc.getNextWorkDay(now);
  • 方法名是获取"当月第一个工作日",但调用的是 getNextWorkDay(now)
  • 这实际上是获取下一个工作日,而非当月第一个工作日

3. 缓存 Key 缺少月份区分(潜在问题)

1
2
// 下月工作日缓存 Key
String nextMonthWorkdayKey = RedisKeyPrefixEnum.WORK_DAY.key("nextMonthWorkingday") + ":" + DateUtil.format(now, >DateFormatEnum.DASH_yyyy_MM.getCode());
  • 当前只使用当前月份作为 key 的一部分
  • 但下月第一个工作日应该跨月时需要考虑缓存失效问题
  • 例如:4月28日缓存的"下月第一个工作日"应该是5月1日,但到5月1日时缓存仍然有效

4. Redis 缓存有效期设置不合理

1
redisTemplate.opsForValue().set(key, workDay, 24, TimeUnit.HOURS);
  • 注释说"24 小时",但代码实际设置的是 24 * 30 = 720 小时(因为第一个参数是值,第二个是数量)
  • 应该明确是 24 小时还是更长的有效期

5. 缺少空值处理

  • 如果 RPC 调用返回 null 或抛出异常,没有对应的容错处理

6. 不必要的导入

1
import org.springframework.test.context.TestConstructor;

这个测试框架的导入不应该出现在生产代码中。


四、安全与规范检查

检查项 状态 说明
敏感信息 ✅ 通过 无敏感信息硬编码
空指针风险 ⚠️ 需注意 RPC 返回值未做空检查
日志规范 ⚠️ 需注意 info 日志过于频繁,适合改为 debug
异常处理 ❌ 缺失 缺少 try-catch 保护

五、建议修复优先级

优先级 问题 修复建议
🔴 高 缓存重复查询 优化为单次查询
🔴 高 缓存有效期单位 确认 24 小时语义
🟡 中 日期逻辑错误 确认业务需求,修正方法调用
🟡 中 缺少异常处理 添加 try-catch 和降级逻辑
🟢 低 删除无用导入 移除 TestConstructor

六、总结

该分支实现了基础的工作日查询功能,整体代码结构清晰,但在缓存实现细节业务逻辑准确性上存在几个需要修复的问题。建议在合并前修复上述高优先级问题,以确保功能正确和性能最优。


1.3 生成单元测试

案例:生成测试用例

场景:写完业务代码,需要补充单元测试。或者要改动一个功能,先生成单元测试,然后让AI改动,改动后运行测试用例,确保功能正常后,再补充新增代码的单元测试。

案例:生成单元测试

场景:写完业务代码,需要补充单元测试。或者要改动一个功能,先生成单元测试,然后在让AI改动,改动后运行测试用例,确保功能正常后,在补充新增代码的单元测试。

提示词:com.vxlink.repay.ext.util.IdCardExtractor#extract 帮我生成单元测试。

后续会让AI填充测试数据,运行测试验证。


二、编码辅助

2.1 写SQL

案例:查询缺失数据的资产包

业务需求:查询资产包表,关键字段为空的记录。

提示词:请帮我写SQL,查询资产包表 [Copy 10+ 资产包表的DDL]中,关键字段为空的记录。关键字如下
转让签约日期、债转通知公告日期、成交金额、基准日债权总额、基准日债权本金、债转协议名称、债转协议编号

<think>用户想查询资产包表中关键字段为空的记录。关键字段包括:

  1. 转让签约日期 (transfer_signing_date)
  2. 债转通知公告日期 (debt_transfer_notice_time)
  3. 成交金额 (transaction_price)
  4. 基准日债权总额 (compute_total_amt)
  5. 基准日债权本金 (compute_total_principal_amt)
  6. 债转协议名称 (asset_agreement_name)
  7. 债转协议编号 (asset_agreement_code)

这是一个简单的查询任务,我可以直接写出SQL语句。用户说的是"关键字段为空的记录",应该是指这些字段中任意一个为空,或者可能需要同时满足多个为空。

我先写一个查询这些字段为空的SQL,使用 OR 条件连接,这样只要任意一个关键字段为空就会被查询出来。
</think>

根据您提供的表结构和关键字段,我为您编写查询这些字段为空的记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SELECT 
id,
asset_code,
asset_name,
project_name,
transfer_signing_date,
debt_transfer_notice_time,
transaction_price,
compute_total_amt,
compute_total_principal_amt,
asset_agreement_name,
asset_agreement_code,
asset_status,
created_date
FROM pm_asset
WHERE is_deleted = 0
AND (
transfer_signing_date IS NULL
OR debt_transfer_notice_time IS NULL
OR transaction_price IS NULL
OR compute_total_amt IS NULL
OR compute_total_principal_amt IS NULL
OR asset_agreement_name IS NULL
OR asset_agreement_code IS NULL
)
ORDER BY created_date DESC;

说明:

  • 使用 is_deleted = 0 过滤掉已删除的记录
  • 使用 OR 连接,只要任意一个关键字段为空就会被查询出来
  • 如需查询所有关键字段都为空的记录,将 OR 改为 AND

需要我帮您执行这个查询吗?

如果你让他执行 ,他会反问你连接信息。当然到这一步,我么就不要再执行了。


2.2 变量命名

案例:摆脱命名困难症

痛点:经常为一个变量名纠结半天,真正做到了见名知意。

解决: AI 生成的代码命名非常规范。像xxxPO再也没有了(非驼峰命名,idea下划线提示,但这玩意也不能在团队间规约,因为很多人这么写)

再也不用忍受这样的命名了。


2.3 写正则表达式

案例:提取身份证号

场景:从文本中提取所有身份证号。

提示词: 我需要从一下备注 [Copy 10+ 银行流水表中的备注] 中尽可能的提取身份证号。帮我写一个用于提取用户姓名、身份证号的的方法,用Java实现。


三、脚本工具

3.1 启动服务

问题描述:前端启动失败,不知道原因,也不关心原因

提示词:启动服务


3.2 打开本地应用

案例:一键启动开发环境

场景:每天早上需要打开多个应用(IDE、数据库工具、终端、浏览器)。

解决:让AI帮我写一个打开所有应用的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

# 一键打开常用开发工具
# 使用方法: ./open-dev-tools.sh

echo "正在打开开发工具..."

# 打开微信
open -a "WeChat"
echo "✓ 微信"

# 打开 IntelliJ IDEA
open -a "IntelliJ IDEA"
echo "✓ IntelliJ IDEA"

# 打开 Chrome
open -a "Google Chrome"
echo "✓ Chrome"

# 打开 VSCode
open -a "Visual Studio Code"
echo "✓ VSCode"

echo ""
echo "所有应用已启动完成!"

commond 中这么写

1
2
3
4
cat cs-devtools.md
# 一键打开常用开发工具

执行脚本~/.config/opencode/scripts/vx-dev-tools.sh 打开常用的开发工具

案例:我想听歌

1
2
3
4
5
6
7
cat cs-music.md
# 打开网易云音乐

直接执行以下命令打开本地网易云音乐:

```bash
open /Applications/NetEaseMusic.app

注意 opencode 不会自动发现commond 但是 claude code 默认会自动发现,除非明确禁止。我认为commond应该是命令不开启的,只维护自己的明确的指令,防止注意力分散。


3.3 Git批量操作

案例:批量推送项目更新

场景:需要同时更新14个git仓库。需要每一个项目都新建一个分支,都添加一个 Agent.md 文件,然后推送到远程仓库。在发起合并请求的PR。

结果

  • 成功推送:14个项目 ✅
  • 已更新:2个项目 ⏭️
  • 有冲突:1个项目 ⚠️(需要手动合并)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
成功推送的项目 ✅
- vxlink-apihub-server
- vxlink-archive-server
- vxlink-asset-server
- vxlink-bill-server
- vxlink-business-agent-server
- vxlink-case-server
- vxlink-channel
- vxlink-common
- vxlink-credit-server
- vxlink-minioa-server
- vxlink-mobile-server
- vxlink-payment-server
- vxlink-registration-server
- vxlink-repay-server
- vxlink-restructure

3.4 写Shell脚本

案例:写爬虫

1
2
3
4
5
6
7
@/Users/chenshang/Work/ShellScript/Crawler/PingAn/Controller/ArchiveController.sh 

第89行 result 的返回结构如下:

{"returnStatus":"S","returnCode":"000000","errorSeqNo":null,"returnMessage":"交易成功","data":{"fileSeq":null,"fileName":"bhzx147-20260420230837.zip","uploadTime":null,"udmpDocId":"UDMP-880c4fbd8750f26a9efa323ffad04e188fad33c045-7bf2ffd9-20260420150837-00000001","udmpFileUrl":"https://udmp-bucket.pabos.com.cn/7bf2ffd9/UDMP-880c4fbd8750f26a9efa323ffad04e188fad33c045-7bf2ffd9-20260420150837-00000001/1?attname=bhzx147-20260420230837.zip&e=1776698317&token=XiuoDEpawTR8CupvQ1qwN7BIFo3rSjjyRX2R2MSk:TC4mIxxMhkEt7r1qHsO2HFUxH7o="}}

现在需要拿到url下载下来文件。帮我补全修复这个脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<think>用户想让我修复这个脚本,从第89行的result中提取URL并下载文件。

从返回的JSON结构来看:
- result 是JSON字符串
- 需要从 `result.returnMessage` 提取错误信息
- 需要从 `result.data.udmpFileUrl` 提取下载URL

让我先看看当前脚本的逻辑,然后修复它。

当前的代码:
```bash
# 批量下载
local result=$(batchFilesDownload "${projectId}" "${fileManageDtoList}")
log_info "result=${result}"

这里 result 已经包含了下载结果,包括 udmpFileUrl。现在需要:

  1. 从result中提取udmpFileUrl
  2. 使用这个URL下载文件

让我看看现有的batchFilesDownload函数做了什么,以及是否有其他下载相关的函数。

我需要先搜索相关文件来了解现有的函数。
</think>

我来帮你分析并修复这个脚本。首先让我了解一下相关的服务函数。

<think>让我看看PersonalLoanArchive.sh中的batchFilesDownload函数。
</think>

<think>让我看看batchFilesDownload函数的完整实现,以及是否有其他下载相关的函数。
</think>

<think>我明白了。batchFilesDownload 函数调用API获取文件信息,返回的结果中包含 udmpFileUrl。但是现在看起来,API返回的是一个包含文件URL的结果,而不是直接下载文件。

用户的需求是:从result中提取udmpFileUrl,然后用这个URL下载文件。

让我修复ArchiveController.sh的代码。从第87行开始,需要:

  1. 提取udmpFileUrl
  2. 用curl下载文件

看代码,当前逻辑:

1
local result=$(batchFilesDownload "${projectId}" "${fileManageDtoList}")

result返回的是:

1
{"returnStatus":"S","returnCode":"000","errorSeqNo":null,"returnMessage":"交易成功","data":{"fileSeq":null,"fileName":"bhzx147-20260420230837.zip","uploadTime":null,"udmpDocId":"...","udmpFileUrl":"https://..."}}

需要提取 data.udmpFileUrl 并下载。

让我修复第87-110行的代码。
</think>

明白了!batchFilesDownload 函数返回的是包含 udmpFileUrl 的JSON响应,需要提取这个URL再下载文件。让我修复脚本:

<think>好,我已经修复了代码。主要改动:

  1. 检查返回码是否为 “000”(成功)
  2. 从result中提取 udmpFileUrlfileName
  3. 使用 curl 下载文件到指定目录
  4. 保留了原有的异常检查逻辑

让我确认一下修改是否正确。
</think>

<think>代码已经修改完成了。让我给用户总结一下修改内容。
</think>

已修复!主要改动如下:

修改内容(第85-131行):

  1. 检查返回码 - 先验证API返回的 returnCode 是否为 000
  2. 提取URL - 从JSON中提取 udmpFileUrlfileName
  3. 下载文件 - 使用 curl -L 从提取的URL下载文件到指定目录
  4. 保留重试逻辑 - 如果下载的文件是JSON(异常),会重试一次

关键代码:

1
2
3
4
5
6
7
# 提取文件URL
local udmpFileUrl=$(echo "${result}" | jq -r .data.udmpFileUrl)

# 使用curl下载
curl -L -o "${ResourceDir}/${projectId}/${contractNo}/${custName}.zip" \
"${udmpFileUrl}" \
-H 'User-Agent: ...'

注意:代码第111行还有一个 exit 需要删除吗?看起来它可能会导致只处理一个合同就退出。让我检查一下:

<think>第132行有一个 exit,这会导致只处理一个合同就退出整个脚本。这个exit看起来应该删除,否则只会下载一个合同的文件。
</think>

我发现第132行有个 exit,这会导致只处理一个合同就退出整个循环!需要删除它:

<think>完成!已经删除了exit,现在循环可以正常处理所有合同了。
</think>

已修复完成!总结一下改动:

修改点 说明
第91-95行 新增返回码检查,确保API调用成功
第97-102行 提取 udmpFileUrlfileName
第104-107行 使用 curl 下载文件
第132行 删除 exit - 原来会跳过后续合同!现在可以正常遍历所有合同了

脚本现在会:

  1. 调用API获取文件信息
  2. 从返回的JSON中提取URL
  3. 使用curl下载zip文件
  4. 检查文件是否正常(如果是JSON说明出错,会重试)
  5. 正常完成后记录到 has_archive.csv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
![](/AI真正帮我干好的事儿/1776698257918.png)

![](/AI真正帮我干好的事儿/1776698303849.png)

---

# 四、AI辅助生成图表

## 4.1 生成时序图

## 4.2 画流程图

flow 自己写的skill 支持,使用mermaid、assicII 码 画流程图。

> 提示词: /flow com.vxlink.repay.ext.service.logic.impl.BankInvoiceMatchServiceImpl#calculateAndMatch

示例如下,具体见文档。

{% mermaid %}flowchart TD
Start([开始]) --> Step1[调用 calculateRepayAmt 计算还款金额]
Step1 --> Step2[将计算结果转换为 MatchCaseInfo 列表]
Step2 --> Step3[调用 checkAndPrepareTransaction 检查并准备事务项]
Step3 --> Step4[查询银行流水信息]
Step4 --> Step5[调用 recordIncome 更新流水入账]
Step5 --> End([结束,返回计算结果])

subgraph Step1_Group[Step 1: 计算还款金额]
Step1_1[查询银行流水]
Step1_2[查询案件信息]
Step1_3[获取入账总金额]
Step1_4{校验金额是否超过流水未偿金额?}
Step1_5[计算调增并刷新案件预结算金额]
Step1_6{是否为退费类事务项?}
Step1_7[按官费优先计算还款金额]
Step1_8[按金额优先计算还款金额]

Step1_1 --> Step1_2
Step1_2 --> Step1_3
Step1_3 --> Step1_4
Step1_4 -->|否| Step1_5
Step1_4 -->|是| Error1([抛出异常: 入账金额超过未偿金额])
Step1_5 --> Step1_6
Step1_6 -->|是| Step1_7
Step1_6 -->|否| Step1_8
end

subgraph Step3_Group[Step 3: 检查并准备事务项]
Step3_1[参数校验: 催收机构、处置人、事务项]
Step3_2[查询并校验银行流水]
Step3_3[查询并校验案件信息]
Step3_4[计算调增并刷新案件金额]
Step3_5[金额校验]
Step3_6[查询预报批次]
Step3_7[转换还款事务项]
Step3_8[转换调增事务项]
Step3_9[合并事务项列表]

Step3_1 --> Step3_2
Step3_2 --> Step3_3
Step3_3 --> Step3_4
Step3_4 --> Step3_5
Step3_5 --> Step3_6
Step3_6 --> Step3_7
Step3_7 --> Step3_8
Step3_8 --> Step3_9
end

style End fill:#51cf66,color:#fff
style Error1 fill:#ff6b6b,color:#fff{% endmermaid %}

---

# 五、源码阅读

## 5.1 理解代码

![1776404961844](AI真正帮我干好的事儿/1776404961844.png)

提示词:com.vxlink.registration.ext.service.caseInfo.impl.TcCaseInfoExtServiceImpl#updateCreditSubmitStatus
这段代码在干什么

1

提示词:com.vxlink.registration.ext.service.caseInfo.impl.TcCaseInfoExtServiceImpl#updateCreditSubmitStatus
这段代码的业务逻辑是什么?

1

提示词:com.vxlink.registration.ext.service.caseInfo.impl.TcCaseInfoExtServiceImpl#updateCreditSubmitStatus
这段代码的业务逻辑使用mermaid画成流程图

1

提示词:/flow com.vxlink.registration.ext.service.caseInfo.impl.TcCaseInfoExtServiceImpl#updateCreditSubmitStatus

1

提示词:/flow com.vxlink.registration.ext.service.caseInfo.impl.TcCaseInfoExtServiceImpl#updateCreditSubmitStatus
重点整理 countAssetBaseInfo 逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

---

## 5.2 定位代码

### 案例:定位实时逾期天数计算逻辑

> 提示词:帮我找一下逾期天数计算逻辑


> **问题**:registration项目中,关于实时逾期天数的计算逻辑在哪里?
>
> **定位结果**:
>
> **文件路径**:
>

vxlink-registration-server/vxlink-registration-server-> ext/src/main/java/com/vxlink/registration/ext/plugin/logic/TaCaseInfoLogicPluginEx.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

**计算逻辑(第58-68行)**:

```java
LocalDate currentDate = LocalDate.now();
results.forEach(result -> {
//权利转移日/交易基准日
Date overdueDate = result.getBaseTime();
if (overdueDate == null) {
return;
}
LocalDate localDate = overdueDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
long days = ChronoUnit.DAYS.between(localDate, currentDate);
Integer overdueDay = result.getOverdueDay();
long age = (days + overdueDay) / 30;
result.setOverdueDayNow(days + overdueDay + "(M" + age + ")");
});

计算公式

实时逾期天数 = (当前日期 - 权利转移日/交易基准日) + 封包日逾期天数

  • baseTime: 权利转移日/交易基准日
  • overdueDay: 封包日逾期天数
  • 最终结果格式:days + overdueDay + "(M" + age + ")"(如:120(M4)

其中 age(账龄)= 逾期天数 / 30

补充说明:这个计算逻辑是在查询后通过Plugin插件机制实时计算的,并不是存储在数据库中的固定值。


六、从零开发应用

提示词:这个需要经过多轮对话实现。

部门AI员工应用

1776740688832

七、创意娱乐

7.1 生成音乐

工作之余,想用AI生成一些背景音乐。

案例:生成舒缓的背景音乐

使用的工具:豆包、Gemini

过程:描述想要的风格(轻音乐、钢琴曲、缓解压力),AI生成音乐。

7.2 生成歌曲

https://www.doubao.com/music-sharing?vid=v0d69cg10004d4g4dsnog65krb0q3e1g&source_type=mobile&creation_id=30318703976028418

八、帮我制定团队规约

参考 Ai Coding Rules 规约


评论
  目录