最近在写 MyBatis 的 mapper.xml 时遇到一个坑，记录一下。问题是这样的：有个查询条件是状态字段（Integer 类型），当状态为 0 时，这个条件居然没生效。排查了半天，发现问题出在 <if> 标签的判空逻辑上。问题复现mapper.xml 中的写法： SELECT * FROM user <where> <if test="status != null and status != ''"> AND status = #{status} </if> </where> 调用时传入 status = 0，结果这个条件被忽略了，SQL 中没有 AND status = 0。原因分析MyBatis 使用 OGNL 表达式，在 OGNL 中：0 == '' // 结果是 true所以当 status = 0 时：status != null and status != '' // 等价于 true and (0 != '') // 等价于 true and false // 结果是 false条件不成立，<if> 标签里的内容就被跳过了。解决方案方案一：只判断非空（推荐）<if test="status != null"> AND status = #{status} </if>Integer 类型的字段，只需要判断 != null 就行了，不需要判断空字符串。方案二：增加数值判断<if test="status != null and status != '' and status >= 0"> AND status = #{status} </if>这样写也可以，但有点多余，不如方案一简洁。方案三：统一用 1 作为默认值有些团队会约定 Integer 类型字段不用 0，用 1 表示默认状态。// 状态：1-启用，2-禁用 private Integer status;这样就不会有 0 的问题了，但这是规避问题，不是解决问题。其他注意事项字符串字段的判断字符串类型的字段，判空时需要同时判断 null 和空字符串：<if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if>List 判断判断集合是否为空：<if test="ids != null and ids.size() > 0"> AND id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </if>总结这个坑很经典，但很容易踩：Integer 类型字段判空时，只判断 != null 即可不要加 and xxx != '' 的判断，否则 0 会被当成空值字符串类型才需要同时判断 null 和空字符串虽然是个小问题，但排查起来挺费时间的，记录一下以后避免再踩\~扩展阅读除了这个坑，MyBatis 中还有一些常见问题：1. 参数为空时全表查询 <if test="name != null and name != ''"> AND name = #{name} </if>应该明确查询条件，避免参数为空时无条件查询全表。2. ​<where> 标签的使用 SELECT * FROM user <where> <if test="name != null and name != ''"> AND name = #{name} </if> <if test="status != null"> AND status = #{status} </if> </where> <where> 标签会自动处理 AND 前缀，避免 SQL 语法错误。3. ​$ 和 ​# 的区别 AND name = #{name} ORDER BY ${orderBy}能用 #{} 就用 #{}，${} 只在表名、字段名等不能预编译的场景使用。这些细节在写 MyBatis 时都要注意，很容易踩坑\~
最近写代码、写文档，甚至写个简单的总结，第一反应都是打开 AI 工具，直接让它生成。刚开始觉得挺好，效率确实提升了不少。但时间久了，发现自己有点不对劲：碰到问题不再先思考解决方案，而是直接问 AI写东西的时候，思路经常卡住，等着 AI 给我提示甚至有些基础知识都记不住了，反正"有 AI 呢"不知道大家有没有类似的感觉？一、AI 确实很方便，但也很危险先说说 AI 带来的便利吧。tabs几句话就能生成完整函数复杂的算法不需要自己推导Bug 让 AI 帮你找性能优化直接问建议/tab大纲自动生成内容扩充一键完成润色修改不用自己动手格式规范自动处理/tab概念解释通俗易懂案例分析快速理解学习路径自动规划重点内容直接提取/tab这些确实帮我们省了很多时间。但问题来了：当我们可以轻松获得答案时，还会主动思考吗？[scode type="yellow" size="default"]就像计算器的出现，让很多人的心算能力退化了一样。AI 的普及，也可能让我们的思考能力逐渐减弱。[/scode]二、思维变懒的几个表现2.1 丧失独立思考能力前几天看到一个技术问题，我的第一反应是"问 ChatGPT"，而不是：这个问题的本质是什么？我可以用哪些方法解决？哪种方案更适合当前场景？等到 AI 给出答案后，我发现自己连判断对错的能力都弱化了。2.2 记忆力下降以前记一些重要的 API、配置参数、命令行，用多了自然就记住了。现在呢？"反正可以问 AI，记它干嘛""忘了就问，多方便""不用背这些死记硬背的东西"结果就是：离开 AI，连一些基础操作都做不完整。2.3 缺乏深度思考AI 给的答案通常都是"现成"的，我们直接拿来用就行。但这导致我们：不去追问"为什么是这样"不去思考"有没有更好的方案"不去探究"底层原理是什么"长期下来，知识体系越来越零散，理解也越来越肤浅。三、AI 时代的正确打开方式AI 不是不能用，而是要用对方式。3.1 AI 是助手，不是替代品[scode type="green" size="default"]正确方式：先自己思考，再用 AI 补充[/scode]举个例子：碰到一个技术难题，正确的流程应该是：1. 自己分析问题 ├── 问题是什么？ ├── 可能的原因有哪些？ └── 我能想到哪些解决方案？ ​ 2. 查阅资料 ├── 官方文档怎么说的？ ├── 有没有类似的案例？ └── 社区里怎么讨论的？ ​ 3. 自己尝试解决 ├── 方案 A 试试看 ├── 方案 B 也试试 └── 记录遇到的问题 ​ 4. 最后再用 AI ├── 对比我的思路和 AI 的建议 ├── 问问 AI 有没有我没想到的点 └── 让 AI 帮我优化方案而不是：1. 直接问 AI："这个问题怎么解决？" 2. 复制粘贴 AI 的代码 3. 运行一下，能跑就行 4. 完事[scode type="blue" size="default"]核心区别：前者是你主导思考，AI 辅助决策；后者是 AI 替你思考，你只是执行。[/scode]3.2 把 AI 当作"审稿人"而不是"代笔者"写东西的时候，试试这样的方式：[scode type="yellow" size="default"]错误方式：直接让 AI 生成全文，自己一字不改[/scode]"帮我写一篇关于 XXX 的文章，1000 字" → AI 生成全文 → 复制粘贴发布 → 完成[scode type="green" size="default"]正确方式：自己写，AI 帮你优化[/scode]1. 自己先写初稿 ├── 用自己的语言表达 ├── 按自己的思路组织 └── 把想法完整呈现 ​ 2. 让 AI 帮你优化 ├── "帮我检查逻辑问题" ├── "哪些地方表述不清？" ├── "有没有更好的表达方式？" └── "补充一些我遗漏的观点" ​ 3. 结合 AI 的建议修改 ├── 采纳合理的建议 ├── 保持自己的风格 └── 加入自己的思考区别很明显：前者：没有 AI，你写不出东西后者：没有 AI，你也能写，只是 AI 帮你写得更好3.3 用 AI 拓展思维，而不是限制思维AI 给的答案往往是"标准答案"，但不一定是"最优答案"。[scode type="blue" size="default"]我的做法：问完 AI 后，再多问几个问题[/scode]问题："如何优化这段代码的性能？" ​ AI 回答："可以用缓存减少数据库查询" ​ 继续追问： - "除了缓存，还有其他方案吗？" - "每种方案的优缺点是什么？" - "什么场景下用哪种方案更合适？" - "有没有什么坑需要注意？"通过追问，你获得的不是"一个答案"，而是"多种视角"和"深度理解"。四、一些实用的建议4.1 设定"思考时间"在打开 AI 工具前，给自己设定一个思考时间：简单问题：5 分钟中等问题：15 分钟复杂问题：30 分钟只有自己思考过后，再去问 AI。你会发现：很多问题其实自己就能解决自己思考过的答案，理解得更深即使 AI 的答案更好，你也能对比出差异4.2 记录思考过程把解决问题的过程记录下来：## 问题：XXX ​ ### 我的分析 1. 问题本质是... 2. 可能的原因有... 3. 我想到了这些方案... ​ ### 我的尝试 方案 A：... 效果 ... 方案 B：... 效果 ... ​ ### AI 的建议 1. ... 2. ... ​ ### 最终方案 综合我的尝试和 AI 的建议，采用了... ​ ### 反思 - 我的思路哪里没想到？ - AI 的建议哪里不够好？ - 下次如何改进？写下来本身就是深度思考的过程。4.3 定期"断网"练习每周给自己一段时间，完全不用 AI：手写一段代码独立解决一个问题自己写一篇文档刚开始会很难，但习惯了之后，你会发现：自己的思考能力在恢复记忆力在提升对知识的理解更深刻了4.4 建立自己的知识体系不要每次都问 AI，而是：1. 碰到新知识，先深入理解 2. 整理成自己的笔记 3. 定期回顾和复习 4. 形成知识体系 ​ 需要时： - 先查自己的笔记 - 再去查官方文档 - 最后才用 AI 补充自己的知识体系，才是最可靠的"外脑"。五、AI 时代，思考能力反而更重要了很多人会说："AI 这么强大，我们还需要自己思考吗？"我的答案是：更需要了。5.1 AI 会犯错AI 的答案不一定对，需要你判断：代码有 bug 吗？逻辑有漏洞吗？是否符合实际场景？有没有更好的方案？如果你没有思考能力，怎么判断 AI 的对错？5.2 AI 需要引导AI 给的答案质量，取决于你问的问题：❌ "这个代码怎么优化？" → AI 可能给出泛泛而谈的建议 ​ ✅ "这段代码在处理百万级数据时很慢， 我发现主要瓶颈在数据库查询上， 有没有办法减少查询次数？" → AI 会给出针对性的优化方案能问出好问题，本身就是深度思考的体现。5.3 AI 不能替代创造AI 可以帮你生成代码、写文档，但：产品创意需要你想技术选型需要你定架构设计需要你做核心算法需要你推导这些创造性的工作，AI 只能辅助，不能替代。六、总结AI 是个强大的工具，但工具的使用方式决定了它是"助力"还是"阻力"。[scode type="red" size="default"]警惕：不要让 AI 成为你的"外挂大脑"，而要让它成为你的"思考加速器"。[/scode]核心原则：先思考，再 AI：自己先分析，再用 AI 补充用 AI 拓展，而不是替代：让 AI 帮你想得更多，而不是替你想保持学习能力：定期"断网"练习，保持独立思考能力建立知识体系：自己的笔记和经验，比 AI 更可靠最后想说：AI 时代，思考能力不是不重要了，而是更重要了。因为当所有人都用 AI 时，能独立思考、深度思考的人，反而更稀缺、更有价值。
前言在内容审核、用户评论、社区管理等场景中，敏感词检测是必不可少的功能。本文将介绍如何在 Spring Boot 项目中集成 sensitive-word 库，实现高效的敏感词检测。tabs用户昵称、签名过滤评论、弹幕审核论坛帖子过滤聊天内容监管客服质检/tab🚀 高性能：基于 DFA 算法，14万+ QPS🎯 准确率高：支持数字变体、重复词等检测🔧 易集成：开箱即用，注解驱动📦 功能丰富：内置大量敏感词库🔌 可扩展：支持自定义敏感词和白名单/tabSpring Boot 2.x/3.xsensitive-word 0.18.0DFA 算法（确定有限状态自动机）注解式编程/tab项目地址：https://github.com/houbb/sensitive-word一、快速开始1.1 Maven 依赖<dependency> <groupId>com.github.houbb</groupId> <artifactId>sensitive-word</artifactId> <version>0.18.0</version> </dependency>1.2 基础使用import com.github.houbb.sensitive.word.bs.SensitiveWordBs; import com.github.houbb.sensitive.word.core.SensitiveWordHelper; ​ public class SensitiveWordDemo { public static void main(String[] args) { String text = "五星红旗迎风飘扬，毛主席的画像屹立在天安门前。"; ​ // 1. 检测是否包含敏感词 boolean contains = SensitiveWordHelper.contains(text); System.out.println("是否包含敏感词: " + contains); // true ​ // 2. 查找第一个敏感词 String first = SensitiveWordHelper.findFirst(text); System.out.println("第一个敏感词: " + first); // 五星红旗 ​ // 3. 查找所有敏感词 List<String> all = SensitiveWordHelper.findAll(text); System.out.println("所有敏感词: " + all); // [五星红旗, 毛主席, 天安门] ​ // 4. 替换敏感词 String replaced = SensitiveWordHelper.replace(text, '*'); System.out.println("替换后: " + replaced); // ***迎风飘扬，***的画像屹立在***前。 } }二、核心功能详解2.1 四种检测方法方法参数返回值说明contains(String text)待检测文本boolean是否包含敏感词findFirst(String text)待检测文本String第一个敏感词findAll(String text)待检测文本List所有敏感词replace(String text)待检测文本String替换敏感词为*String text = "这是一个测试，包含不良信息。"; ​ // 快速检测 if (SensitiveWordHelper.contains(text)) { // 包含敏感词，拒绝提交 throw new BusinessException("内容包含敏感词"); } ​ // 查找具体敏感词 String firstWord = SensitiveWordHelper.findFirst(text); List<String> allWords = SensitiveWordHelper.findAll(text); ​ // 替换敏感词 String cleanText = SensitiveWordHelper.replace(text);2.2 数字变体检测这个功能可以检测用户用数字替换字符的变体：String text1 = "法轮功"; String text2 = "法0轮0功"; String text3 = "法O轮O功"; ​ SensitiveWordBs wordBs = SensitiveWordBs.newInstance() .enableNumCheck(true) // 启用数字变体检测 .enableEnglishCheck(true) // 启用英文字母变体检测 .init(); ​ System.out.println(wordBs.contains(text1)); // true System.out.println(wordBs.contains(text2)); // true System.out.println(wordBs.contains(text3)); // true[scode type="yellow" size="default"]注意：数字变体检测会增加计算量，根据实际需求选择是否开启。[/scode]三、Spring Boot 集成3.1 配置文件在 application.yaml 中配置敏感词检测：sensitive-word: # 是否启用敏感词检测，默认为 true enabled: true ​ # 敏感词检测模式 # true=严格模式（检测所有），false=快速模式（发现第一个就返回） strict-mode: false ​ # 是否启用数字变体检测 # 例如：将 "法轮功" 替换为 "法0轮0功" enable-num-check: true ​ # 是否启用邮箱检测 enable-email-check: true ​ # 是否启用 URL 检测 enable-url-check: true ​ # 是否启用 IPv4 检测 enable-ipv4-check: false ​ # 自定义敏感词列表 custom-words: - 自定义敏感词1 - 自定义敏感词2 ​ # 自定义白名单列表 white-list: - 允许的词1 - 允许的词23.2 注解使用字段级别检测import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordCheck; ​ @Data @Schema(description = "用户信息修改请求参数") public class UserProfileEditReqVO { ​ @Schema(description = "用户ID") private Long userId; ​ @Schema(description = "用户昵称") @SensitiveWordCheck(value = "用户昵称") private String nickname; ​ @Schema(description = "个人简介") @SensitiveWordCheck(value = "个人简介") private String bio; }方法级别检测import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordValidate; import org.springframework.web.bind.annotation.*; ​ @RestController @RequestMapping("/user/profile") public class UserProfileController { ​ @Resource private UserService userService; ​ @PostMapping("/update") public CommonResult<Boolean> updateUserProfile( @RequestBody @Valid @SensitiveWordValidate // 触发敏感词检测 UserProfileEditReqVO editReqVO ) { return CommonResult.success(userService.updateProfile(editReqVO)); } }Service 层检测import com.yourpackage.framework.sensitiveword.core.annotation.SensitiveWordValidate; ​ @Service public class UserServiceImpl implements UserService { ​ @Override public Boolean updateProfile(@SensitiveWordValidate UserProfileEditReqVO editReqVO) { // 方法参数会被自动检测 // 如果包含敏感词，会抛出 SensitiveWordException // ... } }3.3 完整工作流程客户端请求 ↓ Controller 层 ↓ @SensitiveWordValidate 注解检测 ↓ 包含敏感词？ ├─ 是 → 抛出 SensitiveWordException → 返回 400 错误 └─ 否 → Service 层处理 → 持久化 → 返回成功实际案例：// 1. 用户提交昵称修改请求 POST /user/profile/update Content-Type: application/json { "nickname": "我是主席", // 包含敏感词 "avatarUrl": "https://example.com/avatar.jpg" } // 2. 后端检测流程 @SensitiveWordValidate UserProfileEditReqVO editReqVO ↓ 检测到 "主席" 是敏感词 ↓ 抛出 SensitiveWordException: 用户昵称包含敏感词: 主席 ↓ 返回 400 错误 // 3. 响应 { "code": 400, "msg": "用户昵称包含敏感词: 主席", "data": null }四、自定义敏感词和白名单4.1 配置文件方式在 application.yaml 中配置：sensitive-word: # 自定义敏感词 custom-words: - 垃圾信息 - 广告词 - 违法词汇 # 白名单（这些词不会被检测为敏感词） white-list: - 主席 - 总统 - 领导4.2 编程方式import com.github.houbb.sensitive.word.support.allow.WordAllows; import com.github.houbb.sensitive.word.support.deny.WordDenys; import java.util.Arrays; import java.util.List; // 自定义敏感词列表 List<String> myDenyWords = Arrays.asList( "垃圾信息", "广告词", "违法词汇" ); // 自定义白名单列表 List<String> myAllowWords = Arrays.asList( "主席", "总统" ); SensitiveWordBs wordBs = SensitiveWordBs.newInstance() .wordDeny(WordDenys.chain() .add(myDenyWords) // 添加自定义敏感词 .add(WordDenys.defaults()) // 保留默认敏感词库 ) .wordAllow(WordAllows.chain() .add(myAllowWords) // 添加自定义白名单 .add(WordAllows.defaults()) // 保留默认白名单 ) .init();4.3 动态加载（数据库）import com.github.houbb.sensitive.word.support.deny.IWordDeny; import org.springframework.stereotype.Component; import java.util.List; @Component public class DatabaseWordDeny implements IWordDeny { @Resource private SensitiveWordMapper sensitiveWordMapper; @Override public List<String> deny() { // 从数据库加载敏感词列表 return sensitiveWordMapper.selectAll(); } }使用方式：SensitiveWordBs wordBs = SensitiveWordBs.newInstance() .wordDeny(new DatabaseWordDeny()) .init();五、高级配置5.1 自定义替换字符String replaced = SensitiveWordHelper.replace(text, "❌"); // 输出：这是一个❌，包含❌。5.2 自定义替换策略import com.github.houbb.sensitive.word.support.replace.WordReplace; // 自定义替换策略 WordReplace replace = (word, stringBuilder) -> { stringBuilder.append("[屏蔽]"); }; String result = SensitiveWordBs.newInstance() .wordReplace(replace) .init() .replace(text);5.3 指定检测类型import com.github.houbb.sensitive.word.bs.SensitiveWordBs; SensitiveWordBs wordBs = SensitiveWordBs.newInstance() // 只检测数字 .wordDeny(WordDenys.defaults()) // 白名单 .wordAllow(WordAllows.defaults()) // 启用邮箱检测 .enableEmailCheck(true) // 启用 URL 检测 .enableUrlCheck(true) // 启用数字变体检测 .enableNumCheck(true) .init();六、性能优化6.1 性能指标根据官方测试数据（机器配置：i7-6700, 16GB RAM）：场景QPS简单文本检测140,000+复杂文本检测100,000+数字变体检测80,000+大量敏感词库50,000+[scode type="blue" size="default"]提示：实际性能取决于机器配置、词典大小和检测策略。[/scode]6.2 优化建议tabssensitive-word: strict-mode: false # 快速模式检测到第一个敏感词就返回适合实时验证场景性能提升 30-50%/tabsensitive-word: enable-email-check: false enable-url-check: false enable-ipv4-check: false enable-num-check: false只启用必需的检测减少不必要的计算性能提升 20-40%/tab@Component public class SensitiveWordCache { private final Cache<String, Boolean> cache = Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(1, TimeUnit.HOURS) .build(); public boolean contains(String text) { return cache.get(text, key -> SensitiveWordHelper.contains(key) ); } }重复内容直接返回缓存结果适合热点内容检测性能提升 90%+/tab@Async public void checkAsync(String content, Consumer<Boolean> callback) { boolean result = SensitiveWordHelper.contains(content); callback.accept(result); }非阻塞式检测适合高并发场景提升系统吞吐量/tab6.3 批量检测优化import java.util.List; import java.util.concurrent.*; import java.util.stream.Collectors; public class BatchSensitiveWordChecker { private final ExecutorService executor = Executors.newFixedThreadPool(10); /** * 批量检测敏感词 */ public List<String> batchCheck(List<String> texts) { List<Future<String>> futures = texts.stream() .map(text -> executor.submit(() -> { if (SensitiveWordHelper.contains(text)) { return text; } return null; })) .collect(Collectors.toList()); return futures.stream() .map(future -> { try { return future.get(); } catch (Exception e) { return null; } }) .filter(Objects::nonNull) .collect(Collectors.toList()); } }七、异常处理7.1 自定义异常处理import com.yourpackage.framework.sensitiveword.core.exception.SensitiveWordException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class SensitiveWordExceptionHandler { @ExceptionHandler(SensitiveWordException.class) public CommonResult<?> handleSensitiveWordException(SensitiveWordException e) { // 获取检测到的敏感词列表 List<String> words = e.getWords(); // 记录日志 log.warn("检测到敏感词: {}", words); // 返回友好的错误信息 return CommonResult.error(400, "内容包含敏感词，请修改后提交"); } }7.2 敏感词记录import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Slf4j @Aspect @Component public class SensitiveWordLogAspect { @Before("@annotation(check)") public void logSensitiveWord(SensitiveWordCheck check) { log.info("检测字段: {}", check.value()); } @Before("@annotation(validate)") public void logSensitiveWordValidate(SensitiveWordValidate validate) { log.info("执行敏感词检测"); } }八、实战案例8.1 用户昵称过滤需求：用户修改昵称时，检测并拒绝包含敏感词的昵称。实现：@Data @Schema(description = "用户信息修改请求参数") public class UserProfileEditReqVO { @Schema(description = "用户昵称") @SensitiveWordCheck(value = "用户昵称") private String nickname; }测试：// 测试用例1：正常昵称 { "nickname": "快乐的小猪" } // 结果：通过 ✅ // 测试用例2：包含敏感词 { "nickname": "我是主席" } // 结果：拒绝 ❌ // 返回：用户昵称包含敏感词: 主席8.2 评论内容审核需求：用户提交评论时，自动替换敏感词。实现：@Service public class CommentService { public void createComment(CommentCreateReqVO reqVO) { String content = reqVO.getContent(); // 替换敏感词 String cleanContent = SensitiveWordHelper.replace(content); // 保存清理后的内容 CommentDO comment = new CommentDO(); comment.setContent(cleanContent); commentMapper.insert(comment); // 如果内容被修改，通知用户 if (!content.equals(cleanContent)) { log.info("评论内容包含敏感词，已自动替换"); } } }8.3 批量内容审核需求：管理员批量审核用户提交的内容。实现：@Service public class ContentAuditService { /** * 批量审核内容 */ public AuditResult batchAudit(List<String> contents) { AuditResult result = new AuditResult(); for (String content : contents) { // 检测敏感词 List<String> words = SensitiveWordHelper.findAll(content); if (!words.isEmpty()) { // 记录违规内容 result.addViolation(content, words); } } return result; } /** * 获取内容审核报告 */ public String generateReport(List<String> contents) { StringBuilder report = new StringBuilder(); report.append("内容审核报告\n"); report.append("==========================================\n"); int total = contents.size(); int passed = 0; int rejected = 0; for (String content : contents) { List<String> words = SensitiveWordHelper.findAll(content); if (words.isEmpty()) { passed++; report.append("✅ 通过: ").append(content).append("\n"); } else { rejected++; report.append("❌ 拒绝: ").append(content).append("\n"); report.append(" 敏感词: ").append(words).append("\n"); } } report.append("==========================================\n"); report.append("总计: ").append(total).append("\n"); report.append("通过: ").append(passed).append("\n"); report.append("拒绝: ").append(rejected).append("\n"); return report.toString(); } }九、最佳实践9.1 敏感词管理tabs从官方仓库更新敏感词库根据业务需求添加自定义词定期审查白名单/tab普通敏感词：警告并替换严重敏感词：直接拒绝白名单词汇：允许通过/tab敏感词列表纳入版本管理记录每次修改的原因支持快速回滚/tab记录所有敏感词检测统计敏感词出现频率分析违规趋势/tab9.2 性能监控import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Slf4j @Aspect @Component public class SensitiveWordPerformanceAspect { @Around("@annotation(validate)") public Object monitorPerformance(ProceedingJoinPoint pjp, SensitiveWordValidate validate) throws Throwable { long start = System.currentTimeMillis(); try { return pjp.proceed(); } finally { long duration = System.currentTimeMillis() - start; // 记录检测耗时 if (duration > 100) { log.warn("敏感词检测耗时: {}ms", duration); } else { log.info("敏感词检测耗时: {}ms", duration); } } } }9.3 降级策略@Service public class SensitiveWordService { @Value("${sensitive-word.enabled:true}") private boolean enabled; public boolean check(String text) { if (!enabled) { // 降级：敏感词检测关闭时直接通过 return false; } try { return SensitiveWordHelper.contains(text); } catch (Exception e) { // 降级：检测失败时记录日志但放行 log.error("敏感词检测异常，降级通过", e); return false; } } }十、总结[scode type="blue" size="default"]核心要点：sensitive-word 是一款高性能的敏感词检测库，基于 DFA 算法，支持多种检测策略和自定义扩展。[/scode]主要优势：高性能：基于 DFA 算法，单机 14万+ QPS易集成：注解驱动，开箱即用功能丰富：支持数字变体、邮箱、URL 等检测可扩展：支持自定义敏感词和白名单线程安全：初始化后可安全用于多线程环境适用场景：✅ 用户昵称、签名过滤✅ 评论、弹幕审核✅ 论坛帖子过滤✅ 聊天内容监管✅ 客服质检参考资料：sensitive-word 官方文档DFA 算法详解
前言你有没有遇到过这种情况：在 Markdown 中写 **加粗文本**，结果并没有被加粗？特别是在中文环境下，比如：……那猹却将身一扭，反从他的胯下逃走了。**这少年便是闰土。**我认识他时，也不过十多岁……你会发现 "这少年便是闰土。" 并没有被渲染成粗体！[scode type="red" size="default"]先说结论：这不是 bug，这是 CommonMark 规范的规定！是由于自然语言之间的差异导致的。[/scode]一、问题复现1.1 失效的场景# ❌ 加粗失效 这是**加粗**。这是中文句号。 ​ # ❌ 加粗失效 这是**加粗**，这是逗号 ​ # ❌ 加粗失效 这是**加粗**！这是感叹号 ​ # ✅ 加粗生效（后面有空格） 这是**加粗** 后面有空格 ​ # ✅ 加粗生效（后面是英文） This is **bold** text.规律：当 ** 后面紧跟中文标点符号时，加粗会失效。二、原因分析2.1 CommonMark 规范Markdown 使用 * 和 _ 作为强调指示符：被单个 * 或 _ 包裹的文本 → 斜体（）被两个 * 或 _ 包裹的文本 → 粗体（）但是，何时被识别为强调，有一套复杂的规则定义在 CommonMark 规范 中。2.2 核心概念：定界符序列（Delimiter Run）定界符类指的是：一个或一串非转义的 *一个或一串非转义的 _左侧定界符序列（left-flanking）一个定界符序列需要同时满足：后面不能是空白当前面没有空白或标点符号时，后面不能是标点符号右侧定界符序列（right-flanking）一个定界符序列需要同时满足：前面不能是空白当后面没有空白或标点符号时，前面不能是标点符号2.3 问题根源以 **这少年便是闰土。**我认识 为例：……闰土。**我认识 ^^ 这里是两个星号 ​ 前面字符：。 后面字符：我判断是否为右侧定界符序列：✅ 前面不是空白（是 。）❌ 后面不是空白或标点符号（是 我）❌ 前面是标点符号（。）结论：** 不满足右侧定界符序列的条件，不会被识别为结束粗体的标识符。2.4 为什么要这样定义？为了支持嵌套分隔符，例如：**one **two two **three** two two** one**应该渲染为：粗体开始one粗体开始two two粗体结束three粗体开始two two粗体结束one粗体结束如果规则不这么严格，就无法正确解析嵌套结构。代价：使用非空格分词语言（如中文）的用户只能默默接受……三、解决方案方案1：手动加空格（不推荐）这是**加粗** 。（在标点前加空格）[scode type="yellow" size="default"]缺点：破坏了中文的书写习惯，格式不统一。[/scode]方案2：使用零宽空格（推荐）⭐零宽空格（Zero-Width Space, ZWSP） 是一种不可打印的 Unicode 字符，编码为 U+200B。它的特殊之处：肉眼看不见：显示时不会占用任何空间不是 Unicode 空格字符：不属于 Zs 类（分隔符-空格），而是属于 Cf 类（其他-格式）可以触发换行：只在需要换行时生效验证：ZWSP 不是空格console.log(' '.charCodeAt(0)); // 32 (普通空格) console.log('\u200b'.charCodeAt(0)); // 8203 (ZWSP) ​ console.log(/\s/.test(' ')); // true console.log(/\s/.test('\u200b')); // false ​ // Java 中也是如此 System.out.println(Character.isWhitespace(' ')); // true System.out.println(Character.isWhitespace('\u200b')); // falseUnicode 类别Unicode 字符按类别区分，主要大类用第一个字母表示，小类用第二个字母表示：Zs 类（分隔符-空格 Separator, Space）：17 个字符，包括：普通空格 U+0020不换行空格 U+00A0其他 15 个空格字符Cf 类（其他-格式 Other, Format）：包括：零宽空格 ZWSP ​U+200B ⭐（本文的主角）零宽非连接符 ZWNJ U+200C零宽连接符 ZWJ U+200D其他格式字符关键点：ZWSP 属于 Cf 类，不属于 Zs 类，因此它不是 Unicode 定义的"空格字符"。这也是它能够绕过 CommonMark 规则限制的原因。方案3：使用下划线（部分场景）这是__加粗__。这是中文句号。[scode type="yellow" size="default"]注意：下划线 __ 同样遵循 CommonMark 规范，也会遇到同样的问题。[/scode]四、代码实现4.1 基础解决方案/** * 在加粗标记后的中文标点前插入零宽空格 */ function fixChineseBold(content) { return content.replace( /\*\*([^*]+)\*\*([，。！？；：])/g, '**$1**$2' ) } ​ // 使用 const markdown = '这是**加粗**。这是句号。' const fixed = fixChineseBold(markdown) // 输出：这是**加粗**。这是句号。 // 渲染后：这是**加粗**。这是句号。（加粗生效）4.2 uni-app 项目实战在我的花卷面试题项目中，就遇到了这样的问题；核心实现/** * 为流式输出的 Markdown 内容添加零宽字符 * 避免渲染引擎在流式输出时误解析不完整的 Markdown 语法 * * @param content Markdown 内容 * @returns 处理后的内容 */ export function processStreamMarkdown(content: string): string { if (!content) { return content } ​ let result = '' let i = 0 ​ while (i < content.length) { // 1. 检测粗斜体 ***text*** if (i + 5 < content.length && content.substring(i, i + 3) === '***') { const endPos = content.indexOf('***', i + 3) if (endPos !== -1) { const innerText = content.substring(i + 3, endPos) if (innerText.length > 0) { // 插入零宽空格：***\u200Btext\u200B*** result += '***\u200B' + innerText + '\u200B***' i = endPos + 3 continue } } } ​ // 2. 检测加粗 **text** if (i + 3 < content.length && content.substring(i, i + 2) === '**') { const endPos = content.indexOf('**', i + 2) if (endPos !== -1 && content.substring(endPos + 2, endPos + 3) !== '*') { const innerText = content.substring(i + 2, endPos) if (innerText.length > 0 && !innerText.includes('*')) { // 插入零宽空格：**\u200Btext\u200B** result += '**\u200B' + innerText + '\u200B**' i = endPos + 2 continue } } } ​ // 3-6. 处理 __text__、___text___、*text*、_text_（类似逻辑） // ... ​ // 普通字符，直接添加 result += content[i] i++ } ​ return result }组件中使用 <view class="markdown-view"> <mp-html :markdown="true" :content="contentAi" :tag-style="tagStyle" /> </view> ​ 4.3 通用解决方案/** * 通用的 Markdown 加粗修复函数 * 支持中文标点和多种场景 */ function fixMarkdownBold(markdown) { const patterns = [ // **text**后跟中文标点 [/\*\*([^*]+?)\*\*([，。！？；：、])/g, '**$1**$2'], ​ // __text__后跟中文标点 [/__([^_]+?)__([，。！？；：、])/g, '__$1__$2'], ​ // *text*后跟中文标点 [/\*([^*]+?)\*([，。！？；：、])/g, '*$1*$2'], ​ // _text_后跟中文标点 [/ _([^_]+?)_ ([，。！？；：、])/g, '_$1_$2'], ​ // 前面有中文标点的情况 [/([，。！？；：、])\*\*([^*]+?)\*\*/g, '$1**$2**'], [/([，。！？；：、])__([^_]+?)__/g, '$1__$2__'], ] ​ let result = markdown patterns.forEach(([pattern, replacement]) => { result = result.replace(pattern, replacement) }) ​ return result }五、原理深入5.1 为什么 ZWSP 能解决问题？回到 CommonMark 规则：右侧定界符序列的条件：前面不能是空白当后面没有空白或标点符号时，前面不能是标点符号插入 ZWSP 后：……闰土。**•我认识 ^^ 这里插入 ZWSP ​ 前面字符：• (ZWSP，不是标点) 后面字符：我判断：✅ 前面不是空白（是 ZWSP）✅ 后面不是空白或标点符号（是 我）✅ 前面不是标点符号（ZWSP 不是标点）结论：** 满足右侧定界符序列的条件，可以被识别为结束粗体的标识符。5.2 ZWSP 的其他用途// 1. 控制长文本换行 const longText = 'LongLongLongWord\u200BBreakBeforeHereLongWord' // 当容器宽度不足时，会在 ZWSP 处换行 ​ // 2. 防止邮箱被采集 const email = 'contact\u200B@example.com' // 显示为：contact@example.com // 复制后：contact@example.com（但某些工具会看到 ZWSP） ​ // 3. URL 中隐藏信息（不推荐） const url = 'https://example.com\u200B/path' ​ // 4. 绕过敏感词检查（某些场景） // 各位后端大佬们注意了！实现功能时要考虑这种情况5.3 其他零宽字符字符编码名称用途零宽空格U+200BZWSP可选换行点零宽非连接符U+200CZWNJ阻止连字零宽连接符U+200DZWJ强制连字词分隔符U+2060WJ阻止换行// 零宽非连接符示例（阿拉伯语） // 某些语言需要字符连接显示，ZWNJ 可以阻止连接 const arabic = 'لا\u200Cفت' // لا + ZWNJ + فت ​ // 零宽连接符示例（emoji 组合） const family = '👨\u200D👩\u200D👧\u200D👦' // 显示为：👨•👩•👧•👦（家庭 emoji）六、最佳实践6.1 何时使用零宽空格？tabsAI 流式输出场景用户输入的 Markdown需要完美格式的地方中文内容为主/tab代码搜索场景（可能影响搜索）需要复制粘贴的地方数据库存储（可能影响索引）SEO 优化/tab纯英文内容（不需要）已经有空格的地方代码块中的内容/tab6.2 性能考虑// ❌ 不推荐：每次都替换 function renderMarkdown(markdown) { return marked(fixMarkdownBold(markdown)) } ​ // ✅ 推荐：缓存处理结果 const cache = new Map() function renderMarkdownCached(markdown) { if (cache.has(markdown)) { return cache.get(markdown) } const result = marked(fixMarkdownBold(markdown)) cache.set(markdown, result) return result }6.3 流式输出场景// AI 回答流式输出 async function streamAIResponse(prompt) { const response = await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ prompt }) }) ​ const reader = response.body.getReader() const decoder = new TextDecoder() let markdown = '' ​ while (true) { const { done, value } = await reader.read() if (done) break ​ const chunk = decoder.decode(value) markdown += chunk ​ // 渲染时处理零宽字符 const html = marked(processStreamMarkdown(markdown)) updateUI(html) } }6.4 Vue/React 组件封装 ​ ​ <MarkdownRender :markdown="aiResponse" :stream-mode="true" />七、工具推荐7.1 在线工具Unicode 表：复制零宽字符CommonMark 规范：查看详细规则Markdown 在线测试：测试 Markdown 渲染7.2 VS Code 插件// settings.json { "markdown.preview.breaks": true, "markdown.preview.fontFamily": "Consolas, 'Microsoft YaHei'", "markdown.extension.orderedList.autoRenumber": true }7.3 npm 包# 安装 marked（Markdown 解析器） npm install marked # 安装 markdown-it（更强大的解析器） npm install markdown-it// 使用 markdown-it import MarkdownIt from 'markdown-it' import { fixMarkdownBold } from './markdown-utils' const md = new MarkdownIt() function render(markdown) { return md.render(fixMarkdownBold(markdown)) }八、常见问题Q1：零宽空格会影响复制粘贴吗？A：可能会有影响。取决于复制的工具：浏览器复制：通常会保留 ZWSP某些编辑器：会过滤 ZWSP终端粘贴：可能会显示为特殊字符解决方案：// 复制时清除零宽字符 function copyToClipboard(text) { const cleanText = text.replace(/[\u200B-\u200D\uFEFF]/g, '') navigator.clipboard.writeText(cleanText) }Q2：零宽空格会影响搜索吗？A：可能影响。取决于搜索引擎：前端搜索：可以正常搜索数据库 LIKE：可能受影响Elasticsearch：可能需要特殊配置解决方案：// 搜索时清除零宽字符 function search(keyword, content) { const cleanKeyword = keyword.replace(/[\u200B-\u200D\uFEFF]/g, '') const cleanContent = content.replace(/[\u200B-\u200D\uFEFF]/g, '') return cleanContent.includes(cleanKeyword) }Q3：有没有更简单的解决方案？A：有！使用支持中文的 Markdown 解析器：// 使用 marked.js + 自定义扩展 import { marked } from 'marked' import { mangle } from 'marked-mangle' const renderer = new marked.Renderer() // 自定义加粗渲染 marked.setOptions({ renderer: renderer, breaks: true, gfm: true }) // 或者使用支持中文的解析器 // 但主流解析器都遵循 CommonMark 规范九、总结tabsCommonMark 规范的定界符序列规则中文是"非空格分词语言"规则设计考虑了嵌套结构，牺牲了中文体验/tab✅ 使用零宽空格（U+200B）✅ 在合适的场景下自动处理✅ 注意对复制、搜索的影响❌ 不建议手动加空格（破坏格式）/tabAI 流式输出场景必须处理使用成熟的工具函数考虑缓存和性能做好降级方案/tab[scode type="blue" size="default"]核心要点：零宽空格是解决 Markdown 加粗失效的利器，但要在合适的场景使用，并注意其对复制、搜索的影响。[/scode]十、参考资源CommonMark 规范 - 强调和强调指示符掘金文章：为什么掘金的 Markdown 加粗语法（……）有时候不生效？Unicode 标准 - 零宽空格marked.js 文档
一、ps - 查看进程ps 是最常用的进程查看命令，对于 Java 应用来说尤其重要。常用场景查看 Java 进程：# 查看所有 Java 进程 ps aux | grep java ​ # 或使用 pgrep（更简洁） pgrep -l java ​ # 查看 Tomcat 进程 ps aux | grep tomcat ​ # 查看指定端口的进程 ps aux | grep 8080查看进程详细信息：# 查看指定 PID 的进程 ps -fp 1234 ​ # 查看进程树（查看父子关系） ps axjf | grep java ​ # 按内存使用排序（找内存泄漏） ps aux --sort=-%mem | grep java | head -n 5 ​ # 按 CPU 使用排序（找 CPU 占用高） ps aux --sort=-%cpu | grep java | head -n 5[scode type="blue" size="default"]提示：生产环境的 Java 应用通常以 jar 包或 Tomcat 方式运行，用 ps 找进程时注意应用名称。[/scode]二、top/htop - 系统监控top 和 htop 是实时监控系统资源的重要工具，排查性能问题时必备。top 基本使用# 启动 top top ​ # 只查看指定进程 top -p 1234 ​ # 按 CPU 排序（按 P） # 按内存排序（按 M） # 退出（按 q）htop 推荐htop 界面更友好，支持鼠标操作。# 安装 htop # Ubuntu/Debian sudo apt-get install htop ​ # CentOS/RHEL sudo yum install htop ​ # 启动 htop ​ # F3 搜索进程（输入 java） # F9 杀死进程 # F10 退出Java 应用排查场景：CPU 占用高：找到 Java 进程，看是哪个线程占用高内存泄漏：看 RES/VIRT 列，持续增长可能有问题线程数过多：查看 Java 进程的线程数是否异常三、netstat/ss - 网络排查netstat 和 ss 用于查看网络连接和端口占用，排查端口冲突时很有用。netstat 使用# 查看所有监听端口 sudo netstat -tlnp ​ # 查看 8080 端口被谁占用 sudo netstat -tlnp | grep 8080 ​ # 查看指定进程的网络连接 sudo netstat -anp | grep 1234 ​ # 统计 TCP 连接数 sudo netstat -an | grep ESTABLISHED | wc -lss 推荐（更快）# 查看监听端口 sudo ss -tlnp ​ # 查看端口占用 sudo ss -tlnp | grep 8080 ​ # 查看进程打开的 socket sudo ss -p | grep 1234常见问题场景：启动 Spring Boot 报错 "端口已被占用"查看 Tomcat 默认 8080 端口是否被占用查看数据库连接池是否达到上限四、tail - 查看日志tail 是 Java 工程师使用频率最高的命令之一，实时查看日志必不可少。常用用法# 查看文件最后 100 行 tail -n 100 application.log ​ # 实时跟踪日志（最常用） tail -f application.log ​ # 同时查看多个日志文件 tail -f application.log | grep ERROR ​ # 实时查看并高亮错误 tail -f application.log | grep --color=auto -E "ERROR|WARN" ​ # 查看日志最后 N 行并持续监控 tail -n 100 -f application.log多日志文件监控# 监控多个日志 tail -f application.log system.log ​ # 或使用 multitail（支持多窗口） # 安装 sudo apt-get install multitail ​ # 使用 multitail application.log system.log[scode type="yellow" size="default"]注意：生产环境日志量很大，建议配合 grep 过滤，否则终端会刷爆。[/scode]五、grep - 搜索内容grep 用于在文件中搜索文本，分析日志时必不可少。常用场景# 搜索错误日志 grep ERROR application.log # 忽略大小写搜索 grep -i error application.log # 显示行号 grep -n ERROR application.log # 查看匹配行的前后 5 行（查看上下文） grep -C 5 "Exception" application.log # 只显示匹配行的后 5 行 grep -A 5 "Exception" application.log # 统计出现次数 grep -c "ERROR" application.log # 递归搜索所有日志文件 grep -r "Exception" /var/log/apps/ # 反向匹配（不包含某内容） grep -v "DEBUG" application.log组合使用# 实时查看错误日志 tail -f application.log | grep ERROR # 查看最近 1000 行的异常 tail -n 1000 application.log | grep -A 5 "Exception" # 统计各类型日志数量 grep -E "ERROR|WARN|INFO|DEBUG" application.log | sort | uniq -c六、kill - 管理进程kill 用于结束进程，重启 Java 应用时经常用到。常用用法# 正常结束进程 kill 1234 # 强制结束进程（强制关闭） kill -9 1234 # 结束多个进程 kill -9 1234 5678 # 结束所有 java 进程（谨慎使用） sudo killall java # 结束指定名称的进程 pkill -f tomcat # 根据 PID 精确结束 kill -TERM 1234Java 应用重启流程：# 1. 找到 Java 进程 PID ps aux | grep java # 2. 正常关闭（等待请求处理完） kill 1234 # 3. 如果关不掉，强制关闭 kill -9 1234 # 4. 重新启动应用 nohup java -jar app.jar > app.log 2>&1 &[scode type="red" size="default"]警告：生产环境慎用 kill -9，可能导致数据丢失或事务未回滚。优先使用正常关闭方式。[/scode]七、find - 查找文件find 用于在文件系统中查找文件，找配置文件或日志文件时很有用。常用场景# 按名称查找配置文件 find / -name "application.properties" # 查找目录 find / -type d -name "logs" # 查找最近 7 天修改的文件 find /var/log -name "*.log" -mtime -7 # 查找大于 100M 的文件 find / -size +100M # 查找并删除旧日志 find /var/log/apps -name "*.log" -mtime +30 -delete # 查找空目录 find /path -type d -emptyJava 应用场景# 查找所有 jar 包 find /opt/app -name "*.jar" # 查找 Java 进程的 pid 文件 find / -name "*.pid" # 查找配置文件 find /opt/app -name "application*.yml" # 查找 core dump 文件 find / -name "core*"八、tar/zip - 解压部署部署 Java 应用时，经常需要解压压缩包。tar 常用# 解压 .tar.gz tar -xzf apache-tomcat-9.0.50.tar.gz # 解压到指定目录 tar -xzf app.tar.gz -C /opt/ # 查看 .tar.gz 内容（不解压） tar -tzf app.tar.gz # 压缩目录 tar -czf app-backup.tar.gz /opt/app # 排除某些文件压缩 tar -czf app.tar.gz /opt/app --exclude="*.log"zip 常用# 解压 zip 文件 unzip app.zip # 解压到指定目录 unzip app.zip -d /opt/app # 查看 zip 内容 unzip -l app.zip # 压缩目录 zip -r app.zip /opt/app部署流程示例：# 1. 上传部署包 scp app.zip user@server:/tmp/ # 2. 登录服务器解压 cd /opt unzip /tmp/app.zip # 3. 备份旧版本 mv app app.bak # 4. 启动新版本 cd /opt/app/bin ./startup.sh九、curl/wget - 接口测试curl 和 wget 用于测试接口和下载文件，API 开发必备。curl 测试接口# GET 请求 curl http://localhost:8080/api/user/1 # POST JSON 数据 curl -X POST http://localhost:8080/api/user \ -H "Content-Type: application/json" \ -d '{"name":"test","age":18}' # 只查看响应头（排查问题） curl -I http://localhost:8080/health # 查看详细请求过程（调试用） curl -v http://localhost:8080/api/user/1 # 忽略 SSL 证书（测试环境） curl -k https://localhost:8443/api/health # 设置超时时间 curl -m 5 http://localhost:8080/api/health # 保存响应到文件 curl -o response.json http://localhost:8080/api/user/1wget 下载文件# 下载 JDK wget https://download.oracle.com/java/jdk/jdk-11.tar.gz # 下载 Maven 依赖 wget https://repo1.maven.org/maven2/.../jar-file.jar # 断点续传（下载大文件） wget -c https://example.com/large-file.zip # 后台下载 wget -b https://example.com/file.zip十、df/du - 磁盘排查磁盘空间不足是 Java 应用的常见问题，需要熟练掌握。df 查看磁盘使用# 查看磁盘使用情况 df -h # 查看指定目录 df -h /var/log # 查看 inode 使用情况（文件数超限） df -i # 查看/tmp 目录（Java 应用临时文件） df -h /tmpdu 查看目录大小# 查看当前目录大小 du -sh . # 查看日志目录大小 du -sh /var/log/apps # 查看子目录大小（找占用大的） du -h --max-depth=2 /var/log/apps | sort -rh # 查找最大的 10 个文件 find /var/log/apps -type f -exec du -h {} + | sort -rh | head -n 10 # 查找所有 .log 文件大小 find /var/log/apps -name "*.log" -exec du -ch {} + | grep total常见问题场景：日志文件占满磁盘Java 临时文件堆满 /tmpHeap dump 文件过大应用无法写日志（磁盘满）清理方案：# 清理 30 天前的日志 find /var/log/apps -name "*.log" -mtime +30 -delete # 清理 core dump find / -name "core*" -mtime +7 -delete # 清理临时文件 rm -rf /tmp/app-cache/*额外推荐：free - 内存监控Java 应用的内存问题很常见，free 是必备工具。基本用法# 查看内存使用 free -h # 持续监控（每 2 秒） watch -n 2 free -h # 以 MB 为单位显示 free -m # 查看详细内存信息 cat /proc/meminfoJava 应用内存排查：# 1. 查看系统内存 free -h # 2. 查看 Java 进程内存占用 ps aux | grep java # 3. 查看 JVM 堆内存（需要 jmap） jmap -heap 1234 # 4. 生成 Heap Dump（分析内存泄漏） jmap -dump:format=b,file=heap.hprof 1234组合使用场景场景一：应用启动失败# 1. 查看端口是否被占用 sudo netstat -tlnp | grep 8080 # 2. 查看是否有残留进程 ps aux | grep java # 3. 查看启动日志 tail -f application.log # 4. 检查磁盘空间 df -h场景二：应用响应慢# 1. 查看 CPU 和内存 top htop # 2. 查看 Java 进程线程数 ps -eLf | grep java | wc -l # 3. 查看网络连接 sudo netstat -an | grep ESTABLISHED | wc -l # 4. 查看错误日志 tail -n 100 application.log | grep ERROR场景三：内存泄漏排查# 1. 持续监控内存 watch -n 5 free -h # 2. 查看 Java 进程内存 ps aux | grep java # 3. 生成 Heap Dump jmap -dump:format=b,file=heap.hprof 1234 # 4. 查看 GC 日志 tail -f gc.log快速参考[tabs][tab name="进程管理" active="true"]# 查看 Java 进程 ps aux | grep java # 查看端口占用 sudo netstat -tlnp | grep 8080 # 结束进程 kill -9 1234[/tab][tab name="日志查看"]# 实时查看日志 tail -f application.log # 查看最近 100 行 tail -n 100 application.log # 搜索错误 grep ERROR application.log[/tab][tab name="系统监控"]# 系统监控 top htop # 查看内存 free -h # 查看磁盘 df -h[/tab][tab name="部署相关"]# 解压部署包 tar -xzf app.tar.gz # 查看目录大小 du -sh /opt/app # 测试接口 curl http://localhost:8080/health[/tab][/tabs]实际工作中最重要的是：熟练使用 ps + netstat，快速定位进程和端口问题掌握 tail + grep，高效查看和分析日志了解 top + free，排查性能和内存问题接下来我会整理JDK自带的常用命令。相关文章：[post cid="35"/]
整理下热门Linux常用命令一、基础命令sudo 权限提升sudo 是 Linux 系统中用于以超级用户（root）权限执行命令的工具。常用参数：-u 指定用户-i 登录为 root 用户-s 执行 shell-b 在后台执行使用示例：# 以 root 权限执行命令 sudo nginx -t ​ # 以 root 权限打开 shell sudo -i ​ # 以指定用户执行命令 sudo -u nginx nginx -t ​ # 编辑系统文件 sudo vi /etc/nginx/nginx.conf ​ # 查看sudo权限 sudo -l为什么要用 sudo：系统配置文件通常需要 root 权限修改服务管理需要 root 权限日志文件需要 root 权限查看端口 80、443 需要 root 权限绑定chmod 文件权限chmod 用于修改文件或目录的权限。权限说明：r (read)：读权限，数值 4w (write)：写权限，数值 2x (execute)：执行权限，数值 1常用参数：-R 递归修改-v 显示详细信息-c 类似 -v，但只显示修改过的文件使用示例：# 数字方式：755 = rwxr-xr-x chmod 755 /path/to/file ​ # 符号方式：给所有用户添加执行权限 chmod +x /path/to/script.sh ​ # 移除其他用户的写权限 chmod o-w /path/to/file ​ # 给所有者添加写权限 chmod u+w /path/to/file ​ # 递归修改目录权限 chmod -R 755 /var/www/html ​ # 配置文件权限（所有者可读写，其他人只读） chmod 644 /etc/nginx/nginx.conf ​ # 目录权限（可执行） chmod 755 /var/www/html ​ # 脚本文件（可执行） chmod +x deploy.sh常用权限值：644：文件（rw-r--r--），所有者可读写，其他人只读755：目录/脚本（rwxr-xr-x），所有者可读写执行，其他人读执行600：敏感文件（rw-------），只有所有者可读写777：完全开放（不推荐生产环境使用）chown 文件所有者chown 用于修改文件或目录的所有者和所属组。常用参数：-R 递归修改-v 显示详细信息使用示例：# 修改文件所有者 sudo chown nginx:nginx /var/www/html/index.html ​ # 修改目录所有者 sudo chown www-data:www-data /var/www/html ​ # 只修改所有者（不修改组） sudo chown nginx /var/www/html ​ # 只修改组 sudo chown :nginx /var/www/html ​ # 递归修改目录及其内容 sudo chown -R nginx:nginx /var/www/html ​ # 修改为当前用户 sudo chown $USER:$USER /path/to/fileuseradd 用户管理useradd 用于创建新用户。常用参数：-m 创建用户主目录-s 指定 shell-g 指定主组-G 指定附加组-d 指定主目录使用示例：# 创建运行用户 sudo useradd -r -s /sbin/nologin nginx ​ # 创建普通用户 sudo useradd -m -s /bin/bash username ​ # 创建用户并指定主目录 sudo useradd -m -d /home/custom username ​ # 创建用户并添加到多个组 sudo useradd -m -G sudo,docker username ​ # 设置用户密码 sudo passwd username ​ # 删除用户 sudo userdel username ​ # 删除用户及其主目录 sudo userdel -r username ​ # 查看用户信息 id usernamecp 复制文件cp 用于复制文件或目录。常用参数：-r 递归复制目录-p 保留文件属性-v 显示详细信息-i 覆盖前提示-a 归档模式（保留所有属性）使用示例：# 复制文件 cp file1.txt file2.txt ​ # 复制文件到目录 cp file.txt /path/to/directory/ ​ # 复制目录 cp -r /source/dir /destination/dir ​ # 保留文件属性复制 cp -p file.txt file_backup.txt ​ # 覆盖前提示 cp -i file.txt /path/to/existing_file.txt ​ # 复制多个文件到目录 cp file1.txt file2.txt file3.txt /path/to/directory/ ​ # 配置备份 sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backupmv 移动/重命名文件mv 用于移动或重命名文件。常用参数：-i 覆盖前提示-v 显示详细信息-u 只更新较旧的文件使用示例：# 重命名文件 mv oldname.txt newname.txt ​ # 移动文件到目录 mv file.txt /path/to/directory/ ​ # 移动目录 mv /source/dir /destination/dir ​ # 配置备份 sudo mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bakmkdir 创建目录mkdir 用于创建目录。常用参数：-p 递归创建父目录-v 显示详细信息使用示例：# 创建目录 mkdir /path/to/directory ​ # 递归创建多级目录 mkdir -p /path/to/nested/directory ​ # 创建多个目录 mkdir dir1 dir2 dir3 ​ # 创建日志目录 sudo mkdir -p /var/log/nginxrm 删除文件rm 用于删除文件或目录。常用参数：-r 递归删除目录-f 强制删除（不提示）-i 删除前提示-v 显示详细信息使用示例：# 删除文件 rm file.txt ​ # 递归删除目录 rm -r /path/to/directory ​ # 强制递归删除（不提示） rm -rf /path/to/directory ​ # 删除前提示 rm -i file.txt ​ # 删除空目录 rmdir empty_directory ​ [scode type="red" size="default"] **警告**：rm -rf 命令非常危险，特别是删除系统目录！使用前务必确认路径正确。 [/scode]cat 查看文件内容cat 用于查看文件内容。常用参数：-n 显示行号-b 显示行号（空白行不编号）-s 压缩多个空白行为一行使用示例：# 查看文件内容 cat /etc/nginx/nginx.conf # 查看文件并显示行号 cat -n /etc/nginx/nginx.conf # 查看多个文件 cat file1.txt file2.txt # 合并文件 cat file1.txt file2.txt > combined.txt # 查看文件并翻页 cat /etc/nginx/nginx.conf | lesstail 查看文件尾部tail 用于查看文件尾部内容，常用于查看日志。常用参数：-f 实时跟踪文件变化-n 指定显示行数-F 类似 -f，但文件被删除后会重试使用示例：# 查看文件最后 10 行 tail /var/log/nginx/access.log # 查看文件最后 100 行 tail -n 100 /var/log/nginx/access.log # 实时跟踪日志变化 tail -f /var/log/nginx/access.log # 实时跟踪多个日志文件 tail -f /var/log/nginx/*.loghead 查看文件头部head 用于查看文件头部内容。常用参数：-n 指定显示行数使用示例：# 查看文件前 10 行 head /etc/nginx/nginx.conf # 查看文件前 50 行 head -n 50 /etc/nginx/nginx.confgrep 搜索文本grep 用于在文件中搜索文本。常用参数：-i 忽略大小写-r 递归搜索-n 显示行号-v 反向匹配（显示不匹配的行）-E 使用正则表达式使用示例：# 在文件中搜索 grep "error" /var/log/nginx/error.log # 忽略大小写搜索 grep -i "error" /var/log/nginx/error.log # 显示行号 grep -n "error" /var/log/nginx/error.log # 递归搜索目录 grep -r "server_name" /etc/nginx/ # 正则表达式搜索 grep -E "error|warn|fail" /var/log/nginx/error.log # 统计匹配行数 grep -c "200" /var/log/nginx/access.log # 管道配合使用 ps aux | grep nginxless 分页查看less 用于分页查看文件内容，可以上下翻页。常用参数：-N 显示行号-S 不换行使用示例：# 分页查看文件 less /etc/nginx/nginx.conf # 查看大日志文件 less /var/log/nginx/access.log # 搜索内容（在 less 中按 /） # 按 n 查找下一个，N 查找上一个 # 按 q 退出快捷键：空格 / f：向下翻页b：向上翻页j / ↓：向下一行k / ↑：向上一行/关键词：搜索n：下一个搜索结果N：上一个搜索结果q：退出find 查找文件find 用于在目录中查找文件。常用参数：-name 按文件名查找-type 按文件类型查找（f=文件，d=目录）-size 按文件大小查找-mtime 按修改时间查找-exec 对查找到的文件执行命令使用示例：# 按名称查找文件 find /etc -name "nginx.conf" # 查找目录 find /var -type d -name "log" # 查找大于 100M 的文件 find /var/log -size +100M # 查找 7 天前修改的文件 find /var/log -mtime +7 # 查找并删除日志文件 find /var/log -name "*.log" -mtime +30 -delete # 查找空目录 find /path -type d -empty二、文件操作zip 文件压缩zip 是常用的压缩工具，但默认可能没安装。安装 zip：# Ubuntu/Debian sudo apt-get install zip unzip # CentOS/RHEL sudo yum install zip unzip # macOS brew install zip常用参数：-r 递归压缩目录-q 静默模式-e 加密-9 最高压缩率使用示例：# 压缩单个文件 zip archive.zip file1.txt file2.txt # 递归压缩目录 zip -r archive.zip /path/to/directory # 排除某些文件 zip -r archive.zip /path/to/directory -x "*.log" # 查看压缩包内容（不解压） unzip -l archive.zip # 解压到指定目录 unzip archive.zip -d /path/to/destinationtar 归档tar 是 Linux 上常用的归档工具。常用参数：-c 创建归档-x 解压归档-v 显示详细信息-f 指定文件名-z 使用 gzip 压缩-j 使用 bzip2 压缩使用示例：# 创建 .tar.gz 归档 tar -czf archive.tar.gz /path/to/directory # 解压 .tar.gz 归档 tar -xzf archive.tar.gz # 解压到指定目录 tar -xzf archive.tar.gz -C /path/to/destination # 查看 .tar.gz 内容（不解压） tar -tzf archive.tar.gz # 创建 .tar.bz2 归档（压缩率更高） tar -cjf archive.tar.bz2 /path/to/directory # 解压 .tar.bz2 归档 tar -xjf archive.tar.bz2三、系统监控top 进程监控top 实时显示系统进程和资源使用情况。使用示例：# 启动 top top # 指定更新间隔（3秒） top -d 3 # 监控指定用户 top -u nginx # 监控指定进程 top -p 1234交互命令：P：按 CPU 排序M：按内存排序N：按 PID 排序q：退出k：杀死进程（输入 PID）r：重新设置进程优先级htop 增强 tophtop 是 top 的增强版，界面更友好。安装 htop：# Ubuntu/Debian sudo apt-get install htop # CentOS/RHEL sudo yum install htop # macOS brew install htop使用示例：htop特点：彩色显示鼠标操作可视化 CPU、内存、交换分区F3 搜索进程F9 杀死进程F10 退出ps 查看进程ps 用于查看当前运行的进程。常用参数：-aux 显示所有进程（BSD 格式）-ef 显示所有进程（Unix 格式）-e 显示所有进程-f 显示完整格式--sort 排序使用示例：# 查看所有进程 ps aux # 查看指定进程 ps aux | grep nginx # 查看进程树 ps axjf # 按 CPU 使用率排序 ps aux --sort=-%cpu | head -n 10 # 按内存使用率排序 ps aux --sort=-%mem | head -n 10 # 查看指定用户的进程 ps -u nginx # 查看进程的完整信息 ps -f -p 1234输出字段说明：USER：进程所属用户PID：进程 ID%CPU：CPU 使用率%MEM：内存使用率VSZ：虚拟内存大小（KB）RSS：常驻内存大小（KB）STAT：进程状态START：启动时间TIME：累计 CPU 时间COMMAND：命令名称free 内存使用free 用于查看内存使用情况。常用参数：-h 以人类可读格式显示（GB/MB）-b 以字节为单位-m 以 MB 为单位-g 以 GB 为单位-s 持续监控（指定间隔秒数）使用示例：# 查看内存使用 free -h # 持续监控（每 2 秒刷新） free -h -s 2 # 以 MB 为单位显示 free -m # 以 GB 为单位显示 free -g输出说明：total：总内存used：已使用内存free：空闲内存shared：共享内存buff/cache：缓冲和缓存available：可用内存df 磁盘使用df 用于查看磁盘使用情况。常用参数：-h 以人类可读格式显示-T 显示文件系统类型-i 显示 inode 使用情况使用示例：# 查看磁盘使用 df -h # 查看指定目录 df -h /var/log # 查看文件系统类型 df -Th # 查看 inode 使用情况 df -i # 查看所有文件系统 df -adu 目录大小du 用于查看目录占用的磁盘空间。常用参数：-h 以人类可读格式显示-s 只显示总计--max-depth 设置显示深度使用示例：# 查看当前目录大小 du -sh . # 查看指定目录大小 du -sh /var/log # 查看子目录大小 du -h --max-depth=1 /var/log # 查看最大的 10 个目录 du -h /var/log | sort -rh | head -n 10 # 排除某些目录 du -h --exclude="*.log" /path/to/diruptime 系统运行时间uptime 用于查看系统运行时间和平均负载。使用示例：uptime输出说明：当前时间系统运行时间登录用户数1分钟、5分钟、15分钟平均负载w 登录用户w 用于查看已登录用户和他们在做什么。使用示例：w输出说明：登录用户终端远程主机登录时间空闲时间CPU 使用当前正在执行的命令四、网络相关curl 测试接口curl 是常用的命令行工具，用于测试 HTTP 接口。常用参数：-X 请求方法-H 添加请求头-d 发送 POST 数据-v 显示详细信息-o 保存到文件-I 只显示响应头-k 忽略证书错误使用示例：# GET 请求 curl http://example.com/api # POST JSON 数据 curl -X POST http://example.com/api \ -H "Content-Type: application/json" \ -d '{"key":"value"}' # 发送文件 curl -X POST http://example.com/upload \ -F "file=@/path/to/file.txt" # 包含认证信息 curl -u username:password http://example.com/api # 只查看响应头 curl -I http://example.com # 保存响应到文件 curl -o output.html http://example.com # 查看详细请求过程（调试用） curl -v http://example.com/api # 忽略 SSL 证书错误 curl -k https://example.com # 设置超时时间 curl -m 10 http://example.comwget 下载文件wget 用于从网络下载文件。安装 wget：# Ubuntu/Debian sudo apt-get install wget # CentOS/RHEL sudo yum install wget # macOS brew install wget常用参数：-O 保存为指定文件名-P 保存到指定目录-c 断点续传-b 后台下载-r 递归下载--limit-rate 限制下载速度使用示例：# 下载文件 wget http://example.com/file.zip # 下载并重命名 wget -O newname.zip http://example.com/file.zip # 下载到指定目录 wget -P /path/to/directory http://example.com/file.zip # 断点续传 wget -c http://example.com/largefile.zip # 限制下载速度为 200k/s wget --limit-rate=200k http://example.com/file.zip # 后台下载 wget -b http://example.com/file.zip # 递归下载整个网站 wget -r -np -k http://example.com/ping 网络连通性测试ping 用于测试网络连通性。常用参数：-c 发送次数-i 间隔时间（秒）-s 设置数据包大小-W 超时时间（秒）使用示例：# 测试网络连通性 ping www.baidu.com # 发送 4 次后停止 ping -c 4 www.baidu.com # 设置超时时间为 2 秒 ping -W 2 www.baidu.com # 间隔 1 秒发送一次 ping -i 1 www.baidu.comnetstat 网络状态netstat 用于查看网络连接状态。使用示例：# 查看所有监听端口 sudo netstat -tlnp # 查看 TCP 连接 sudo netstat -tnp # 查看 80 端口占用情况 sudo netstat -tlnp | grep :80 # 统计各状态连接数 sudo netstat -an | awk '/tcp/ {print $6}' | sort | uniq -c # 持续监控网络连接 sudo watch -n 1 'netstat -an | grep :80 | wc -l'ss 网络状态（推荐）ss 是 netstat 的现代替代品，速度更快。使用示例：# 查看所有监听端口 sudo ss -tlnp # 查看 80 端口占用 sudo ss -tlnp | grep :80 # 查看所有 TCP 连接 sudo ss -tnp # 查看进程打开的所有端口 sudo ss -p # 显示套接字统计信息 sudo ss -s # 查看连接到特定端口的进程 sudo ss -tlnp | grep :80 | awk '{print $6}' | sort -ulsof 查看打开的文件lsof 可以查看哪些进程打开了哪些端口。使用示例：# 查看所有监听的端口 sudo lsof -i -P -n # 查看 80 端口被哪个进程占用 sudo lsof -i :80 # 查看指定进程打开的文件 sudo lsof -p 1234 # 查看 Nginx 进程打开的所有文件 sudo lsof -c nginx # 查看 TCP 连接 sudo lsof -i TCP # 查看删除但还被占用的文件 sudo lsof +L1ip 地址管理ip 是现代 Linux 网络配置工具。使用示例：# 查看所有网络接口 ip addr show # 或 ip a # 查看路由表 ip route show # 查看特定接口的 IP 地址 ip addr show eth0 # 添加临时 IP 地址 sudo ip addr add 192.168.1.100/24 dev eth0 # 删除 IP 地址 sudo ip addr del 192.168.1.100/24 dev eth0 # 查看邻居信息（ARP 表） ip neigh showiptables 防火墙iptables 用于配置防火墙规则。使用示例：# 查看所有规则 sudo iptables -L -n -v # 查看 NAT 规则 sudo iptables -t nat -L -n -v # 清空所有规则 sudo iptables -F # 开放 80 端口 sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT # 开放 443 端口 sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT # 保存规则 # Ubuntu/Debian sudo netfilter-persistent save # CentOS/RHEL 7/8 sudo iptables-save > /etc/sysconfig/iptables # 添加端口转发 sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80五、服务管理systemctl 服务管理systemctl 用于管理 systemd 服务。使用示例：# 启动服务 sudo systemctl start nginx # 停止服务 sudo systemctl stop nginx # 重启服务 sudo systemctl restart nginx # 平滑重启（不断开连接） sudo systemctl reload nginx # 查看服务状态 sudo systemctl status nginx # 开机自启 sudo systemctl enable nginx # 关闭开机自启 sudo systemctl disable nginx # 查看是否开机自启 sudo systemctl is-enabled nginx # 查看服务启动失败日志 sudo journalctl -u nginx -e # 查看所有服务状态 sudo systemctl list-units --type=service --state=runningservice 服务管理（旧版）service 是旧版的服务管理命令，兼容 SysVinit。使用示例：# 启动服务 sudo service nginx start # 停止服务 sudo service nginx stop # 重启服务 sudo service nginx restart # 查看服务状态 sudo service nginx status六、性能测试ab 压力测试ab (Apache Bench) 是常用的 HTTP 压力测试工具。安装 ab：# Ubuntu/Debian sudo apt-get install apache2-utils # CentOS/RHEL sudo yum install httpd-tools # macOS brew install httpd使用示例：# 发送 100 个请求，并发数 10 ab -n 100 -c 10 http://example.com/ # 发送 1000 个请求，并发数 50，保存详细信息 ab -n 1000 -c 50 -g result.txt http://example.com/ # 设置超时时间 ab -n 100 -c 10 -t 30 http://example.com/ # POST 请求测试 ab -n 100 -c 10 -p data.txt -T application/json http://example.com/apiwrk 压力测试wrk 是更现代的性能测试工具。安装 wrk：# Ubuntu/Debian sudo apt-get install wrk # CentOS/RHEL sudo yum install wrk # macOS brew install wrk使用示例：# 12 线程，连接数 100，持续 30 秒 wrk -t12 -c100 -d30s http://example.com/ # 使用 POST 请求 wrk -t12 -c100 -d30s -s post.lua http://example.com/api七、问题排查查看 CPU 占用# 查看整体 CPU 使用情况 top # 查看所有进程 CPU 使用率 ps aux --sort=-%cpu | head -n 10 # 持续监控 CPU（每 2 秒） watch -n 2 "ps aux --sort=-%cpu | head -n 10" # 查看某个进程的 CPU 使用 top -p 1234 # 查看每个 CPU 核心的使用情况 mpstat -P ALL查看内存占用# 查看内存使用情况 free -h # 查看进程内存占用排序 ps aux --sort=-%mem | head -n 10 # 持续监控内存 watch -n 2 free -h # 查看某个进程的内存占用 ps -o pid,ppid,cmd,%mem,%cpu -p 1234查看磁盘占用# 查看磁盘使用情况 df -h # 查看某个目录大小 du -sh /var/log # 查看 Inode 使用情况 df -i # 查找最大的文件 find /var/log -type f -exec du -h {} + | sort -rh | head -n 10 # 查找最大的目录 du -h --max-depth=2 /var/log | sort -rh | head -n 10查看网络问题# 测试网络连通性 ping -c 4 www.baidu.com # 查看 DNS 解析 nslookup www.baidu.com # 跟踪路由 traceroute www.baidu.com # 查看端口占用 sudo netstat -tlnp | grep :80 sudo ss -tlnp | grep :80 sudo lsof -i :80 # 测试端口连通性 telnet 192.168.1.1 80 nc -zv 192.168.1.1 80 # 查看网络连接数 sudo netstat -an | grep ESTABLISHED | wc -l # 查看各状态 TCP 连接数 sudo netstat -an | awk '/tcp/ {print $6}' | sort | uniq -c查看系统负载# 查看系统负载 uptime top htop # 查看系统运行时间 uptime # 查看登录用户 w who查看进程问题# 查看所有进程 ps aux # 查找僵尸进程 ps aux | grep Z # 查看指定进程 ps aux | grep nginx # 查看进程树 ps axjf # 查看父进程和子进程 pstree -p # 杀死进程 sudo kill 1234 # 强制杀死进程 sudo kill -9 1234 # 杀死所有同名进程 sudo killall nginx查看日志问题# 实时查看日志 sudo tail -f /var/log/nginx/error.log # 查看最近 100 行日志 sudo tail -n 100 /var/log/nginx/error.log # 搜索错误日志 sudo grep "error" /var/log/nginx/error.log # 搜索所有日志文件 sudo grep -r "error" /var/log/nginx/ # 查看系统日志 sudo journalctl -xe # 查看某个服务的日志 sudo journalctl -u nginx -f # 查看内核日志 sudo dmesg | tail八、常用组合命令快速排查问题# 1. 检查配置文件语法 sudo nginx -t # 2. 查看服务状态 sudo systemctl status nginx # 3. 查看错误日志 sudo tail -f /var/log/nginx/error.log # 4. 查看监听端口 sudo netstat -tlnp | grep nginx # 5. 测试本地访问 curl http://localhost # 6. 检查进程是否存在 ps aux | grep nginx | grep -v grep性能监控组合# 查看 CPU、内存、负载 top # 查看进程和资源使用 htop # 实时监控资源 vmstat 1 # 查看 I/O 状态 iostat -x 1 # 查看详细系统信息 uname -a相关文章：[post cid="14"/][post cid="4"/]
整理下网络热门AI能力供应商一、纯大语言模型（文本对话）国外主流厂商厂商模型对话页面官网特点OpenAIGPT-5 / GPT-4.5https://chatgpt.comhttps://openai.com综合能力强，对话自然，代码生成和调试能力优秀AnthropicClaude Opus 4.5 / Claude 4.5https://claude.aihttps://www.anthropic.com编程能力最强，长文本处理（200K+上下文），安全性好GoogleGemini 3 Pro / Flashhttps://gemini.google.comhttps://deepmind.google推理能力强，上下文超大（100万+tokens），Google生态集成国内主流厂商厂商模型对话页面特点阿里巴巴通义千问 Qwen2.5 / Maxhttps://tongyi.aliyun.com中文理解强，开源程度高，API价格友好DeepSeekDeepSeek-V3 / R1https://chat.deepseek.com代码生成能力强，价格极具竞争力1字节跳动豆包 Doubao-1.5-prohttps://www.doubao.com中文能力强，在国产模型评测中排名前列2百度文心一言 4.0https://yiyan.baidu.com中文理解强，与百度生态集成智谱AIGLM-4.5https://chatglm.cn中英双语，企业级应用成熟月之暗面Kimi k1.5https://kimi.moonshot.cn长文本处理（200万字上下文）零一万物Yi-Largehttps://01.ai开源友好，性能不错二、多模态模型（文本+图像/视频/音频）国外厂商厂商模型对话页面特点OpenAIGPT-4V / GPT-4ohttps://chatgpt.com图像理解生成，实时语音对话，视频分析GoogleGemini 3 Pro / Ultrahttps://gemini.google.com原生多模态，支持音视频图文，连续多图输入AnthropicClaude 3.5 Visionhttps://claude.ai图像理解强，适合图表文档分析，OCR优秀国内厂商厂商模型对话页面特点阿里巴巴通义千问多模态https://tongyi.aliyun.com图像理解，文档解析强，中文优化字节跳动豆包 Doubao-1.5-vision-prohttps://www.doubao.com任意分辨率图像，极端长宽比支持百度文心一言多模态https://yiyan.baidu.com图像生成理解，文图转换三、推理模型（专门用于复杂推理）厂商模型对话页面特点OpenAIo3 / o3-minihttps://chatgpt.com推理能力强，数学竞赛表现优异3DeepSeekDeepSeek-R1https://chat.deepseek.com推理能力强，开源友好GoogleGemini 2.5 Deep Thinkhttps://gemini.google.com思考模型，多思路交叉对比阿里巴巴Qwen-QwQhttps://tongyi.aliyun.com推理模型，数学和逻辑能力强四、编程模型（专门针对代码生成）厂商模型官网特点DeepSeekDeepSeek-Coderhttps://www.deepseek.com代码生成强，HumanEval表现优异OpenAICodexhttps://openai.comGitHub Copilot底层模型Hugging FaceStarCoderhttps://huggingface.co开源代码模型，可本地部署阿里巴巴通义灵码https://tongyi.aliyun.com/lingma与阿里云集成，中文注释支持五、行业模型（特定领域）领域厂商模型对话页面特点医疗百川智能Baichuan-M3 Plus4-循证增强医疗大模型，幻觉率低于3%，HealthBench全球第一5医疗-其他医疗模型-持续更新中法律---持续更新中金融---持续更新中六、编程助手（IDE/插件/命令行）AI-First IDE（独立IDE）工具官网特点定价Cursorhttps://cursor.comAI深度集成，Agent模式，多文件重构个人版免费/专业版付费Windsurfhttps://codeium.com/windsurfAI原生IDE，Flow模式协作免费Traehttps://trae.ai国产AI IDE，完全免费，中文支持好免费IDE插件工具官网特点定价GitHub Copilothttps://github.com/features/copilot行业标准，用户最多，Copilot Chat$10-19/月Continuehttps://continue.dev开源免费，支持多模型，可自部署免费Codeiumhttps://codeium.com免费版功能强大，支持多IDE免费通义灵码https://tongyi.aliyun.com/lingma阿里出品，中文注释支持好免费腾讯CodeBuddyhttps://cloud.tencent.com/product/codebuddy腾讯云出品，与云服务集成免费命令行工具工具官网特点OpenCode6https://github.com/opencode-opencode/opencode开源AI编程助手，70k+⭐，支持75+LLM提供商Claude Codehttps://claude.ai/codeAnthropic官方CLI，项目级上下文，Agent模式Aiderhttps://aider.chatGit工作流集成，自动创建PRCursor CLIhttps://cursor.comCursor IDE命令行版选型建议大模型选择场景首选备选通用对话GPT-5、Claude 4.5Gemini 3 Pro代码编写Claude Opus 4.5、GPT-5DeepSeek-Coder长文本处理Claude Opus 4.5Gemini 3 Pro、Kimi中文场景通义千问、DeepSeek豆包、文心一言成本敏感DeepSeek开源模型本地部署复杂推理o3、DeepSeek-R1Qwen-QwQ编程助手选择场景首选备选日常开发GitHub CopilotCodeium复杂项目CursorWindsurf免费方案ContinueTrae命令行Claude CodeAider开源方案OpenCodeContinue最后AI领域发展很快，新的模型和工具层出不穷。这篇文章整理的是截止2026年1月的情况。后续如果有新的模型发布，我会持续更新。选型时关键是根据自己的场景和需求来选，不要盲目追求最新的模型。适合自己的，才是最好的。参考引用 2025年国产大模型前十名 - DeepSeek-R1/V3排名前列 ↩ 2025年国产大模型评测 - 豆包大模型与文心一言领衔 ↩ OpenAI o3 发布说明 - 在2025 AIME数学竞赛中表现优异 ↩ 百川智能发布循证增强医疗大模型 - Baichuan-M3 Plus发布 ↩ 王小川2026开年亮相 - Baichuan-M3在HealthBench评测中以65.1分位列全球第一 ↩ OpenCode GitHub - 70k+ Stars，开源版Claude Code ↩
Maven settings.xml 配置详解每次换电脑或者重装系统，都要重新配置Maven的settings.xml文件。里面的配置项不少，很多都不记得是干什么的。这里整理下相关内容。settings.xml 的位置settings.xml文件可以在两个位置：1. 用户级别配置${user.home}/.m2/settings.xml在macOS和Linux上是 ~/.m2/settings.xml，在Windows上是 C:\Users\你的用户名\.m2\settings.xml。2. 全局配置${maven.home}/conf/settings.xml用户级别配置优先级更高，会覆盖全局配置。完整配置示例（带注释）先给你一个完整的settings.xml配置文件，每一项都加了注释说明：<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> ​ <localRepository>${user.home}/.m2/repository</localRepository> ​ <offline>false</offline> ​ <interactiveMode>true</interactiveMode> ​ <pluginGroups> <pluginGroup>org.apache.maven.plugins</pluginGroup> <pluginGroup>org.codehaus.mojo</pluginGroup> </pluginGroups> ​ <proxies> <proxy> <id>my-proxy</id> <active>true</active> <protocol>http</protocol> <host>proxy.example.com</host> <port>8080</port> <username>代理用户名（如果需要）</username> <password>代理密码（如果需要）</password> <nonProxyHosts>localhost|127.0.0.1</nonProxyHosts> </proxy> </proxies> ​ <servers> <server> <id>my-nexus-releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>my-nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server> </servers> ​ <mirrors> <mirror> <id>aliyun</id> <name>Aliyun Maven</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> ​ <profiles> <profile> <id>jdk-17</id> <activation> <activeByDefault>true</activeByDefault> <jdk>17</jdk> </activation> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion> </properties> </profile> ​ <profile> <id>jdk-1.8</id> <activation> <activeByDefault>false</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> ​ <profile> <id>dev</id> <properties> <env>dev</env> <db.url>jdbc:mysql://localhost:3306/dev</db.url> </properties> <repositories> <repository> <id>aliyun</id> <url>https://maven.aliyun.com/repository/public</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories> </profile> ​ <profile> <id>prod</id> <properties> <env>production</env> <db.url>jdbc:mysql://prod-db:3306/prod</db.url> </properties> </profile> </profiles> ​ <activeProfiles> <activeProfile>jdk-17</activeProfile> <activeProfile>dev</activeProfile> </activeProfiles> ​ </settings>各配置项详细说明下面结合我本地的配置文件，逐个说明每个配置项的用途。1. localRepository - 本地仓库路径指定Maven本地仓库的位置。<localRepository>/Users/smalleyes/environment/maven_repository</localRepository>默认值：${user.home}/.m2/repository说明：Maven会把下载的jar包、插件都存放在本地仓库。默认在用户目录下的.m2/repository。我改成了自定义路径 /Users/smalleyes/environment/maven_repository，主要是想统一管理开发工具的依赖文件。为什么改？默认路径在系统盘，占用C盘空间方便备份和迁移多个Maven版本可以共用同一个本地仓库如果不配置：就用默认的 ~/.m2/repository。2. pluginGroups - 插件组<pluginGroups> </pluginGroups>说明：这是插件组ID列表。当你在pom.xml中使用插件时，如果没有指定完整的groupId，Maven会在这个列表里查找。举例说明：比如你在pom.xml里这样配置插件：<plugin> <artifactId>maven-compiler-plugin</artifactId> </plugin>没有指定groupId，Maven会去pluginGroups里找。如果配置了：<pluginGroups> <pluginGroup>org.apache.maven.plugins</pluginGroup> <pluginGroup>org.codehaus.mojo</pluginGroup> </pluginGroups>Maven会自动尝试 org.apache.maven.plugins:maven-compiler-plugin 和 org.codehaus.mojo:maven-compiler-plugin。我的配置是空的：因为大部分情况下，我都会在pom.xml里写完整的groupId和artifactId，所以这个配置没填。3. proxies - 代理配置<proxies> </proxies>说明：如果你的网络需要通过代理访问外网，需要在这里配置代理信息。示例配置：<proxies> <proxy> <id>my-proxy</id> <active>true</active> <protocol>http</protocol> <host>proxy.example.com</host> <port>8080</port> <username>proxyuser</username> <password>proxypass</password> <nonProxyHosts>localhost|127.0.0.1</nonProxyHosts> </proxy> </proxies>字段说明：id：代理的唯一标识active：是否激活protocol：代理协议（http、https、socks等）host：代理服务器地址port：代理端口username/password：代理认证信息（如果需要）nonProxyHosts：不走代理的主机，用 | 分隔我的配置是空的：公司网络不需要代理，所以没配置。什么时候需要：公司网络有限制，需要通过代理访问外网Maven仓库某些内网环境4. servers - 服务器认证<servers> <!-- <server> <id>releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>snapshots</id> <username>admin</username> <password>admin123</password> </server> --> </servers>说明：配置Maven仓库的认证信息。当Maven需要部署（deploy）构件到私服时，需要用户名和密码。示例配置：<servers> <server> <id>my-nexus</id> <username>admin</username> <password>admin123</password> </server> <server> <id>aliyun-releases</id> <username>你的账号</username> <password>你的密码</password> </server> <server> <id>github</id> <username>你的GitHub用户名</username> <password>GitHub Token</password> </server> </servers>重要：这里的 <id> 要和pom.xml里distributionManagement的repository id对应上：<distributionManagement> <repository> <id>my-nexus</id> <url>http://你的私服地址/releases</url> </repository> </distributionManagement>我的配置被注释了：因为暂时不需要部署到私服，所以注释掉了。5. mirrors - 镜像配置<mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>说明：镜像仓库用来替代远程仓库。最常见的场景就是用国内镜像加速Maven中央仓库的下载。字段说明：id：镜像的唯一标识name：镜像名称url：镜像地址mirrorOf：被镜像的仓库IDmirrorOf 的几种写法： <mirrorOf>central</mirrorOf> <mirrorOf>central,repo1,repo2</mirrorOf> <mirrorOf>*</mirrorOf> <mirrorOf>*,!repo1</mirrorOf>常用国内镜像：<mirrors> <mirror> <id>aliyun</id> <name>Aliyun Maven</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> <mirror> <id>tencent</id> <name>Tencent Maven</name> <url>https://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url> <mirrorOf>central</mirrorOf> </mirror> <mirror> <id>huaweicloud</id> <name>Huawei Cloud Maven</name> <url>https://repo.huaweicloud.com/repository/maven/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>我的配置：用了阿里云镜像，替换Maven中央仓库。下载速度快很多。6. profiles - Profile配置Profile是Maven的一组配置，可以在不同环境下激活不同的配置。我本地配置了两个profile：<profiles> <profile> <id>jdk-17</id> <activation> <activeByDefault>true</activeByDefault> <jdk>17</jdk> </activation> <properties> <JAVA_HOME>/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home</JAVA_HOME> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion> </properties> </profile> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>false</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> </profiles>说明：我配置了JDK 17和JDK 1.8两个profile，JDK 17默认激活。profile 的激活方式：1. 默认激活：<activation> <activeByDefault>true</activeByDefault> </activation>2. 命令行激活：mvn clean install -P jdk-1.83. JDK版本激活：<activation> <jdk>17</jdk> </activation>4. 操作系统激活：<activation> <os> <family>windows</family> </os> </activation>5. 文件存在激活：<activation> <file> <exists>/path/to/file</exists> </file> </activation>profile 里可以配置什么：<profile> <id>my-profile</id> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <my.property>value</my.property> </properties> <repositories> <repository> <id>my-repo</id> <url>http://my-repo</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>my-plugin-repo</id> <url>http://my-plugin-repo</url> </pluginRepository> </pluginRepositories> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> 17 <target>17</target> </configuration> </plugin> </plugins> </build> </profile>常见使用场景：不同环境的配置（开发、测试、生产）不同JDK版本的切换不同操作系统的配置我配置文件中没有涉及的其他配置项1. activeProfiles - 激活的Profile作用：指定默认激活的profile。<activeProfiles> <activeProfile>jdk-17</activeProfile> <activeProfile>my-profile</activeProfile> </activeProfiles>说明：这等价于在命令行执行 mvn -P jdk-17,my-profile。使用场景：想让某些profile默认激活，又不想在profile里写 activeByDefault2. offline - 离线模式作用：设置Maven是否在离线模式下运行。<offline>false</offline>默认值：false说明：true：Maven不会联网下载任何依赖，所有依赖都必须在本地仓库false：正常模式，会联网下载依赖使用场景：网络不通的环境已下载完所有依赖，不想联网验证命令行等效：mvn clean install -o3. interactiveMode - 交互模式作用：设置Maven是否与用户交互。<interactiveMode>true</interactiveMode>默认值：true说明：true：Maven会在需要时提示用户输入（比如版本号）false：Maven使用默认值，不提示用户使用场景：CI/CD自动化构建，设为 false本地开发，保持 true最后settings.xml文件配置项不多，但每个都挺实用的。本地仓库路径、镜像配置、Profile这几个是最常用的。其他的根据实际情况配置就好。我把这些整理出来，以后换电脑或者帮别人配置Maven的时候，直接照着来就行。如果你有其他配置技巧，也欢迎补充。
鉴别一下网络热门Git命令。日常开发常用命令这些是最常用的，基本上每天都会用到。基本操作# 克隆仓库 git clone <url> # 查看当前状态 git status # 添加文件到暂存区 git add <file> # 添加单个文件 git add . # 添加所有修改 git add -A # 添加所有修改（包括删除） # 提交更改 git commit -m "提交信息" # 拉取最新代码 git pull # 推送到远程 git push分支操作# 查看所有分支 git branch # 查看本地分支 git branch -a # 查看所有分支（包括远程） # 创建分支 git branch <branch-name> # 切换分支 git checkout <branch-name> git switch <branch-name> # Git较新版本推荐 # 创建并切换到新分支 git checkout -b <branch-name> git switch -c <branch-name> # 删除分支 git branch -d <branch-name> # 删除本地分支 git push origin --delete <branch-name> # 删除远程分支查看差异# 查看工作区和暂存区的差异 git diff # 查看暂存区和上一次提交的差异 git diff --cached # 查看两次提交之间的差异 git diff <commit1> <commit2>查看历史# 查看提交历史 git log # 简洁的提交历史 git log --oneline # 查看最近N条提交 git log -n # 查看分支图 git log --graph --oneline高级用法这些用法在处理复杂场景时很有用。1. 暂存工作区（git stash）有时候你在当前分支改了一半，突然需要切换到其他分支处理问题。代码还没写完不想提交，可以用stash先暂存起来。# 暂存当前修改 git stash # 暂存并添加说明 git stash save "说明信息" # 查看暂存列表 git stash list # 恢复最近的暂存 git stash pop # 恢复指定暂存 git stash apply stash@{n} # 删除暂存 git stash drop stash@{n}使用场景：你在开发功能，突然有个紧急bug要修。先stash暂存，改完bug再回来继续开发。2. 修改提交（git commit --amend）提交后发现信息写错了，或者漏加了文件，可以用amend修改。# 修改最近一次提交的信息 git commit --amend # 修改最近一次提交的信息（不修改内容） git commit --amend -m "新的提交信息" # 把暂存区的文件加入到最近一次提交 git commit --amend --no-edit注意：amend会改变提交历史，如果已经push到远程，慎用。3. 撤销操作有时候会改错或者提交错，需要撤销。# 撤销工作区的修改（恢复到暂存区状态） git checkout -- <file> # 撤销暂存区的修改（保留到工作区） git reset HEAD <file> # 撤销最近一次提交（保留修改到工作区） git reset --soft HEAD~1 # 撤销最近一次提交（不保留修改） git reset --hard HEAD~1 # 回到指定提交（不保留修改） git reset --hard <commit-hash>4. cherry-pick（挑选提交）有时候想把某个分支的某个提交应用到当前分支。# 把指定提交应用到当前分支 git cherry-pick <commit-hash> # 挑选多个提交 git cherry-pick <commit1> <commit2>使用场景：你在feature分支修了个bug，想把这个修复单独应用到master分支，但又不想合并整个feature分支。5. rebase（变基）rebase可以把一系列提交整理成更清晰的历史。# 把当前分支的提交变基到目标分支 git rebase <target-branch> # 交互式rebase，可以编辑、删除、合并提交 git rebase -i HEAD~n # n是要操作的提交数量使用场景一：你的分支从master拉出来后，master又有新提交。你想让master的最新代码成为你分支的基础，可以：git checkout feature git rebase master使用场景二：你想把最近3个提交合并成一个。可以：git rebase -i HEAD~3然后在编辑器里把后面两个提交改成squash或s，保存退出。Git会打开编辑器让你写合并后的提交信息。rebase的注意事项：只在本地分支使用rebase不要对已经push的提交做rebaserebase会改变提交历史，多人协作时要小心6. 查看某个文件的修改历史想看某个文件是谁改的、什么时候改的。# 查看文件的所有提交记录 git log <file> # 查看文件的详细修改 git log -p <file> # 查看文件每一行的修改信息 git blame <file>使用场景：某段代码不知道谁写的、为什么要这样写，用blame一下就知道了。7. 搜索代码# 在所有提交中搜索包含指定内容的提交 git log -S "搜索内容" # 在当前代码中搜索 git grep "搜索内容"8. 打标签发布版本时可以打标签。# 创建标签 git tag v1.0.0 # 给指定提交打标签 git tag v1.0.0 <commit-hash> # 查看所有标签 git tag # 推送标签到远程 git push origin v1.0.0 git push origin --tags # 推送所有标签 # 删除标签 git tag -d v1.0.0 # 删除本地标签 git push origin --delete v1.0.0 # 删除远程标签实际场景举例场景一：功能开发到一半，需要紧急修bug# 1. 暂存当前工作 git stash # 2. 切换到master git checkout master # 3. 拉取最新代码 git pull # 4. 创建fix分支 git checkout -b fix/bug-xxx # 5. 修复bug并提交 git add . git commit -m "修复xxx问题" # 6. 推送到远程 git push origin fix/bug-xxx # 7. bug修完了，回到功能分支 git checkout feature # 8. 恢复之前暂存的工作 git stash pop场景二：提交后发现漏加了文件# 1. 把漏掉的文件加到暂存区 git add <file> # 2. 修改最近一次提交 git commit --amend --no-edit场景三：想把最近3个提交合并成一个# 1. 交互式rebase git rebase -i HEAD~3 # 2. 在编辑器里，把第一个提交保留为pick， # 后面两个改成squash或s # 3. 保存后，Git会打开编辑器让你写合并后的提交信息场景四：分支拉出来很久了，想同步master的最新代码# 方式一：用merge（会有合并提交） git checkout feature git merge master # 方式二：用rebase（历史更清晰） git checkout feature git rebase master场景五：把某个提交从feature分支单独应用到master# 1. 切换到master git checkout master # 2. 找到要应用的提交hash git log feature # 3. cherry-pick这个提交 git cherry-pick <commit-hash>其他实用技巧1. 配置别名有些命令太长，可以设置别名。# 设置别名 git config --global alias.st status git config --global alias.co checkout git config --global alias.br branch git config --global alias.ci commit git config --global alias.unstage 'reset HEAD --' git config --global alias.last 'log -1 HEAD' git config --global alias.visual 'log --graph --oneline' # 使用 git st # 等同于 git status git co master # 等同于 git checkout master2. 查看某个命令的详细帮助git help <command> git <command> --help man git-<command>3. 查看某个文件的远程地址git remote -v4. 清理未跟踪的文件# 查看哪些文件会被删除（只是查看） git clean -n # 删除未跟踪的文件 git clean -f # 删除未跟踪的文件和目录 git clean -fd # 删除ignored文件 git clean -fX最后Git命令很多，但常用的其实就那些。掌握日常开发的基本命令，再了解几个高级用法，基本就够用了。遇到问题不要慌，Git大部分操作都是可以撤销的。实在不行就查文档，或者问问AI。我把这些命令整理出来，以后想用直接查。也希望能帮到你。如果觉得有用，可以收藏一下，说不定哪天就用上了。
最近看到一个有意思的词，叫「Vibe Coding」。这是前OpenAI AI研究总监Andrej Karpathy在2025年提出的一个概念。什么是Vibe Coding简单说，就是一种用自然语言写代码的方式。传统写代码是这样的：你有个想法，然后打开编辑器，一行一行敲代码，调试，改bug，再调试。Vibe Coding是这样的：你有个想法，然后跟AI对话，告诉它你要什么，让它帮你写代码。你不需要自己敲键盘，主要是"说"和"看"。Karpathy的原话是："fully give in to the vibes, embrace exponentials, and forget that the code even exists"。大意就是：完全跟着感觉走，接受指数级增长，忘记代码本身的存在。听着挺玄乎，但实际用起来其实很简单。我用Vibe Coding做了「花卷面试题」之前做的「花卷面试题」项目，就是用这种方式完整做出来的。对比一下传统开发方式和Vibe Coding的区别：传统开发周期传统做一个刷题小程序，大概是这样的流程：需求分析（1-2天）：想清楚要什么功能，怎么设计技术选型（半天）：选框架、选数据库、选服务器原型设计（2-3天）：画原型图，设计页面交互前端开发（1-2周）：写页面，写逻辑，调接口后端开发（1-2周）：写接口，设计数据库联调测试（3-5天）：前后端联调，改bug部署上线（1-2天）：部署到服务器，配置域名算下来，一个完整项目至少要1-2个月。Vibe Coding方式用Vibe Coding是这样的：1. 项目构思我告诉AI：我想做一个面试题刷题小程序，类似题库应用，用户可以按分类看题、收藏、看历史记录。支持H5和小程序。AI问了我几个问题：要什么分类？要不要用户系统？要不要搜索功能？要不要深色模式？我回答了这些问题，AI就生成了一个初步的功能清单。2. 原型设计我让AI帮我设计页面结构。AI直接给了我一个页面清单和跳转关系：首页（分类展示）题目列表页题目详情页搜索页个人中心收藏页浏览记录页还给我画了每个页面的布局说明，比如首页顶部是搜索框，下面是分类网格。3. 技术选型我问AI用什么技术栈比较好。考虑到要同时支持H5和小程序，AI推荐了uni-app + Vue3 + TypeScript。理由是：uni-app可以一套代码编译到多端Vue3生态好，上手快TypeScript类型安全，减少bug我同意了，AI就帮我生成了项目脚手架。4. 开发过程开发的时候，我基本上是这样和AI配合的：写首页：我：帮我写一个首页，顶部是搜索框，下面显示所有一级分类，每个分类是一个卡片，显示图标和名称。支持下拉刷新。AI：给我生成了首页的Vue组件，包含了布局、数据请求、刷新逻辑。我看了一下，觉得样式不太好看，就告诉AI：卡片样式改一下，加个阴影，间距调大一点。AI很快就调整好了。写题目详情页：我：题目详情页需要显示题目内容和答案，用Markdown渲染。如果用户未激活，答案部分要折叠，点击才能展开。AI：写了一个详情页，用了Markdown渲染组件，还加了权限判断。我测试了一下，发现Markdown里的代码块没有高亮。我把这个问题丢给AI，AI很快就加上了代码高亮。5. Bug修复开发过程中遇到bug，我是这样处理的：有次列表页报错了，我把错误信息复制给AI。AI看了之后说是因为数据格式不对，告诉我需要加个数据转换，还直接给了修改后的代码。还有次小程序真机调试没问题，但H5版有跨域问题。我问AI，AI告诉我要配置代理，还给了具体的配置代码。基本上遇到的问题，丢给AI都能快速解决。整个项目从0到上线，大概用了2-3周时间。比传统方式快了很多。Vibe Coding存在的问题虽然Vibe Coding能提高效率，但也确实有一些问题需要面对。1. AI的上下文问题这是最常见的问题。AI的上下文窗口是有限的，对话太长之后，AI会"忘记"之前的内容。会导致什么问题？比如你让AI写了一个组件，过了很久又让它改另一个相关的组件。AI可能忘了第一个组件是怎么写的，导致两个组件的风格不统一，或者接口对不上。我有次让AI写了题目列表页的接口，后来又让它写详情页。结果AI忘了列表页用的字段名，详情页用了不一样的命名，导致数据对不上。怎么解决？把重要的设计决策记下来，比如API字段定义、组件接口规范每次开始新任务前，把相关背景信息简单说一下把项目分成几个小的上下文，比如前端、后端、配置，分别处理用支持项目记忆的AI工具（比如Claude的Projects功能）2. 提示词问题提示词写得好不好，直接影响AI输出的质量。提示词不规范会导致什么问题？问题一：理解偏差我一开始说"帮我写个列表"，AI写了个很简单的东西。后来我才明白，应该说"帮我写一个题目列表页面，包含标题、分类标签、收藏按钮，支持下拉刷新和上拉加载，每页显示20条数据"。说得越具体，AI写得越准。问题二：缺少边界条件有次我让AI写搜索功能，只说了"要支持关键词搜索"。结果AI写了个前端搜索，数据量一大就卡了。后来我补充了："搜索要调用后端接口，支持防抖，搜索结果高亮关键词"，AI就写对了。问题三：风格不统一不同时候问AI，它可能用不同的代码风格。有时候用箭头函数，有时候用普通函数。有时候用async/await，有时候用Promise。怎么解决？提示词要具体：说明输入、输出、边界条件建立规范：告诉AI你的代码风格要求提供示例：给AI看一个类似的代码，让它照着写及时反馈：AI写得不满意，直接告诉它哪里要改如何结合Vibe Coding提高开发效率根据我的经验，这几个方法能让Vibe Coding更高效：1. 把项目拆成小任务不要一口气让AI做整个项目。拆成小的、独立的任务，一步步来。比如做题目列表页，可以拆成：先写UI布局再加数据请求然后加刷新加载最后加收藏功能这样每个任务都能在一个对话里完成，上下文更清晰。2. 先让AI出方案，再让它实现写代码前，先让AI给出设计方案。比如："我要做一个题目列表页，你帮我设计一下页面结构和数据流"AI会给你一个方案，你看看合不合理，不合适就让AI改。确定了再让它写代码。这样可以避免写到一半发现设计有问题，推倒重来。3. 建立项目规范一开始就告诉AI你的规范：代码风格：用2空格缩进，用箭头函数命名规范：组件用PascalCase，变量用camelCase文件组织：页面放在pages目录，组件放在components目录注释要求：复杂逻辑要加注释这样AI写的代码会更统一，后面维护也方便。4. 要会看代码，不要完全依赖AI虽然AI能写代码，但你至少要能看懂。要知道AI写的是什么，有没有问题，符不符合你的需求。完全看不懂的话，AI写错了你也发现不了。我其实对Vue和uni-app都不算特别熟，但基本的编程概念是懂的。AI写的代码我能看个大概，有问题也能指出来。5. 善用AI的解释功能遇到看不懂的代码，直接让AI解释。"这段代码是什么意思？为什么要这样写？"AI会告诉你代码的逻辑和设计思路。不仅能解决问题，还能学到东西。6. 保留重要的对话记录AI给出的重要的设计决策、接口定义、配置代码，最好保存下来。可以建一个文档，记录这些关键信息。下次对话的时候，把这些信息告诉AI，保持上下文一致。最后Vibe Coding不是说要取代程序员，而是提供了一种新的方式。它的本质是：你负责"想"和"判断"，AI负责"写"和"实现"。适合中小项目，或者想快速验证想法的时候。大型复杂项目可能还是需要专业开发团队。如果你想做个什么东西，不妨试试这种方式。就像我做「花卷面试题」，一开始我也不相信自己能做出来。但一步步和AI配合，最后还真成了。当然，这种方式还在早期阶段，会有各种问题。但整体来说，确实能让更多人把想法变成现实。这就够了。[scode type="blue" size=""]这就是AI理解的Vibe Coding[/scode]