首页
壁纸
统计
友链
Search
1
Vue2详细笔记
37 阅读
2
Nestjs概述-中文
19 阅读
3
ExpressAPI
17 阅读
4
答辩
14 阅读
5
JavaScript企业数据处理实用指南
13 阅读
Nodejs
Vue
Java
Msql
登录
Search
Wasnl
累计撰写
36
篇文章
累计收到
1
条评论
首页
栏目
Nodejs
Vue
Java
Msql
页面
壁纸
统计
友链
搜索到
36
篇与
的结果
2026-05-25
Agentic Coding 第一性原理笔记
Agentic Coding 第一性原理笔记一、LLM 的本质大语言模型的工作方式可概括为:预测下一个 token。自回归生成过程:计算当前位置与上下文中每个位置的相关程度相关性高的位置获得更高权重,形成概率分布根据权重加权聚合上下文信息,预测下一个 token关键需求:提供清晰的成功标准(明确的奖励信号)允许试错,观察模型的决策模式二、Coding Agent 的典型工具文件操作:read_file、write_file、edit_file(支持局部编辑)代码执行:shell/terminal(运行命令、安装依赖、执行测试)代码搜索:grep/search(文本/模式搜索)、semantic_search(语义搜索)项目导航:list_directory、find_files工具的粒度、参数设计、返回格式直接决定 Agent 的能力边界。三、常见挑战与解决方案1. 会话间失忆现象:会话之间无持久记忆。方案:使用结构化任务追踪(如 issue tracker)会话结束生成状态摘要,供下次使用将重要决策和发现记录在固定位置(而非对话历史)2. 上下文窗口耗尽方案:精简工具输出,返回摘要或相关片段简化工具 schema,避免深度嵌套结构将大任务分解为子任务,每个使用新上下文3. 有效上下文远小于标称值即使 1M token 窗口,实际有效利用仅 10-15%。「Dumb Zone」:窗口中间 40-60% 区域召回率下降、推理变差(Lost in the Middle)。策略:将上下文利用率控制在 15% 以内可显著缓解。4. 新发现的任务被丢弃现象:LLM 发现问题但上下文紧张时选择忽略。方案:提供外部工具,让 Agent 随时记录发现任务结束时要求列出未处理的问题建立“发现即记录”的流程5. 过早宣告完成方案:将整体计划保存在上下文外的固定位置定期对照原始计划检查进度使用结构化任务追踪,而非依赖 Agent 记忆四、最佳实践1. 短对话优于长对话核心原则:保持对话简短、专注,每个对话只做一件事。以对话为工作单位:功能:用户登录后的会话管理 [对话1] 调研现有代码结构 → 输出关键文件列表 [对话2] 实现基础功能 → 核心 session 保存逻辑 [对话3] 添加错误处理 → 边界情况处理 [对话4] 编写测试 → 单元测试与集成测试 [对话5] 代码审查 → 检查规范与安全 [对话6] 清理与重构 → 根据审查调整对话间上下文共享:引用之前对话的结论利用 Git 状态(查看 diff 或提交记录)将决策记录在 AGENTS.md 等文件直接 #mention 相关文件不要在一个对话完成所有事;当任务完成或对话混乱时,立即开始新对话。2. 编写有效的项目配置文件回答三个问题:WHAT(技术栈/结构)、WHY(设计决策)、HOW(运行/测试命令)少即是多:前沿模型可靠遵循约 150-200 条指令,Agent 系统提示已占用约 50 条。✅ 简洁 + 指向详细文档❌ 塞满所有规范细节渐进式披露:agent_docs/ ├── building_the_project.md ├── running_tests.md ├── code_conventions.md ├── service_architecture.md └── database_schema.md偏好指针而非副本:用 file:line 引用权威代码位置,避免文档中嵌入过时代码片段。不要让 Agent 做 Linter 的工作:代码风格应交由自动化工具,不写进配置文件。花费时间仔细考量配置文件的每一行,是 ROI 最高的投资之一。3. 实践建议对话超过 80K-100K token → 开始新对话完成独立子任务后 → 开始新对话Agent 出现“醉酒”症状(重复、遗忘、偏离目标) → 立即开始新对话将“开始新对话”视为正常流程,而非失败重试五、Compounding Engineering(复利工程)核心理念:不只是在解决问题,而是在教育系统。每个 PR、bug、代码审查都应成为永久教训,更新 Agent 的默认行为。实践方式将经验沉淀到项目文档AGENTS.md 示例:## 代码风格 - 使用 async/await 而非 Promise.then() - 错误处理必须包含具体错误类型 - 变量命名遵循 PR #234 的模式 ## 已知陷阱 - session 模块的 save() 是异步的,必须 await - 不要在循环中调用 API,使用批量接口 ## 成功模式 - 新增 API 端点,参考 PR #241 的错误处理 - 测试覆盖率要求参考 PR #219 的反馈Bug 修复产生长期价值添加 lint 规则预防在 Rules/AGENTS.md 中记录陷阱编写测试防止回归更新代码审查清单代码审查中提取模式反馈是否适用于未来代码?→ 记录为编码规范Agent 下次能否自动应用?→ 固化知识建立可复用工作流程## 工作流程:添加新 API endpoint 1. 先编写接口测试(参考 tests/api/example.test.ts) 2. 实现端点,遵循 src/api/users.ts 的模式 3. 添加错误处理,使用 AppError 类 4. 更新 API 文档 5. 运行完整测试套件验证值得投资的开发者体验文档:架构决策记录(ADR)、API 使用示例、已知陷阱和常见错误代码结构:清晰命名、单一职责、显式依赖反馈速度:极致快速反馈,减少无效上下文堆积快速校验脚本:组合类型检查、lint、局部测试,几秒出结果就近文档:模块目录下放 README,Agent 直接读取结构化错误信息:报错带原因、示例、参考文档六、用工程约束“驯服”Agent1. 强制执行标准(pre-commit hook)#!/bin/bash # .git/hooks/pre-commit echo "Running type check..." npm run typecheck || exit 1 echo "Running linter..." npm run lint || exit 1 echo "Running tests..." npm test || exit 1 echo "All checks passed!"2. 拦截“偷懒”行为Agent 可能尝试 git commit --no-verify 绕过检查,使用 wrapper 脚本拦截并给出明确指引:$ git commit --no-verify ERROR: Commit Rejected. GUIDANCE FOR THE AI AGENT: You have attempted to bypass the required pre-commit verification. All code must pass quality checks before it can be committed. DO NOT BYPASS THE CHECKS. YOU MUST FIX THE UNDERLYING ERRORS. ...attempt the commit again *without* the '--no-verify' flag.3. 设计 AI 友好的错误信息格式:[错误代码] 错误原因 + 具体数据 + 怎么解决 + 文档位置❌ throw new Error("Something went wrong")✅ throw new Error("[AUTH_TOKEN_EXPIRED] 令牌已过期,过期时间:2024-01-15T10:30:00Z。请调用 refreshToken() 刷新。参考文档:docs/auth.md#token-refresh")
2026年05月25日
4 阅读
0 评论
0 点赞
2026-04-29
微信小程序
以下整理了一份生产环境微信小程序必备 API 及对应数据库表设计,覆盖登录鉴权、版本更新、性能监控、错误处理、容灾等核心模块。前端 API 均指微信原生能力,后端 API 为开发者服务器需要实现的接口。一、登录与用户身份1.1 前端 APIAPI说明wx.login()获取临时登录凭证 code,用于换取 openidwx.checkSession()检查当前登录态是否过期<button open-type="getPhoneNumber">获取用户手机号(需加密数据解密)wx.getUserProfile()获取用户头像昵称(必须用户手动点击允许)1.2 后端 API方法路径说明POST/api/user/login接收 code,调用微信 code2Session 换取 openid,生成自定义 token 返回POST/api/user/update-profile接收加密的手机号/用户信息,解密后更新用户资料GET/api/user/info通过 token 获取当前用户信息1.3 数据库表用户表 users字段类型说明idbigint(20)主键,自增openidvarchar(64)微信 openid,唯一索引unionidvarchar(64)微信 unionid(可选)nicknamevarchar(64)用户昵称avatar_urlvarchar(512)头像地址phonevarchar(20)手机号(加密存储)created_atdatetime创建时间updated_atdatetime更新时间登录凭证表 user_tokens字段类型说明idbigint(20)主键,自增user_idbigint(20)关联 users.idtokenvarchar(256)jwt tokenplatformtinyint(2)登录平台(微信小程序=1)expire_atdatetime过期时间created_atdatetime创建时间可选用 JWT 无状态方案,无需存储 token,但生产环境建议保留该表以支持强制下线、多设备管理。二、版本更新管理2.1 前端 APIAPI说明wx.getUpdateManager()管理小程序版本更新,提示用户重启2.2 后端 API(可选)方法路径说明GET/api/config/version返回最低要求版本号,前端可在启动时检查,若当前版本过低可引导升级或限制使用2.3 数据库表应用版本表 app_versions字段类型说明idint(11)主键,自增versionvarchar(20)版本号,如 "3.2.1"min_versionvarchar(20)最低兼容版本update_descvarchar(512)更新描述force_updatetinyint(1)是否强制更新 1=是 0=否platformvarchar(20)平台,如 "weapp"created_atdatetime创建时间即使有小程序自带的更新机制,此表仍可用于 首次冷启动时的版本拦截 或 后端对低版本用户做接口限制。三、网络状态监听3.1 前端 API(纯客户端,无后端接口)API说明wx.onNetworkStatusChange()监听网络切换(Wi-Fi / 4G / 无网络),触发界面提示或重连wx.onAppHide()应用切到后台,可手动关闭连接wx.onAppShow()应用切回前台,可重新校验登录态无需对应数据库表,但前端需在封装请求时根据网络状态做自动重试。四、性能监控与错误上报4.1 前端 APIAPI说明wx.getPerformance()获取性能对象wx.reportPerformance(id, cost)上报自定义性能指标(id 需先在后台配置)wx.onError()监听脚本错误wx.onUnhandledRejection()监听未处理的 Promise 拒绝wx.onPageNotFound()页面不存在监听wx.onLazyLoadError()异步组件加载失败监听4.2 后端 API方法路径说明POST/api/log/error接收客户端 JS 异常、网络请求错误等POST/api/log/performance接收页面加载耗时、接口耗时等性能数据4.3 数据库表错误日志表 error_logs字段类型说明idbigint(20)主键,自增user_idbigint(20)关联用户(可空)pagevarchar(256)错误发生的页面路径error_typevarchar(64)错误类型(JS错误、接口错误、资源错误)messagetext错误消息stacktext错误堆栈device_infojson设备信息(型号、系统、微信版本等)app_versionvarchar(20)小程序版本号occur_atdatetime发生时间created_atdatetime上报时间性能日志表 performance_logs字段类型说明idbigint(20)主键,自增user_idbigint(20)关联用户(可空)pagevarchar(256)页面路径metricvarchar(64)指标名(如 page_load_time)valueint(11)耗时(毫秒)extrajson扩展信息(网络类型等)app_versionvarchar(20)版本号created_atdatetime上报时间这两个表是生产环境问题排查的基础,必须建好并定期归档。五、通用配置与容灾5.1 后端 API方法路径说明GET/api/config/all拉取客户端需要的全局配置(如客服电话、公告、功能开关)5.2 数据库表配置表 configs字段类型说明idint(11)主键,自增keyvarchar(64)配置键,唯一valuetext配置值(支持 JSON)descriptionvarchar(256)描述updated_atdatetime更新时间典型配置项:maintenance_mode:是否开启维护模式force_update_version:强制更新最低版本contact_phone:客服电话home_banner:首页轮播图数据六、数据安全补充6.1 前端策略所有网络请求必须使用 https / wss敏感数据存储时使用 wx.setStorageSync 配合加密(或使用 wx.setStorage 的安全标记 encrypted)6.2 后端策略appsecret 严禁出现在前端代码,只能在服务端调用微信接口生成的 session_key 只能保存在服务端,不可返回给前端所有接口做好参数校验和限流6.3 对应数据库表(可选)密钥管理表 app_secrets(服务端内部)字段类型说明idint(11)主键app_idvarchar(32)小程序 appidsecretvarchar(128)appsecret(加密存储)token_expireint(11)自定义登录态有效时间(秒)created_atdatetime创建时间七、完整 API 清单总结优先级模块前端 API后端 API数据库表🔴 最高登录鉴权wx.login wx.checkSession getPhoneNumber/api/user/login /api/user/infousers user_tokens🔴 最高错误监控wx.onError onUnhandledRejection /onPageNotFound/api/log/errorerror_logs🟡 高版本更新wx.getUpdateManager/api/config/versionapp_versions🟡 高性能上报wx.reportPerformance getPerformance/api/log/performanceperformance_logs🟡 高网络监听onNetworkStatusChange onAppHide/Show——🟢 中全局配置—/api/config/allconfigs🟢 中数据安全——app_secrets以上内容足以支撑一个小程序的生产环境稳定运行。建议在 app.js 启动时统一完成如下初始化工作:拉取全局配置 + 版本检查校验登录态(有则续期,无则重新登录)注册全局错误监听注册网络变化监听启动更新管理器这样可确保小程序从启动到使用的全链路具备高可用性。
2026年04月29日
11 阅读
0 评论
0 点赞
2026-04-17
今早学习笔记
今早学习笔记(父子组件通信 + Vuex 核心)一、父子组件通信(企业级规范)核心原则:父业务 + 子UI,单向数据流核心逻辑:子组件只管表单渲染、交互,父组件只管业务、接口,不混乱、高复用1. 子组件(表单/弹窗)职责只负责:表单渲染、数据校验、弹窗显隐(自己管理状态)、收集表单数据不负责:不写任何API请求、不处理业务逻辑核心方法:- open\(row\):供父组件调用,打开弹窗(支持新增/编辑,row为编辑数据) - close\(\):自己关闭弹窗,重置表单 - submit\(\):表单校验通过后,通过 $emit 把数据抛给父组件 状态管理:弹窗isShow、表单formData,全部写在子组件data中,自己管理2. 父组件(列表页)职责负责:列表数据、接口请求(新增/编辑/删除)、页面刷新、成功/失败提示核心操作:- 通过 ref 调用子组件方法(open\(\)),打开弹窗 - 监听子组件 $emit 事件,接收表单数据,调用对应API - API请求成功后,刷新列表,给出提示 3. 通信方式(标准写法)父 → 子:ref 调用子组件方法(如:this.$refs.formRef.open())子 → 父:$emit 抛数据(如:this\.$emit(\&\#39;submit\&\#39;, formData))禁忌:子组件不能直接修改props,不能写API,父组件不插手子组件内部状态4. 示例核心代码(回顾)// 子组件:只抛数据,不调API handleSubmit() { this.$refs.form.validate(valid => { if (valid) { this.$emit('submit', this.formData); // 抛数据给父组件 this.close(); } }); } // 父组件:接收数据,调API async handleSubmit(formData) { try { formData.id ? await updateApi(formData) : await addApi(formData); this.$Message.success('操作成功'); this.getList(); // 刷新列表 } catch (e) { this.$Message.error('操作失败'); } }二、API交互规范(企业级)核心原则:API 按“全局/页面”划分存放,不滥用1. API 存放位置(关键区分)API类型存放位置典型例子原因全局公共APIVuex 的 action 中获取用户信息、权限、菜单、登录、登出全局共享,调用后需更新Vuex全局状态,多页面复用页面业务API父组件中列表查询、新增/编辑用户、修改密码、删除仅当前页面使用,数据只渲染当前页面,不共享组件私有API子组件中(极少)仅子组件自身使用的极小接口不影响其他组件,无需全局/父组件管理2. 禁忌禁止子组件写业务API(如新增、修改密码),影响复用性禁止把页面私有API放进Vuex,导致store臃肿、混乱禁止API调用后不处理异常(需加try/catch,给出提示)三、Vuex 核心内容(重点中的重点)核心定位:全局状态管理中心,解决跨组件共享数据、多层通信问题1. Vuex 5大核心关键词(定义+作用+示例)(1)State定义:Vuex 的数据源,存储全局共享数据(唯一数据源)通俗说:全局的 data,所有组件都能读取(不能直接修改)示例: `const state = \{userInfo: {}, // 全局用户信息permissions: [] // 全局权限码}`易错点:不能直接修改 state(如:this.$store.state.userInfo = {} 错误)(2)Mutation定义:唯一能修改 State 的地方,必须是同步函数(不能写异步)作用:同步修改全局状态,接收 state 和 自定义参数示例: `const mutations = \{// state:Vuex自动传入;data:commit时传入的参数SET\_USER\_INFO(state, data) { state.userInfo = data; // 唯一正确修改state的方式}}`规则:只能同步,不能写API、定时器(3)Action定义:处理异步操作(API请求)、复杂业务逻辑,不直接修改state作用:调API,拿到数据后,通过 commit 调用 Mutation 修改state核心语法:第一个参数是 context 上下文对象,可解构简化 `// 解构写法(ES6,推荐)async getUserInfo({ commit }) {const res = await getUserInfoApi(); // 异步调APIcommit(\&\#39;SET\_USER\_INFO\&\#39;, res); // 调用mutation改state}// 不解构写法(兼容ES5)async getUserInfo(context) { const res = await getUserInfoApi(); context.commit(\&\#39;SET\_USER\_INFO\&\#39;, res); // 完整写法}`补充:Action 也能处理同步逻辑,还能通过 dispatch 调用其他Action(4)Getter定义:Vuex 的全局计算属性,对 state 数据进行加工、过滤作用:派生数据,不修改原state,多组件复用相同计算逻辑示例: `// 全局getters\.jsconst getters = {userId: state =\> state.user.userInfo.user\_id, // 派生用户IDhasPerm: state =\> (permCode) =\> { // 派生权限判断方法 return state.user.permissions.includes(permCode);}}`(5)Module定义:Vuex 模块化拆分,按业务分模块管理(避免代码臃肿)核心:必须开启 namespaced: true(命名空间),防止模块间命名冲突示例(user模块): `export default \{namespaced: true, // 开启命名空间(企业级必开)state: {},mutations: {},actions: {}}`模块合并:在 store/index.js 中导入所有子模块,合并到 modules 中2. 关键调用关键词(commit / dispatch)关键词使用位置调用对象作用示例commitAction 内部(主要)、组件(极少)Mutation触发同步修改statecommit(\&\#39;user/SET\_USER\_INFO\&\#39;, res)dispatch组件(主要)、Action 内部Action触发异步操作/业务逻辑this.$store.dispatch(\&\#39;user/getUserInfo\&\#39;)3. 组件中使用 Vuex(map系列辅助函数)核心原则:数据类(state、getters)放 computed;方法类(mutations、actions)放 methods示例(完整写法): `import \{ mapState, mapGetters, mapMutations, mapActions \} from \&\#39;vuex\&\#39; export default { computed: {// 子模块state(带命名空间,必须写模块名) \.\.\.mapState\(\&\#39;user\&\#39;, \[\&\#39;userInfo\&\#39;, \&\#39;permissions\&\#39;\]\), // 全局getters(不用写模块名) \.\.\.mapGetters\(\[\&\#39;userId\&\#39;, \&\#39;hasPerm\&\#39;\]\)}, methods: {// 子模块mutations(带命名空间) \.\.\.mapMutations\(\&\#39;user\&\#39;, \[\&\#39;SET\_USER\_INFO\&\#39;\]\), // 子模块actions(带命名空间) \.\.\.mapActions\(\&\#39;user\&\#39;, \[\&\#39;getUserInfo\&\#39;, \&\#39;logout\&\#39;\]\), // 调用方式(映射后直接调用) login\(\) \{ this\.getUserInfo\(\); // 等价于 this\.$store\.dispatch\(\&\#39;user/getUserInfo\&\#39;\) this\.SET\_USER\_INFO\(data\); // 等价于 this\.$store\.commit\(\&\#39;user/SET\_USER\_INFO\&\#39;, data\) \}}}`易错点:- mapState/mapGetters 不能放 methods 里,反之亦然 - 带命名空间的模块,映射时必须写模块名(如 \&\#39;user\&\#39;) 4. 多参数传递(Vuex 官方规则)规则:Mutation / Action 只能接收 1 个自定义参数多参数解决方案:打包成一个对象传递 `// Action 中 commit(多参数打包)commit(\&\#39;SET\_USER\_INFO\&\#39;, {userInfo: res,test: \&\#39;测试数据\&\#39;})// Mutation 中接收(解构对象)SET\_USER\_INFO(state, { userInfo, test }) { state.userInfo = userInfo; state.testData = test;}`5. 兼容写法(不支持ES6/ES2020)现代语法(ES6+)兼容写法(ES5)解构 { commit }context.commit(不解构,完整调用)obj?.a?.b(可选链)obj \&\& obj.a \&\& obj.b(逻辑与替代)SET\_USER\_INFO(state, { a, b })SET\_USER\_INFO(state, payload) → const a = payload.a补充:实际开发中用 Babel 自动转译,不用手写兼容代码6. Vuex 模块化目录结构(企业级标准)src/store/ ├── index.js # 入口文件,合并所有子模块、挂载Vuex ├── getters.js # 全局getter汇总 └── modules/ # 业务模块拆分 ├── user.js # 用户模块(信息、权限) ├── permission.js # 权限模块(菜单、按钮权限) └── layout.js # 布局模块(侧边栏、全屏)四、核心易错点汇总(重点记忆)调用Action:this.$store\.dispatch\(\)(不是 $state,dispatch 不拼写错误)Mutation 只能同步,Action 可异步,不能搞反修改state必须通过Mutation,不能直接修改Vuex模块必须开启 namespaced: true,否则会命名冲突map系列辅助函数:数据类放computed,方法类放methods多参数必须打包成对象,Vuex不支持多个自定义参数API存放:全局公共API放Vuex,页面业务API放父组件五、整体核心流程(串联所有知识点)组件点击操作(如打开弹窗)→ 通过 ref 调用子组件 open() 方法子组件完成表单交互、校验 → 通过 $emit 把数据抛给父组件父组件接收数据 → 调用页面业务API(新增/编辑等)→ 提示+刷新列表若涉及全局数据(如用户信息)→ 组件 dispatch 调用 Vuex 的 ActionAction 调全局API → 拿到数据后 commit 调用 MutationMutation 同步修改 state → 组件通过 mapState/mapGetters 读取数据,视图更新通用企业级设计思路:父业务 + 子 UI 表单弹窗模式这套思路适用于所有后台管理系统:新增弹窗、编辑弹窗、表单抽屉、密码框、配置面板等,只要是「列表 + 表单弹窗」的结构,都能直接套用。全程遵守:单向数据流、职责分离、高复用、无全局污染,是国内互联网公司后台系统的标准范式。一、核心设计原则(4 条铁律)子组件只做 UI,不碰业务子组件 = 纯渲染组件:只负责表单、校验、弹窗开关、收集数据,绝不写接口。父组件只做业务,不碰渲染细节父组件 = 业务控制器:负责列表、接口请求、状态管理、刷新页面。通信只走两条路父 → 子:ref 调用方法子 → 父:$emit 抛数据谁的状态谁管理弹窗显隐、表单数据,全部由子组件自己管理,父组件不插手内部状态。二、通用职责划分(背会直接用)🔹 子组件(Form 弹窗)负责:弹窗显示 / 隐藏(isShow 写在自己 data)表单数据(formData)表单校验规则打开 / 重置 / 关闭方法表单校验通过后,把数据抛给父组件不写任何 API、不请求后端🔹 父组件(List 列表页)负责:表格数据、分页、查询点击按钮 → 通过 ref 打开子组件接收子组件抛来的表单数据调用新增 / 编辑 / 删除接口接口成功 / 失败提示刷新列表数据三、通用开发步骤(万能流程)拆分组件一个业务弹窗 = 一个独立子组件新增用户 → UserAddForm.vue编辑密码 → LoginPwdForm.vue配置参数 → ConfigForm.vue编写子组件(固定结构)写弹窗(Modal/Drawer)写表单 + 校验规则data 里定义:isShow、formData、loading写公共方法:open(row)、close()、submit()校验通过后 $emit('submit', formData)父组件引入并挂载import 子组件注册 components模板中放入子组件,绑定 ref 和 @submit点击按钮调用 this.$refs.xxx.open()父组件处理业务逻辑接收子组件数据调用对应 API成功提示 + 刷新列表失败提示
2026年04月17日
6 阅读
0 评论
0 点赞
2026-04-10
JUnit 5 单元测试完整笔记
JUnit 5 单元测试完整笔记一、JUnit 5 概述JUnit 5 是 Java 生态中最主流的单元测试框架,提供丰富的注解、断言和扩展机制。1.1 框架构成(三个子项目)子项目说明JUnit Platform在 JVM 上启动测试框架的基础平台,定义了 TestEngine APIJUnit Jupiter包含 JUnit 5 全新的编程模型和扩展机制JUnit Vintage提供对 JUnit 3 和 JUnit 4 测试的兼容运行支持二、核心测试注解JUnit 5 通过注解控制测试执行流程与行为。注解说明使用场景@Test标记一个方法为测试方法基本单元测试@BeforeEach在每个测试方法执行前运行测试数据准备、对象初始化@AfterEach在每个测试方法执行后运行资源清理、状态重置@BeforeAll在当前测试类的所有测试方法执行前运行一次(需为 static)类级别初始化(如建立数据库连接)@AfterAll在所有测试方法执行后运行一次(需为 static)类级别资源释放@DisplayName为测试类或方法自定义易读的显示名称提升测试报告的可读性@Disabled临时禁用某个测试方法或类跳过尚未实现或需要暂时忽略的测试@ParameterizedTest声明参数化测试方法使用多组数据驱动同一测试逻辑@RepeatedTest重复执行指定次数的测试验证方法稳定性或简单压力测试@Timeout指定测试方法执行超时时间性能验证、防止死循环三、断言方法(Assertions)JUnit 5 提供多种断言来验证测试结果。方法用途assertEquals(expected, actual)断言期望值与实际值相等assertTrue(condition) / assertFalse(condition)断言条件为真 / 假assertThrows(ExceptionType.class, executable)断言执行代码会抛出指定异常assertAll(executables...)组合多个断言,所有断言都会执行再报告失败(避免因一个失败而中断后续验证)四、参数化测试数据源通过 @ParameterizedTest 结合数据源注解,用多组数据测试同一逻辑。注解说明@ValueSource提供简单值数组(如 ints = {1, 2, 3})@CsvSource提供 CSV 格式的字符串数据,支持多列参数@MethodSource引用一个返回 Stream / Iterable 的静态方法提供测试数据@EnumSource使用枚举类的所有值或指定子集作为测试数据五、测试编写原则与最佳实践5.1 好的测试实践(推荐)✅ 示例代码@Test @DisplayName("当输入两个正数时应该返回正确的和") void shouldReturnSumWhenAddingTwoPositiveNumbers() { // Arrange - 准备测试数据 Calculator calculator = new Calculator(); // Act - 执行被测试的方法 int result = calculator.add(2, 3); // Assert - 验证结果 assertEquals(5, result); }✅ 核心原则AAA 模式:Arrange(准备) → Act(执行) → Assert(断言)命名清晰:测试方法名应具有描述性,表达“测什么”和“预期结果”使用 @DisplayName:进一步提高测试报告可读性单一职责:每个测试方法只验证一个功能点覆盖边界条件与异常:不仅要测试正常路径,还要测试空值、零值、负数、越界等异常场景5.2 应该避免的实践(反模式)❌ 反面示例@Test void test1() { // 测试多个不相关的功能 assertEquals(5, calculator.add(2, 3)); assertEquals(6, calculator.multiply(2, 3)); assertEquals("hello", stringUtils.reverse("olleh")); }❌ 错误做法测试方法名无意义(如 test1、testAdd)一个测试方法中验证多个不相关的功能点测试之间存在执行顺序或数据依赖忽略边界条件与异常分支测试代码逻辑过于复杂或包含大量重复代码六、常用测试类型类型说明基本功能测试验证方法在正常输入下的预期行为边界值测试测试输入为 null、空字符串、0、最大值、最小值等边界情况异常测试使用 assertThrows 验证错误输入时是否正确抛出异常参数化测试使用多组数据驱动同一测试逻辑,减少重复代码性能测试结合 @Timeout 或性能测试框架验证执行效率七、测试编写注意事项测试独立性:每个测试应能单独运行,不依赖其他测试的执行结果或执行顺序。数据自包含:测试数据应在测试方法内部准备,避免使用共享可变全局状态。断言消息:为关键断言添加描述性消息,便于快速定位失败原因。资源清理:在 @AfterEach 或 @AfterAll 中释放资源(如文件流、数据库连接)。代码简洁:保持测试代码易读、易维护,必要时可提取私有辅助方法。八、测试覆盖率目标指标建议值整体代码覆盖率≥ 80%核心业务逻辑重点覆盖,尽量达到 90% 以上公共方法所有 public 方法均应有对应测试异常分支与边界条件必须包含相关测试用例覆盖率工具使用 JaCoCo 等工具生成报告并持续监控笔记总结完毕。 按照以上结构和内容复习,即可系统掌握 JUnit 5 的使用方法与单元测试最佳实践。
2026年04月10日
5 阅读
0 评论
0 点赞
2026-04-10
Redis 核心数据类型与命令参考手册
Redis 核心数据类型与命令参考手册一、 Redis 数据类型概览数据类型核心特性适用场景字符串 (String)二进制安全,最大512MB,支持原子计数操作。缓存用户信息、计数器(访问量、点赞)、分布式锁、会话存储。哈希 (Hash)键值对集合,适合存储对象,节省内存。每个哈希可存 2^32 - 1 个字段。存储用户/商品信息、配置管理、购物车数据。列表 (List)有序字符串列表,支持双端操作,最多可存 2^32 - 1 个元素。消息队列、最新动态列表、任务队列、历史记录。集合 (Set)无序且唯一的字符串集合,支持集合运算(交、并、差)。标签系统、好友关系、权限管理、去重处理。有序集合 (Sorted Set)元素唯一,按分数(Score)排序,支持范围查询。排行榜、优先级队列、时间线、价格区间查询。二、 通用命令与规范1. 命令语法规范大小写:命令名称不区分大小写(建议大写)。分隔符:参数之间用空格分隔。参数表示:[ ] 表示可选参数,| 表示多选一。2. 键(Key)的命名规范风格:使用冒号 : 作为分隔符,采用层次化结构。示例:user:1001:profile、cache:article:1001、lock:order:1001。原则:使用有意义的名称,保持一致的命名风格,避免过长的键名。3. 键的通用操作命令命令描述示例EXISTS key检查键是否存在EXISTS user:1001TYPE key返回键的数据类型TYPE user:1001DEL key [key ...]删除一个或多个键DEL user:1001 user:1002RENAME key newkey重命名键RENAME oldname newnameEXPIRE key seconds设置键的过期时间(秒)EXPIRE session:abc123 3600TTL key查看键的剩余生存时间(秒)TTL session:abc123PERSIST key移除键的过期时间PERSIST session:abc123KEYS pattern查找匹配模式的键 (生产环境慎用,会阻塞)KEYS user:*SCAN cursor [MATCH pattern]非阻塞式迭代数据库中的键SCAN 0 MATCH user:* COUNT 10DBSIZE返回当前数据库的键数量DBSIZE三、 数据类型详解与命令参考1. 字符串 (String)基本操作命令描述示例SET key value设置字符串值SET name "Redis"GET key获取字符串值GET nameAPPEND key value追加字符串到末尾APPEND name " Database"STRLEN key获取字符串长度STRLEN name数值操作(原子性)命令描述示例INCR key将数值递增 1INCR page:viewsINCRBY key increment将数值增加指定值INCRBY user:1001:points 100DECR key将数值递减 1DECR counterDECRBY key decrement将数值减少指定值DECRBY user:1001:points 50批量与条件操作命令描述示例MSET k1 v1 k2 v2批量设置多个键值对MSET k1 v1 k2 v2MGET k1 k2批量获取多个键的值MGET k1 k2SETNX key value仅当键不存在时设置 (常用于分布式锁)SETNX lock:order "locked"SETEX key seconds value设置值并指定过期时间SETEX session:abc 3600 "data"SET key value EX seconds NX组合命令:带过期时间的锁SET lock:key "val" EX 30 NX2. 哈希 (Hash)基本操作命令描述示例HSET key field value设置哈希字段的值HSET user:1001 name "张三"HGET key field获取哈希字段的值HGET user:1001 nameHDEL key field删除哈希字段HDEL user:1001 temp_fieldHLEN key获取哈希字段数量HLEN user:1001HKEYS key获取所有字段名HKEYS user:1001HVALS key获取所有字段值HVALS user:1001HGETALL key获取所有字段和值 (慎用,大哈希会阻塞)HGETALL user:1001批量与数值操作命令描述示例HMSET key f1 v1 f2 v2批量设置多个字段HMSET user:1002 name "李四" age 30HMGET key f1 f2批量获取多个字段的值HMGET user:1002 name ageHINCRBY key field increment将字段值增加指定整数HINCRBY user:1001 login_count 1HSCAN key cursor MATCH pattern迭代哈希中的字段HSCAN user:1001 0 MATCH "*count*"3. 列表 (List)命令描述示例LPUSH key value [value ...]从左侧(头部)插入元素LPUSH mylist "world" "hello"RPUSH key value [value ...]从右侧(尾部)插入元素RPUSH mylist "redis"LPOP key从左侧弹出元素LPOP mylistRPOP key从右侧弹出元素RPOP mylistLLEN key获取列表长度LLEN mylistLRANGE key start stop获取指定范围元素LRANGE mylist 0 2LTRIM key start stop保留指定范围的元素(修剪列表)LTRIM mylist 0 99BLPOP key timeout阻塞式左端弹出(队列用)BLPOP queue1 30BRPOP key timeout阻塞式右端弹出BRPOP queue1 304. 集合 (Set)命令描述示例SADD key member [member ...]添加元素到集合SADD tags "tech" "redis"SREM key member从集合中删除元素SREM tags "tech"SISMEMBER key member检查元素是否存在SISMEMBER tags "redis"SMEMBERS key获取集合所有元素 (慎用,大集合会阻塞)SMEMBERS tagsSCARD key获取集合元素数量SCARD tagsSINTER key1 key2计算集合交集SINTER set1 set2SUNION key1 key2计算集合并集SUNION set1 set2SDIFF key1 key2计算集合差集 (key1 - key2)SDIFF set1 set2SSCAN key cursor MATCH pattern迭代集合中的元素SSCAN tags 0 MATCH "user:*"5. 有序集合 (Sorted Set)命令描述示例ZADD key score member [score member ...]添加元素及分数ZADD leaderboard 100 "player1"ZREM key member删除元素ZREM leaderboard "player1"ZSCORE key member获取元素的分数ZSCORE leaderboard "player1"ZINCRBY key increment member增加元素的分数ZINCRBY leaderboard 10 "player1"ZCARD key获取集合元素总数ZCARD leaderboard范围查询 ZRANGE key start stop [WITHSCORES]按排名升序获取 (0为第1名)ZRANGE leaderboard 0 2 WITHSCORESZREVRANGE key start stop [WITHSCORES]按排名降序获取ZREVRANGE leaderboard 0 2ZRANGEBYSCORE key min max按分数范围升序获取ZRANGEBYSCORE leaderboard 80 100ZRANK key member获取元素升序排名 (从0开始)ZRANK leaderboard "player1"ZREVRANK key member获取元素降序排名ZREVRANK leaderboard "player1"删除与运算 ZREMRANGEBYRANK key start stop按排名范围删除元素ZREMRANGEBYRANK leaderboard 0 2ZREMRANGEBYSCORE key min max按分数范围删除元素ZREMRANGEBYSCORE leaderboard 0 60ZUNIONSTORE dest numkeys key [key ...]计算并集并存储ZUNIONSTORE result 2 set1 set2ZINTERSTORE dest numkeys key [key ...]计算交集并存储ZINTERSTORE result 2 set1 set2四、 补充:HyperLogLog (基数估算)一种概率性数据结构,用于在极小的内存(12KB)下估算海量数据的去重元素数量(基数),误差率约 0.81%。命令描述PFADD key element [element ...]添加元素PFCOUNT key返回估算的基数PFMERGE destkey sourcekey [sourcekey ...]合并多个 HyperLogLog五、 性能与选择建议键管理:生产环境严禁使用 KEYS,改用 SCAN;大集合遍历用 SSCAN/HSCAN/ZSCAN。批量操作:优先使用 MSET/MGET、HMSET/HMGET 减少网络开销。过期时间:缓存数据务必设置 EXPIRE,防止内存无限增长。数据类型选型:需要去重且无序:Set。需要排序且唯一:Sorted Set。需要有序但允许重复:List。存储对象且需单独更新字段:Hash。简单缓存/计数器:String。
2026年04月10日
6 阅读
0 评论
0 点赞
1
2
3
4
...
8