- 博客(71)
- 收藏
- 关注
原创 从网页禅道到 AI 能调用的工具:我的禅道 MCP 实现思路分享
整个项目串起来,其实就是:AI 发现 MCP 工具,照着 JSON Schema 生成调用参数,Java 服务这边维护禅道的登录会话,通过 JSON 接口和 HTML 页面拿数据,解析成统一的任务、需求、Bug 模型,返回一份紧凑的 Markdown,完整数据、图片、附件另外存到本地——AI 再结合任务上下文和本地代码继续干活。回头看,最初的想法很单纯:不想再手动复制任务描述、下载图片、整理附件了,让 Codex、Claude Code 这类工具自己去禅道里拿信息。
2026-08-12 23:49:08
355
1
原创 实习日志:异步场景创建链路检查与完成状态刷新修复
今天继续处理昨天那个“场景数据异步生成”的需求。昨天已经把主体代码写完了,接口也初步跑通了,所以今天主要围绕代码检查、日志补充、完整链路验证和提交前问题修复展开。这个需求不是简单把一个接口改成异步就结束了。因为场景创建背后会初始化很多业务数据,比如场景基础参数、预测计划数据、申报数据、约束数据、碳价数据等。以前这些逻辑都在一次接口调用里同步执行,前端点击创建后需要一直等待后端处理完成。现在改成异步之后,请求返回变快了,但后端也必须把“创建中、创建失败、已完成、未完成”这些状态维护清楚。
2026-05-26 20:41:23
355
原创 实习日志:申报报价状态补全与场景异步创建优化
今天原本以为要继续做上周提到的曲线修改任务,但这个任务暂时还没下来,所以我主动找组长要了新的需求。上午主要处理了两个和申报报价相关的小需求,下午则开始做一个场景创建接口的改造。整体看下来,上午更偏业务规则补全,下午的任务复杂度明显高一些,涉及异步创建、状态流转、数据完整性校验和批量插入性能优化。
2026-05-25 20:46:06
356
原创 Java 实习日记:城市碳排放统计修正与断面灵敏度查询改造
今天主要处理了两个和低碳调度结果分析相关的接口问题。一个是短周期碳排放测算中,各城市碳排放量少算了联络线输入电量对应的碳排放;另一个是断面灵敏度查询接口,需要从原来的断面设备取数,改成按拓扑节点灵敏度阈值取数。这两个需求一开始看起来都不算复杂。城市碳排放那边像是“补一部分联络线数据”,断面灵敏度那边像是“加一个阈值过滤”。但真正读代码之后发现,它们都不是简单地在 SQL 里多加一个条件,而是要先把业务口径和数据链路理清楚。
2026-05-21 18:15:25
370
原创 Java 实习日记:断面分析基态限额为空问题的排查与修复
今天主要处理了一个和潮流分析有关的 Bug,问题现象是:断面分析列表接口中,部分设备返回的“基态正向限额”和“基态反向限额”为空,但从业务上看这些设备应该是有约束限额的。虽然这次最终代码改动也不算特别大,但排查过程对我来说收获挺明显的。因为这个问题不是简单的空指针或者字段没赋值,而是涉及“字段到底应该从哪里来”的业务口径问题。
2026-05-20 17:11:57
311
原创 Java 实习日记:断面状态筛选 Bug 修复与对比案例日期过滤优化
这次任务里用到的技术点其实都比较常见,主要还是 Spring Boot 的接口、Service 层业务逻辑、MyBatis-Plus 查询条件、DTO 返回对象和枚举状态判断。真正有难度的地方不是语法,而是业务口径。以前我看到筛选条件,可能第一反应就是加一个 eq,或者在 if 里多写一个判断。但这次排查之后更明显地感觉到,写业务代码之前要先弄清楚几个问题:这个字段代表瞬时状态,还是最终状态;这些状态之间有没有优先级;前端传的参数表示精确筛选,还是包含筛选;
2026-05-19 17:42:48
451
原创 Java 实习日记:报价状态校验和碳排放指标排查
今天的任务不算特别多,主要是下午服务器环境出了问题,项目一直访问不稳定,调试被耽误了不少时间。最终只完成了两个主要事项:上午修复了一个申报数据报价完成状态的 Bug,下午排查了一个碳排放统计接口中总量指标为负数的问题。虽然代码量不大,但这两个问题都比较典型:一个涉及业务状态的自动维护,另一个涉及数据统计口径的排查。对我这个 Java 实习生来说,收获还是比较明显的。
2026-05-18 18:16:11
401
原创 Java 实习日记:从业务表关系到节点价格分析接口改造
今天这两个提交分别对应了平均节点价格计算逻辑的补充,以及节点列表、节点价格相关接口的调整。对我来说,这是一次比较典型的后端业务开发经历:先理解业务口径,再定位数据来源,最后用代码把计算和接口串起来。也更加意识到,Java 后端开发不只是写 Controller、Service、DAO,更重要的是能把业务规则翻译成稳定、清晰、可维护的代码。尤其是在实习阶段,遇到不懂的表关系和字段含义时,及时向组长确认,比自己盲猜更重要。业务理解到位之后,代码才有方向。
2026-05-15 17:57:57
353
原创 Java 实习日记:一次 Excel 导入校验 Bug 的定位与数据更新逻辑优化
今天最大的收获有三个:第一,导入类需求的校验不能只看前置逻辑是否存在,更要确认校验对象是否和最终落库对象一致。第二,排查页面问题时,要先验证数据和业务公式,不能默认页面显示异常就一定是接口 Bug。第三,数据更新逻辑要尽量避免简单粗暴的全量删除,能按差异处理时,就应该保留有效数据,减少对用户已有数据的影响。
2026-05-14 17:28:57
542
2
原创 Java 实习日志:一次报价规则适配、列表规则精简与导入校验修复
今天的实习内容主要围绕“申报报价”相关功能展开。从上午的批量报价生成规则调整,到下午的列表筛选规则精简,再到导入报价的边界校验补充,整体算是比较完整地经历了一次“需求理解、代码定位、实现改动、接口验证、踩坑复盘”的流程。
2026-05-13 18:25:27
341
原创 Java 实习日记:算法调用链路中的文件归档下载与任务状态异步维护
新增算法归档文件下载接口,支持将输入、输出文件打包返回给前端。为算法调用流程增加任务状态记录,并通过线程池异步执行,减少对主请求线程的阻塞。根据团队规范调整技术选型,去掉新增依赖,改为调用系统 tar 命令完成压缩。将长周期算法接入系统总算法调用入口,保持前端调用方式统一。配合测试数据验证完整链路,并修复了一个数值单位转换问题。这次开发让我更明显地感受到,真实业务里的后端开发不只是“把功能写出来”。还要考虑依赖管理、路径安全、异常处理、异步任务状态、接口兼容性,以及测试数据验证。
2026-05-12 17:19:14
507
原创 实习日志:完成算法调用总接口并修复联调问题
最后我手动在数据库里构造了一条比较完整的测试数据,包括案例年份、发电量数据、月度系数和算法参数,再去调用测试服务器接口,整个流程终于跑通了。尤其是这种涉及 Excel、外部算法程序和数据库结果入库的流程,不能只关注代码能不能执行,还要关注文件路径、模板结构、历史数据清理、异常信息、重复调用以及测试数据完整性。这个接口的目标不是单纯“启动一下算法”,而是把整个链路串起来:校验案例数据、生成输入 Excel、调用服务器上的算法程序、等待算法执行完成、解析结果 Excel、最后把结果入库。
2026-05-12 13:27:25
254
原创 Java实习日记: Excel 模板导出与结果导入实践
所以实现时,我的重点不是“怎么把数据写进 Excel”,而是“怎么在不破坏模板结构的前提下,把数据写到正确的位置”。这个功能的作用是读取处理完成后的 Excel 结果文件,解析其中的数据,然后写入数据库。有些维度在模板中已经有固定顺序,导出时不能直接按照数据库查询出来的顺序写,而是要先读取模板里的顺序,再按这个顺序回填数据。在写数据之前,还需要做一些数据口径转换。前一天的工作主要围绕两个 Excel 相关的后端功能展开:一个是根据系统里的业务数据生成输入文件,另一个是读取处理完成后的结果文件并写回数据库。
2026-05-11 18:06:40
256
原创 Spring 事务为什么会失效?结合底层原理总结 7 个常见场景
最近在学习 Spring 事务的时候,我一开始的想法其实很简单:只要在方法上加了 `@Transactional`,Spring 就会自动帮我开启事务,方法执行成功就提交,执行失败就回滚。但真正往下看之后,我发现 Spring 事务并没有表面上那么“无脑”。它确实帮我们简化了事务开发,但前提是很多条件都得满足。比如方法调用要经过代理、异常类型要符合回滚规则、对象本身还得是 Spring 管理的 Bean。只要这些环节里有一个地方没对上,事务就可能失效。
2026-05-10 17:04:52
373
原创 我用 AI 做了一个完整的 Java 求职训练平台,从想法到落地竟然没手写代码!
本文分享我用 AI Coding 完成一个 Java 求职训练平台的全过程。项目基于 Spring Boot 3、Spring Security、JWT、Spring AI Alibaba、MySQL、Redis、RabbitMQ、Quartz、Vue 3 等技术栈实现,核心链路包括画像整理、题单生成、集中作答、统一点评和深度报告。文章重点复盘业务流程设计、后端架构思路、技术选型以及 AI Coding 实战经验。
2026-04-17 12:49:42
387
原创 从手写 Redis 锁到 Redisson:我对分布式锁安全性的理解
最近在学习分布式锁的时候,我一开始的想法其实很简单:单机环境下可以用或控制并发,那到了分布式环境里,是不是只要把“锁”放到 Redis 里就行了?但真正往下看之后,我发现 Redis 分布式锁并没有表面上那么简单。它确实能解决一人一单、库存扣减、定时任务防重复执行这类问题,但如果实现不严谨,也会带来锁误删、锁提前过期、主从切换后锁失效等风险。这篇文章主要是结合我自己的学习过程,梳理几个问题:Redis 分布式锁到底是怎么实现的?为什么很多人手写的锁并不安全?
2026-04-15 19:57:45
455
原创 基于 Redis 实现动态热点数据缓存,打造图库精选功能
这次从理论学习到完整落地的过程,给我带来了非常大的收获。我对缓存的理解从「静态固定缓存」升级到了「动态自适应缓存」。之前做缓存开发,大多是提前把固定数据放入缓存,或者手动设置过期时间,本质上还是静态的。而这套方案让我明白,缓存可以是「活」的 —— 它可以完全基于用户行为自动调整,自动发现热点数据,淘汰冷数据,最大化利用有限的缓存资源,这才是缓存设计的精髓。还有 Redis 数据结构选型的重要性。不同的 Redis 数据结构,对应着完全不同的业务场景。
2026-03-31 10:54:58
601
原创 数据库和缓存的一致性如何保证?
学到这里,我还有一个疑问:为什么所有的最优方案都是删除缓存,而不是更新缓存呢?更新缓存的话,下次读请求不就直接命中了吗,缓存命中率不是更高?操作更轻量级,出错概率更低:删除一个 key,比更新一个复杂的缓存数据简单太多。实际业务里,缓存的数据往往不是单表数据,可能是多张表关联聚合的结果,比如商品详情,要关联商品表、价格表、库存表,更新缓存需要重新查询聚合所有数据,耗时久、逻辑复杂,很容易出问题。懒加载,节省计算资源。
2026-03-23 18:07:14
474
原创 MySQL 事务隔离级别详解
读未提交隔离级别最低,并发性能最好,但无法解决任何并发问题,实际业务极少使用;读提交解决了脏读问题,保留了较好的并发性能,适合读多写少、对数据一致性要求不极致的业务场景;可重复读是 MySQL InnoDB 的默认隔离级别,解决了脏读和不可重复读问题,同时通过 MVCC 和临键锁极大程度避免了幻读,兼顾了一致性和并发性能,是绝大多数业务场景的最优选择;串行化彻底杜绝了所有并发问题,但并发性能极差,仅适用于对数据一致性要求极高的低并发场景。
2026-03-23 14:38:08
349
原创 算法题:定长子串中元音的最大数目
的子串,仅相差「最左侧移出的字符」和「最右侧移入的字符」。滑动窗口通过「仅增减边界字符的元音计数」,替代「重新统计整个窗口的元音数」,实现效率提升。的子串里,包含元音字母(a/e/i/o/u)的最大数量。暴力解法需遍历所有长度为。可通过「复用窗口信息」将时间复杂度优化至 O(n),是本题的最优解。的子串并统计元音数,时间复杂度为 O(nk),效率较低;题目要求找到字符串中长度为。
2026-03-17 11:10:58
60
原创 用 MySQL 实现可重入锁:事务为什么是核心?
可重入锁(也叫递归锁)的核心特性是:同一个线程可以多次获取同一把锁,不会因为自己持有锁而发生死锁。线程 A 先获取锁,执行业务逻辑;业务逻辑中又调用了另一个需要同一把锁的方法;线程 A 可以再次成功获取锁,而不会被阻塞;只有当线程 A 释放锁的次数等于获取锁的次数时,锁才会真正被释放。在 Java 里就是典型的可重入锁,而我们要做的,是用 MySQL 模拟出类似的效果。用 MySQL 实现可重入锁,本质是用数据库表存储锁状态,用事务保证锁的互斥性、原子性和生命周期。
2026-03-16 11:57:48
548
原创 算法题:压缩字符串
1.统计长度时,必须先判断i < n(下标合法),再判断字符是否匹配,避免数组越界;2.全程在原数组上修改,仅用常量级临时变量,满足 O (1) 空间要求;3.通过「取余拆分 + 反转」实现数字转字符,避免使用字符串 / 额外数组存储数字。
2026-03-16 10:46:18
64
原创 算法题:除了自身以外数组的乘积
数组,分别表示当前位置左边和右边元素的乘积。从右到左遍历数组,同时计算右边的乘积,并更新。从左到右遍历数组,计算每个位置左边的乘积。为了减少空间复杂度,我们可以将。,我们可以将左边的乘积。,表示空子数组的乘积。,表示空子数组的乘积。
2026-03-11 13:57:42
55
原创 算法题:反转字符串中的单词
需要注意的是当i==0时,此时s.charAt(i)是不为空的,所以循环中i会--,导致退出循环,这样会丢失s中的第一个单词,所以在循环开始前需要处理i==0的情况。在处理每个单词时,我们还需要跳过所有的空格,直到找到下一个单词。最后,我们返回拼接好的字符串。方法去除返回结果中的尾部空格,确保结果是规范的。字符串的首尾可能会有不必要的空格,我们首先通过。来提取出当前的单词,并将其添加到结果字符串中。方法去掉字符串两端的空格,避免影响后续操作。用来标记当前单词的右边界,当遇到空格时,来记录当前单词的边界。
2026-03-11 11:34:13
171
原创 算法题:种花问题
由于「花不能种植在相邻的地块上」,如果要在下标 i 处种花,需要满足 flowerbed[i−1],flowerbed[i],flowerbed[i+1] 均为 0。,直接由 JVM 底层实现(非 Java 代码),比手动循环拷贝数组效率高得多,是数组拷贝的首选方式。每种一朵花,就把 num加 1。如果最后 num>n,则返回 true,否则返回 false。System.arraycopy()是 Java 提供的。从左到右遍历数组,能种花就立刻种花。
2026-03-11 10:56:39
47
原创 算法题:字符串的最大公因子
如果两个字符串有公因子 abc,那么 str1 就是 m 个 abc 的重复,str2 是 n 个 abc 的重复,连起来就是 m+n 个 abc,好像 m+n 个 abc 跟 n+m 个 abc 是一样的。所以如果 str1 + str2 === str2 + str1 就意味着有解。(str1.length, str2.length) 的字符串。:不断用较小的数去除较大的数,直到余数为 0。:当余数为 0 时,较大的数就是最大公约数。当确定有解的情况下,最优解是长度为。重复这个过程,直到某次计算中。
2026-03-10 18:21:24
63
原创 力扣hot100:颜色分类
因为我们只需要区分三种颜色:0、1、2,所以可以用 O(1) 的空间复杂度来完成任务。这是一个典型的“三色排序”问题,要求将数组中的 0、1、2 按照顺序排列,且不能使用额外的排序函数。如果遇到 0,表示这是红色元素,应该放到数组的红色区域,因此我们将其放到。如果遇到 1,表示这是白色元素,应该放到数组的白色区域,因此我们将其放到。在每次插入对应颜色后都会右移,确保下次处理的元素能插入到正确的位置。如果遇到 2,表示这是蓝色元素,无需处理,继续向右遍历。(蓝色),则不做处理,继续遍历。
2026-03-06 13:58:31
264
原创 java动态代理解析
动态代理是 Java 中一种非常重要的技术,它允许我们在程序运行时动态地生成代理类,并且可以在不修改目标类的情况下改变其行为。Java 提供了两种主要的动态代理方式:JDK 动态代理和 CGLIB 动态代理。本文将分别介绍这两种方式,并附上相应的代码分析,以及比较他们见的区别。
2026-03-05 15:42:41
497
原创 力扣hot100:最长公共子序列
为了求解最长公共子序列问题,我们可以使用递归来逐步探查每对字符是否匹配,并根据匹配与否决定如何继续计算子问题。:当任意一个字符串已经被遍历完(即其中一个指针越界),公共子序列的长度为 0。来记录已经计算过的结果,避免重复计算。,说明当前字符匹配,可以将这个字符纳入公共子序列,此时问题变为。,说明这两个字符匹配,公共子序列长度加 1,继续求解。数组用于存储子问题的结果,避免重复计算。开始的最长公共子序列的长度。分别表示两个字符串的长度,,选择其中较大的结果,即。的子问题,结果加 1。,选择两者的最大值。
2026-03-05 11:01:23
58
原创 ArrayList扩容机制源码分析
无参构造函数,以无参数构造方法创建ArrayList时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。: 通过指定初始容量来创建一个ArrayList。: 通过另一个集合来初始化ArrayList,容量初始为c.size()。ArrayList的add()方法会将元素添加到内部数组。如果数组容量不够,就会触发扩容机制。这是扩容机制的核心。首先检查当前数组的容量是否足够存放新元素。如果不够,就会调用grow()方法进行扩容。
2026-03-04 17:11:06
509
原创 力扣hot100:最小路径和
这道题也可以通过递归+记忆化搜索(DFS + Memoization)来解决。每个位置的最小路径和是由它的左边或上边的最小路径和加上当前位置的值来决定的。,表示当前位置的最小路径和是当前位置的值与从上方或左方的最小路径和之和。对于每个点,递归计算这两个方向的最小值,然后加上当前点的值。第一行只能从左向右走,第一列只能从上往下走。数组记录已经计算过的路径和,避免重复计算。,因为起点到起点的路径和就是它本身。的最小路径和,然后将结果存储在。数组中避免重复计算。我们定义一个递归函数。,表示越界路径无效。
2026-03-04 14:45:10
62
原创 力扣hot100:分割等和子集
问题转化:首先,设数组 的元素和为 。如果 可以被分割成两个相等的子集,那么每个子集的和应该是 。但是要注意, 必须是偶数。因为如果 是奇数, 不是整数,那么不可能找到两个和相等的子集。直接返回 。如果 是偶数,我们的目标就是找到是否可以从 中选出一个子集,使得其和为 。这个问题可以转化为一个 背包问题,即判断是否可以通过选择某些元素来填满一个总容量为 的背包。背包问题的解法:这就是一个典型的 0-1 背包问题,其目标是通过选择数组中的元素,是否能填满一个容量为 的背包。这个问题可以通过 动态
2026-03-04 14:17:54
231
原创 HashMap源码阅读记录:resize()方法
HashMap的resize()方法是整个HashMap实现中最核心、最复杂的方法之一。它负责HashMap的初始化和扩容操作,既要处理首次创建HashMap时的初始化,也要处理数据量增长时的扩容。这个方法的设计体现了Java集合框架中高效内存管理和算法优化的精髓。
2026-03-03 16:37:06
985
原创 力扣hot100:乘积最大的子数组
最大乘积子数组可以通过前面的最大乘积或者最小乘积来扩展,尤其是当数组中有负数时,最小乘积可能会与负数相乘变成最大的乘积。通过比较前一个元素的最大乘积和最小乘积乘以当前元素,或者仅仅是当前元素本身,得到当前位置的最大乘积。:最小乘积子数组同样可以通过前面的最大乘积或者最小乘积来扩展,尤其是负数的情况下。:将前面的最小值乘上当前元素,负负得正,可能会变成最大值。:初始化第一个元素的最大乘积和最小乘积为。数组中的最大值,返回整个数组的最大乘积。为结尾的子数组的最大乘积和最小乘积。:将前面的最大值乘上当前元素。
2026-03-03 11:07:35
78
原创 力扣hot100:最长递增子序列
状态定义: 表示以 为结尾的最长递增子序列的长度。状态转移方程: 对于每个元素 ,我们遍历它前面的所有元素 (),如果 ,说明 可以接在 后面,形成一个递增子序列。因此,我们有以下转移方程:其中, 表示以 为结尾的最长递增子序列的长度加上当前的 。初始条件: 每个元素自己本身可以形成一个递增子序列,因此:结果: 最终的结果是数组中所有 的最大值,表示整个数组的最长递增子序列的长度:
2026-03-03 10:25:55
175
原创 力扣hot100:单词拆分
max(n−maxLen,0),只要其中一个 i 满足 s[i:n) 在 wordDict 中且 dfs(i)=true,那么 dfs(n) 就是 true。数组,记录每个位置是否已经计算过,避免重复的计算。个字符是否能拼接成字典中的单词。已经计算过,则直接返回缓存值,避免重复计算。遍历字典中的单词,找出字典中单词的最大长度。时,表示当前已经到了字符串的开头,返回。,表示空字符串是可以由字典拼接出来的。,使得查找字典中的单词时能够提高效率。能够通过字典中的单词拼接成功。是字典中的一个单词,那么。
2026-02-28 14:47:48
364
原创 力扣hot100:零钱兑换
这道题是一个典型的背包问题,使用动态规划(DP)思想来求解。我们通过递归+记忆化搜索的方式来解决问题,以避免重复计算。,说明我们已经计算过该子问题,直接返回存储的结果,避免重复计算。为了避免递归过程中多次计算相同的子问题,我们引入了一个。,表示找到了可行解,返回最小硬币数。不使用当前硬币,直接考虑剩余金额和剩余硬币。如果当前硬币面额大于金额,直接跳过当前硬币。,表示没有更多的硬币可选,检查金额是否为。使用当前硬币,然后递归处理剩余金额(即。个硬币开始,能否用最少的硬币数来组成。,表示这些子问题尚未计算。
2026-02-28 11:33:00
530
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅